685 lines
21 KiB
C++
685 lines
21 KiB
C++
|
|
uint32_t colourTest;
|
|
|
|
/*
|
|
* Initialise variables and SI4432 for the low frequency sweep
|
|
*/
|
|
void initBandscope()
|
|
{
|
|
// set up checkerboard sizes
|
|
waterfallHeight = WATERFALL_HEIGHT;
|
|
gridHeight = SCREEN_HEIGHT - waterfallHeight - 10;
|
|
gridWidth = SCREEN_WIDTH;
|
|
yGrid = Y_GRID; // no of grid divisions
|
|
yDelta = gridHeight / yGrid; // no of points/division
|
|
xGrid = X_GRID;
|
|
xOrigin = 0;
|
|
yOrigin = 0;
|
|
displayPoints = setting.BandscopePoints;
|
|
xDelta = SCREEN_WIDTH / xGrid;
|
|
|
|
ClearDisplay ();
|
|
/*
|
|
* Set up the "img" Sprite. This is the image for the graph. It makes for faster display
|
|
* updates and less flicker.
|
|
*
|
|
* 16 bit colour depth is faster than 8 and much faster than 4 bit! BUT - sprites
|
|
* pushed to it do not have correct colour - 8 bit and it is fine.
|
|
*
|
|
* All marker sprites are WHITE for now.
|
|
*/
|
|
tft.unloadFont();
|
|
|
|
img.unloadFont();
|
|
img.deleteSprite();
|
|
|
|
img.setTextSize ( 1 );
|
|
img.setColorDepth ( 16 );
|
|
img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
|
|
img.createSprite ( 2, gridHeight + 1 ); // Only 2 columns wide
|
|
|
|
|
|
/*
|
|
* The "tSprite" is used for displaying the data above the scan grid - we don't use it in this mode
|
|
* The "sSprite" is for displaying the sidebar stuff, but reused here for the waterfall
|
|
*/
|
|
|
|
tSprite.deleteSprite();
|
|
|
|
sSprite.deleteSprite();
|
|
sSprite.setColorDepth (16); // we don't need 16bit but its faster
|
|
sSprite.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
|
|
sSprite.createSprite ( gridWidth, waterfallHeight ); // Full width
|
|
sSprite.setScrollRect(0, 0, gridWidth, waterfallHeight, TFT_BLACK);
|
|
/*
|
|
* Create and draw the sprite for the gain scale
|
|
*/
|
|
CreateGridScale ();
|
|
|
|
// Make sure everything will be reset
|
|
old_settingAttenuate = -1000;
|
|
old_settingPowerGrid = -1000;
|
|
old_settingMax = -1;
|
|
old_settingMin = -1;
|
|
old_startFreq = -1;
|
|
old_stopFreq = -1;
|
|
old_ownrbw = -1;
|
|
old_vbw = -1;
|
|
old_settingAverage = -1;
|
|
old_settingSpur = -100;
|
|
old_bandwidth = 0;
|
|
|
|
SetRX ( 0 ); // LO to transmit, RX to receive
|
|
|
|
xmit.SetDrive ( setting.Drive ); // Set transmitter power level
|
|
rcvr.SetPreampGain ( setting.PreampGain );
|
|
|
|
sweepStartDone = false; // Make sure this initialize is only done once per sweep
|
|
initSweep = true;
|
|
|
|
tinySA_mode = BANDSCOPE;
|
|
setting.Mode = tinySA_mode;
|
|
ResetBandscopeMenuStack(); // Put menu stack back to root level
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* This function section handles the fast bandscope sweep
|
|
* The display is split and shows a waterfall.
|
|
* The number of points is reduced, and frequency change is done using an offset to allow the
|
|
* delay time between changing frequency and taking a reading to be reduced.
|
|
*
|
|
* Frequency scheme:
|
|
* When the LO frequency is < 480MHz the LO can be adjusted +- 80kHz from the
|
|
* nominal frequency in 156.25Hz steps, ie +- 512 steps.
|
|
* Actually this is not possible! +-511 steps is
|
|
*
|
|
*
|
|
* If the LO is above 480MHz the the adjustment is +-160kHz in 312.5Hz steps.
|
|
* If the IF is 434MHz then 480MHz -> 46Mhz for the signal being analysed, so fine
|
|
* for most of the HF bands.
|
|
*
|
|
* In bandscope mode the RBW is fixed at the minimum 2.6kHz, span at 200kHz and
|
|
* there are 80 data points
|
|
*
|
|
* 200kHz -> 2.5kHz steps between each reading or 16 * 156.25Hz if in low band
|
|
*
|
|
* Start by setting the LO to the frequency for the start of the sweep plus 80kHz
|
|
* and set the offset value at -80kHz.
|
|
* At each reading increment the offset value by 16 (8 in high band).
|
|
* In this mode the delay time between reading is set at a shorter value than
|
|
* normally used by the RBW as the LO does not turn off at each change in offset, unlike
|
|
* a normal frequency change.
|
|
* When the offset value reaches +80kHz then we need to reset the LO (using normal delaytime)
|
|
* and continue until we get to the end of the sweep.
|
|
*
|
|
* Due to the limitation of not being able to do +-512, or +-16 stesp, we will use +-31 steps per frequency
|
|
* jump instead
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
void doBandscope()
|
|
{
|
|
static uint32_t autoSweepStep = 0;
|
|
static uint32_t autoSweepFreq = 0;
|
|
static uint32_t autoSweepFreqStep = 0;
|
|
static unsigned long setFreqMicros;
|
|
static unsigned long nowMicros;
|
|
static unsigned long bandscopeDelay;
|
|
|
|
|
|
static int16_t pointMinGain; // to record minimum gain for the current display point
|
|
static int16_t pointMaxRSSI; // to record max RSSI of the samples in the current display point
|
|
static uint32_t pointMaxFreq; // record frequency where maximum occurred
|
|
|
|
static int16_t lastMode; // Record last operating mode (sig gen, normal)
|
|
|
|
static uint16_t minRSSI = 255; // Minimum level for the sweep
|
|
static uint16_t lastMinRSSI; // Minimum level for the previous sweep
|
|
|
|
static bool resetAverage; // Flag to indicate a setting has changed and average valuesneeds to be reset
|
|
|
|
static bool jsonDocInitialised = false;
|
|
static uint16_t chunkIndex;
|
|
|
|
|
|
|
|
/*
|
|
* If paused and at the start of a sweep then do nothing
|
|
*/
|
|
if (!sweepStartDone && paused)
|
|
return;
|
|
|
|
|
|
/*
|
|
* If the "sweepStartDone" flag is false or if the "initSweep" flag is true, we need
|
|
* to set things up for the sweep.
|
|
*/
|
|
|
|
|
|
if (( !sweepStartDone || initSweep || changedSetting ) )
|
|
{
|
|
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things
|
|
{
|
|
sweepPoints = setting.BandscopePoints;
|
|
autoSweepFreqStep = ( setting.BandscopeSpan ) / sweepPoints;
|
|
offsetFreqIncrement = autoSweepFreqStep; // 2500 Hz for 200kHz span, 80 points per sweep
|
|
|
|
bandwidth = rcvr.SetRBW ( setting.BandscopeRBW10, &delaytime ); // Set it in the receiver Si4432
|
|
|
|
//Serial.printf("set rcvr Freq get:%u, tempIF:%u\n", rcvr.GetFrequency(), tempIF);
|
|
rcvr.SetFrequency ( setting.IF_Freq ); // Set the RX Si4432 to the IF frequency
|
|
|
|
|
|
// sweepFreqStep = autoSweepFreqStep; // Step for each reading
|
|
|
|
if ( setting.Attenuate != old_settingAttenuate )
|
|
{
|
|
if ( !att.SetAtten ( setting.Attenuate )) // Set the internal attenuator
|
|
setting.Attenuate = att.GetAtten (); // Read back if limited (setting.Attenuate was outside range)
|
|
old_settingAttenuate = setting.Attenuate;
|
|
}
|
|
|
|
resetAverage = changedSetting;
|
|
|
|
maxGrid = setting.BandscopeMaxGrid;
|
|
minGrid = setting.BandscopeMinGrid;
|
|
|
|
|
|
#ifdef USE_WIFI
|
|
// Vary number of points to send in each chunk depending on delaytime
|
|
// A chunk is sent at the end of each sweep regardless
|
|
wiFiPoints = wiFiTargetTime / delaytime;
|
|
if (wiFiPoints > MAX_WIFI_POINTS)
|
|
wiFiPoints = MAX_WIFI_POINTS;
|
|
if (wiFiPoints > setting.BandscopePoints)
|
|
wiFiPoints = setting.BandscopePoints;
|
|
// Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
|
|
|
|
if ( numberOfWebsocketClients > 0 )
|
|
pushBandscopeSettings ();
|
|
#endif // #ifdef USE_WIFI
|
|
|
|
} // initSweep || changedSetting
|
|
|
|
autoSweepStep = 0; // Set the step counter to zero
|
|
autoSweepFreq = setting.BandscopeStart; // Set the start frequency.
|
|
|
|
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write
|
|
{
|
|
}
|
|
|
|
resetOffsets();
|
|
|
|
// set the offset value in the SI4432
|
|
xmit.SetOffset(offsetValue);
|
|
|
|
setFreqMicros = micros(); // Store the time the frequency was changed
|
|
|
|
// set the LO frequency (offsetFreq is -ve at start!)
|
|
uint32_t xmitFreq = setting.IF_Freq + autoSweepFreq - offsetFreq;
|
|
// Serial.printf("XmitFreq %i\n", xmitFreq);
|
|
xmit.SetFrequency ( xmitFreq );
|
|
// delay will vary depending on whether or not the nominal frequency is changed
|
|
bandscopeDelay = delaytime; // long delay
|
|
|
|
|
|
#ifdef USE_WIFI
|
|
|
|
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
|
|
{
|
|
jsonDocument.clear ();
|
|
chunkIndex = 0;
|
|
|
|
jsonDocument["PreAmp"] = setting.PreampGain;
|
|
jsonDocument["mType"] = "chunkSweep";
|
|
jsonDocument["StartIndex"] = 0;
|
|
jsonDocument["sweepPoints"] = sweepPoints;
|
|
jsonDocument["sweepTime"] = (uint32_t)(sweepMicros/1000);
|
|
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
|
|
jsonDocInitialised = true;
|
|
}
|
|
|
|
else
|
|
jsonDocInitialised = false;
|
|
|
|
#endif // #ifdef USE_WIFI
|
|
|
|
// sweepStep = 0;
|
|
startFreq = setting.BandscopeStart + setting.IF_Freq; // Start freq for the LO
|
|
stopFreq = setting.BandscopeSpan + startFreq; // Stop freq for the LO
|
|
|
|
// Serial.printf(" start %i; stop %i; points %i \n", startFreq, stopFreq, sweepPoints );
|
|
|
|
lastMinRSSI = minRSSI;
|
|
minRSSI = 255; // real value should always be less
|
|
|
|
DisplayBandscopeInfo (); // Display axis and other info
|
|
|
|
sweepStartDone = true; // Make sure this initialize is only done once per sweep
|
|
initSweep = false;
|
|
changedSetting = false;
|
|
|
|
lastSweepStartMicros = sweepStartMicros; // Set last time we got here
|
|
sweepStartMicros = micros(); // Current time
|
|
sweepMicros = sweepStartMicros - lastSweepStartMicros; // Calculate sweep time (no rollover handling)
|
|
|
|
} // End of "if ( !sweepStartDone ) || initSweep || changedSetting )"
|
|
|
|
|
|
/*
|
|
* Here we do the actual sweep. Save the current step and frequencies for the next time
|
|
* through, then wait the required amount of time based on the RBW before taking the
|
|
* signal strength reading and changing the transmitter (LO) frequency.
|
|
*/
|
|
|
|
uint16_t oldSweepStep = autoSweepStep;
|
|
uint32_t oldSweepFreq = autoSweepFreq;
|
|
|
|
/*
|
|
* Wait until time to take the next reading. If a long wait then check the touchscreen
|
|
* and Websockets while we are waiting to improve response
|
|
*/
|
|
nowMicros = micros();
|
|
|
|
while (( nowMicros - setFreqMicros ) < bandscopeDelay )
|
|
{
|
|
|
|
if ( ( nowMicros - setFreqMicros + delaytime > 200 ) &&
|
|
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
|
|
{
|
|
webSocket.loop (); // Check websockets - includes Yield() to allow other tasks to run
|
|
lastWebsocketMicros = nowMicros;
|
|
}
|
|
if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish!
|
|
UiProcessTouch (); // Check the touch screen
|
|
nowMicros = micros();
|
|
}
|
|
|
|
int rxRSSI = rcvr.GetRSSI (); // Read the RSSI from the RX SI4432
|
|
|
|
/*
|
|
* Note that there are two different versions of the print statement to send the
|
|
* RSSI readings to the serial output. You can change which one is commented out.
|
|
*
|
|
* The first one produces a tab separated list of just the frequency and RSSI
|
|
* reading. That format can be easily read inte something like Excel.
|
|
*
|
|
* The second one produces a listing more fit for human consumption!
|
|
*/
|
|
|
|
if ( showRSSI ) // Displaying RSSI?
|
|
{
|
|
// Serial.printf ( "%s\t%03d\n",
|
|
// FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
|
|
Serial.printf ( "Freq: %s - RSSI: %03d\n",
|
|
FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
|
|
}
|
|
|
|
if ( (numberOfWebsocketClients > 0) || (setting.ShowGain) )
|
|
gainReading = GetPreampGain ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains
|
|
|
|
autoSweepFreq += autoSweepFreqStep; // Increment the frequency
|
|
autoSweepStep++; // and increment the step count
|
|
|
|
offsetFreq += offsetFreqIncrement;
|
|
offsetValue += offsetIncrement;
|
|
|
|
/*
|
|
* Change the local oscillator frequency for the next reading and record the time for
|
|
* the RBW required settling delay.
|
|
*/
|
|
|
|
setFreqMicros = micros(); // Store the time the LO frequency was changed
|
|
|
|
if (offsetValue >= 512) // reached offset limits
|
|
{
|
|
resetOffsets();
|
|
xmit.SetOffset(offsetValue);
|
|
uint32_t f = setting.IF_Freq + autoSweepFreq - offsetFreq;
|
|
// Serial.printf("Sweep setFreq %i\n", f);
|
|
xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read
|
|
bandscopeDelay = delaytime;
|
|
}
|
|
else
|
|
{
|
|
xmit.SetOffset(offsetValue);
|
|
bandscopeDelay = offsetDelayTime;
|
|
}
|
|
|
|
|
|
#ifdef USE_WIFI
|
|
|
|
if ( numberOfWebsocketClients > 0 )
|
|
{
|
|
if ( jsonDocInitialised )
|
|
{
|
|
JsonObject dataPoint = Points.createNestedObject (); // Add an object to the Json array to be pushed to the client
|
|
dataPoint["x"] = oldSweepFreq/1000000.0; // Set the x(frequency) value
|
|
dataPoint["y"] = rxRSSI; // Set the y (RSSI) value
|
|
chunkIndex++; // increment no of data points in current WiFi chunk
|
|
|
|
if ( chunkIndex >= wiFiPoints ) // Send the chunk of data and start new jSon document
|
|
{
|
|
String wsBuffer;
|
|
|
|
if ( wsBuffer )
|
|
{
|
|
// Serial.print("D");
|
|
serializeJson ( jsonDocument, wsBuffer );
|
|
// Serial.printf("J%u", wsBuffer.length() );
|
|
unsigned long s = millis();
|
|
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
|
|
if (millis() - s > 1000)
|
|
{
|
|
Serial.println("webSocketTimeout");
|
|
Serial.println(wsBuffer);
|
|
numberOfWebsocketClients = 0;
|
|
}
|
|
// Serial.print("j");
|
|
}
|
|
|
|
else
|
|
Serial.println("No buffer :(");
|
|
}
|
|
}
|
|
if ( ( chunkIndex >= wiFiPoints ) || !jsonDocInitialised ) // Start new jSon document
|
|
{
|
|
chunkIndex = 0;
|
|
jsonDocument.clear();
|
|
jsonDocument["mType"] = "chunkSweep";
|
|
jsonDocument["StartIndex"] = autoSweepStep;
|
|
jsonDocument["sweepPoints"] = sweepPoints;
|
|
jsonDocument["sweepTime"] = (uint32_t)(sweepMicros/1000);
|
|
Points = jsonDocument.createNestedArray ("Points" ); // Add Points array
|
|
jsonDocInitialised = true;
|
|
}
|
|
}
|
|
|
|
#endif // #ifdef USE_WIFI
|
|
|
|
if (rxRSSI < minRSSI) // Detect minimum for sweep
|
|
minRSSI = rxRSSI;
|
|
|
|
|
|
uint16_t pixelsPerPoint = SCREEN_WIDTH / displayPoints;
|
|
for (uint16_t i = 0; i< pixelsPerPoint; i++) {
|
|
|
|
uint16_t tmp = oldSweepStep * pixelsPerPoint + i;
|
|
|
|
myActual[tmp] = rxRSSI;
|
|
|
|
myGain[tmp] = gainReading;
|
|
|
|
DrawCheckerBoard ( tmp ); // Draw the grid
|
|
|
|
if ( resetAverage || setting.Average == AV_OFF ) // Store data, either as read or as rolling average
|
|
myData[tmp] = myActual[oldSweepStep];
|
|
else
|
|
{
|
|
switch ( setting.Average )
|
|
{
|
|
case AV_MIN:
|
|
if ( myData[tmp] > myActual[oldSweepStep] )
|
|
myData[tmp] = myActual[oldSweepStep];
|
|
break;
|
|
|
|
case AV_MAX:
|
|
if ( myData[tmp] < myActual[oldSweepStep] )
|
|
myData[tmp] = myActual[oldSweepStep];
|
|
break;
|
|
|
|
case AV_2:
|
|
myData[tmp] = ( myData[tmp] + myActual[oldSweepStep] ) / 2;
|
|
break;
|
|
|
|
case AV_4:
|
|
myData[tmp] = ( myData[tmp]*3 + myActual[oldSweepStep] ) / 4;
|
|
break;
|
|
|
|
case AV_8:
|
|
myData[tmp] = ( myData[tmp]*7 + myActual[oldSweepStep] ) / 8;
|
|
break;
|
|
}
|
|
DisplayPoint ( myData, tmp, AVG_COLOR );
|
|
}
|
|
|
|
if ( setting.ShowSweep )
|
|
DisplayPoint ( myActual, tmp, DB_COLOR );
|
|
|
|
if ( setting.ShowGain )
|
|
displayGainPoint ( myGain, tmp, GAIN_COLOR );
|
|
|
|
if ( setting.ShowStorage )
|
|
DisplayPoint ( myStorage, tmp, STORAGE_COLOR );
|
|
|
|
// If in the first few points show the scale
|
|
if ( ( tmp < 4 * CHAR_WIDTH ) && (tmp > 0) )
|
|
{
|
|
int16_t scaleX = -tmp + 1; // relative to the img sprite
|
|
img.setPivot( scaleX, 0);
|
|
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour
|
|
}
|
|
|
|
if ( tmp > 0 ) // Only push if not first point (two pixel wide img)
|
|
img.pushSprite ( xOrigin + tmp - 1 , yOrigin );
|
|
|
|
/*
|
|
* put data into the top row of the waterfall
|
|
* 16 bit colours have 5 bits for Red, 6 bits for Green, 5 bits for Blue
|
|
* We will just change the green level here for first test
|
|
*/
|
|
uint32_t level = (uint32_t)( (float)(rxRSSI - setting.WaterfallMin) * setting.WaterfallGain) ; // testing colours
|
|
if (rxRSSI < setting.WaterfallMin)
|
|
level = 0;
|
|
uint32_t green = level;
|
|
uint32_t red = 0;
|
|
uint32_t blue = 0;
|
|
if (green > 63)
|
|
{
|
|
green = 63;
|
|
red = level - 63;
|
|
if ( red > 31 )
|
|
{
|
|
red = 31;
|
|
blue = level - 63 - 31;
|
|
if ( blue > 31 )
|
|
blue = 31;
|
|
}
|
|
}
|
|
|
|
uint32_t pixelColour = (red << 11) + (green << 5) + blue;
|
|
|
|
if (colourTest > 0) // delete at some stage
|
|
pixelColour = colourTest;
|
|
|
|
// Serial.printf("rxRSSI %i; red %i; green %i; blue %i; colour %i \n", rxRSSI, red, green, blue, pixelColour);
|
|
sSprite.drawPixel ( tmp, 0, pixelColour );
|
|
|
|
}
|
|
|
|
myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
|
|
|
|
|
|
if ( autoSweepStep >= sweepPoints ) // If we have got to the end of the sweep
|
|
{
|
|
// autoSweepStep = 0;
|
|
sweepStartDone = false;
|
|
resetAverage = false;
|
|
|
|
if ( sweepCount < 2 )
|
|
sweepCount++; // Used to disable wifi at start
|
|
|
|
if ( myActual[setting.BandscopePoints-1] == 0 ) // Ensure a value in last data point
|
|
{
|
|
myActual[setting.BandscopePoints-1] = rxRSSI; // Yes, save it
|
|
myGain[setting.BandscopePoints-1] = gainReading;
|
|
myFreq[setting.BandscopePoints-1] = oldSweepFreq;
|
|
}
|
|
|
|
if ( showRSSI == 1 ) // Only show it once?
|
|
showRSSI = 0; // Then turn it off
|
|
|
|
#ifdef USE_WIFI
|
|
if (( numberOfWebsocketClients > 0) && jsonDocInitialised && (chunkIndex > 0) )
|
|
{
|
|
String wsBuffer;
|
|
|
|
if (wsBuffer)
|
|
{
|
|
serializeJson ( jsonDocument, wsBuffer );
|
|
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
|
|
}
|
|
|
|
else
|
|
Serial.println ( "No buffer :(");
|
|
}
|
|
#endif // #ifdef USE_WIFI
|
|
|
|
// scroll the waterfall down one pixel
|
|
sSprite.scroll( 0, 1 );
|
|
sSprite.pushSprite( 0, gridHeight + 1 );
|
|
|
|
} // End of "if ( autoSweepStep >= sweepPoints )"
|
|
|
|
} // End of "doBandscope"
|
|
|
|
|
|
/*
|
|
* "DisplayBandscopeInfo" - Draws the frequency info below the checkerboard. Called
|
|
* when a setting is changed to set axis labels
|
|
*/
|
|
|
|
void DisplayBandscopeInfo ()
|
|
{
|
|
const char *averageText[] = { " OFF", " MIN", " MAX", " 2", " 4", " 8" };
|
|
const char *referenceOutText[] = { " 30", " 15", " 10", " 4", " 3", " 2", " 1" };
|
|
|
|
double fStart;
|
|
double fCenter;
|
|
double fStop;
|
|
|
|
// enum { SA_LOW_RANGE, SA_HIGH_RANGE, SIG_GEN_LOW_RANGE, SIG_GEN_HIGH_RANGE, IF_SWEEP, ZERO_SPAN_LOW_RANGE, ZERO_SPAN_HIGH_RANGE, TRACKING_GENERATOR };
|
|
tSprite.fillSprite ( BLACK );
|
|
tSprite.setTextColor ( WHITE );
|
|
|
|
|
|
/*
|
|
* Update frequency labels at bottom if changed
|
|
*/
|
|
|
|
tft.setTextColor ( WHITE,BLACK );
|
|
tft.setTextSize ( 1 );
|
|
|
|
fStart = (double)( setting.BandscopeStart / 1000000.0 ); // Start freq
|
|
fCenter = (double)( ( setting.BandscopeStart + setting.BandscopeSpan/2.0 ) / 1000000.0 );
|
|
fStop = (double)( (setting.BandscopeStart + setting.BandscopeSpan ) / 1000000.0 ) ; // Stop freq
|
|
|
|
if ( old_startFreq != fStart || old_stopFreq != fStop )
|
|
{
|
|
// Serial.printf("DisplayBandscopeInfo fStart %f; old_startFreq %f \n", fStart, old_startFreq);
|
|
// Serial.printf("DisplayBandscopeInfo fStop %f; old_stopFreq %f \n", fStop, old_stopFreq);
|
|
|
|
tft.fillRect ( xOrigin, SCREEN_HEIGHT -
|
|
CHAR_HEIGHT, SCREEN_WIDTH - xOrigin - 1, SCREEN_HEIGHT - 1, BLACK );
|
|
|
|
// Show operating mode
|
|
tft.setCursor ( xOrigin + 50, SCREEN_HEIGHT - CHAR_HEIGHT );
|
|
tft.setTextColor ( DB_COLOR );
|
|
tft.printf ( "Mode:%s", modeText[setting.Mode] );
|
|
tft.setTextColor ( WHITE );
|
|
|
|
tft.setCursor ( xOrigin + 2, SCREEN_HEIGHT - CHAR_HEIGHT );
|
|
|
|
tft.print ( fStart );
|
|
|
|
tft.setCursor ( SCREEN_WIDTH - 25, SCREEN_HEIGHT - CHAR_HEIGHT );
|
|
tft.print ( fStop );
|
|
|
|
|
|
/*
|
|
* Show the center frequency:
|
|
*/
|
|
tft.setCursor ( SCREEN_WIDTH / 2 - 20 + xOrigin, SCREEN_HEIGHT - CHAR_HEIGHT );
|
|
tft.print ( fCenter );
|
|
tft.print ( "(MHz)" );
|
|
|
|
old_startFreq = fStart; // Save current frequency range
|
|
old_stopFreq = fStop; // For next time
|
|
}
|
|
|
|
tft.setCursor ( 220, SCREEN_HEIGHT - CHAR_HEIGHT ); // Show sweep time
|
|
tft.printf ( "%6ums", sweepMicros / 1000 );
|
|
|
|
|
|
/*
|
|
* We use the "tSprite" to paint the data at the top of the screen to avoid
|
|
* flicker.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Show marker values:
|
|
*
|
|
* The "xPos" and "yPos" arrays are the coordinates of where to place the marker data.
|
|
*
|
|
* The "posIndex" variable keeps track of the next available position for the marker
|
|
* data. If we want fixed positions for each marker, then change the "xPos" and "yPos"
|
|
* indicies to use "m".
|
|
*/
|
|
|
|
int xPos[MARKER_COUNT] = { 20, 20, 160, 160 };
|
|
int yPos[MARKER_COUNT] = { 0, CHAR_HEIGHT, 0, CHAR_HEIGHT };
|
|
int posIndex = 0;
|
|
|
|
for ( int m = 0; m < MARKER_COUNT; m++ )
|
|
{
|
|
tSprite.setCursor ( xPos[m], yPos[m] );
|
|
if (( marker[m].isEnabled()) && ( setting.ShowSweep || setting.Average != AV_OFF ))
|
|
{
|
|
tSprite.setTextColor ( WHITE );
|
|
tSprite.printf ( "%u:%5.1fdBm %8.4fMHz", marker[m].Index()+1,
|
|
rssiTodBm ( oldPeaks[m].Level ), oldPeaks[m].Freq / 1000000.0 );
|
|
}
|
|
else
|
|
{
|
|
tSprite.setTextColor ( DARKGREY );
|
|
tSprite.printf ( "%u:", marker[m].Index()+1 );
|
|
}
|
|
posIndex++;
|
|
}
|
|
|
|
int x = tSprite.width () - 45;
|
|
tSprite.setTextColor ( WHITE );
|
|
|
|
tSprite.pushSprite ( xOrigin, 0 ); // Write sprite to the display
|
|
|
|
updateSidebar = false;
|
|
} // End of "DisplayBandscopeInfo"
|
|
|
|
|
|
void resetOffsets ()
|
|
{
|
|
if (setting.BandscopeStart < 480000000) // low range. Assume never change range mid sweep!
|
|
{
|
|
offsetStep = -31;
|
|
offsetIncrement = 16; // 16 * 156.25 = 2500
|
|
}
|
|
else // high range
|
|
{
|
|
offsetStep = -63;
|
|
offsetIncrement = 8; // 8 * 312.5 = 2500
|
|
}
|
|
|
|
if (setting.BandscopeSpan == 400000) // wider span, same no of points
|
|
offsetIncrement = offsetIncrement * 2;
|
|
|
|
|
|
offsetFreq = offsetStep * offsetFreqIncrement;
|
|
offsetValue = offsetStep * offsetIncrement;
|
|
}
|