Merge pull request #2 from DPWilde/Bandscope

Bandscope
This commit is contained in:
DPWilde 2020-08-22 09:34:48 +01:00 committed by GitHub
commit 9348e1d97f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 2108 additions and 625 deletions

View File

@ -1,9 +1,23 @@
uint32_t colourTest;
/* /*
* Initialise variables and SI4432 for the low frequency sweep * Initialise variables and SI4432 for the low frequency sweep
*/ */
void initBandscope() 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 (); ClearDisplay ();
/* /*
* Set up the "img" Sprite. This is the image for the graph. It makes for faster display * Set up the "img" Sprite. This is the image for the graph. It makes for faster display
@ -22,25 +36,25 @@ void initBandscope()
img.setTextSize ( 1 ); img.setTextSize ( 1 );
img.setColorDepth ( 16 ); img.setColorDepth ( 16 );
img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
img.createSprite ( 2, GRID_HEIGHT + 1 ); // Only 2 columns wide img.createSprite ( 2, gridHeight + 1 ); // Only 2 columns wide
/* /*
* The "tSprite" is used for displaying the data above the scan grid. * 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(); tSprite.deleteSprite();
tSprite.setRotation ( 0 );
tSprite.setTextSize ( 1 );
tSprite.setColorDepth ( 16 );
tSprite.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
tSprite.createSprite ( tft.width() - X_ORIGIN, Y_ORIGIN );
sSprite.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 * Create and draw the sprite for the gain scale
*/ */
CreateGainScale (); CreateGridScale ();
// Make sure everything will be reset // Make sure everything will be reset
old_settingAttenuate = -1000; old_settingAttenuate = -1000;
@ -65,9 +79,7 @@ void initBandscope()
tinySA_mode = BANDSCOPE; tinySA_mode = BANDSCOPE;
setting.Mode = tinySA_mode; setting.Mode = tinySA_mode;
Serial.println("before reset bandscope stack");
ResetBandscopeMenuStack(); // Put menu stack back to root level ResetBandscopeMenuStack(); // Put menu stack back to root level
Serial.println("End of initBandscope");
} }
@ -75,9 +87,37 @@ void initBandscope()
/* /*
* This function section handles the fast bandscope sweep * This function section handles the fast bandscope sweep
* The display is split and shows a waterfall * The display is split and shows a waterfall.
* Number of points is reduced, and frequency change is done using an offset to aallow the delay time between * The number of points is reduced, and frequency change is done using an offset to allow the
* changing frequency and taking a reading to be reduced * 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
*
*/ */
@ -87,12 +127,10 @@ void doBandscope()
static uint32_t autoSweepStep = 0; static uint32_t autoSweepStep = 0;
static uint32_t autoSweepFreq = 0; static uint32_t autoSweepFreq = 0;
static uint32_t autoSweepFreqStep = 0; static uint32_t autoSweepFreqStep = 0;
static uint32_t nextPointFreq = 0; // Frequency for the next display point. Used for substeps
static unsigned long setFreqMicros; static unsigned long setFreqMicros;
static unsigned long nowMicros; static unsigned long nowMicros;
static unsigned long bandscopeDelay;
static uint32_t sweepStep; // Step count
static uint32_t sweepFreqStep;
static int16_t pointMinGain; // to record minimum gain for the current display point 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 int16_t pointMaxRSSI; // to record max RSSI of the samples in the current display point
@ -100,14 +138,7 @@ static uint32_t pointMaxFreq; // record frequency where maximum occurred
static int16_t lastMode; // Record last operating mode (sig gen, normal) static int16_t lastMode; // Record last operating mode (sig gen, normal)
static uint16_t currentPointRSSI; static uint16_t minRSSI = 255; // Minimum level for the sweep
static uint16_t peakRSSI;
static uint16_t prevPointRSSI;
static uint32_t peakFreq;
static uint16_t peakIndex;
static uint16_t pointsPastPeak;
static uint16_t pointsPastDip;
static uint16_t minRSSI; // Minimum level for the sweep
static uint16_t lastMinRSSI; // Minimum level for the previous 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 resetAverage; // Flag to indicate a setting has changed and average valuesneeds to be reset
@ -134,20 +165,17 @@ static uint16_t chunkIndex;
{ {
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things
{ {
Serial.println("InitBandscope or changedSetting");
sweepPoints = setting.BandscopePoints; sweepPoints = setting.BandscopePoints;
autoSweepFreqStep = ( setting.BandscopeSpan ) / sweepPoints; autoSweepFreqStep = ( setting.BandscopeSpan ) / sweepPoints;
offsetFreqIncrement = autoSweepFreqStep; // 2500 Hz for 200kHz span, 80 points per sweep
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution bandwidth = rcvr.SetRBW ( setting.BandscopeRBW10, &delaytime ); // Set it in the receiver Si4432
ownrbw = 2.6; // and fix the resolution bandwidth to 2.6kHz
bandwidth = rcvr.SetRBW ( ownrbw * 10.0, &delaytime ); // Set it in the receiver Si4432
//Serial.printf("set rcvr Freq get:%u, tempIF:%u\n", rcvr.GetFrequency(), tempIF); //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 rcvr.SetFrequency ( setting.IF_Freq ); // Set the RX Si4432 to the IF frequency
sweepFreqStep = autoSweepFreqStep; // Step for each reading // sweepFreqStep = autoSweepFreqStep; // Step for each reading
if ( setting.Attenuate != old_settingAttenuate ) if ( setting.Attenuate != old_settingAttenuate )
{ {
@ -158,6 +186,9 @@ static uint16_t chunkIndex;
resetAverage = changedSetting; resetAverage = changedSetting;
maxGrid = setting.BandscopeMaxGrid;
minGrid = setting.BandscopeMinGrid;
#ifdef USE_WIFI #ifdef USE_WIFI
// Vary number of points to send in each chunk depending on delaytime // Vary number of points to send in each chunk depending on delaytime
@ -167,28 +198,34 @@ static uint16_t chunkIndex;
wiFiPoints = MAX_WIFI_POINTS; wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > setting.BandscopePoints) if (wiFiPoints > setting.BandscopePoints)
wiFiPoints = setting.BandscopePoints; wiFiPoints = setting.BandscopePoints;
Serial.printf("No of wifiPoints set to %i\n", wiFiPoints); // Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
if ( numberOfWebsocketClients > 0 ) if ( numberOfWebsocketClients > 0 )
pushBandscopeSettings (); pushBandscopeSettings ();
#endif // #ifdef USE_WIFI #endif // #ifdef USE_WIFI
} // initSweep || changedSetting } // initSweep || changedSetting
autoSweepStep = 0; // Set the step counter to zero autoSweepStep = 0; // Set the step counter to zero
autoSweepFreq = setting.BandscopeStart; // Set the start frequency. autoSweepFreq = setting.BandscopeStart; // Set the start frequency.
nextPointFreq = autoSweepFreq + autoSweepFreqStep;
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write 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 setFreqMicros = micros(); // Store the time the frequency was changed
xmit.SetFrequency ( setting.IF_Freq + autoSweepFreq ); // set the LO frequency, tempIF is offset if spur reduction on
// 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 #ifdef USE_WIFI
@ -212,49 +249,16 @@ static uint16_t chunkIndex;
#endif // #ifdef USE_WIFI #endif // #ifdef USE_WIFI
sweepStep = 0; // sweepStep = 0;
startFreq = setting.BandscopeStart + setting.IF_Freq; // Start freq for the LO startFreq = setting.BandscopeStart + setting.IF_Freq; // Start freq for the LO
stopFreq = setting.BandscopeSpan + startFreq; // Stop 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 ); // Serial.printf(" start %i; stop %i; points %i \n", startFreq, stopFreq, sweepPoints );
if ( setActualPowerRequested )
{
SetPowerLevel ( actualPower );
setActualPowerRequested = false;
// Serial.printf ( "Setting actual Power %f \n", actualPower );
}
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
/*
* Copy the values for the peaks (marker positions) to the old versions. No need to
* reset the indicies or frequencies; just the "Level".
*/
for ( int i = 0; i < MARKER_COUNT; i++ )
{
oldPeaks[i].Level = peaks[i].Level;
oldPeaks[i].Index = peaks[i].Index;
oldPeaks[i].Freq = peaks[i].Freq;
peaks[i].Level = 0;
}
//DisplayInfo (); // Display axis, top and side bar text
peakLevel = 0; // Reset the peak values for the sweep
peakFreq = 0.0;
peakGain = 100; // Set to higher than gain can ever be
lastMinRSSI = minRSSI; lastMinRSSI = minRSSI;
minRSSI = 300; // Higher than it can be minRSSI = 255; // real value should always be less
DisplayBandscopeInfo (); // Display axis and other info
pointsPastPeak = 0; // Avoid possible peak detection at start of sweep
peakRSSI = 0;
sweepStartDone = true; // Make sure this initialize is only done once per sweep sweepStartDone = true; // Make sure this initialize is only done once per sweep
initSweep = false; initSweep = false;
@ -282,20 +286,17 @@ static uint16_t chunkIndex;
*/ */
nowMicros = micros(); nowMicros = micros();
while (( nowMicros - setFreqMicros ) < delaytime ) while (( nowMicros - setFreqMicros ) < bandscopeDelay )
{ {
if ( ( nowMicros - setFreqMicros + delaytime > 200 ) && if ( ( nowMicros - setFreqMicros + delaytime > 200 ) &&
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) ) ( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
{ {
// Serial.print("W"); webSocket.loop (); // Check websockets - includes Yield() to allow other tasks to run
webSocket.loop (); // Check websockets - includes Yield() to allow other events to run
// Serial.println("w");
lastWebsocketMicros = nowMicros; lastWebsocketMicros = nowMicros;
} }
if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish! if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish!
UiProcessTouch (); // Check the touch screen UiProcessTouch (); // Check the touch screen
// Serial.println("w");
nowMicros = micros(); nowMicros = micros();
} }
@ -311,29 +312,44 @@ static uint16_t chunkIndex;
* The second one produces a listing more fit for human consumption! * The second one produces a listing more fit for human consumption!
*/ */
// if ( showRSSI ) // Displaying RSSI? if ( showRSSI ) // Displaying RSSI?
// { {
// Serial.printf ( "%s\t%03d\n", // Serial.printf ( "%s\t%03d\n",
// FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output // FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
Serial.printf ( "Freq: %s - RSSI: %03d\n", Serial.printf ( "Freq: %s - RSSI: %03d\n",
FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
// } }
if ( (numberOfWebsocketClients > 0) || (setting.ShowGain) ) if ( (numberOfWebsocketClients > 0) || (setting.ShowGain) )
gainReading = GetPreampGain ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains gainReading = GetPreampGain ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains
autoSweepFreq += sweepFreqStep; // Increment the frequency autoSweepFreq += autoSweepFreqStep; // Increment the frequency
sweepStep++; // and increment the step count autoSweepStep++; // and increment the step count
offsetFreq += offsetFreqIncrement;
offsetValue += offsetIncrement;
/* /*
* Change the transmitter frequency for the next reading and record the time for * Change the local oscillator frequency for the next reading and record the time for
* the RBW required settling delay. * the RBW required settling delay.
*/ */
uint32_t f = setting.IF_Freq + autoSweepFreq;
setFreqMicros = micros(); // Store the time the LO frequency was changed setFreqMicros = micros(); // Store the time the LO frequency was changed
xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read
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 #ifdef USE_WIFI
@ -376,7 +392,7 @@ static uint16_t chunkIndex;
chunkIndex = 0; chunkIndex = 0;
jsonDocument.clear(); jsonDocument.clear();
jsonDocument["mType"] = "chunkSweep"; jsonDocument["mType"] = "chunkSweep";
jsonDocument["StartIndex"] = sweepStep; jsonDocument["StartIndex"] = autoSweepStep;
jsonDocument["sweepPoints"] = sweepPoints; jsonDocument["sweepPoints"] = sweepPoints;
jsonDocument["sweepTime"] = (uint32_t)(sweepMicros/1000); jsonDocument["sweepTime"] = (uint32_t)(sweepMicros/1000);
Points = jsonDocument.createNestedArray ("Points" ); // Add Points array Points = jsonDocument.createNestedArray ("Points" ); // Add Points array
@ -386,164 +402,110 @@ static uint16_t chunkIndex;
#endif // #ifdef USE_WIFI #endif // #ifdef USE_WIFI
myActual[autoSweepStep] = rxRSSI; if (rxRSSI < minRSSI) // Detect minimum for sweep
minRSSI = rxRSSI;
myGain[autoSweepStep] = gainReading;
DrawCheckerBoard ( oldSweepStep ); // Draw the grid for the point in the sweep we have just read 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 if ( resetAverage || setting.Average == AV_OFF ) // Store data, either as read or as rolling average
myData[oldSweepStep] = myActual[oldSweepStep]; myData[tmp] = myActual[oldSweepStep];
else else
{ {
switch ( setting.Average ) switch ( setting.Average )
{ {
case AV_MIN: case AV_MIN:
if ( myData[oldSweepStep] > myActual[oldSweepStep] ) if ( myData[tmp] > myActual[oldSweepStep] )
myData[oldSweepStep] = myActual[oldSweepStep]; myData[tmp] = myActual[oldSweepStep];
break; break;
case AV_MAX: case AV_MAX:
if ( myData[oldSweepStep] < myActual[oldSweepStep] ) if ( myData[tmp] < myActual[oldSweepStep] )
myData[oldSweepStep] = myActual[oldSweepStep]; myData[tmp] = myActual[oldSweepStep];
break; break;
case AV_2: case AV_2:
myData[oldSweepStep] = ( myData[oldSweepStep] + myActual[oldSweepStep] ) / 2; myData[tmp] = ( myData[tmp] + myActual[oldSweepStep] ) / 2;
break; break;
case AV_4: case AV_4:
myData[oldSweepStep] = ( myData[oldSweepStep]*3 + myActual[oldSweepStep] ) / 4; myData[tmp] = ( myData[tmp]*3 + myActual[oldSweepStep] ) / 4;
break; break;
case AV_8: case AV_8:
myData[oldSweepStep] = ( myData[oldSweepStep]*7 + myActual[oldSweepStep] ) / 8; myData[tmp] = ( myData[tmp]*7 + myActual[oldSweepStep] ) / 8;
break; break;
} }
DisplayPoint ( myData, oldSweepStep, AVG_COLOR ); DisplayPoint ( myData, tmp, AVG_COLOR );
} }
if ( setting.ShowSweep ) if ( setting.ShowSweep )
DisplayPoint ( myActual, oldSweepStep, DB_COLOR ); DisplayPoint ( myActual, tmp, DB_COLOR );
if ( setting.ShowGain ) if ( setting.ShowGain )
displayGainPoint ( myGain, oldSweepStep, GAIN_COLOR ); displayGainPoint ( myGain, tmp, GAIN_COLOR );
if ( setting.ShowStorage ) if ( setting.ShowStorage )
DisplayPoint ( myStorage, oldSweepStep, STORAGE_COLOR ); DisplayPoint ( myStorage, tmp, STORAGE_COLOR );
if ( setting.SubtractStorage ) // If in the first few points show the scale
rxRSSI = 128 + rxRSSI - myStorage[oldSweepStep]; if ( ( tmp < 4 * CHAR_WIDTH ) && (tmp > 0) )
/*
* Record the peak values but not if freq low enough to detect the LO
*/
if ( peakLevel < myData[oldSweepStep] )
{ {
peakIndex = oldSweepStep; int16_t scaleX = -tmp + 1; // relative to the img sprite
peakLevel = myData[oldSweepStep];
peakFreq = oldSweepFreq;
// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep);
// displayPeakData ();
}
/*
* Save values used by peak detection. Need to save the previous value as we only
* know we have a peak once past it!
*/
prevPointRSSI = currentPointRSSI;
currentPointRSSI = myData[oldSweepStep];
/*
* Peak point detection. Four peaks, used to position the markers
*/
if ( currentPointRSSI >= prevPointRSSI ) // Level or ascending
{
pointsPastDip ++;
if ( pointsPastDip == PAST_PEAK_LIMIT )
{
pointsPastPeak = 0;
}
if ( currentPointRSSI > peakRSSI )
{
peakRSSI = currentPointRSSI; // Store values
peakFreq = oldSweepFreq;
peakIndex = oldSweepStep;
}
}
else
{
pointsPastPeak ++; // only a true peak if value decreased for a number of consecutive points
if ( pointsPastPeak == PAST_PEAK_LIMIT ) // We have a peak
{
pointsPastDip = 0;
/*
* Is this peak bigger than previous ones? Only check if bigger than smallest peak so far
*/
if ( peakRSSI > peaks[MARKER_COUNT-1].Level )
{
for ( uint16_t p = 0; p < MARKER_COUNT; p++ )
{
if ( peakRSSI > peaks[p].Level )
{
for ( uint16_t n = 3; n > p; n-- ) // Shuffle lower level peaks down
memcpy ( &peaks[n], &peaks[n-1], sizeof ( peak_t ));
peaks[p].Level = peakRSSI; // Save the peak values
peaks[p].Freq = peakFreq;
peaks[p].Index = peakIndex;
break;
}
}
}
peakRSSI = 0; // Reset peak values ready for next peak
} // We have a peak
} // Descending
/*
* Draw the markers if main sweep is displayed. The markers know if they are enabled or not
* Only paint if sweep step is in range where there will be a marker
*/
if ( setting.ShowSweep || setting.Average != AV_OFF )
{
for ( int p = 0; p < MARKER_COUNT; p++ )
if (( abs ( oldSweepStep - oldPeaks[p].Index )
<= MARKER_SPRITE_HEIGHT / 2 ) && ( oldPeaks[p].Level > (lastMinRSSI + MARKER_NOISE_LIMIT) ))
marker[p].Paint ( &img, oldPeaks[p].Index - oldSweepStep,
rssiToImgY ( oldPeaks[p].Level ) );
}
// If in the last few points and gain trace is displayed show the gain scale
if ( setting.ShowGain && (oldSweepStep > setting.BandscopePoints - 2 * CHAR_WIDTH) )
{
int16_t scaleX = setting.BandscopePoints - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite
img.setPivot( scaleX, 0); img.setPivot( scaleX, 0);
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour 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 );
if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img) /*
img.pushSprite ( X_ORIGIN+oldSweepStep-1, Y_ORIGIN ); * 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 myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
if ( sweepStep >= sweepPoints ) // If we have got to the end of the sweep if ( autoSweepStep >= sweepPoints ) // If we have got to the end of the sweep
{ {
// autoSweepStep = 0; // autoSweepStep = 0;
sweepStartDone = false; sweepStartDone = false;
@ -552,8 +514,6 @@ static uint16_t chunkIndex;
if ( sweepCount < 2 ) if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start sweepCount++; // Used to disable wifi at start
oldPeakLevel = peakLevel; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[setting.BandscopePoints-1] == 0 ) // Ensure a value in last data point if ( myActual[setting.BandscopePoints-1] == 0 ) // Ensure a value in last data point
{ {
myActual[setting.BandscopePoints-1] = rxRSSI; // Yes, save it myActual[setting.BandscopePoints-1] = rxRSSI; // Yes, save it
@ -565,7 +525,6 @@ static uint16_t chunkIndex;
showRSSI = 0; // Then turn it off showRSSI = 0; // Then turn it off
#ifdef USE_WIFI #ifdef USE_WIFI
if (( numberOfWebsocketClients > 0) && jsonDocInitialised && (chunkIndex > 0) ) if (( numberOfWebsocketClients > 0) && jsonDocInitialised && (chunkIndex > 0) )
{ {
String wsBuffer; String wsBuffer;
@ -579,8 +538,147 @@ static uint16_t chunkIndex;
else else
Serial.println ( "No buffer :("); Serial.println ( "No buffer :(");
} }
#endif // #ifdef USE_WIFI #endif // #ifdef USE_WIFI
} // End of "if ( sweepStep >= sweepPoints )" // scroll the waterfall down one pixel
} // End of "doSweepLow" 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;
}

View File

@ -8,6 +8,16 @@
*/ */
void initIF_Sweep() void initIF_Sweep()
{ {
// set up checkerboard sizes
gridHeight = GRID_HEIGHT;
gridWidth = DISPLAY_POINTS;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = X_ORIGIN;
yOrigin = Y_ORIGIN;
displayPoints = DISPLAY_POINTS;
xDelta = displayPoints / xGrid;
init_sweep(); init_sweep();
@ -62,10 +72,10 @@ static uint16_t chunkIndex;
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to work out some basic things if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to work out some basic things
{ {
Serial.println("Init IFSweep or changedSetting"); Serial.println("Init IFSweep or changedSetting");
autoSweepFreqStep = ( stopFreq_IF - startFreq_IF ) / DISPLAY_POINTS; autoSweepFreqStep = ( stopFreq_IF - startFreq_IF ) / displayPoints;
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution vbw = autoSweepFreqStep / 1000.0; // Set the video resolution
// ownrbw = ((float) ( stopFreq_IF - startFreq_IF )) / DISPLAY_POINTS / 1000.0; // kHz // ownrbw = ((float) ( stopFreq_IF - startFreq_IF )) / displayPoints / 1000.0; // kHz
// //
// if ( ownrbw < 2.6 ) // If it's less than 2.6KHz // if ( ownrbw < 2.6 ) // If it's less than 2.6KHz
// ownrbw = 2.6; // set it to 2.6KHz // ownrbw = 2.6; // set it to 2.6KHz
@ -81,7 +91,7 @@ static uint16_t chunkIndex;
bandwidth = rcvr.SetRBW ( 106.0, &delaytime ); // Set it in the receiver Si4432. delaytime is returned bandwidth = rcvr.SetRBW ( 106.0, &delaytime ); // Set it in the receiver Si4432. delaytime is returned
sweepPoints = DISPLAY_POINTS; // At least the right number of points for the display sweepPoints = displayPoints; // At least the right number of points for the display
sweepFreqStep = ( stopFreq_IF - startFreq_IF ) / sweepPoints; // Step for each reading sweepFreqStep = ( stopFreq_IF - startFreq_IF ) / sweepPoints; // Step for each reading
@ -89,14 +99,17 @@ static uint16_t chunkIndex;
xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output
maxGrid = setting.MaxGrid;
minGrid = setting.MinGrid;
#ifdef USE_WIFI #ifdef USE_WIFI
// Vary number of points to send in each chunk depending on delaytime // Vary number of points to send in each chunk depending on delaytime
// A chunk is sent at the end of each sweep regardless // A chunk is sent at the end of each sweep regardless
wiFiPoints = wiFiTargetTime / delaytime; wiFiPoints = wiFiTargetTime / delaytime;
if (wiFiPoints > MAX_WIFI_POINTS) if (wiFiPoints > MAX_WIFI_POINTS)
wiFiPoints = MAX_WIFI_POINTS; wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > DISPLAY_POINTS*OVERLAP) if (wiFiPoints > displayPoints*OVERLAP)
wiFiPoints = DISPLAY_POINTS*OVERLAP; wiFiPoints = displayPoints*OVERLAP;
// Serial.printf("No of wifiPoints set to %i\n", wiFiPoints); // Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
pushIFSweepSettings(); pushIFSweepSettings();
@ -445,15 +458,15 @@ static uint16_t chunkIndex;
} }
// If in the last few points and gain trace is displayed show the gain scale // If in the last few points and gain trace is displayed show the gain scale
if ( setting.ShowGain && (oldSweepStep > DISPLAY_POINTS - 2 * CHAR_WIDTH) ) if ( setting.ShowGain && (oldSweepStep > displayPoints - 2 * CHAR_WIDTH) )
{ {
int16_t scaleX = DISPLAY_POINTS - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite int16_t scaleX = displayPoints - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite
img.setPivot( scaleX, 0); img.setPivot( scaleX, 0);
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour
} }
if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img) if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( X_ORIGIN+oldSweepStep-1, Y_ORIGIN ); img.pushSprite ( xOrigin+oldSweepStep-1, yOrigin );
myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
@ -469,11 +482,11 @@ static uint16_t chunkIndex;
oldPeakLevel = peakLevel; //Save value of peak level for use by the "SetPowerLevel" function oldPeakLevel = peakLevel; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[DISPLAY_POINTS-1] == 0 ) // Ensure a value in last data point if ( myActual[displayPoints-1] == 0 ) // Ensure a value in last data point
{ {
myActual[DISPLAY_POINTS-1] = rxRSSI; // Yes, save it myActual[displayPoints-1] = rxRSSI; // Yes, save it
myGain[DISPLAY_POINTS-1] = gainReading; myGain[displayPoints-1] = gainReading;
myFreq[DISPLAY_POINTS-1] = oldSweepFreq; myFreq[displayPoints-1] = oldSweepFreq;
} }
if ( showRSSI == 1 ) // Only show it once? if ( showRSSI == 1 ) // Only show it once?

509
RXsweep.ino Normal file
View File

@ -0,0 +1,509 @@
/*
* RX sweep fixes the LO and sweeps the receiver from a start value to an stop value to
* measure the RX FIR bandpass pass filter response. This eleimates any effect from the SAW
* and low pass filters before the receiver.
* It requires a fixed strength and frequency signal, and this is provided
* by setting the reference output to say 15MHz. Avoid 30Mhz due to some stray clock signal
* The input should be connected to the reference signal output with an external cable
*/
void initRX_Sweep()
{
// set up checkerboard sizes
gridHeight = GRID_HEIGHT;
gridWidth = DISPLAY_POINTS;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = X_ORIGIN;
yOrigin = Y_ORIGIN;
displayPoints = DISPLAY_POINTS;
xDelta = displayPoints / xGrid;
init_sweep();
SetRXsweepSpan (setting.Bandwidth10 * 1000); // 10 * bandwidth
tinySA_mode = RX_SWEEP;
setting.Mode = tinySA_mode;
ResetRXsweepMenuStack(); // Put menu stack back to root level
}
void doRX_Sweep()
{
static uint32_t autoSweepStep = 0;
static uint32_t autoSweepFreq = 0;
static uint32_t autoSweepFreqStep = 0;
static uint32_t nextPointFreq = 0; // Frequency for the next display point. Used for substeps
static unsigned long setFreqMicros;
static unsigned long nowMicros;
static uint32_t sweepStep; // Step count
static uint32_t sweepFreqStep;
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 currentPointRSSI;
static uint16_t peakRSSI;
static uint16_t prevPointRSSI;
static uint32_t peakFreq;
static uint16_t peakIndex;
static uint16_t pointsPastPeak;
static uint16_t pointsPastDip;
static uint16_t minRSSI; // Minimum level for the sweep
static uint16_t lastMinRSSI; // Minimum level for the previous sweep
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 (( !sweepStartDone || initSweep || changedSetting ) )
{
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to work out some basic things
{
Serial.println("Init RXSweep or changedSetting");
autoSweepFreqStep = ( stopFreq_RX - startFreq_RX ) / displayPoints;
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution
/*
* Use the RBW setting. If zero then it is meaningless, use smallest instead
*/
bandwidth = rcvr.SetRBW ( setting.Bandwidth10, &delaytime ); // Set it in the receiver Si4432. delaytime is returned
sweepPoints = displayPoints;
sweepFreqStep = ( stopFreq_RX - startFreq_RX ) / sweepPoints; // Step for each reading
att.SetAtten ( 0 ); // Set the internal attenuator
xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output
maxGrid = setting.MaxGrid;
minGrid = setting.MinGrid;
#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 > displayPoints*OVERLAP)
wiFiPoints = displayPoints*OVERLAP;
// Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
pushIFSweepSettings();
#endif // #ifdef USE_WIFI
} // initSweep || changedSetting
autoSweepStep = 0; // Set the step counter to zero
autoSweepFreq = startFreq_RX; // Set the start frequency.
nextPointFreq = autoSweepFreq + autoSweepFreqStep;
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write
{
}
//Serial.printf("set rcvr Freq get:%u, tempIF:%u\n", rcvr.GetFrequency(), tempIF);
rcvr.SetFrequency ( autoSweepFreq ); // Set the RX Si4432 to the start of the sweep
setFreqMicros = micros(); // Store the time the frequency was changed
xmit.SetFrequency ( sigFreq_RX + setting.IF_Freq ); // set the LO frequency to the IF plus reference
// Serial.printf("autoSweepFreq init: %u\n", autoSweepFreq);
#ifdef USE_WIFI
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
{
jsonDocument.clear ();
chunkIndex = 0;
jsonDocument["PreAmp"] = setting.PreampGain; // Fixed gain
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 = startFreq_RX; // Start freq for the RX
stopFreq = stopFreq_RX; // Stop freq for the RX
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
DisplayInfo (); // Display axis, top and side bar text
peakLevel = 0; // Reset the peak values for the sweep
peakFreq = 0.0;
peakGain = 100; // Set to higher than gain can ever be
lastMinRSSI = minRSSI;
minRSSI = 300; // Higher than it can be
/*
* Copy the values for the peaks (marker positions) to the old versions. No need to
* reset the indicies or frequencies; just the "Level".
*/
for ( int i = 0; i < MARKER_COUNT; i++ )
{
oldPeaks[i].Level = peaks[i].Level;
oldPeaks[i].Index = peaks[i].Index;
oldPeaks[i].Freq = peaks[i].Freq;
peaks[i].Level = 0;
}
pointsPastPeak = 0; // Avoid possible peak detection at start of sweep
peakRSSI = 0;
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 ) < delaytime )
{
if ( ( nowMicros - setFreqMicros + delaytime > 200 ) &&
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
{
// Serial.print("W");
webSocket.loop (); // Check websockets - includes Yield() to allow other events to run
// Serial.println("w");
lastWebsocketMicros = nowMicros;
}
if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish!
UiProcessTouch (); // Check the touch screen
// Serial.println("w");
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 += sweepFreqStep; // Increment the frequency
sweepStep++; // and increment the step count
// Serial.printf("autoSweepFreq: %u Step: %u\n", autoSweepFreq, sweepStep);
/*
* Change the transmitter frequency for the next reading and record the time for
* the RBW required settling delay.
*
* Not we also set the lo here to simulate the effect on the filters in a real sweep
*/
setFreqMicros = micros(); // Store the time the LO frequency was changed
xmit.SetFrequency ( sigFreq_RX + setting.IF_Freq ); // set the LO frequency to the IF plus reference
rcvr.SetFrequency ( autoSweepFreq ); // Set the RX Si4432 to the IF frequency
// Serial.printf("Step: %i Required: %i Actual %i\n", sweepStep, autoSweepFreq, rcvr.GetFrequency());
#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
// Serial.printf ( "Add point chunkIndex %u, sweepStep %u of %u \n", chunkIndex, sweepStep, sweepPoints);
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"] = sweepStep;
jsonDocument["sweepPoints"] = sweepPoints;
jsonDocument["sweepTime"] = (uint32_t)(sweepMicros/1000);
Points = jsonDocument.createNestedArray ("Points" ); // Add Points array
jsonDocInitialised = true;
}
}
#endif // #ifdef USE_WIFI
if ( rxRSSI > pointMaxRSSI ) // RSSI > maximum value for this point so far?
{
myActual[autoSweepStep] = rxRSSI; // Yes, save it
pointMaxRSSI = rxRSSI; // Added by G3ZQC - Remember new maximim
pointMaxFreq = oldSweepFreq;
}
if ( gainReading < pointMinGain ) // Gain < minimum gain for this point so far?
{
myGain[autoSweepStep] = gainReading; // Yes, save it
pointMinGain = gainReading; // Added by G3ZQC - Remember new minimum
}
if (rxRSSI < minRSSI) // Detect minimum for sweep
minRSSI = rxRSSI;
/*
* Have we enough readings for this display point? If yes, so do any averaging etc, reset
* the values so peak in the frequency step is recorded and update the display.
*/
if ( autoSweepFreq >= nextPointFreq )
{
nextPointFreq = nextPointFreq + autoSweepFreqStep; // Next display point frequency
autoSweepStep++; // Increment the index
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
DrawCheckerBoard ( oldSweepStep ); // Draw the grid for the point in the sweep we have just read
DisplayPoint ( myActual, oldSweepStep, DB_COLOR );
if ( setting.ShowGain )
displayGainPoint ( myGain, oldSweepStep, GAIN_COLOR );
/*
* Record the peak values
*/
if ( oldSweepStep > 0)
{
if ( peakLevel < myActual[oldSweepStep] )
{
peakIndex = oldSweepStep;
peakLevel = myActual[oldSweepStep];
peakFreq = oldSweepFreq;
// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep);
// displayPeakData ();
}
/*
* Save values used by peak detection. Need to save the previous value as we only
* know we have a peak once past it!
*/
prevPointRSSI = currentPointRSSI;
currentPointRSSI = myActual[oldSweepStep];
/*
* Peak point detection. Four peaks, used to position the markers
*/
if ( currentPointRSSI >= prevPointRSSI ) // Level or ascending
{
pointsPastDip ++;
if ( pointsPastDip == PAST_PEAK_LIMIT )
{
pointsPastPeak = 0;
}
if ( currentPointRSSI > peakRSSI )
{
peakRSSI = currentPointRSSI; // Store values
peakFreq = oldSweepFreq;
peakIndex = oldSweepStep;
}
}
else
{
pointsPastPeak ++; // only a true peak if value decreased for a number of consecutive points
if ( pointsPastPeak == PAST_PEAK_LIMIT ) // We have a peak
{
pointsPastDip = 0;
/*
* Is this peak bigger than previous ones? Only check if bigger than smallest peak so far
*/
if ( peakRSSI > peaks[MARKER_COUNT-1].Level )
{
for ( uint16_t p = 0; p < MARKER_COUNT; p++ )
{
if ( peakRSSI > peaks[p].Level )
{
for ( uint16_t n = 3; n > p; n-- ) // Shuffle lower level peaks down
memcpy ( &peaks[n], &peaks[n-1], sizeof ( peak_t ));
peaks[p].Level = peakRSSI; // Save the peak values
peaks[p].Freq = peakFreq;
peaks[p].Index = peakIndex;
break;
}
}
}
peakRSSI = 0; // Reset peak values ready for next peak
} // We have a peak
} // Descending
} // if (( autoSweepFreq > 1000000 ) && (oldSweepStep > 0))
/*
* Draw the markers if main sweep is displayed. The markers know if they are enabled or not
* Only paint if sweep step is in range where there will be a marker
*/
for ( int p = 0; p < MARKER_COUNT; p++ )
{
if (( abs ( oldSweepStep - oldPeaks[p].Index )
<= MARKER_SPRITE_HEIGHT / 2 ) && ( oldPeaks[p].Level > (lastMinRSSI + MARKER_NOISE_LIMIT) ))
marker[p].Paint ( &img, oldPeaks[p].Index - oldSweepStep,
rssiToImgY ( oldPeaks[p].Level ) );
}
// If in the last few points and gain trace is displayed show the gain scale
if ( setting.ShowGain && (oldSweepStep > displayPoints - 2 * CHAR_WIDTH) )
{
int16_t scaleX = displayPoints - 2 * CHAR_WIDTH - oldSweepStep + 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 ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( xOrigin+oldSweepStep-1, yOrigin );
myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
} // End of "if ( autoSweepFreq >= nextPointFreq )"
if ( sweepStep >= sweepPoints ) // If we have got to the end of the sweep
{
// autoSweepStep = 0;
sweepStartDone = false;
if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start
oldPeakLevel = peakLevel; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[displayPoints-1] == 0 ) // Ensure a value in last data point
{
myActual[displayPoints-1] = rxRSSI; // Yes, save it
myGain[displayPoints-1] = gainReading;
myFreq[displayPoints-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
} // End of "if ( sweepStep >= sweepPoints )"
}

View File

@ -17,11 +17,14 @@ void initSigLow()
//img.unloadFont(); // Free up memory from any previous incarnation of img //img.unloadFont(); // Free up memory from any previous incarnation of img
img.deleteSprite(); img.deleteSprite();
img.setColorDepth ( 16 ); img.setColorDepth ( 16 );
img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
img.createSprite ( 320, 55 ); // used for frequency display img.createSprite ( 320, 55 ); // used for frequency display
img.loadFont(SA_FONT_LARGE); img.loadFont(SA_FONT_LARGE);
SetRX ( 1 ); // LO and RX both in receive until turned on by UI SetRX ( 1 ); // LO and RX both in receive until turned on by UI
xmit.SetOffset ( 0 ); // make sure frequency offset registers are zero
rcvr.SetOffset ( 0 );
#ifdef SI_TG_IF_CS #ifdef SI_TG_IF_CS
if (tgIF_OK) { if (tgIF_OK) {
@ -47,7 +50,7 @@ void initSigLow()
setting.Mode = tinySA_mode; setting.Mode = tinySA_mode;
tft.unloadFont(); tft.unloadFont();
tft.setCursor ( X_ORIGIN + 50, SCREEN_HEIGHT - CHAR_HEIGHT ); tft.setCursor ( xOrigin + 50, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.setTextSize(1); tft.setTextSize(1);
tft.setTextColor ( YELLOW ); tft.setTextColor ( YELLOW );
tft.printf ( "Mode:%s", modeText[setting.Mode] ); tft.printf ( "Mode:%s", modeText[setting.Mode] );

View File

@ -4,6 +4,17 @@
*/ */
void initSweepLow() void initSweepLow()
{ {
// set up checkerboard sizes
gridHeight = GRID_HEIGHT;
gridWidth = DISPLAY_POINTS;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = X_ORIGIN;
yOrigin = Y_ORIGIN;
displayPoints = DISPLAY_POINTS;
xDelta = displayPoints / xGrid;
init_sweep(); init_sweep();
tinySA_mode = SA_LOW_RANGE; tinySA_mode = SA_LOW_RANGE;
@ -47,6 +58,9 @@ static uint16_t peakRSSI;
static uint16_t prevPointRSSI; static uint16_t prevPointRSSI;
static uint32_t peakFreq; static uint32_t peakFreq;
static uint16_t peakIndex; static uint16_t peakIndex;
static uint16_t maxRSSI;
static uint32_t maxFreq;
static uint16_t maxIndex;
static uint16_t pointsPastPeak; static uint16_t pointsPastPeak;
static uint16_t pointsPastDip; static uint16_t pointsPastDip;
static uint16_t minRSSI; // Minimum level for the sweep static uint16_t minRSSI; // Minimum level for the sweep
@ -56,6 +70,7 @@ static bool resetAverage; // Flag to indicate a setting has changed and avera
static bool jsonDocInitialised = false; static bool jsonDocInitialised = false;
static uint16_t chunkIndex; static uint16_t chunkIndex;
static uint32_t offsetIF; // IF frequency offset by half the bandwidth to position in the centre of the filter
@ -77,7 +92,7 @@ static uint16_t chunkIndex;
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things
{ {
//Serial.println("InitSweep or changedSetting"); //Serial.println("InitSweep or changedSetting");
autoSweepFreqStep = ( setting.ScanStop - setting.ScanStart ) / DISPLAY_POINTS; autoSweepFreqStep = ( setting.ScanStop - setting.ScanStart ) / displayPoints;
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution vbw = autoSweepFreqStep / 1000.0; // Set the video resolution
ownrbw = setting.Bandwidth10 / 10.0; // and the resolution bandwidth ownrbw = setting.Bandwidth10 / 10.0; // and the resolution bandwidth
@ -97,6 +112,12 @@ static uint16_t chunkIndex;
old_ownrbw = ownrbw; old_ownrbw = ownrbw;
} }
/*
* The FIR filters in the SI4432 are centred above the nominal IF frequency as that is where the signals are meant to be.
* To make sure we are not on the edge of the filters offset the IF frequency down by half the bandwidth
*/
offsetIF = setting.IF_Freq - setting.Bandwidth10 * 50; // bW10 is in kHz * 10, so * 100-> kHz, halved
/* /*
* Need multiple readings for each pixel in the display to avoid missing signals. * Need multiple readings for each pixel in the display to avoid missing signals.
* Work out how many points needed for the whole sweep: * Work out how many points needed for the whole sweep:
@ -104,8 +125,8 @@ static uint16_t chunkIndex;
sweepPoints = (uint32_t)(( setting.ScanStop - setting.ScanStart ) / bandwidth / 1000.0 * OVERLAP + 0.5); // allow for some overlap (filters will have 3dB roll off at edge) and round up sweepPoints = (uint32_t)(( setting.ScanStop - setting.ScanStart ) / bandwidth / 1000.0 * OVERLAP + 0.5); // allow for some overlap (filters will have 3dB roll off at edge) and round up
if ( sweepPoints < DISPLAY_POINTS ) if ( sweepPoints < displayPoints )
sweepPoints = DISPLAY_POINTS; // At least the right number of points for the display sweepPoints = displayPoints; // At least the right number of points for the display
sweepFreqStep = ( setting.ScanStop - setting.ScanStart ) / sweepPoints; // Step for each reading sweepFreqStep = ( setting.ScanStop - setting.ScanStart ) / sweepPoints; // Step for each reading
@ -120,14 +141,17 @@ static uint16_t chunkIndex;
xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output if wanted xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output if wanted
maxGrid = setting.MaxGrid;
minGrid = setting.MinGrid;
#ifdef USE_WIFI #ifdef USE_WIFI
// Vary number of points to send in each chunk depending on delaytime // Vary number of points to send in each chunk depending on delaytime
// A chunk is sent at the end of each sweep regardless // A chunk is sent at the end of each sweep regardless
wiFiPoints = wiFiTargetTime / delaytime; wiFiPoints = wiFiTargetTime / delaytime;
if (wiFiPoints > MAX_WIFI_POINTS) if (wiFiPoints > MAX_WIFI_POINTS)
wiFiPoints = MAX_WIFI_POINTS; wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > DISPLAY_POINTS*OVERLAP) if (wiFiPoints > displayPoints*OVERLAP)
wiFiPoints = DISPLAY_POINTS*OVERLAP; wiFiPoints = displayPoints*OVERLAP;
//Serial.printf("No of wifiPoints set to %i\n", wiFiPoints); //Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
if ( numberOfWebsocketClients > 0 ) if ( numberOfWebsocketClients > 0 )
@ -169,11 +193,13 @@ static uint16_t chunkIndex;
uint32_t IF_Shift = ownrbw * 1000; // bandwidth in Hz uint32_t IF_Shift = ownrbw * 1000; // bandwidth in Hz
if (IF_Shift > MAX_IF_SHIFT) if (IF_Shift > MAX_IF_SHIFT)
IF_Shift = MAX_IF_SHIFT; IF_Shift = MAX_IF_SHIFT;
tempIF = setting.IF_Freq - IF_Shift; tempIF = offsetIF - IF_Shift;
} else { } else {
tempIF = setting.IF_Freq; tempIF = offsetIF;
} }
spurToggle = !spurToggle; spurToggle = !spurToggle;
//Serial.printf("tempIF %u, spurOffset=%i, spur:%i, Toggle:%i\n", tempIF, tempIF - setting.IF_Freq, setting.Spur, spurToggle); //Serial.printf("tempIF %u, spurOffset=%i, spur:%i, Toggle:%i\n", tempIF, tempIF - setting.IF_Freq, setting.Spur, spurToggle);
@ -269,6 +295,7 @@ static uint16_t chunkIndex;
pointsPastPeak = 0; // Avoid possible peak detection at start of sweep pointsPastPeak = 0; // Avoid possible peak detection at start of sweep
peakRSSI = 0; peakRSSI = 0;
maxRSSI = 0;
sweepStartDone = true; // Make sure this initialize is only done once per sweep sweepStartDone = true; // Make sure this initialize is only done once per sweep
initSweep = false; initSweep = false;
@ -347,7 +374,7 @@ static uint16_t chunkIndex;
uint32_t f = tempIF + autoSweepFreq; uint32_t f = tempIF + autoSweepFreq;
xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read
// Serial.printf("Required: %i Actual %i\n", tempIF+autoSweepFreq, xmit.GetFrequency()); // Serial.printf("LO Required: %i Actual %i\n", tempIF+autoSweepFreq, xmit.GetFrequency());
#ifdef SI_TG_LO_CS #ifdef SI_TG_LO_CS
if (tgLO_OK && (trackGenSetting.Mode == 1) ) if (tgLO_OK && (trackGenSetting.Mode == 1) )
@ -496,11 +523,11 @@ static uint16_t chunkIndex;
if (( autoSweepFreq > MARKER_MIN_FREQUENCY ) && (oldSweepStep > 0)) if (( autoSweepFreq > MARKER_MIN_FREQUENCY ) && (oldSweepStep > 0))
{ {
if ( peakLevel < myData[oldSweepStep] ) if ( maxRSSI <= myActual[oldSweepStep] )
{ {
peakIndex = oldSweepStep; maxIndex = oldSweepStep;
peakLevel = myData[oldSweepStep]; maxRSSI = myActual[oldSweepStep];
peakFreq = oldSweepFreq; maxFreq = oldSweepFreq;
// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep); // Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep);
// displayPeakData (); // displayPeakData ();
@ -586,15 +613,15 @@ static uint16_t chunkIndex;
} }
// If in the last few points and gain trace is displayed show the gain scale // If in the last few points and gain trace is displayed show the gain scale
if ( setting.ShowGain && (oldSweepStep > DISPLAY_POINTS - 2 * CHAR_WIDTH) ) if ( setting.ShowGain && (oldSweepStep > displayPoints - 2 * CHAR_WIDTH) )
{ {
int16_t scaleX = DISPLAY_POINTS - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite int16_t scaleX = displayPoints - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite
img.setPivot( scaleX, 0); img.setPivot( scaleX, 0);
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour
} }
if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img) if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( X_ORIGIN+oldSweepStep-1, Y_ORIGIN ); img.pushSprite ( xOrigin+oldSweepStep-1, yOrigin );
myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
@ -609,13 +636,15 @@ static uint16_t chunkIndex;
if ( sweepCount < 2 ) if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start sweepCount++; // Used to disable wifi at start
oldPeakLevel = peakLevel; //Save value of peak level for use by the "SetPowerLevel" function // Serial.printf("MaxRSSI = %i, freq = %i\n", maxRSSI, maxFreq);
if ( myActual[DISPLAY_POINTS-1] == 0 ) // Ensure a value in last data point oldPeakLevel = maxRSSI; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[displayPoints-1] == 0 ) // Ensure a value in last data point
{ {
myActual[DISPLAY_POINTS-1] = rxRSSI; // Yes, save it myActual[displayPoints-1] = rxRSSI; // Yes, save it
myGain[DISPLAY_POINTS-1] = gainReading; myGain[displayPoints-1] = gainReading;
myFreq[DISPLAY_POINTS-1] = oldSweepFreq; myFreq[displayPoints-1] = oldSweepFreq;
} }
if ( showRSSI == 1 ) // Only show it once? if ( showRSSI == 1 ) // Only show it once?

398
cmd.cpp
View File

@ -17,9 +17,9 @@
* here in place of much redundant code in those modules. * here in place of much redundant code in those modules.
*/ */
#include "Cmd.h" // Our associated header #include "cmd.h" // Our associated header
#include "preferences.h" // For "SAVE" & "RECALL" #include "preferences.h" // For "SAVE" & "RECALL"
#include "Marker.h" // Marker class definition #include "marker.h" // Marker class definition
/* /*
* These are the objects for the PE4302 and Si4432 modules. They are created in the * These are the objects for the PE4302 and Si4432 modules. They are created in the
* main program file: * main program file:
@ -47,11 +47,29 @@ extern int changedSetting; // Something in "setting" changed
extern uint16_t tinySA_mode; extern uint16_t tinySA_mode;
extern uint8_t myData[DISPLAY_POINTS+1]; /*
extern uint8_t myStorage[DISPLAY_POINTS+1]; * Variables to determine size of grid and waterfall
* In Bandscope mode the grid is reduced. In future it may be possible to add
* a waterfall to the main sweep, but not yet
*/
extern uint16_t gridHeight;
extern uint16_t gridWidth;
extern uint16_t yGrid; // no of grid divisions
extern uint16_t yDelta; // no of points/division
extern uint16_t xGrid;
extern uint16_t xOrigin;
extern uint16_t yOrigin;
extern uint16_t displayPoints;
extern uint16_t xDelta;
extern uint16_t waterfallHeight;
extern uint8_t myData[SCREEN_WIDTH+1];
extern uint8_t myStorage[SCREEN_WIDTH+1];
extern float bandwidth; // The current bandwidth (not * 10) extern float bandwidth; // The current bandwidth (not * 10)
extern unsigned long delaytime; // In microseconds extern unsigned long delaytime; // In microseconds
extern unsigned long offsetDelayTime; // In microseconds
extern unsigned long wiFiTargetTime; extern unsigned long wiFiTargetTime;
extern uint16_t wiFiPoints; extern uint16_t wiFiPoints;
@ -76,10 +94,15 @@ extern uint32_t startFreq_IF; // Last value of the IF sweep start frequency
extern uint32_t stopFreq_IF; // Last value of the IF sweep end frequency extern uint32_t stopFreq_IF; // Last value of the IF sweep end frequency
extern uint32_t sigFreq_IF; // Last value of the IF sweep end frequency extern uint32_t sigFreq_IF; // Last value of the IF sweep end frequency
extern uint32_t startFreq_RX; // Last value of the IF sweep start frequency
extern uint32_t stopFreq_RX; // Last value of the IF sweep end frequency
extern uint32_t sigFreq_RX; // Last value of the IF sweep end frequency
extern uint8_t showRSSI; // When true, RSSI readings are displayed extern uint8_t showRSSI; // When true, RSSI readings are displayed
extern int initSweep; // Set by the "RSSI" command to restart the sweep extern int initSweep; // Set by the "RSSI" command to restart the sweep
extern bool paused; extern bool paused;
extern uint32_t colourTest;
extern Marker marker[MARKER_COUNT]; // Array of marker objects extern Marker marker[MARKER_COUNT]; // Array of marker objects
@ -97,6 +120,9 @@ extern void initSweepLow ();
extern void initSigLow (); extern void initSigLow ();
extern void initIF_Sweep (); extern void initIF_Sweep ();
extern void setMode (uint16_t newMode); extern void setMode (uint16_t newMode);
extern uint8_t dBmToRSSI ( double dBm );
/* /*
* The "msgTable" provides a way of translating the ASCII message to the internal * The "msgTable" provides a way of translating the ASCII message to the internal
* number. Needs to be re-ordered based on expected usage. * number. Needs to be re-ordered based on expected usage.
@ -143,14 +169,20 @@ extern void setMode (uint16_t newMode);
{ "SPUR", MSG_SPUR }, // Set Spur reduction on/off { "SPUR", MSG_SPUR }, // Set Spur reduction on/off
{ "WIFITIME", MSG_WIFI_UPDATE }, // Set WiFi Update target time { "WIFITIME", MSG_WIFI_UPDATE }, // Set WiFi Update target time
{ "WIFIPTS", MSG_WIFI_POINTS }, // Set WiFi points { "WIFIPTS", MSG_WIFI_POINTS }, // Set WiFi points
{ "SKTINT", MSG_WEBSKT_INTERVAL }, // Set WebSocket interval { "SKTINT", MSG_WEBSKT_INTERVAL }, // Set WebSocket interval
{ "SGRXDRIVE", MSG_SG_RX_DRIVE }, // Set Signal Generator RX Drive level { "SGRXDRIVE", MSG_SG_RX_DRIVE }, // Set Signal Generator RX Drive level
{ "SGLODRIVE", MSG_SG_LO_DRIVE }, // Set Signal Generator LO Drive level { "SGLODRIVE", MSG_SG_LO_DRIVE }, // Set Signal Generator LO Drive level
{ "TGIFDRIVE", MSG_TG_IF_DRIVE }, // Set Track Generator IF Drive level { "TGIFDRIVE", MSG_TG_IF_DRIVE }, // Set Track Generator IF Drive level
{ "TGLODRIVE", MSG_TG_LO_DRIVE }, // Set Track Generator LO Drive level { "TGLODRIVE", MSG_TG_LO_DRIVE }, // Set Track Generator LO Drive level
{ "IFSIGNAL", MSG_IFSIGNAL }, // IF sweep signal frequency { "IFSIGNAL", MSG_IFSIGNAL }, // IF sweep signal frequency
{ "TGOFFSET", MSG_TGOFFSET }, // Offset TG IF from SA IF to reduce pass through { "TGOFFSET", MSG_TGOFFSET }, // Offset TG IF from SA IF to reduce pass through
{ "", MSG_NONE } // Unrecognized command { "CTEST", MSG_COLOURTEST}, // Work out how colours work!
{ "OFFDEL", MSG_OFFDELAY }, // Offset tuning delay
{ "WFMIN", MSG_WFMIN }, // Min level for waterfall colours
{ "WFGAIN", MSG_WFGAIN }, // Gain for waterfall colours
{ "RXSWEEP", MSG_RX_SWEEP }, // Set RX Sweep Mode
{ "RXSIGNAL", MSG_RXSIGNAL }, // IF sweep signal frequency
{ "", MSG_NONE } // Unrecognized command
}; };
int msgCount = ELEMENTS ( msgTable); int msgCount = ELEMENTS ( msgTable);
@ -197,6 +229,7 @@ void ShowMenu ()
Serial.println ( " SALO..........Set to analyse mode low frequency range" ); Serial.println ( " SALO..........Set to analyse mode low frequency range" );
Serial.println ( " SGLO..........Set to signal generator mode low frequency range" ); Serial.println ( " SGLO..........Set to signal generator mode low frequency range" );
Serial.println ( " IFSWEEP.......Set to IF Sweep mode to analyse the TinySA SAW filters" ); Serial.println ( " IFSWEEP.......Set to IF Sweep mode to analyse the TinySA SAW filters" );
Serial.println ( " RXSWEEP.......Set to RX Sweep mode to analyse the TinySA FIR filters" );
Serial.println ( " BANDSCOPE.....Set to BANDSCOPE mode" ); Serial.println ( " BANDSCOPE.....Set to BANDSCOPE mode" );
Serial.println ( " TRACES........Turn display traces on or off ['GAIN' or 'dB']" ); Serial.println ( " TRACES........Turn display traces on or off ['GAIN' or 'dB']" );
@ -208,6 +241,9 @@ void ShowMenu ()
Serial.printf ( " SCALE.........Set the dB/horizontal line value; currently: %ddB\n", setting.PowerGrid ); Serial.printf ( " SCALE.........Set the dB/horizontal line value; currently: %ddB\n", setting.PowerGrid );
Serial.printf ( " WFMIN.........Set the minimum RSSI level for waterfall colouring: %i\n", setting.WaterfallMin );
Serial.printf ( " WFGAIN........Set the gain for waterfall colouring: %d\n", setting.WaterfallGain );
Serial.println ( "\nOther Commands:\n" ); Serial.println ( "\nOther Commands:\n" );
Serial.print ( " DRIVE.........Local oscillator drive level [0 to 7]; currently: " ); Serial.print ( " DRIVE.........Local oscillator drive level [0 to 7]; currently: " );
@ -240,6 +276,8 @@ void ShowMenu ()
Serial.printf ( " DELAY.........Timestep in uS, currently: %uuS\n", delaytime ); Serial.printf ( " DELAY.........Timestep in uS, currently: %uuS\n", delaytime );
Serial.printf ( " OFFDEL........Timestep in uS for Bandscope, currently: %uuS\n", offsetDelayTime );
Serial.print ( " VFO...........Set or get active VFO [R, ( L or T ), I(tg If), G(tG LO)]; currently: " ); Serial.print ( " VFO...........Set or get active VFO [R, ( L or T ), I(tg If), G(tG LO)]; currently: " );
if ( VFO == RX_4432 ) if ( VFO == RX_4432 )
@ -442,6 +480,7 @@ int16_t addr; // Si4432 register address
int16_t tempValue; // Temporary value of something int16_t tempValue; // Temporary value of something
int32_t tempFreq; // Temporary frequency int32_t tempFreq; // Temporary frequency
float tempFloat;
uint32_t freq1; // Several other temporary frequencies uint32_t freq1; // Several other temporary frequencies
uint32_t freq2; // used in computing the center uint32_t freq2; // used in computing the center
@ -641,7 +680,7 @@ uint8_t reg69; // Ditto
* Unlike the touch-screen equivalent command, here you can set any value you like as opposed * Unlike the touch-screen equivalent command, here you can set any value you like as opposed
* to chosing from a list of standard settings. * to chosing from a list of standard settings.
* *
* If no number is entered, we report the current setting. * If no number is entered, we report the current setting.
*/ */
case MSG_SCALE: case MSG_SCALE:
@ -926,6 +965,20 @@ uint8_t reg69; // Ditto
Serial.printf ( "Time per scan step = %uuS\n", delaytime ); Serial.printf ( "Time per scan step = %uuS\n", delaytime );
return true; return true;
/*
* The "OFFDEL" command sets the timestep in uS for tuning using offsets.
*/
case MSG_OFFDELAY: // Set the timestep in uS, currently 2000
if ( dataLen != 0 ) // Anything entered?
{
offsetDelayTime = atol ( dataBuff ); // Yes, set new delay time
DisplayInfo ();
}
Serial.printf ( "Offset delay time per bandscope step = %uuS\n", offsetDelayTime );
return true;
/* /*
* The "VFO" command sets the active VFO * The "VFO" command sets the active VFO
@ -1270,6 +1323,9 @@ uint8_t reg69; // Ditto
* The "TUNE" comand allows the use to specify the crystal load capacitance for the * The "TUNE" comand allows the use to specify the crystal load capacitance for the
* selected VFO. This teaks the frequency. See A440 and the TinySA documentation for * selected VFO. This teaks the frequency. See A440 and the TinySA documentation for
* info on how it really works. * info on how it really works.
*
* We need to force the SI4432 to recalibrate the PLL after each tune by putting it into READY mode
*
*/ */
case MSG_TUNE: case MSG_TUNE:
@ -1453,7 +1509,7 @@ uint8_t reg69; // Ditto
{ {
tempValue = atoi ( dataBuff ); // Yes, get new value tempValue = atoi ( dataBuff ); // Yes, get new value
// check valid // check valid
if ( (tempValue > 10) && (tempValue < DISPLAY_POINTS * OVERLAP) ) if ( (tempValue > 10) && (tempValue < displayPoints * OVERLAP) )
{ {
wiFiPoints = tempValue; wiFiPoints = tempValue;
Serial.printf ( "Chunk size set to: %u\n", tempValue ); Serial.printf ( "Chunk size set to: %u\n", tempValue );
@ -1461,7 +1517,7 @@ uint8_t reg69; // Ditto
} }
else else
{ {
Serial.printf("Invalid - must be between 10 and %u\n", DISPLAY_POINTS * OVERLAP); Serial.printf("Invalid - must be between 10 and %u\n", displayPoints * OVERLAP);
return false; return false;
} }
} }
@ -1505,6 +1561,10 @@ uint8_t reg69; // Ditto
setMode(IF_SWEEP); setMode(IF_SWEEP);
return true; return true;
case MSG_RX_SWEEP:
setMode(RX_SWEEP);
return true;
case MSG_TGOFFSET: case MSG_TGOFFSET:
if ( dataLen != 0 ) // Frequency specified? if ( dataLen != 0 ) // Frequency specified?
{ {
@ -1526,7 +1586,7 @@ uint8_t reg69; // Ditto
freq1 = ParseFrequency ( dataBuff ); // Yes, get new frequency freq1 = ParseFrequency ( dataBuff ); // Yes, get new frequency
SetIFsweepSigFreq (freq1); SetIFsweepSigFreq (freq1);
} }
Serial.printf ( "If Signal frequency: %s\n", FormatFrequency ( GetIFsweepSigFreq() )); Serial.printf ( "IF Sweep Signal frequency: %s\n", FormatFrequency ( GetIFsweepSigFreq() ));
return true; return true;
@ -1557,6 +1617,70 @@ uint8_t reg69; // Ditto
return false; return false;
} }
case MSG_COLOURTEST:
if ( dataLen != 0 ) // Anything entered?
{
tempValue = atoi ( dataBuff ); // Yes, get new value
if ( ( tempValue >= 0 ) && (tempValue <= 65535) )
{
colourTest = tempValue;
return true;
}
else // Nothing specified
{
Serial.println ( "Invalid Colour" );
return false;
}
}
/*
* The "WFMIN" command sets the min RSSI level for waterfall colouring.
* The value is entered in dBm so the value can be judged from the waterfall
*
* If no number is entered, we report the current setting.
*/
case MSG_WFMIN:
if ( dataLen != 0 ) // Value specified?
{
tempValue = atoi ( dataBuff ); // Yes, get grid reference value
SetWFMin ( tempValue ); // Set it
DisplayInfo (); // Re display the scan
}
Serial.printf ( "Waterfall Minimum RSSI value: %i\n", setting.WaterfallMin );
return true;
/*
* The "WFGAIN" command sets the min RSSI level for waterfall colouring.
*
* If no number is entered, we report the current setting.
*/
case MSG_WFGAIN:
if ( dataLen != 0 ) // Value specified?
{
tempFloat = atof ( dataBuff ); // Yes, get grid reference value
SetWFGain ( tempFloat ); // Set it
Serial.printf("tempFloat = %4.2f\n", tempFloat);
DisplayInfo (); // Re display the scan
}
Serial.printf ( "Waterfall colour gain: %4.2f\n", setting.WaterfallGain );
return true;
/*
* Set signal frequency fro RX Sweep
*/
case MSG_RXSIGNAL:
if ( dataLen != 0 ) // Frequency specified?
{
freq1 = ParseFrequency ( dataBuff ); // Yes, get new frequency
SetRXsweepSigFreq (freq1);
}
Serial.printf ( "RX Sweep Signal frequency: %s\n", FormatFrequency ( GetRXsweepSigFreq() ));
return true;
default: default:
return false; return false;
} // End of "switch" } // End of "switch"
@ -1953,7 +2077,7 @@ int oldMin = setting.MinGrid;
int oldMax = setting.MaxGrid; int oldMax = setting.MaxGrid;
setting.MaxGrid = ref; setting.MaxGrid = ref;
setting.MinGrid = ref - Y_GRID * setting.PowerGrid; setting.MinGrid = ref - yGrid * setting.PowerGrid;
if (( oldMin != setting.MinGrid ) || ( oldMax != setting.MaxGrid )) if (( oldMin != setting.MinGrid ) || ( oldMax != setting.MaxGrid ))
{ {
@ -2014,7 +2138,7 @@ void SetPowerGrid ( int g )
int oldGrid = setting.PowerGrid; int oldGrid = setting.PowerGrid;
setting.PowerGrid = g; setting.PowerGrid = g;
setting.MinGrid = setting.MaxGrid - Y_GRID * g; setting.MinGrid = setting.MaxGrid - yGrid * g;
if ( oldGrid != setting.PowerGrid ) if ( oldGrid != setting.PowerGrid )
{ {
@ -2083,7 +2207,7 @@ int oldAtten = setting.Attenuate;
void SetStorage ( void ) void SetStorage ( void )
{ {
for ( int i = 0; i < DISPLAY_POINTS; i++ ) for ( int i = 0; i < displayPoints; i++ )
myStorage[i] = myData[i]; myStorage[i] = myData[i];
setting.ShowStorage = true; setting.ShowStorage = true;
@ -2179,6 +2303,7 @@ int16_t tempBw;
if ( setting.Bandwidth10 != oldRBW ) if ( setting.Bandwidth10 != oldRBW )
{ {
changedSetting = true; changedSetting = true;
SetRXsweepSpan (setting.Bandwidth10 * 1000); // 10 * bandwidth
WriteSettings (); WriteSettings ();
} }
} }
@ -2622,6 +2747,189 @@ uint32_t GetIFsweepSigFreq ( void )
} }
/*
* Specific span setting for RX Sweep mode
*
*
* "SetSweepSpan" will maintain the previous center frequency and attempt to simply
* change the range of the scan. If the new limits cause the start frequency to go
* or the stop frequency to exceed "STOP_MAX", the range will be adjusted to maintain
* an equal range on both sides of the center frequency.
*/
void SetRXsweepSpan ( uint32_t spanRange )
{
uint32_t startFreq; // These are used in computing the
uint32_t stopFreq; // Scan limits
uint32_t centerFreq; // Current center frequency
uint32_t halfSpan; // Half of the frequency span
uint32_t lastStart; // Old ssweep start frequency
uint32_t lastStop; // and old stop frequency
// Serial.print ( "Requested span = " ); Serial.println ( FormatFrequency ( spanRange ));
lastStart = startFreq_RX; // Save the current start frequency
lastStop = stopFreq_RX; // and the current stop frequency
halfSpan = spanRange / 2; // Half the requested span
centerFreq = setting.IF_Freq; // Always the IF Freq for this mode
startFreq = centerFreq - halfSpan; // New computed start frequency
stopFreq = centerFreq + halfSpan; // New computed stop frequency
/*
* Now we have to range check the computed start and stop frequencies and if either
* of those is out of bounds, we will adjust the span accordingly. We'll maintain
* the previous center frequency though.
*/
if ( stopFreq > RX_STOP_MAX ) // Range check the stop frequency
{
stopFreq = RX_STOP_MAX; // Set the stop frequency at the upper limit
halfSpan = stopFreq - centerFreq; // Re-compute half span
startFreq = centerFreq - halfSpan; // and recompute start frequency
}
if ( startFreq < RX_START_MIN ) // Range check the start frequency
{
startFreq = RX_START_MIN; // Set start at the limit
halfSpan = centerFreq - startFreq; // Re-compute half span
stopFreq = centerFreq + halfSpan; // and re-compute stop frequency
}
startFreq = centerFreq - halfSpan; // Re-compute start and
stopFreq = centerFreq + halfSpan; // stop frequencies
// Serial.print ( "Computed start = " ); Serial.println ( FormatFrequency ( startFreq ));
// Serial.print ( "Computed stop = " ); Serial.println ( FormatFrequency ( stopFreq ));
// Serial.print ( "Half span = " ); Serial.println ( FormatFrequency ( halfSpan ));
startFreq_RX = startFreq; // Set new start frequency
stopFreq_RX = stopFreq; // Set new stop frequency
if (( startFreq_RX != lastStart )
|| ( stopFreq_RX != lastStop )) // Did it change
{
changedSetting = true; // Yes it did
RedrawHisto (); // Redraw labels and restart sweep with new settings
}
}
uint32_t GetRXsweepSpan ()
{
return stopFreq_RX - startFreq_RX;
}
void SetRXsweepStart ( uint32_t startFreq )
{
uint32_t lastStart; // Old sweep start frequency
uint32_t lastStop; // and old stop frequency
// Serial.print ( "Requested start = " ); Serial.println ( FormatFrequency ( startFreq ));
lastStart = startFreq_RX; // Save the current start frequency
lastStop = stopFreq_RX; // and the current stop frequency
if ( startFreq < RX_START_MIN ) // Range check
startFreq = RX_START_MIN;
if ( startFreq > RX_STOP_MAX ) // Range check
startFreq = RX_STOP_MAX;
if ( startFreq > stopFreq_RX ) // Start can't be more than current stop
stopFreq_RX = RX_STOP_MAX; // So set stop at maximum
startFreq_RX = startFreq; // Set new start frequency
if (( startFreq_RX != lastStart )
|| ( stopFreq_RX != lastStop )) // Did it change?
{
changedSetting = true; // Yes it did
RedrawHisto (); // Redraw labels and restart sweep with new settings
// WriteSettings (); // and save the setting structure
}
}
uint32_t GetRXsweepStart ( void )
{
return startFreq_RX;
}
void SetRXsweepStop ( uint32_t stopFreq )
{
uint32_t lastStart; // Old sweep start frequency
uint32_t lastStop; // and old stop frequency
// Serial.print ( "Requested stop = " ); Serial.println ( FormatFrequency ( stopFreq ));
lastStart = startFreq_RX; // Save the current start frequency
lastStop = stopFreq_RX; // and the current stop frequency
if ( stopFreq < RX_START_MIN ) // Range check
stopFreq = RX_START_MIN;
if ( stopFreq > RX_STOP_MAX ) // Range check
stopFreq = RX_STOP_MAX;
if ( stopFreq < startFreq_RX ) // Stop can't be less than current start
startFreq_RX = RX_START_MIN; // so set start to minimum frequency
stopFreq_RX = stopFreq; // Set new stop frequency
if (( startFreq_RX != lastStart )
|| ( stopFreq_RX != lastStop )) // Did it change?
{
changedSetting = true; // Yes it did
RedrawHisto (); // Redraw labels and restart sweep with new settings
// WriteSettings (); // and save the setting structure
}
}
uint32_t GetRXsweepStop ( void )
{
return stopFreq_RX;
}
void SetRXsweepSigFreq ( uint32_t sigFreq )
{
uint32_t lastSigFreq;
// Serial.print ( "Requested RX sweep signal = " ); Serial.println ( FormatFrequency ( sigFreq_RX ));
lastSigFreq = sigFreq_RX;
// check in a sensible range
if ( ( sigFreq < 1000000 ) || (sigFreq > 100000000 ) )
Serial.println(" Out of range - must be between 1,000,000 and 100,000,000Hz");
else
{
sigFreq_RX = sigFreq;
if ( sigFreq_RX != lastSigFreq )
changedSetting = true;
}
RedrawHisto (); // Redraw labels and restart sweep with new settings
}
uint32_t GetRXsweepSigFreq ( void )
{
return sigFreq_RX;
}
/* /*
* Specific start/stop frequency setting for Bandscope mode * Specific start/stop frequency setting for Bandscope mode
*/ */
@ -2646,8 +2954,29 @@ uint32_t lastStart; // Old sweep start frequency
{ {
changedSetting = true; // Yes it did changedSetting = true; // Yes it did
initSweep = true; initSweep = true;
// RedrawHisto (); // Redraw labels and restart sweep with new settings WriteSettings (); // and save the setting structure
// WriteSettings (); // and save the setting structure }
}
/*
* "SetRBW" - Sets the resolution bandwidth in the "setting" structure. We don't
* bother to actually set it in the receiver Si4432 as that is done at the start
* of each scan cycle.
*/
void SetBandscopeRBW ( int bw )
{
int16_t oldRBW = setting.BandscopeRBW10;
int16_t tempBw = bw;
setting.BandscopeRBW10 = bw;
tempBw = rcvr.SetRBW ( bw, &delaytime );
if ( tempBw != oldRBW )
{
changedSetting = true;
WriteSettings ();
} }
} }
@ -2662,7 +2991,6 @@ uint32_t GetBandscopeStart ( void )
/* /*
* Specific start/stop frequency setting for Bandscope mode * Specific start/stop frequency setting for Bandscope mode
*/ */
void SetBandscopeSpan ( uint32_t spanFreq ) void SetBandscopeSpan ( uint32_t spanFreq )
{ {
uint32_t lastSpan; // Old sweep span uint32_t lastSpan; // Old sweep span
@ -2695,6 +3023,25 @@ uint32_t GetBandscopeSpan ( void )
} }
/*
* "SetBandscopeLevel" - Sets the decibel level for the top line of the graph in Bandscope mode.
*/
void SetBandscopeLevel ( int ref )
{
int oldMin = setting.BandscopeMinGrid;
int oldMax = setting.BandscopeMaxGrid;
setting.BandscopeMaxGrid = ref;
setting.BandscopeMinGrid = ref - yGrid * setting.PowerGrid;
if (( oldMin != setting.BandscopeMinGrid ) || ( oldMax != setting.BandscopeMaxGrid ))
{
changedSetting = true;
WriteSettings ();
}
}
/* /*
@ -2884,3 +3231,18 @@ void SetFreq ( int vfo, uint32_t freq )
if ( vfo == TX_4432 ) // Transmitter? if ( vfo == TX_4432 ) // Transmitter?
xmit.SetFrequency ( freq ); xmit.SetFrequency ( freq );
} }
void SetWFMin (int16_t level)
{
if ( (level >=-130) && (level < -50) )
setting.WaterfallMin = dBmToRSSI( (double)level );
Serial.printf("Watterfall min set to %i \n", setting.WaterfallMin );
WriteSettings();
}
void SetWFGain (float gain)
{
if ((gain > 0.1) && (gain < 3.0))
setting.WaterfallGain = gain;
WriteSettings();
}

43
cmd.h
View File

@ -13,8 +13,8 @@
#define _CMD_H_ // Prevent double inclusion #define _CMD_H_ // Prevent double inclusion
#include "simpleSA.h" // General program definitions #include "simpleSA.h" // General program definitions
#include "PE4302.h" // Class definition for thePE4302 attenuator #include "pE4302.h" // Class definition for thePE4302 attenuator
#include "Si4432.h" // Class definition for the Si4432 transceiver #include "si4432.h" // Class definition for the Si4432 transceiver
#include "preferences.h" // For saving the "setting" structure #include "preferences.h" // For saving the "setting" structure
#include <SPI.h> #include <SPI.h>
@ -72,6 +72,13 @@
#define MSG_TGOFFSET 43 // Offset TG IF frequency from SA IF #define MSG_TGOFFSET 43 // Offset TG IF frequency from SA IF
#define MSG_BANDSCOPE 44 // Set Bandscope Mode #define MSG_BANDSCOPE 44 // Set Bandscope Mode
#define MSG_IFSWEEP 45 // Set IF Sweep Mode #define MSG_IFSWEEP 45 // Set IF Sweep Mode
#define MSG_OFFDELAY 46 // Adjust offset tuning delay time before taking reading
#define MSG_WFMIN 47 // set the min level for the waterfall colours
#define MSG_WFGAIN 48 // set the gain for waterfall colouring
#define MSG_RX_SWEEP 49 // Set the RX Sweep Mode
#define MSG_RXSIGNAL 50 // Set frequency of injected signal for IF Sweep
#define MSG_COLOURTEST 99 // test of waterfall colours - remove this when done!
typedef struct // Keeps everything together typedef struct // Keeps everything together
@ -161,24 +168,40 @@ uint8_t GetPreampGain ( bool* agc, uint8_t* reg );
void SetSweepStart ( uint32_t freq ); // Added in Version 2.3 void SetSweepStart ( uint32_t freq ); // Added in Version 2.3
uint32_t GetSweepStart ( void ); uint32_t GetSweepStart ( void );
void SetSweepStop ( uint32_t freq ); // Added in Version 2.3 void SetSweepStop ( uint32_t freq ); // Added in Version 2.3
uint32_t GetSweepStop ( void ); uint32_t GetSweepStop ( void );
void SetIFsweepStart ( uint32_t freq ); // Added in Version 3.0c
uint32_t GetIFsweepStart ( void );
void SetBandscopeStart ( uint32_t freq ); // Added in Version 3.0f void SetBandscopeStart ( uint32_t freq ); // Added in Version 3.0f
uint32_t GetBandscopeStart ( void ); uint32_t GetBandscopeStart ( void );
void SetBandscopeSpan ( uint32_t freq ); // Added in Version 3.0f void SetBandscopeSpan ( uint32_t freq ); // Added in Version 3.0f
uint32_t GetBandscopeSpan ( void ); uint32_t GetBandscopeSpan ( void );
void SetIFsweepStop ( uint32_t freq ); // Added in Version 3.0c void SetBandscopeRBW ( int v ); // Sets the resolution bandwidth
void SetBandscopeLevel ( int ref ); // Sets the decibel level for the top line of the graph
void SetIFsweepStart ( uint32_t freq ); // Added in Version 3.0c
uint32_t GetIFsweepStart ( void );
void SetIFsweepStop ( uint32_t freq ); // Added in Version 3.0c
uint32_t GetIFsweepStop ( void ); uint32_t GetIFsweepStop ( void );
void SetIFsweepSigFreq ( uint32_t freq ); // Added in Version 3.0c void SetIFsweepSigFreq ( uint32_t freq ); // Added in Version 3.0c
uint32_t GetIFsweepSigFreq ( void ); uint32_t GetIFsweepSigFreq ( void );
void SetRXsweepStart ( uint32_t freq );
uint32_t GetRXsweepStart ( void );
void SetRXsweepStop ( uint32_t freq );
uint32_t GetRXsweepStop ( void );
void SetRXsweepSpan ( uint32_t freq );
uint32_t GetRXsweepSpan ( void );
void SetRXsweepSigFreq ( uint32_t freq );
uint32_t GetRXsweepSigFreq ( void );
void SetSweepCenter ( uint32_t freq, uint8_t span ); void SetSweepCenter ( uint32_t freq, uint8_t span );
uint32_t GetSweepCenter ( void ); uint32_t GetSweepCenter ( void );
@ -189,4 +212,8 @@ void SetFreq ( int vfo, uint32_t freq );
bool UpdateMarker ( uint8_t mkr, char action ); bool UpdateMarker ( uint8_t mkr, char action );
void SetWFMin (int16_t level);
void SetWFGain (float gain);
#endif // End of "Cmd.h" #endif // End of "Cmd.h"

View File

@ -2,7 +2,7 @@
* "Marker.cpp" Contains the code for the "Marker" class functions * "Marker.cpp" Contains the code for the "Marker" class functions
*/ */
#include "Marker.h" // Class definition #include "marker.h" // Class definition
/* /*
* There are two constructors; the first simply creates an uninitialized * There are two constructors; the first simply creates an uninitialized

View File

@ -2,7 +2,7 @@
* "Menu.cpp" implements the "Menuitem" class. * "Menu.cpp" implements the "Menuitem" class.
*/ */
#include "Menu.h" // Class definition #include "menu.h" // Class definition
Menuitem::Menuitem () {} // Placeholder constructor Menuitem::Menuitem () {} // Placeholder constructor

16
my_SA.h
View File

@ -92,6 +92,18 @@
#define IF_STOP_MAX 500000000 // 500MHz #define IF_STOP_MAX 500000000 // 500MHz
#define IF_START_MIN 400000000 // 400MHz #define IF_START_MIN 400000000 // 400MHz
/*
* Some definitions for RX-Sweep to test the internal FIR filters
*/
#define RX_SWEEP_START 432000000
#define RX_SWEEP_STOP 436000000
// Limits for keypad entry of IF sweep frequency start/stop
#define RX_STOP_MAX 440000000 // 500MHz
#define RX_START_MIN 430000000 // 400MHz
/* /*
* Spur reduction shifts the IF down from its normal setting every other scan * Spur reduction shifts the IF down from its normal setting every other scan
@ -200,8 +212,8 @@
* The values here were Erik's original values * The values here were Erik's original values
*/ */
#define TX_CAPACITANCE 0x64 // Transmitter (LO) crystal load capacitance #define TX_CAPACITANCE 0x65 // Transmitter (LO) crystal load capacitance
#define RX_CAPACITANCE 0x64 // Receiver crystal load capacitance #define RX_CAPACITANCE 0x59 // Receiver crystal load capacitance
#define TG_LO_CAPACITANCE 0x64 // Tracking generator LO crystal load capacitance #define TG_LO_CAPACITANCE 0x64 // Tracking generator LO crystal load capacitance
#define TG_IF_CAPACITANCE 0x62 // Tracking generator IF crystal load capacitance #define TG_IF_CAPACITANCE 0x62 // Tracking generator IF crystal load capacitance

View File

@ -8,7 +8,7 @@
* I2C GPIO expander to drive a parallel version of the PE4302 module. * I2C GPIO expander to drive a parallel version of the PE4302 module.
*/ */
#include "PE4302.h" #include "pE4302.h"
/* /*

View File

@ -14,7 +14,7 @@
*/ */
#include <SPI.h> // Serial Peripheral Interface library #include <SPI.h> // Serial Peripheral Interface library
#include "Si4432.h" // Our header file #include "si4432.h" // Our header file
#include <esp32-hal-spi.h> #include <esp32-hal-spi.h>
/* /*
@ -25,21 +25,35 @@
bandpassFilter_t Si4432::_bandpassFilters[] bandpassFilter_t Si4432::_bandpassFilters[]
{ {
// bw*10, settle, dwn3, ndec, filset // bw*10, settle, dwn3, ndec, filset
{ 26, 7500, 0, 5, 1 }, // 0 "AUTO" selection possibility { 26, 6800, 0, 5, 1 }, // 0 "AUTO" selection possibility
{ 31, 7000, 0, 5, 3 }, // 1 If user selects 3KHz -> 3.1KHz actual { 28, 6600, 0, 5, 2 }, // 1 "AUTO" selection possibility
{ 59, 3700, 0, 4, 3 }, // 2 "AUTO" selection possibility { 31, 6600, 0, 5, 3 }, // 2 If user selects 3KHz -> 3.1KHz actual
{ 106, 2500, 0, 3, 2 }, // 3 If user selects 10KHz -> 10.6KHz actual { 32, 6600, 0, 5, 4 }, // 3 "AUTO" selection possibility
{ 322, 1000, 0, 2, 6 }, // 4 If user selects 30KHz -> 32.2KHz actual { 37, 6600, 0, 5, 5 }, // 4 "AUTO" selection possibility
{ 377, 1000, 0, 1, 1 }, // 5 "AUTO" selection possibility { 42, 6600, 0, 5, 6 }, // 5 "AUTO" selection possibility
{ 562, 700, 0, 1, 5 }, // 6 "AUTO" selection possibility { 45, 5500, 0, 5, 7 }, // 6 "AUTO" selection possibility
{ 832, 600, 0, 0, 2 }, // 7 "AUTO" selection possibility { 49, 5000, 0, 4, 1 }, // 7 "AUTO" selection possibility
{ 1121, 500, 0, 0, 5 }, // 8 If user selects 100KHz -> 112.1KHz actual { 54, 4200, 0, 4, 2 }, // 8 "AUTO" selection possibility
{ 1811, 500, 1, 1, 9 }, // 9 "AUTO" selection possibility { 59, 3700, 0, 4, 3 }, // 9 "AUTO" selection possibility
{ 2488, 450, 1, 0, 2 }, // 10 "AUTO" selection possibility { 72, 3300, 0, 4, 5 }, // 10 "AUTO" selection possibility
{ 3355, 400, 1, 0, 8 }, // 11 If user selects 300KHz -> 335.5KHz actual { 106, 2500, 0, 3, 2 }, // 11 If user selects 10KHz -> 10.6KHz actual
{ 3618, 300, 1, 0, 9 }, // 12 "AUTO" selection possibility { 162, 2000, 0, 3, 6 }, // 12 "AUTO" selection possibility
{ 4685, 300, 1, 0, 11 }, // 13 "AUTO" selection possibility { 210, 1600, 0, 2, 2 }, // 13 "AUTO" selection possibility
{ 6207, 300, 1, 0, 14 } // 14 "AUTO" selection possibility { 227, 1500, 0, 2, 3 }, // 14 "AUTO" selection possibility
{ 240, 1400, 0, 2, 4 }, // 15 "AUTO" selection possibility
{ 282, 1000, 0, 2, 5 }, // 16 "AUTO" selection possibility
{ 322, 1000, 0, 2, 6 }, // 17 If user selects 30KHz -> 32.2KHz actual
{ 377, 1000, 0, 1, 1 }, // 18 "AUTO" selection possibility
{ 562, 700, 0, 1, 5 }, // 19 "AUTO" selection possibility
{ 832, 600, 0, 0, 2 }, // 20 "AUTO" selection possibility
{ 1121, 500, 0, 0, 5 }, // 21 If user selects 100KHz -> 112.1KHz actual
{ 1811, 500, 1, 1, 9 }, // 22 "AUTO" selection possibility
{ 2251, 1400, 1, 0, 1 }, // 23 "AUTO" selection possibility
{ 2488, 450, 1, 0, 2 }, // 24 "AUTO" selection possibility
{ 3355, 400, 1, 0, 8 }, // 25 If user selects 300KHz -> 335.5KHz actual
{ 3618, 300, 1, 0, 9 }, // 26 "AUTO" selection possibility
{ 4685, 300, 1, 0, 11 }, // 27 "AUTO" selection possibility
{ 6207, 300, 1, 0, 14 } // 28 "AUTO" selection possibility
}; };
@ -97,82 +111,6 @@ bool Si4432::Init ( uint8_t cap )
if ( !Reset () ) // Reset the module if ( !Reset () ) // Reset the module
return false; // If that failed return false; // If that failed
SubInit (); // Things common to both modules
/*
*
* We turn on receive mode ("RXON"), the PLL ("PLLON") and ready mode
* ("XTON"). The Si4432 module does not have the 32.768 kHz crystal for
* the microcontroller, so we do not turn on the "X32KSEL" bit.
*
* The initial frequency is set to 433.92 MHz which is a typical IF
* Finally the GPIO-2 pin is set to ground.
*
* 03/24 - Logic verified against A440 register edscription document
*/
WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on
Tune ( cap ); // Set the crystal capacitance to fine tune frequency
SetFrequency ( 443920000 ); // 443.92MHz
delayMicroseconds ( 300 ); // Time to allow the SI4432 state machine to do its stuff
Serial.printf ( "End of Init - _cs = %i\n", _cs );
return true;
}
//bool Si4432::TX_Init ( uint8_t cap, uint8_t power )
//{
// _type = TX_4432; // We're a transmitter
//
// if ( !Reset () ) // Reset the module
// return false; // If that failed
// _pwr = power; // Set the transmitter power level
// SubInit (); // Things common to both modules
//
//
///*
// * Settings specific to the transmitter module.
// *
// * This is almost identical to how we set up the receiver except we turn
// * on the "TXON" bit (0x08) instead of the "RXON" bit. We also set the
// * transmitter power based on the mixer module being used. ("CalcPower"
// * function sets the value).
// *
// * GPIO-2 is handled differently; here, we set GPIO-2 to output the
// * microcontroller clock at maximum drive level (0xC0). We also set the
// * microcontroller clock speed to 10MHz.
// *
// * 03/24 - Logic verified against A440
// */
//
// Tune ( cap ); // Set the crystal capacitance
// WriteByte ( REG_TXPWR, _pwr ); // Power based on mixer in use
//
// WriteByte ( REG_GPIO2, 0xC0 ); // Maximum drive and microcontroller clock output
// WriteByte ( REG_MCOC, 0x02 ); // Set 10MHz clock output
//
// SetFrequency ( 443920000 ); // 433.92MHz (setting.IF_Freq???)
//
// delayMicroseconds ( 300 ); // Doesn't work without this!
//
// WriteByte ( REG_OFC1, ( TXON | PLLON | XTON )); // Transmitter, PLL and "Ready Mode" all on
//
//// Serial.println ( "End of TX_Init" );
//
// return true;
//}
/*
* "SubInit" is used by the "Init" function to set up
* all the registers.
*/
void Si4432::SubInit ()
{
/* /*
@ -257,10 +195,32 @@ void Si4432::SubInit ()
WriteByte ( REG_GPIO1, 0x15 ); // GPIO-1 RX State (output) WriteByte ( REG_GPIO1, 0x15 ); // GPIO-1 RX State (output)
WriteByte ( REG_GPIO2, 0x1F ); // Set GPIO-2 output to ground until needed WriteByte ( REG_GPIO2, 0x1F ); // Set GPIO-2 output to ground until needed
/*
*
* We turn on receive mode ("RXON"), the PLL ("PLLON") and ready mode
* ("XTON"). The Si4432 module does not have the 32.768 kHz crystal for
* the microcontroller, so we do not turn on the "X32KSEL" bit.
*
* The initial frequency is set to 433.92 MHz which is a typical IF
* Finally the GPIO-2 pin is set to ground.
*
* 03/24 - Logic verified against A440 register edscription document
*/
Tune ( cap ); // Set the crystal capacitance to fine tune frequency
WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on
SetFrequency ( 443920000 ); // 443.92MHz
delayMicroseconds ( 300 ); // Time to allow the SI4432 state machine to do its stuff
Serial.printf ( "End of Init - _cs = %i\n", _cs );
return true;
} }
/* /*
* "Reset" - Initializes the Si4432. * "Reset" - Initializes the Si4432.
* *
@ -369,7 +329,7 @@ uint8_t Si4432::ReadByte ( uint8_t reg )
void Si4432::SetFrequency ( uint32_t freq ) void Si4432::SetFrequency ( uint32_t freq )
{ {
int hbsel; // High/low band select bit int hbsel; // high band select, but shifted to the correct location in the byte
int sbsel; // Sideband select bit int sbsel; // Sideband select bit
uint16_t carrier; // Carrier frequency uint16_t carrier; // Carrier frequency
uint32_t reqFreq = freq; // Copy of requested frequency uint32_t reqFreq = freq; // Copy of requested frequency
@ -392,7 +352,7 @@ uint8_t registerBuf[4]; // Used to send frequency data in burst mode
/* /*
* add half the frequency resolution to required frequency so the actual value is rounded to nearest, not lowest * add half the frequency resolution to required frequency so the actual value is rounded to nearest, not lowest
* Frequency resoluion is 156.25 in low band, 312.5 in high band but freq is already divided above * Frequency resoluion is 156.25Hz in low band, 312.5Hz in high band but freq is already divided above
*/ */
freq = freq + 78; freq = freq + 78;
/* /*
@ -440,56 +400,31 @@ uint8_t registerBuf[4]; // Used to send frequency data in burst mode
/* /*
* M0WID mod - Update the frequency in "burst" mode as opposed to separate * M0WID mod - Update the frequency in "burst" mode as opposed to separate
* writes for each register, but only update what is needed * writes for each register
*/ */
uint8_t ncf1 = ( carrier >> 8 ) & 0xFF; uint8_t ncf1 = ( carrier >> 8 ) & 0xFF;
// if (fbs != _fbs) // write all three registers registerBuf[0] = REG_FBS|0x80; // First register in write mode (bit 7 set)
// { registerBuf[1] = fbs; // FBS register value
registerBuf[0] = REG_FBS|0x80; // First register in write mode (bit 7 set) registerBuf[2] = ncf1 ; // NCF1 value
registerBuf[1] = fbs; // FBS register value registerBuf[3] = carrier & 0xFF; // NCF0 value
registerBuf[2] = ncf1 ; // NCF1 value
registerBuf[3] = carrier & 0xFF; // NCF0 value //_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE )); digitalWrite ( _cs, LOW ); // Select the correct device
// spiSimpleTransaction(_spi->bus()); _spi->transfer ( registerBuf, 4 ); // Send the data
digitalWrite ( _cs, LOW ); // Select the correct device digitalWrite ( _cs, HIGH ); // Deselect the device
_spi->transfer ( registerBuf, 4 ); // Send the data //_spi->endTransaction();
digitalWrite ( _cs, HIGH ); // Deselect the device _ncf1 = ncf1;
//_spi->endTransaction(); _fbs = fbs;
_ncf1 = ncf1;
_fbs = fbs;
// }
// else if (ncf1 != _ncf1) // Only write both bytes of the carrier data
// {
// registerBuf[0] = REG_NCF1|0x80; // First register in write mode (bit 7 set)
// registerBuf[1] = ncf1 ; // NCF1 value
// registerBuf[2] = carrier & 0xFF; // NCF0 value
// digitalWrite ( _cs, LOW ); // Select the correct device
// _spi->transfer ( registerBuf, 3 ); // Send the data
// digitalWrite ( _cs, HIGH ); // Deselect the device
// _ncf1 = ncf1;
// }
// else // Only write the least significant byte of the carrier register
// {
// registerBuf[0] = REG_NCF0|0x80; // First register in write mode (bit 7 set)
// registerBuf[1] = carrier & 0xFF; // NCF0 value
// digitalWrite ( _cs, LOW ); // Select the correct device
// _spi->transfer ( registerBuf, 2 ); // Send the data
// digitalWrite ( _cs, HIGH ); // Deselect the device
//
// }
uint32_t fb = ( fbs & F_BAND ) ; uint32_t fb = ( fbs & F_BAND ) ;
hbsel = hbsel>>5; // should be 1 or 0 _hbsel = hbsel>>5; // should be 1 or 0
// _freq will contain the actual frequency, not necessarily what was requested // _freq will contain the actual frequency, not necessarily what was requested
_freq = (double)(10000000 * (hbsel + 1 )) * ( (double)fb + (double)24 + (double)carrier / (double)64000) ; _freq = (double)(10000000 * (_hbsel + 1 )) * ( (double)fb + (double)24 + (double)carrier / (double)64000) ;
// Serial.printf("set Freq :%i, actual:%i, fb:%i, fc:%i, hbsel:%i\n", reqFreq, _freq, fb, carrier, hbsel); // Serial.printf("set Freq :%i, actual:%i, fb:%i, fc:%i, hbsel:%i\n", reqFreq, _freq, fb, carrier, hbsel);
// _spi->endTransaction(); // Release the bus
// delayMicroseconds ( WRITE_DELAY ); // Delay needed when writing frequency
// Serial.print ( ", N = " ); // Serial.print ( ", N = " );
// Serial.print ( N ); // Serial.print ( N );
@ -518,7 +453,87 @@ uint8_t registerBuf[4]; // Used to send frequency data in burst mode
* handled by the calling program. * handled by the calling program.
*/ */
// delayMicroseconds ( _dt ); // M0WID - Delay depends on RBW // delayMicroseconds ( _dt );
}
/*
* "SetOffsetFreq" adjusts the frequency from the nominal value set in SetFrequency.
* It is intended for use by the SI4432 AFC algorithm to enable more accurate
* frequency matching of different radios, but we can turn off the AFC and use it
* to adjust the frequency without having the Si4432 go through its TX-RX-TX state machine
* and enable reduced edlay times from setting a frequency to getting valid readings
* The offset can vary +- 80kHz in low bands (f<480MHz) and +-160kHz in high bands (480-960MHz)
* negative numbers are twos complement of the positive offset
*/
void Si4432::SetOffsetFreq ( int32_t freq )
{
uint8_t registerBuf[3]; // Used to send data in burst mode
uint32_t posFreq;
if (freq < 0)
posFreq = -freq;
else
posFreq = freq;
uint16_t fo = (double)posFreq/(156.25 * (double)(_hbsel + 1));
if (freq < 0)
// do twos complement - invert the bits (0-9) (use XOR) and add one
fo = (fo ^ 0x3FF) + 1;
//Serial.printf(" offset frequency %i fo=%3x \n", freq, fo);
// write to the Si4432
registerBuf[0] = REG_FOFF1|0x80; // First register in write mode (bit 7 set)
registerBuf[1] = fo & 0xFF; // first 8 bits
registerBuf[2] = (fo >> 8 ) & 0x03; // last 2 bits
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( registerBuf, 3 ); // Send the data
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
}
/*
* "SetOffset" sets the frequency offset registers to correspond to the number of offset steps
*/
void Si4432::SetOffset ( int32_t offset )
{
uint8_t registerBuf[3]; // Used to send data in burst mode
uint16_t posOffset;
if (offset < 0)
posOffset = -offset;
else
posOffset = offset;
uint16_t fo = posOffset;
if (offset < 0)
// do twos complement - invert the bits (0-9) (use XOR) and add one
fo = (fo ^ 0x3FF) + 1;
//Serial.printf(" posoffset %3x, offset %i fo=%3x \n", posOffset, offset, fo);
// write to the Si4432
registerBuf[0] = REG_FOFF1|0x80; // First register in write mode (bit 7 set)
registerBuf[1] = fo & 0xFF; // first 8 bits
registerBuf[2] = (fo >> 8 ) & 0x03; // last 2 bits
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( registerBuf, 3 ); // Send the data
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
} }
@ -541,8 +556,11 @@ float Si4432::SetRBW ( float reqBandwidth10, unsigned long* delaytime_p ) // "re
*/ */
if ( reqBandwidth10 <= _bandpassFilters[0].bandwidth10 ) if ( reqBandwidth10 <= _bandpassFilters[0].bandwidth10 )
{
filter = 0; filter = 0;
Serial.print ("Minimum RBW");
}
/* /*
* If the requested bandwidth is greater or equal to the value in the first entry, * If the requested bandwidth is greater or equal to the value in the first entry,
* find the setting that is nearest (and above) the requested setting. * find the setting that is nearest (and above) the requested setting.
@ -551,7 +569,7 @@ float Si4432::SetRBW ( float reqBandwidth10, unsigned long* delaytime_p ) // "re
while (( _bandpassFilters[filter-1].bandwidth10 > reqBandwidth10 - 0.01 ) && ( filter > 0 )) while (( _bandpassFilters[filter-1].bandwidth10 > reqBandwidth10 - 0.01 ) && ( filter > 0 ))
filter--; filter--;
// Serial.print ( "filter = " ); Serial.println ( filter ); Serial.printf ( "Required = %f, filter = %i\n", reqBandwidth10, filter); Serial.println ( filter );
/* /*
@ -748,15 +766,35 @@ void Si4432::RxMode () // Put module in RX mode
WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on
} }
void Si4432::ReadyMode () // Put module into ready mode, rx and tx off
{
WriteByte ( REG_OFC1, XTON ); // "Ready Mode" on
}
void Si4432::SetMode (uint8_t newMode)
{
WriteByte ( REG_OFC1, newMode ); // "Ready Mode" on
}
uint8_t Si4432::GetMode ()
{
return ReadByte ( REG_OFC1 );
}
/* /*
* "Tune" sets the crystal tuning capacitance. * "Tune" sets the crystal tuning capacitance.
*
* We need to cycle the PLL to force a recalibration after the tune
*/ */
void Si4432::Tune ( uint8_t cap ) // Set the crystal tuning capacitance void Si4432::Tune ( uint8_t cap ) // Set the crystal tuning capacitance
{ {
_capacitance = cap; // Save in local data _capacitance = cap; // Save in local data
WriteByte ( REG_COLC, _capacitance ); // Send to the Si4432 WriteByte ( REG_COLC, _capacitance ); // Send to the Si4432
uint8_t currentMode = ReadByte ( REG_OFC1 );
ReadyMode ();
delayMicroseconds(300);
SetMode ( currentMode );
} }
/* /*

View File

@ -142,6 +142,9 @@
#define REG_TXPWR 0x6D // Transmitter Power #define REG_TXPWR 0x6D // Transmitter Power
#define REG_FOFF1 0x73 // Frequency Offset registers
#define REG_FOFF2 0x74
#define REG_FBS 0x75 // Frequency Band Select #define REG_FBS 0x75 // Frequency Band Select
#define SBSEL 0x40 #define SBSEL 0x40
#define HBSEL 0x20 #define HBSEL 0x20
@ -175,9 +178,11 @@ void SetFrequency ( uint32_t Freq ); // Set module's frequency
uint32_t GetFrequency (); // Get the module's frequency uint32_t GetFrequency (); // Get the module's frequency
uint32_t ReadFrequency (); // Read frequency from SI4432 uint32_t ReadFrequency (); // Read frequency from SI4432
void SetOffsetFreq ( int32_t freq ); // Set a frequency offset from the nominal frequency
void SetOffset ( int32_t offset ); // Offset based on number of offset increments
void SetPowerReference ( int freq ); // Set the GPIO output for the LO void SetPowerReference ( int freq ); // Set the GPIO output for the LO
void SetDrive ( uint8_t level ); // Sets the drive level void SetDrive ( uint8_t level ); // Sets the drive level
void TxMode ( uint8_t level ); // Put module in TX mode
float SetRBW ( float reqBandwidth10, unsigned long* delaytime_p ); // "reqBandwidth" in kHz * 10 float SetRBW ( float reqBandwidth10, unsigned long* delaytime_p ); // "reqBandwidth" in kHz * 10
@ -188,7 +193,13 @@ bool PreAmpAGC(); // Reads the register and return true if agc set to a
bool GetPreAmpAGC (); // Return true if agc set to auto bool GetPreAmpAGC (); // Return true if agc set to auto
uint8_t GetRSSI (); // Get Receiver Signal Strength uint8_t GetRSSI (); // Get Receiver Signal Strength
void RxMode (); // Put module in RX mode void RxMode (); // Put module in RX mode
void TxMode ( uint8_t level ); // Put module in TX mode
void ReadyMode (); // Put module in READY mode, crystal on, PLL off
uint8_t GetMode ();
void SetMode (uint8_t newMode);
void Tune ( uint8_t cap ); // Set the crystal tuning capacitance void Tune ( uint8_t cap ); // Set the crystal tuning capacitance
void WriteByte ( byte reg, byte setting ); // Write a byte of data to the specified register void WriteByte ( byte reg, byte setting ); // Write a byte of data to the specified register
@ -221,6 +232,7 @@ uint8_t _pwr; // Current power output (TX only?)
uint8_t _type; // Transmitter or receiver uint8_t _type; // Transmitter or receiver
uint8_t _capacitance; // Crystal load capacitance uint8_t _capacitance; // Crystal load capacitance
SPIClass* _spi; // Pointer to the SPI object SPIClass* _spi; // Pointer to the SPI object
int _hbsel; // High band (1) or low band (0)
uint8_t _fbs; // Current value of frequency band select register uint8_t _fbs; // Current value of frequency band select register
uint8_t _ncf1; // Current value for most significant byte of carrier uint8_t _ncf1; // Current value for most significant byte of carrier
uint32_t _freq; // Current actual frequency uint32_t _freq; // Current actual frequency

View File

@ -26,13 +26,13 @@
#ifndef TINYSA_H_ #ifndef TINYSA_H_
#define TINYSA_H_ // Prevent double inclusion #define TINYSA_H_ // Prevent double inclusion
#include "My_SA.h" // User settable parameters #include "my_SA.h" // User settable parameters
#include <Arduino.h> // General Arduino definitions #include <Arduino.h> // General Arduino definitions
#include <TFT_eSPI.h> #include <TFT_eSPI.h>
#define PROGRAM_NAME "TinySA" // These are for the WiFi interface #define PROGRAM_NAME "simpleSA" // These are for the WiFi interface
#define PROGRAM_VERSION "3.0" // Current version is 3.0 #define PROGRAM_VERSION "0.01" // Current version is 0.01 - beta!
/* /*
@ -41,7 +41,7 @@
* it out, it will stay set at '1'. * it out, it will stay set at '1'.
*/ */
#define TRACE_COUNT 1 // Number fo different traces available #define TRACE_COUNT 1 // Number of different traces available
/* /*
@ -49,6 +49,7 @@
*/ */
#define DISPLAY_POINTS 290 // Number of scan points in a sweep #define DISPLAY_POINTS 290 // Number of scan points in a sweep
#define BANDSCOPE_POINTS 80 // Number of scan points in a bandscope sweep
#define CHAR_HEIGHT 8 // Height of a character #define CHAR_HEIGHT 8 // Height of a character
#define HALF_CHAR_H 4 // Half a character height #define HALF_CHAR_H 4 // Half a character height
#define CHAR_WIDTH 6 // Width of a character #define CHAR_WIDTH 6 // Width of a character
@ -63,6 +64,9 @@
#define GRID_HEIGHT ( Y_GRID * DELTA_Y ) // Height of checkerboard #define GRID_HEIGHT ( Y_GRID * DELTA_Y ) // Height of checkerboard
#define WATERFALL_HEIGHT 100 // Height of waterfall in Bandscope mode
/* /*
* Definitions used in signal generator mode * Definitions used in signal generator mode
*/ */
@ -175,7 +179,7 @@
*/ */
enum { SA_LOW_RANGE, SA_HIGH_RANGE, SIG_GEN_LOW_RANGE, SIG_GEN_HIGH_RANGE, 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, BANDSCOPE }; IF_SWEEP, ZERO_SPAN_LOW_RANGE, ZERO_SPAN_HIGH_RANGE, TRACKING_GENERATOR, BANDSCOPE, RX_SWEEP };
/* /*
@ -229,7 +233,7 @@ typedef struct {
int8_t Attenuate = 0; // Attenuator setting (***) int8_t Attenuate = 0; // Attenuator setting (***)
int8_t Generate = 0; // Signal generator mode if not zero (***) int8_t Generate = 0; // Signal generator mode if not zero (***)
int16_t Bandwidth10 = 0; // Resolution Bandwidth setting*10; 0 = auto int16_t Bandwidth10 = 0; // Resolution Bandwidth setting*10; 0 = auto
int16_t LevelOffset = 0; // Related to power reading; not sure what though! int16_t LevelOffset = 0; // Calibration value (move to config?)
int8_t ReferenceOut = 1; // Transmitter GPIO2 set to 15MHz int8_t ReferenceOut = 1; // Transmitter GPIO2 set to 15MHz
int16_t PowerGrid = 10; // dB/vertical divison on the grid int16_t PowerGrid = 10; // dB/vertical divison on the grid
bool Spur = 0; // Spur reduction on or off bool Spur = 0; // Spur reduction on or off
@ -239,7 +243,7 @@ typedef struct {
bool SubtractStorage = 0; // Subtract stored scan (on or off) bool SubtractStorage = 0; // Subtract stored scan (on or off)
bool ShowGain = 1; // Display gain trace (on or off) bool ShowGain = 1; // Display gain trace (on or off)
bool ShowSweep = 1; // Display dB trace (on or off) bool ShowSweep = 1; // Display dB trace (on or off)
uint8_t Drive = 6; // LO Drive power (***) uint8_t Drive = 6; // LO Drive power (***) (move to config?)
uint8_t SigGenDrive = 5; // Signal generator drive (for RX SI4432) uint8_t SigGenDrive = 5; // Signal generator drive (for RX SI4432)
uint8_t spareUint8t_1 = 5; // spare uint8_t spareUint8t_1 = 5; // spare
uint8_t spareUint8t_2 = 5; // spare uint8_t spareUint8t_2 = 5; // spare
@ -247,10 +251,14 @@ typedef struct {
uint16_t Mode = SA_LOW_RANGE; // Default to low freq range Spectrum analyser mode uint16_t Mode = SA_LOW_RANGE; // Default to low freq range Spectrum analyser mode
uint16_t Timebase = 100; // Timebase for Zero IF modes (milliSeconds) uint16_t Timebase = 100; // Timebase for Zero IF modes (milliSeconds)
int16_t TriggerLevel = -40; // Trigger level for ZeroIF mode (dBm) int16_t TriggerLevel = -40; // Trigger level for ZeroIF mode (dBm)
uint32_t BandscopeStart = 7000000; // Start frequency for bandscope sweep uint32_t BandscopeStart = 14000000; // Start frequency for bandscope sweep
uint32_t BandscopeSpan = 200000; // Span for the bandscope sweep uint32_t BandscopeSpan = 200000; // Span for the bandscope sweep
uint16_t BandscopePoints = 80; // No of points in the sweep. 80/160/320 uint16_t BandscopePoints = BANDSCOPE_POINTS; // No of points in the sweep. 80/160/320
int16_t BandscopeMaxGrid= -30;
int16_t BandscopeMinGrid= -130;
int16_t BandscopeRBW10 = 28; // Resolution Bandwidth setting*10;
int16_t WaterfallMin = 17; // RSSI values below this are treated as zero
double WaterfallGain = 1.5; // Multiply RSSI value to get colour intensity
/* /*
* The following line should read: * The following line should read:

View File

@ -2,6 +2,7 @@
* "simpleSA.ino" * "simpleSA.ino"
* *
* This is the main program file for the "TinySA for ESP32" (spectrum analyzer) * This is the main program file for the "TinySA for ESP32" (spectrum analyzer)
*
* *
* Copyright (C) 2020 David Wilde (M0WID), John Price (WA2FZW) * Copyright (C) 2020 David Wilde (M0WID), John Price (WA2FZW)
* *
@ -22,17 +23,21 @@
* The starting point for this version is the "tinySA_touch05" software developed * The starting point for this version is the "tinySA_touch05" software developed
* by David (M0WID). * by David (M0WID).
* That software was based on the original version for STM "Blue Pill" by Erik Kaashoek PD0EK. * That software was based on the original version for STM "Blue Pill" by Erik Kaashoek PD0EK.
* For more information on that version visit https://groups.io/g/HBTE/topics
*
* Glen VK3PE has designed a set of PCB - see http://www.carnut.info/tinySA/tinySA.html
*
* Erik has since gone on to produce a commecial version, google tinySA, and this software
* has been renamed simpleSA to avoid confusion.
* *
* Modified by John Price (WA2FZW): * Modified by John Price (WA2FZW):
* *
* Version 1.0: * Version 1.0:
*
* Just add comments, try to figure out how it all works and move some stuff * Just add comments, try to figure out how it all works and move some stuff
* around for clarity! * around for clarity!
* *
* *
* Version 1.1: * Version 1.1:
*
* Added the file "Si4432.h" which contains symbols for all the Si4432 * Added the file "Si4432.h" which contains symbols for all the Si4432
* registers and some other things related to it's operation. * registers and some other things related to it's operation.
* *
@ -54,12 +59,10 @@
* *
* *
* Version 1.6: * Version 1.6:
*
* Added the file "Si4432.cpp" and moved all of the functions to handle the * Added the file "Si4432.cpp" and moved all of the functions to handle the
* interface to the hardware out of here. * * interface to the hardware out of here. *
* *
* Version 1.7: * Version 1.7:
*
* Moved all the global variables to the top of the file and moved the "setup" * Moved all the global variables to the top of the file and moved the "setup"
* and "loop" functions to the top where they are customarily found. * and "loop" functions to the top where they are customarily found.
* *
@ -69,18 +72,15 @@
* *
* *
* Version 1.8: * Version 1.8:
*
* Replaced the original code to handle the Si4432 modules with a class/object * Replaced the original code to handle the Si4432 modules with a class/object
* implementation using real SPI protocol. * implementation using real SPI protocol.
* *
* *
* Version 2.0: * Version 2.0:
*
* Overhauled the serial command handler and help menu. * Overhauled the serial command handler and help menu.
* *
* *
* Version 2.1: * Version 2.1:
*
* A major overhaul of the entire architecture! I moved all the functions that * A major overhaul of the entire architecture! I moved all the functions that
* handle reading and processing commands from the serial input into "Cmd.cpp". * handle reading and processing commands from the serial input into "Cmd.cpp".
* This is the first step into being able to use common command processing for * This is the first step into being able to use common command processing for
@ -88,14 +88,12 @@
* *
* *
* Version 2.7: * Version 2.7:
*
* More restructuring. Added markers . Added more * More restructuring. Added markers . Added more
* commands to the serial command handler. Re-organized the touch screen menus * commands to the serial command handler. Re-organized the touch screen menus
* in a more logical hierarchy and added some new capabilities there. * in a more logical hierarchy and added some new capabilities there.
* *
* *
* Version 2.8 Changes by M0WID: * Version 2.8 Changes by M0WID:
*
* Data now pushed to the web clients * Data now pushed to the web clients
* Grid y changed to have 10 divisions * Grid y changed to have 10 divisions
* Various bug fixes * Various bug fixes
@ -128,10 +126,12 @@
* SPI now runs at 16MHz (was at 1MHz - oops!) * SPI now runs at 16MHz (was at 1MHz - oops!)
* Additional functions in ui.cmd to allow signed frequency entry. * Additional functions in ui.cmd to allow signed frequency entry.
* *
* Moved to github for trial * Version 3.0f Changes by M0WID
* Added initial changes for bandscope mode * renamed simpleSA. Doesn't seem that simple to me!
* Added indication of tracking generator on/off * First go at bandscope mode
* Renamed to simpleSA * VSPI reduced to 10Mhz as some boards will not run at 16MHz
* Put onto github - lets see how it works!
*
*/ */
@ -139,10 +139,10 @@
#include <Arduino.h> // Basic Arduino definitions #include <Arduino.h> // Basic Arduino definitions
#include <SPI.h> // Serial Peripheral Interface library #include <SPI.h> // Serial Peripheral Interface library
#include <Wire.h> // I2C library #include <Wire.h> // I2C library
#include "Si4432.h" // Si4432 tranceiver class definitions and prototypes #include "si4432.h" // Si4432 tranceiver class definitions and prototypes
#include "PE4302.h" // PE4302 attenuator class #include "pE4302.h" // PE4302 attenuator class
#include "Cmd.h" // Command processing functions #include "cmd.h" // Command processing functions
#include "Marker.h" // Marker class #include "marker.h" // Marker class
#if USE_WIFI // M0WID - We need the following if using WiFi #if USE_WIFI // M0WID - We need the following if using WiFi
@ -163,7 +163,7 @@
/* /*
* Note the actual definitions for display and touch screen pins used are defined * Note the actual definitions for display and touch screen pins used are defined
* in the file "M0WID_Setup_ILI9341_simpleSA.h" in the "User_Setups" directory of * in the file "M0WID_Setup_ILI9341_TinySA.h" in the "User_Setups" directory of
* the "TFT_eSPI" library. * the "TFT_eSPI" library.
* *
* These are based on using HSPI (spi2) for display and VSPI (spi3) for the SI4432s * These are based on using HSPI (spi2) for display and VSPI (spi3) for the SI4432s
@ -356,22 +356,53 @@ size_t capacity = JSON_ARRAY_SIZE ( MAX_WIFI_POINTS + 1 )
static DynamicJsonDocument jsonDocument ( capacity ); // Buffer for json data to be pushed to the web clients static DynamicJsonDocument jsonDocument ( capacity ); // Buffer for json data to be pushed to the web clients
static JsonArray Points = jsonDocument.createNestedArray ( "Points" ); // add Points array static JsonArray Points = jsonDocument.createNestedArray ( "Points" ); // add Points array
#endif #endif
/*
* Variables to determine size of grid and waterfall
* In Bandscope mode the grid is reduced. In future it may be possible to add
* a waterfall to the main sweep, but not yet
*/
uint16_t gridHeight = GRID_HEIGHT;
uint16_t gridWidth = DISPLAY_POINTS;
uint16_t yGrid = Y_GRID; // no of grid divisions
uint16_t yDelta = gridHeight / yGrid; // no of points/division
uint16_t xGrid = X_GRID;
uint16_t xOrigin = X_ORIGIN;
uint16_t yOrigin = Y_ORIGIN;
uint16_t displayPoints = DISPLAY_POINTS;
uint16_t xDelta = displayPoints / xGrid;
uint16_t waterfallHeight = WATERFALL_HEIGHT;
int16_t maxGrid;
int16_t minGrid;
/*
* Some varibales for the various operating modes
*/
uint16_t tinySA_mode = SA_LOW_RANGE; // Low frequency range uint16_t tinySA_mode = SA_LOW_RANGE; // Low frequency range
const char *modeText[] = { "SALo", "SAHi", "SGLo", "SGHi", "IFSw", "0SpL", "0SpH", "TrGn" }; // For mode display const char *modeText[] = { "SALo", "SAHi", "SGLo", "SGHi", "IFSw", "0SpL", "0SpH", "TrGn", "BScp", "RXSw" }; // For mode display
float bandwidth; // The current bandwidth (not * 10) float bandwidth; // The current bandwidth (not * 10)
unsigned long delaytime = 2000; // M0WID - changed to microseconds and moved here unsigned long delaytime = 2000; // delay time to allow SI4432 filters to settle
uint32_t steps = DISPLAY_POINTS; // Number of frequency steps in the sweep uint32_t steps = displayPoints; // Number of frequency steps in the sweep
uint32_t sweepPoints; // Number of points in the sweep. Can be more than DISPLAY_POINTS if RBW is set less than video resolution uint32_t sweepPoints; // Number of points in the sweep. Can be more than DISPLAY_POINTS if RBW is set less than video resolution
uint32_t startFreq = 0; // Default start frequency is 0MHz uint32_t startFreq = 0; // Default start frequency is 0MHz
uint32_t stopFreq = 100000000; // Default stop frequency is 100MHz uint32_t stopFreq = 100000000; // Default stop frequency is 100MHz
uint32_t tempIF; // IF used for this sweep. Changes if Spur reduction is on uint32_t tempIF; // IF used for this sweep. Changes if Spur reduction is on
/*
* Variables for offset frequency tuning
*/
int32_t offsetFreq; // Frequency offset from nominal setting
int16_t offsetStep; // increments by one at each reading
int16_t offsetValue; // Offset value to be written to Si4432
int16_t offsetIncrement; // Increment of offsetValue per reading
int32_t offsetFreqIncrement; // Increment offsetFreq per reading
unsigned long offsetDelayTime = 5000; // Delay time when using frequency offset to change frequency
int VFO = RX_4432; // Set current VFO for command parser to the receiver Si4432 int VFO = RX_4432; // Set current VFO for command parser to the receiver Si4432
int gainReading; // Current preamp gain (will vary during a scan if AGC enabled) int gainReading; // Current preamp gain (will vary during a scan if AGC enabled)
@ -389,6 +420,7 @@ extern void StartSigGenMenu ( void ); // Function to launch sig gen menu
extern void StartSigGenFreq ( void ); // Function to set frequency extern void StartSigGenFreq ( void ); // Function to set frequency
extern void ResetSAMenuStack ( void ); // Reinitialise stack for SA mode extern void ResetSAMenuStack ( void ); // Reinitialise stack for SA mode
extern void ResetIFsweepMenuStack ( void ); // Reinitialise stack for IF Sweep mode extern void ResetIFsweepMenuStack ( void ); // Reinitialise stack for IF Sweep mode
extern void ResetRXsweepMenuStack ( void ); // Reinitialise stack for IF Sweep mode
extern void ResetBandscopeMenuStack ( void ); // Reinitialise stack for Bandscope mode extern void ResetBandscopeMenuStack ( void ); // Reinitialise stack for Bandscope mode
extern void ShowSplash ( void ); // Displays the splash screen extern void ShowSplash ( void ); // Displays the splash screen
@ -403,8 +435,14 @@ extern void pushBandscopeSettings ();
*/ */
uint32_t startFreq_IF = IF_SWEEP_START; uint32_t startFreq_IF = IF_SWEEP_START;
uint32_t stopFreq_IF = IF_SWEEP_STOP; uint32_t stopFreq_IF = IF_SWEEP_STOP;
uint32_t sigFreq_IF = 20000000; // 30 Mhz reference uint32_t sigFreq_IF = 15000000; // 15 Mhz reference
/*
* Variables for RXSweep Mode
*/
uint32_t startFreq_RX = RX_SWEEP_START;
uint32_t stopFreq_RX = RX_SWEEP_STOP;
uint32_t sigFreq_RX = 15000000; // 15 Mhz reference
/* /*
* Variables for timing websocket event checks: * Variables for timing websocket event checks:
@ -430,33 +468,33 @@ uint32_t sweepCount; // Used to inhibit handling Wifi until
/* /*
* These arrays contain the data for the various scan points; they are used in * These arrays contain the data for the various scan points; they are used in
* here and in the "simpleSA_wifi" modules: * here and in the "TinySA_wifi" modules:
*/ */
uint8_t myData[DISPLAY_POINTS+1]; uint8_t myData[SCREEN_WIDTH+1];
uint8_t myStorage[DISPLAY_POINTS+1]; uint8_t myStorage[SCREEN_WIDTH+1];
uint8_t myActual[DISPLAY_POINTS+1]; uint8_t myActual[SCREEN_WIDTH+1];
uint8_t myGain[DISPLAY_POINTS+1]; // Preamp gain uint8_t myGain[SCREEN_WIDTH+1]; // Preamp gain
uint32_t myFreq[DISPLAY_POINTS+1]; // Frequency for XML file uint32_t myFreq[SCREEN_WIDTH+1]; // Frequency for XML file
uint16_t peakLevel; // Current maximum signal level uint16_t peakLevel; // Current maximum signal level
uint16_t oldPeakLevel; // Old maximum signal level uint16_t oldPeakLevel; // Old maximum signal level
uint16_t peakIndex; uint16_t peakIndex;
uint16_t peakGain; // Actual gain at the peak (ie minimum gain) uint16_t peakGain; // Actual gain at the peak (ie minimum gain)
static int old_settingAttenuate = -1000; static int old_settingAttenuate = -1000;
static int old_settingPowerGrid = -1000; static int old_settingPowerGrid = -1000;
static int old_settingMax = -1; static int old_settingMax = -1;
static int old_settingMin = -1; static int old_settingMin = -1;
static long old_startFreq = -1; static double old_startFreq = -1;
static long old_stopFreq = -1; static double old_stopFreq = -1;
static int ownrbw = 0; static int ownrbw = 0;
static int old_ownrbw = -1; static int old_ownrbw = -1;
static int vbw = 0; static int vbw = 0;
static int old_vbw = -1; static int old_vbw = -1;
static int old_settingAverage = -1; static int old_settingAverage = -1;
static int old_settingSpur = -100; static int old_settingSpur = -100;
static int old_bandwidth = 0; static int old_bandwidth = 0;
int16_t standalone = true; int16_t standalone = true;
uint16_t spacing = 10000; uint16_t spacing = 10000;
@ -721,7 +759,7 @@ bool fsStatus = false; // True if SPIFFS loads ok
startAP (); // Start it up startAP (); // Start it up
#else // Connect to the router using SSID #else // Connect to the router using SSID
// and password defined in simpleSA.h // and password defined in TinySA.h
Serial.println ( "Connecting..." ); // Indicate trying to connect Serial.println ( "Connecting..." ); // Indicate trying to connect
@ -769,7 +807,7 @@ bool fsStatus = false; // True if SPIFFS loads ok
// tft.println("Setup Complete"); // tft.println("Setup Complete");
Serial.printf ( "\nsimpleSA Version %s Initialization Complete\n", PROGRAM_VERSION ); Serial.printf ( "\nTinySA Version %s Initialization Complete\n", PROGRAM_VERSION );
ShowMenu (); // Display the menu of commands on serial monitor ShowMenu (); // Display the menu of commands on serial monitor
@ -860,6 +898,10 @@ loopStartMicros = micros();
doIF_Sweep(); // IF Sweep doIF_Sweep(); // IF Sweep
break; break;
case RX_SWEEP:
doRX_Sweep(); // RX Sweep
break;
case BANDSCOPE: case BANDSCOPE:
doBandscope(); // Bandscope Sweep doBandscope(); // Bandscope Sweep
break; break;
@ -899,14 +941,19 @@ void setMode ( uint16_t newMode )
// initSigHigh(); // initSigHigh();
// break; // break;
case BANDSCOPE:
initBandscope();
break;
case IF_SWEEP: case IF_SWEEP:
initIF_Sweep(); initIF_Sweep();
break; break;
case BANDSCOPE: case RX_SWEEP:
initBandscope(); initRX_Sweep();
break; break;
default: default:
DisplayError ( ERR_WARN, DisplayError ( ERR_WARN,
"Invalid Mode!", "Invalid Mode!",
@ -952,6 +999,13 @@ void menuExit()
initIF_Sweep(); initIF_Sweep();
break; break;
case RX_SWEEP:
if ( setting.Mode == RX_SWEEP )
RedrawHisto();
else
initRX_Sweep();
break;
case BANDSCOPE: case BANDSCOPE:
if ( setting.Mode == BANDSCOPE ) if ( setting.Mode == BANDSCOPE )
RedrawHisto(); RedrawHisto();
@ -968,7 +1022,7 @@ void menuExit()
/* /*
* Initialise common to low, high and IF_Sweeps * Initialise common to low, high, RX and IF_Sweeps
*/ */
void init_sweep() void init_sweep()
{ {
@ -990,7 +1044,7 @@ void init_sweep()
img.setTextSize ( 1 ); img.setTextSize ( 1 );
img.setColorDepth ( 16 ); img.setColorDepth ( 16 );
img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
img.createSprite ( 2, GRID_HEIGHT + 1 ); // Only 2 columns wide img.createSprite ( 2, gridHeight + 1 ); // Only 2 columns wide
/* /*
@ -1033,21 +1087,22 @@ void init_sweep()
old_settingSpur = -100; old_settingSpur = -100;
old_bandwidth = 0; old_bandwidth = 0;
SetRX ( 0 ); // LO to transmit, RX to receive SetRX ( 0 ); // LO to transmit, RX to receive
xmit.SetOffset ( 0 ); // make sure frequency offset registers are zero
xmit.SetDrive ( setting.Drive ); // Set transmitter power level xmit.SetDrive ( setting.Drive ); // Set transmitter power level
rcvr.SetPreampGain ( setting.PreampGain ); rcvr.SetPreampGain ( setting.PreampGain );
#ifdef SI_TG_IF_CS #ifdef SI_TG_IF_CS
if (tgIF_OK) { if (tgIF_OK) {
tg_if.TxMode ( trackGenSetting.IF_Drive ); // turn on the IF oscillator in tracking generator tg_if.TxMode ( trackGenSetting.IF_Drive ); // turn on the IF oscillator in tracking generator
} }
#endif #endif
#ifdef SI_TG_LO_CS #ifdef SI_TG_LO_CS
if (tgLO_OK) { if (tgLO_OK) {
tg_lo.TxMode ( trackGenSetting.LO_Drive ); // turn on the Local Oscillator in tracking generator tg_lo.TxMode ( trackGenSetting.LO_Drive ); // turn on the Local Oscillator in tracking generator
} }
#endif #endif
@ -1331,7 +1386,17 @@ void RedrawHisto ()
old_settingAverage = -1; old_settingAverage = -1;
old_settingSpur = -100; old_settingSpur = -100;
ClearDisplay(); ClearDisplay();
DisplayInfo(); switch (tinySA_mode)
{
case BANDSCOPE:
DisplayBandscopeInfo();
break;
default:
DisplayInfo();
break;
}
DrawFullCheckerBoard(); DrawFullCheckerBoard();
} }
@ -1350,7 +1415,7 @@ double fStart;
double fCenter; double fCenter;
double fStop; 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 }; // 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, RX_SWEEP };
tSprite.fillSprite ( BLACK ); tSprite.fillSprite ( BLACK );
tSprite.setTextColor ( WHITE ); tSprite.setTextColor ( WHITE );
@ -1376,10 +1441,10 @@ double fStop;
sSprite.setCursor ( 0, CHAR_HEIGHT * 2 ); sSprite.setCursor ( 0, CHAR_HEIGHT * 2 );
sSprite.setTextColor ( DB_COLOR ); sSprite.setTextColor ( DB_COLOR );
sSprite.printf ( "%4i", setting.MaxGrid ); sSprite.printf ( "%4i", maxGrid );
sSprite.setCursor ( 0, GRID_HEIGHT + Y_ORIGIN ); sSprite.setCursor ( 0, gridHeight + yOrigin );
sSprite.printf ( "%4i", setting.MinGrid ); sSprite.printf ( "%4i", minGrid );
sSprite.setTextColor ( WHITE ); sSprite.setTextColor ( WHITE );
sSprite.setCursor ( 0, HALF_CHAR_H * 8 ); sSprite.setCursor ( 0, HALF_CHAR_H * 8 );
@ -1493,6 +1558,11 @@ double fStop;
fCenter = (double) ((( startFreq_IF + stopFreq_IF ) / 2.0 ) / 1000000.0 ); fCenter = (double) ((( startFreq_IF + stopFreq_IF ) / 2.0 ) / 1000000.0 );
fStop = stopFreq_IF/1000000.0; fStop = stopFreq_IF/1000000.0;
} }
else if (tinySA_mode == RX_SWEEP) {
fStart = startFreq_RX/1000000.0;
fCenter = (double) ((( startFreq_RX + stopFreq_RX ) / 2.0 ) / 1000000.0 );
fStop = stopFreq_RX/1000000.0;
}
else else
{ {
fStart = (( (double)setting.ScanStart )/ 1000000.0); // Start freq fStart = (( (double)setting.ScanStart )/ 1000000.0); // Start freq
@ -1503,16 +1573,16 @@ double fStop;
if ( old_startFreq != fStart || old_stopFreq != fStop ) if ( old_startFreq != fStart || old_stopFreq != fStop )
{ {
tft.fillRect ( X_ORIGIN, SCREEN_HEIGHT - tft.fillRect ( xOrigin, SCREEN_HEIGHT -
CHAR_HEIGHT, SCREEN_WIDTH - X_ORIGIN - 1, SCREEN_HEIGHT - 1, BLACK ); CHAR_HEIGHT, SCREEN_WIDTH - xOrigin - 1, SCREEN_HEIGHT - 1, BLACK );
// Show operating mode // Show operating mode
tft.setCursor ( X_ORIGIN + 50, SCREEN_HEIGHT - CHAR_HEIGHT ); tft.setCursor ( xOrigin + 50, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.setTextColor ( DB_COLOR ); tft.setTextColor ( DB_COLOR );
tft.printf ( "Mode:%s", modeText[setting.Mode] ); tft.printf ( "Mode:%s", modeText[setting.Mode] );
tft.setTextColor ( WHITE ); tft.setTextColor ( WHITE );
tft.setCursor ( X_ORIGIN + 2, SCREEN_HEIGHT - CHAR_HEIGHT ); tft.setCursor ( xOrigin + 2, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.print ( fStart ); tft.print ( fStart );
// tft.print( "MHz" ); // tft.print( "MHz" );
@ -1525,7 +1595,7 @@ double fStop;
/* /*
* Show the center frequency: * Show the center frequency:
*/ */
tft.setCursor ( SCREEN_WIDTH / 2 - 40 + X_ORIGIN, SCREEN_HEIGHT - CHAR_HEIGHT ); tft.setCursor ( SCREEN_WIDTH / 2 - 40 + xOrigin, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.print ( fCenter ); tft.print ( fCenter );
tft.print ( "(MHz)" ); tft.print ( "(MHz)" );
@ -1583,7 +1653,7 @@ double fStop;
int x = tSprite.width () - 45; int x = tSprite.width () - 45;
tSprite.setTextColor ( WHITE ); tSprite.setTextColor ( WHITE );
tSprite.pushSprite ( X_ORIGIN, 0 ); // Write sprite to the display tSprite.pushSprite ( xOrigin, 0 ); // Write sprite to the display
updateSidebar = false; updateSidebar = false;
} // End of "DisplayInfo" } // End of "DisplayInfo"
@ -1596,12 +1666,12 @@ double fStop;
void DrawFullCheckerBoard() void DrawFullCheckerBoard()
{ {
for ( int p=0; p<DISPLAY_POINTS; p++ ) for ( int p=0; p<displayPoints; p++ )
{ {
DrawCheckerBoard ( p ); DrawCheckerBoard ( p );
if ( p > 0 ) if ( p > 0 )
img.pushSprite( X_ORIGIN+p-1, Y_ORIGIN ); img.pushSprite( xOrigin+p-1, yOrigin );
} }
// Serial.println ( "DrawFullCheckerBoard" ); // Serial.println ( "DrawFullCheckerBoard" );
@ -1612,7 +1682,7 @@ void DrawFullCheckerBoard()
* "DrawCheckerBoard" - Paints the grid. It now uses a sprite so no need to * "DrawCheckerBoard" - Paints the grid. It now uses a sprite so no need to
* erase the old grid, just draw a new one. * erase the old grid, just draw a new one.
* *
* The sprite is two pixels wide, and the image from the previous data point * The img sprite is two pixels wide, and the image from the previous data point
* is scrolled and then new data point drawn. The data from last is scrolled * is scrolled and then new data point drawn. The data from last is scrolled
* so any line from before is retained. * so any line from before is retained.
*/ */
@ -1625,24 +1695,24 @@ void DrawCheckerBoard ( int x )
if ( x == 1 ) if ( x == 1 )
{ {
img.fillSprite ( BLACK ); // Clear the sprite img.fillSprite ( BLACK ); // Clear the sprite
img.drawFastVLine ( 0, 0, GRID_HEIGHT, DARKGREY ); // Draw vertical line at edge img.drawFastVLine ( 0, 0, gridHeight, DARKGREY ); // Draw vertical line at edge
for ( int y=0; y <= Y_GRID; y++ ) for ( int y=0; y <= yGrid; y++ )
img.drawPixel ( 1, y*DELTA_Y, DARKGREY ); // Draw the horizontal grid lines img.drawPixel ( 1, y*yDelta, DARKGREY ); // Draw the horizontal grid lines
} }
else else
{ {
img.setScrollRect ( 0, 0, 2,GRID_HEIGHT, BLACK ); // Scroll the whole sprite img.setScrollRect ( 0, 0, 2,gridHeight, BLACK ); // Scroll the whole sprite
img.scroll ( -1, 0 ); img.scroll ( -1, 0 );
int lastStep = x - 1; int lastStep = x - 1;
if (( x % DELTA_X ) == 0 ) // Need a vertical line here if (( x % xDelta ) == 0 ) // Need a vertical line here
img.drawFastVLine ( 1, 0, GRID_HEIGHT, DARKGREY ); img.drawFastVLine ( 1, 0, gridHeight, DARKGREY );
else else
for ( int y = 0; y <= Y_GRID; y++ ) for ( int y = 0; y <= yGrid; y++ )
img.drawPixel ( 1, y * DELTA_Y, DARKGREY ); // Draw the horizontal grid lines img.drawPixel ( 1, y * yDelta, DARKGREY ); // Draw the horizontal grid lines
} }
} }
@ -1654,18 +1724,18 @@ void DrawCheckerBoard ( int x )
uint16_t rssiToImgY ( uint8_t rSSI ) uint16_t rssiToImgY ( uint8_t rSSI )
{ {
int delta = setting.MaxGrid - setting.MinGrid; int delta = maxGrid - minGrid;
double y = rssiTodBm ( rSSI ); double y = rssiTodBm ( rSSI );
y = ( y - setting.MinGrid ) * GRID_HEIGHT / delta; y = ( y - minGrid ) * gridHeight / delta;
if ( y >= GRID_HEIGHT ) if ( y >= gridHeight )
y = GRID_HEIGHT-1; y = gridHeight-1;
if ( y < 0 ) if ( y < 0 )
y = 0; y = 0;
return GRID_HEIGHT - 1 - (int) y; return gridHeight - 1 - (int) y;
} }
@ -1679,6 +1749,16 @@ double rssiTodBm ( uint8_t rSSI )
} }
/*
* Function to convert rSSi to dBm
*/
uint8_t dBmToRSSI ( double dBm )
{
return ( 2 * ( dBm - setting.Attenuate + 120.0 - setting.LevelOffset ) );
}
/* /*
* "DisplayPoint" - Display a point on the chart. * "DisplayPoint" - Display a point on the chart.
* *
@ -1693,28 +1773,28 @@ void DisplayPoint ( uint8_t* data, int i, int color )
return; return;
int lastPoint = i - 1; int lastPoint = i - 1;
int delta = setting.MaxGrid - setting.MinGrid; int delta = maxGrid - minGrid;
double f0 = (( data[i] / 2.0 + setting.Attenuate ) - 120.0 ) + setting.LevelOffset; // Current point double f0 = (( data[i] / 2.0 + setting.Attenuate ) - 120.0 ) + setting.LevelOffset; // Current point
f0 = ( f0 - setting.MinGrid ) * GRID_HEIGHT / delta; f0 = ( f0 - minGrid ) * gridHeight / delta;
if ( f0 >= GRID_HEIGHT ) if ( f0 >= gridHeight )
f0 = GRID_HEIGHT-1; f0 = gridHeight-1;
if ( f0 < 0 ) if ( f0 < 0 )
f0 = 0; f0 = 0;
double f1 = (( data[lastPoint] / 2.0 + setting.Attenuate ) - 120.0) + setting.LevelOffset; // Previous point double f1 = (( data[lastPoint] / 2.0 + setting.Attenuate ) - 120.0) + setting.LevelOffset; // Previous point
f1 = ( f1 - setting.MinGrid ) * GRID_HEIGHT / delta; f1 = ( f1 - minGrid ) * gridHeight / delta;
if ( f1 >= GRID_HEIGHT ) if ( f1 >= gridHeight )
f1 = GRID_HEIGHT-1; f1 = gridHeight-1;
if ( f1 < 0 ) if ( f1 < 0 )
f1 = 0; f1 = 0;
int Y0 = GRID_HEIGHT - 1 - (int) f0; int Y0 = gridHeight - 1 - (int) f0;
int Y1 = GRID_HEIGHT - 1 - (int) f1; int Y1 = gridHeight - 1 - (int) f1;
img.drawLine ( 0, Y1, 1, Y0, color ); img.drawLine ( 0, Y1, 1, Y0, color );
} }
@ -1735,26 +1815,26 @@ void displayGainPoint ( unsigned char *data, int i, int color )
// Serial.printf ( "gain %f \n", f ); // Serial.printf ( "gain %f \n", f );
f0 = ( f0 ) * GRID_HEIGHT / delta; f0 = ( f0 ) * gridHeight / delta;
if ( f0 >= GRID_HEIGHT ) if ( f0 >= gridHeight )
f0 = GRID_HEIGHT - 1; f0 = gridHeight - 1;
if ( f0 < 0 ) if ( f0 < 0 )
f0 = 0; f0 = 0;
double f1 = ( data[lastPoint] ); double f1 = ( data[lastPoint] );
f1 = ( f1 ) * GRID_HEIGHT / delta; f1 = ( f1 ) * gridHeight / delta;
if ( f1 >= GRID_HEIGHT ) if ( f1 >= gridHeight )
f1 = GRID_HEIGHT - 1; f1 = gridHeight - 1;
if ( f1 < 0 ) if ( f1 < 0 )
f1 = 0; f1 = 0;
int Y0 = GRID_HEIGHT - 1 - (int) f0; int Y0 = gridHeight - 1 - (int) f0;
int Y1 = GRID_HEIGHT - 1 - (int) f1; int Y1 = gridHeight - 1 - (int) f1;
img.drawLine ( 0, Y1, 1, Y0, color ); img.drawLine ( 0, Y1, 1, Y0, color );
} }
@ -1773,7 +1853,7 @@ void CreateGainScale ()
gainScaleSprite.deleteSprite(); gainScaleSprite.deleteSprite();
gainScaleSprite.setAttribute ( PSRAM_ENABLE, false ); gainScaleSprite.setAttribute ( PSRAM_ENABLE, false );
gainScaleSprite.setColorDepth ( 16 ); // Using 16 bit (RGB565) colors gainScaleSprite.setColorDepth ( 16 ); // Using 16 bit (RGB565) colors
gainScaleSprite.createSprite(CHAR_WIDTH * 2, GRID_HEIGHT); gainScaleSprite.createSprite(CHAR_WIDTH * 2, gridHeight);
gainScaleSprite.fillSprite(BLACK); gainScaleSprite.fillSprite(BLACK);
gainScaleSprite.setPivot( 0, 0 ); gainScaleSprite.setPivot( 0, 0 );
gainScaleSprite.setTextSize ( 1 ); gainScaleSprite.setTextSize ( 1 );
@ -1790,11 +1870,60 @@ void CreateGainScale ()
for ( int i = 0; i < 5; i++ ) for ( int i = 0; i < 5; i++ )
{ {
gainScaleSprite.setCursor ( 0, 2 + ( DELTA_Y * i * 2 )); gainScaleSprite.setCursor ( 0, 2 + ( yDelta * i * 2 ));
gainScaleSprite.print ( 50 - ( i * 10 )); gainScaleSprite.print ( 50 - ( i * 10 ));
} }
} }
/*
* "CreateGridScale" puts the decibel values for the main trace into a sprite for
* display at the left side of the Bandscope grid.
* The gainScaleSprite is reused as there in no need for a gain trace on the bandscope
*
* NOTE Due to temporary error in TFT_eSPI library the inverted colour is used
* so TFT_BLUE actually results in TFT_GREEN!
*/
void CreateGridScale ()
{
gainScaleSprite.deleteSprite();
gainScaleSprite.setAttribute ( PSRAM_ENABLE, false );
gainScaleSprite.setColorDepth ( 16 ); // Using 16 bit (RGB565) colors
gainScaleSprite.createSprite(CHAR_WIDTH * 4, gridHeight);
gainScaleSprite.fillSprite(BLACK);
gainScaleSprite.setPivot( 0, 0 );
gainScaleSprite.setTextSize ( 1 );
gainScaleSprite.setTextColor ( TFT_WHITE );
int w = gainScaleSprite.width();
// Serial.printf("Create gain scale - get sprite width %i \n", w);
if ( w != CHAR_WIDTH * 4 )
{
Serial.println ( "Error - no gain scale sprite" );
return;
}
// Markers at every other line, there are 10 lines
int16_t valueInterval = (setting.BandscopeMaxGrid - setting.BandscopeMinGrid) / yGrid * 2 ;
int16_t v = setting.BandscopeMaxGrid;
int16_t yoffset;
for ( int i = 0; i < 5; i++ )
{
if (i > 0)
yoffset = -3;
else
yoffset = 0;
gainScaleSprite.setCursor ( 0, yoffset + ( yDelta * i * 2 ));
gainScaleSprite.printf ( "%4i", v );
v = v - valueInterval;
}
}
/* /*
* "ledcAnalogWrite" - Arduino like analogWrite used for PWM control of backlight. * "ledcAnalogWrite" - Arduino like analogWrite used for PWM control of backlight.
* *

View File

@ -16,8 +16,23 @@
#include "simpleSA_wifi.h" // WiFi definitions #include "simpleSA_wifi.h" // WiFi definitions
#include "Si4432.h" // Si4432 definitions #include "si4432.h" // Si4432 definitions
#include "Cmd.h" // Command processing functions #include "cmd.h" // Command processing functions
/*
* Variables to determine size of grid and waterfall
* In Bandscope mode the grid is reduced. In future it may be possible to add
* a waterfall to the main sweep, but not yet
*/
extern uint16_t gridHeight;
extern uint16_t gridWidth;
extern uint16_t yGrid; // no of grid divisions
extern uint16_t yDelta; // no of points/division
extern uint16_t xGrid;
extern uint16_t displayPoints;
extern uint16_t xDelta;
extern uint16_t waterfallHeight;
extern int bpfCount; // Number of elements in the bandpassFilters array extern int bpfCount; // Number of elements in the bandpassFilters array
extern int updateSidebar; // Flag to indicate no of clients has changed extern int updateSidebar; // Flag to indicate no of clients has changed
@ -376,7 +391,7 @@ void onGetScan ( AsyncWebServerRequest *request )
response->print ( "<?xml version=\"1.0\" encoding=\"utf-16\"?>" ); response->print ( "<?xml version=\"1.0\" encoding=\"utf-16\"?>" );
response->println ( "<Points>" ); response->println ( "<Points>" );
for( int i = 0; i < DISPLAY_POINTS-1; i++ ) // For each data point for( int i = 0; i < displayPoints-1; i++ ) // For each data point
{ {
// Serial.printf ( "<Point F=\"%i\" RSSI=\"%i\"/> %i\n",myFreq[i], myData[i], i ); // Serial.printf ( "<Point F=\"%i\" RSSI=\"%i\"/> %i\n",myFreq[i], myData[i], i );
response->printf ( "<P F=\"%i\" R=\"%i\"/>\n", myFreq[i], myData[i] ); response->printf ( "<P F=\"%i\" R=\"%i\"/>\n", myFreq[i], myData[i] );
@ -394,8 +409,8 @@ void onGetScan ( AsyncWebServerRequest *request )
void onGetGainSweep ( AsyncWebServerRequest *request ) void onGetGainSweep ( AsyncWebServerRequest *request )
{ {
size_t bufferSize = JSON_ARRAY_SIZE ( DISPLAY_POINTS ) size_t bufferSize = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ JSON_OBJECT_SIZE ( 1 ) + DISPLAY_POINTS * JSON_OBJECT_SIZE ( 2 ); + JSON_OBJECT_SIZE ( 1 ) + SCREEN_WIDTH * JSON_OBJECT_SIZE ( 2 );
AsyncJsonResponse * response = new AsyncJsonResponse ( false, bufferSize ); AsyncJsonResponse * response = new AsyncJsonResponse ( false, bufferSize );
// response->addHeader ( "Server","ESP Async Web Server" ); // response->addHeader ( "Server","ESP Async Web Server" );
@ -407,7 +422,7 @@ void onGetGainSweep ( AsyncWebServerRequest *request )
* Add the objects to the array * Add the objects to the array
*/ */
for ( int i = 0; i < DISPLAY_POINTS; i++ ) // For each data point for ( int i = 0; i < displayPoints; i++ ) // For each data point
{ {
JsonObject dataPoint = gainPoints.createNestedObject(); // Add an object to the array JsonObject dataPoint = gainPoints.createNestedObject(); // Add an object to the array
dataPoint["x"] = myFreq[i]/1000000.0; // set the x(frequency) value dataPoint["x"] = myFreq[i]/1000000.0; // set the x(frequency) value
@ -426,14 +441,14 @@ void onGetGainSweep ( AsyncWebServerRequest *request )
void onGetSweep ( AsyncWebServerRequest *request ) void onGetSweep ( AsyncWebServerRequest *request )
{ {
size_t bufferSize = JSON_ARRAY_SIZE ( DISPLAY_POINTS ) size_t bufferSize = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ JSON_OBJECT_SIZE ( 14 ) + DISPLAY_POINTS * JSON_OBJECT_SIZE ( 2 ); + JSON_OBJECT_SIZE ( 14 ) + SCREEN_WIDTH * JSON_OBJECT_SIZE ( 2 );
AsyncJsonResponse * response = new AsyncJsonResponse ( false, bufferSize ); AsyncJsonResponse * response = new AsyncJsonResponse ( false, bufferSize );
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["dispPoints"] = DISPLAY_POINTS; root["dispPoints"] = displayPoints;
root["start"] = setting.ScanStart / 1000.0; root["start"] = setting.ScanStart / 1000.0;
root["stop"] = setting.ScanStop / 1000.0; root["stop"] = setting.ScanStop / 1000.0;
root["IF"] = setting.IF_Freq / 1000000.0; root["IF"] = setting.IF_Freq / 1000000.0;
@ -454,7 +469,7 @@ void onGetSweep ( AsyncWebServerRequest *request )
JsonArray Points = root.createNestedArray ( "Points" ); // Add Points array JsonArray Points = root.createNestedArray ( "Points" ); // Add Points array
for ( int i = 0; i < DISPLAY_POINTS; i++ ) //For each data point for ( int i = 0; i < displayPoints; i++ ) //For each data point
{ {
JsonObject dataPoint = Points.createNestedObject(); // add an object to the array JsonObject dataPoint = Points.createNestedObject(); // add an object to the array
dataPoint["x"] = myFreq[i]/1000000.0; // set the x(frequency) value dataPoint["x"] = myFreq[i]/1000000.0; // set the x(frequency) value
@ -477,7 +492,7 @@ void onGetSettings (AsyncWebServerRequest *request)
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["mType"] = "Settings"; root["mType"] = "Settings";
root["dispPoints"] = DISPLAY_POINTS; root["dispPoints"] = displayPoints;
root["start"] = setting.ScanStart / 1000.0; root["start"] = setting.ScanStart / 1000.0;
root["stop"] = setting.ScanStop / 1000.0; root["stop"] = setting.ScanStop / 1000.0;
root["IF"] = setting.IF_Freq / 1000000.0; root["IF"] = setting.IF_Freq / 1000000.0;
@ -507,12 +522,12 @@ void onGetSettings (AsyncWebServerRequest *request)
*/ */
void pushSettings () void pushSettings ()
{ {
size_t capacity = JSON_ARRAY_SIZE ( DISPLAY_POINTS ) size_t capacity = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ DISPLAY_POINTS*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 ); + SCREEN_WIDTH*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 );
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
jsonDocument["mType"] = "Settings"; jsonDocument["mType"] = "Settings";
jsonDocument["dispPoints"] = DISPLAY_POINTS; jsonDocument["dispPoints"] = displayPoints;
jsonDocument["start"] = setting.ScanStart / 1000.0; jsonDocument["start"] = setting.ScanStart / 1000.0;
jsonDocument["stop"] = setting.ScanStop / 1000.0; jsonDocument["stop"] = setting.ScanStop / 1000.0;
jsonDocument["IF"] = setting.IF_Freq / 1000000.0; jsonDocument["IF"] = setting.IF_Freq / 1000000.0;
@ -551,12 +566,12 @@ static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to
*/ */
void pushIFSweepSettings () void pushIFSweepSettings ()
{ {
size_t capacity = JSON_ARRAY_SIZE ( DISPLAY_POINTS ) size_t capacity = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ DISPLAY_POINTS*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 ); + SCREEN_WIDTH*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 );
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
jsonDocument["mType"] = "Settings"; jsonDocument["mType"] = "Settings";
jsonDocument["dispPoints"] = DISPLAY_POINTS; jsonDocument["dispPoints"] = displayPoints;
jsonDocument["start"] = startFreq_IF / 1000.0; jsonDocument["start"] = startFreq_IF / 1000.0;
jsonDocument["stop"] = stopFreq_IF / 1000.0; jsonDocument["stop"] = stopFreq_IF / 1000.0;
jsonDocument["IF"] = sigFreq_IF / 1000000.0; jsonDocument["IF"] = sigFreq_IF / 1000000.0;
@ -594,8 +609,8 @@ static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to
*/ */
void pushBandscopeSettings () void pushBandscopeSettings ()
{ {
size_t capacity = JSON_ARRAY_SIZE ( DISPLAY_POINTS ) size_t capacity = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ DISPLAY_POINTS*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 ); + SCREEN_WIDTH*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 );
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
jsonDocument["mType"] = "Settings"; jsonDocument["mType"] = "Settings";

View File

@ -9,7 +9,7 @@
#include "Arduino.h" // Basic Arduino definitions #include "Arduino.h" // Basic Arduino definitions
#include "simpleSA.h" // Program definitions #include "simpleSA.h" // Program definitions
#include "Si4432.h" // RF module definitions #include "si4432.h" // RF module definitions
#include <WiFi.h> // WiFi library #include <WiFi.h> // WiFi library
#include <AsyncTCP.h> #include <AsyncTCP.h>
@ -71,11 +71,11 @@
extern settings_t setting; extern settings_t setting;
extern uint8_t myData[DISPLAY_POINTS+1]; extern uint8_t myData[SCREEN_WIDTH+1];
extern uint8_t myStorage[DISPLAY_POINTS+1]; extern uint8_t myStorage[SCREEN_WIDTH+1];
extern uint8_t myActual[DISPLAY_POINTS+1]; extern uint8_t myActual[SCREEN_WIDTH+1];
extern uint8_t myGain[DISPLAY_POINTS+1]; // M0WID addition to record preamp gain extern uint8_t myGain[SCREEN_WIDTH+1]; // M0WID addition to record preamp gain
extern uint32_t myFreq[DISPLAY_POINTS+1]; // M0WID addition to store frequency for XML file extern uint32_t myFreq[SCREEN_WIDTH+1]; // M0WID addition to store frequency for XML file
extern WebSocketsServer webSocket; // Initiated in TinySA.ino extern WebSocketsServer webSocket; // Initiated in TinySA.ino

360
ui.cpp
View File

@ -30,12 +30,12 @@
#include <SPI.h> // SPI Bus handling library #include <SPI.h> // SPI Bus handling library
#include "simpleSA.h" #include "simpleSA.h"
#include "ui.h" // User interface definitions and prototypes #include "ui.h" // User interface definitions and prototypes
#include "Cmd.h" // Command processing functions #include "cmd.h" // Command processing functions
#include "preferences.h" // Save/recall functions #include "preferences.h" // Save/recall functions
#include <TFT_eSPI.h> // Display/Touch Screen header file #include <TFT_eSPI.h> // Display/Touch Screen header file
#include "Menu.h" // "Menuitem" class definition #include "menu.h" // "Menuitem" class definition
#include "Marker.h" // Marker class definition #include "marker.h" // Marker class definition
#include "Si4432.h" // Si4432 class definition #include "si4432.h" // Si4432 class definition
extern Marker marker[MARKER_COUNT]; // Array of markers extern Marker marker[MARKER_COUNT]; // Array of markers
@ -94,8 +94,8 @@ typedef struct {
// to '6' below! // to '6' below!
int8_t digit_mode; int8_t digit_mode;
int8_t current_trace; // 0 to 3 ??? int8_t current_trace; // 0 to 3 ???
int32_t value; // For editing at numeric input area double value; // For editing at numeric input area
int32_t previous_value; double previous_value;
} uistat_t; } uistat_t;
@ -107,8 +107,8 @@ uistat_t uistat = {
6, // digit - See note above 6, // digit - See note above
0, // digit_mode 0, // digit_mode
0, // current_trace 0, // current_trace
0, // value 0.0, // value
0 // previous_value 0.0 // previous_value
}; };
@ -197,7 +197,8 @@ uint8_t operation_requested = OP_NONE; // No operations so far
enum { KM_START, KM_STOP, KM_CENTER, KM_SPAN, KM_FOCUS, enum { KM_START, KM_STOP, KM_CENTER, KM_SPAN, KM_FOCUS,
KM_REFPOS, KM_ATTENUATION, KM_ACTUALPOWER, KM_IFFREQ, KM_PREAMP, KM_REFPOS, KM_ATTENUATION, KM_ACTUALPOWER, KM_IFFREQ, KM_PREAMP,
KM_TUNE, KM_SGFREQ, KM_SGLEVEL, KM_SGLEVCAL, KM_IFSTART, KM_IFSTOP, KM_TUNE, KM_SGFREQ, KM_SGLEVEL, KM_SGLEVCAL, KM_IFSTART, KM_IFSTOP,
KM_IFSIG, KM_TGOFFSET, KM_TGLO_DRIVE, KM_TGIF_DRIVE, KM_BANDSCOPESTART, KM_BANDSCOPESPAN }; KM_IFSIG, KM_TGOFFSET, KM_TGLO_DRIVE, KM_TGIF_DRIVE, KM_BANDSCOPESTART,
KM_WFMIN, KM_WFGAIN, KM_BANDSCOPELEVEL, KM_RXSPAN, KM_RXSIG, KM_RBW };
/* /*
@ -214,7 +215,8 @@ static const char * const keypad_mode_label[] =
"START", "STOP", "CENTER", "SPAN", "FOCUS", "START", "STOP", "CENTER", "SPAN", "FOCUS",
"REFPOS", "ATTEN", "POWER", "IF FREQ", "PREAMP", "REFPOS", "ATTEN", "POWER", "IF FREQ", "PREAMP",
"XTAL CAL", "SG FREQ", "dBm", "Max dBm", "IF START", "IF STOP", "XTAL CAL", "SG FREQ", "dBm", "Max dBm", "IF START", "IF STOP",
"IF Sig Freq", "TG OFFSET", "TG LO Drive", "TG IF Drive", "START", "SPAN" "IF Sig Freq", "TG OFFSET", "TG LO Drive", "TG IF Drive", "START",
"WF MIN", "WF GAIN", "REF LEVEL", "RX SPAN", "RX Sig Freq", "RBW"
}; };
@ -272,10 +274,18 @@ static void menu_format_cb ( int item );
static void menu_scale_cb ( int item ); static void menu_scale_cb ( int item );
static void menu_sweep_cb ( int item ); static void menu_sweep_cb ( int item );
static void menu_IF_sweep_cb ( int item ); // M0WID added 3.0c static void menu_IF_sweep_cb ( int item ); // M0WID added 3.0c
static void menu_RX_sweep_cb ( int item );
static void menu_recall_cb ( int item ); static void menu_recall_cb ( int item );
static void menu_version_cb (int item ); static void menu_version_cb (int item );
static void menu_generate_cb(int item); // WA2FZW - Added in M0WID's Version 05 static void menu_generate_cb(int item); // WA2FZW - Added in M0WID's Version 05
static void menu_Bandscope_cb ( int item ); // M0WID added 3.0f
// forward declarations for bandscope menu items
static void menu_BandscopeStart_cb ( int item );
static void menu_BandscopeRbw_cb ( int item );
static void menu_BandscopeSpan_cb ( int item );
static void menu_BandscopeLevel_cb ( int item );
static void menu_WaterfallMin_cb ( int item );
static void menu_WaterfallGain_cb ( int item );
static void menu_tracking_cb(int item); // M0WID - added in 3.0e static void menu_tracking_cb(int item); // M0WID - added in 3.0e
static void menu_tg_offset_cb(int item); // M0WID - added in 3.0e static void menu_tg_offset_cb(int item); // M0WID - added in 3.0e
@ -433,8 +443,9 @@ static Menuitem menu_mode[] = // Select mode menu
{ {
Menuitem ( MT_FUNC, "\2SWEEP\0LOW", menu_mode_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0LOW", menu_mode_cb ),
Menuitem ( MT_FUNC, "\2SIG\0GEN", menu_mode_cb ), Menuitem ( MT_FUNC, "\2SIG\0GEN", menu_mode_cb ),
Menuitem ( MT_FUNC, "\2IF\0SWEEP", menu_mode_cb ),
Menuitem ( MT_FUNC, "\2BAND\0SCOPE",menu_mode_cb ), Menuitem ( MT_FUNC, "\2BAND\0SCOPE",menu_mode_cb ),
Menuitem ( MT_FUNC, "\2IF\0SWEEP", menu_mode_cb ),
Menuitem ( MT_FUNC, "\2RX\0SWEEP", menu_mode_cb ),
Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_BACK, "<-BACK" ), // Next level up
Menuitem ( MT_END ) // End marker Menuitem ( MT_END ) // End marker
}; };
@ -730,15 +741,51 @@ static Menuitem menu_IFsweep_top[] = // This is the main "IF_SWEEP" menu
}; };
static Menuitem menu_Bandscope_top[] = // This is the main "IF_SWEEP" menu static Menuitem menu_RXsweep_top[] = // This is the main "IF_SWEEP" menu
{ {
Menuitem ( MT_MENU, "MODE", menu_mode ), Menuitem ( MT_MENU, "MODE", menu_mode ),
Menuitem ( MT_FUNC, "\2SWEEP\0START", menu_Bandscope_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0SPAN", menu_RX_sweep_cb ),
Menuitem ( MT_FUNC, "\2SWEEP\0SPAN", menu_Bandscope_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0SIG", menu_RX_sweep_cb ),
Menuitem ( MT_FUNC, "RBW", menu_RX_sweep_cb ),
Menuitem ( MT_MENU, "REFERENCE", menu_refer ), // Select GPIO2 reference frequency
Menuitem ( MT_END ) // End marker Menuitem ( MT_END ) // End marker
}; };
static Menuitem menu_BandscopeSpan[] = // Badscope Span settings
{
Menuitem ( MT_FUNC, "200kHz", menu_BandscopeSpan_cb ),
Menuitem ( MT_FUNC, "400kHz", menu_BandscopeSpan_cb ),
Menuitem ( MT_BACK, "<-BACK" ), // Next level up
Menuitem ( MT_END ) // End marker
};
static Menuitem menu_BandscopeRBW[] = // Resolution bandwidth settings
{
Menuitem ( MT_FUNC, "2.6kHz", menu_BandscopeRbw_cb ), // In auto mode, there are many
Menuitem ( MT_FUNC, "2.8kHz", menu_BandscopeRbw_cb ), // more available settings that
Menuitem ( MT_FUNC, "3.1kHz", menu_BandscopeRbw_cb ), // are roughly the sweep range
Menuitem ( MT_FUNC, "3.7kHz", menu_BandscopeRbw_cb ), // divided by 300.
Menuitem ( MT_FUNC, "4.2kHz", menu_BandscopeRbw_cb ),
Menuitem ( MT_FUNC, "5.4kHz", menu_BandscopeRbw_cb ),
Menuitem ( MT_BACK, "<-BACK" ), // Next level up
Menuitem ( MT_END ) // End marker
};
static Menuitem menu_Bandscope_top[] = // This is the main "IF_SWEEP" menu
{
Menuitem ( MT_MENU, "MODE", menu_mode ),
Menuitem ( MT_FUNC, "\2SWEEP\0START", menu_BandscopeStart_cb ),
Menuitem ( MT_MENU, "\2SWEEP\0SPAN", menu_BandscopeSpan ),
Menuitem ( MT_MENU, "RBW", menu_BandscopeRBW ),
Menuitem ( MT_FUNC, "\2REF\0LEVEL", menu_BandscopeLevel_cb ), // Set top line of the grid
Menuitem ( MT_FUNC, "\2W'FALL\0MIN", menu_WaterfallMin_cb ),
Menuitem ( MT_FUNC, "\2W'FALL\0GAIN", menu_WaterfallGain_cb ),
Menuitem ( MT_END ) // End marker
};
/* /*
* And last but not least the main menu! * And last but not least the main menu!
@ -1111,13 +1158,13 @@ void ShowSplash ( void )
tft.setTextColor ( MAGENTA ); tft.setTextColor ( MAGENTA );
tft.setFreeFont ( &FreeSerifBoldItalic18pt7b ); // Select Free Serif 9 point font tft.setFreeFont ( &FreeSerifBoldItalic18pt7b ); // Select Free Serif 9 point font
tft.drawString ( "TinySA for ESP32", 160, 20 ); tft.drawString ( "simpleSA for ESP32", 160, 20 );
tft.setTextColor ( WHITE ); tft.setTextColor ( WHITE );
tft.setFreeFont ( &FreeSansBold9pt7b ); // Select Free Serif 9 point font tft.setFreeFont ( &FreeSansBold9pt7b ); // Select Free Serif 9 point font
tft.drawString ( "By WA2FZW, M0WID,", 160, 60 ); tft.drawString ( "By WA2FZW, M0WID,", 160, 60 );
tft.drawString ( "VK3PE and G3ZQC", 160, 80 ); tft.drawString ( "VK3PE and G3ZQC", 160, 80 );
tft.drawString ( "Version 3.0", 160, 100 ); tft.drawString ( "Version 0.01", 160, 100 );
tft.drawString ( "Original by Erik (PD0EK)", 160, 120 ); tft.drawString ( "Original tinySA by Erik (PD0EK)", 160, 120 );
tft.setTextDatum ( TL_DATUM ); // Back to default top left tft.setTextDatum ( TL_DATUM ); // Back to default top left
@ -1161,15 +1208,22 @@ void menu_mode_cb ( int item )
break; break;
case 2: // Set IF Sweep mode case 2: // Set IF Sweep mode
setMode(IF_SWEEP); setMode(BANDSCOPE);
ui_mode_normal (); // No menu displayed ui_mode_normal (); // No menu displayed
break; break;
case 3: // Set IF Sweep mode case 3: // Set IF Sweep mode
setMode(BANDSCOPE); setMode(IF_SWEEP);
ui_mode_normal (); // No menu displayed ui_mode_normal (); // No menu displayed
break; break;
case 4: // Set RX Sweep mode
setMode(RX_SWEEP);
ui_mode_normal (); // No menu displayed
break;
} }
changedSetting = true;
} }
@ -1395,7 +1449,7 @@ static void menu_tracking_cb (int item )
} }
/* /*
* "menu_autosettings_cb" seems to set all the defaults then clears the menu * "menu_autosettings_cb" sets defaults then clears the menu
* from the display. * from the display.
* *
* Modified in Version 2.5 by WA2FZW: * Modified in Version 2.5 by WA2FZW:
@ -1432,13 +1486,13 @@ static void menu_touch_cb ( int item )
{ {
switch ( item ) switch ( item )
{ {
case 0: // All these need symbols! case 0: // Touch calibrate
touch_cal_exec (); touch_cal_exec ();
request_to_redraw_grid (); request_to_redraw_grid ();
draw_menu (); draw_menu ();
break; break;
case 1: case 1: // Touch Draw test
touch_draw_test (); touch_draw_test ();
request_to_redraw_grid (); request_to_redraw_grid ();
draw_menu (); draw_menu ();
@ -1739,12 +1793,100 @@ static void menu_IF_sweep_cb ( int item )
ui_process_keypad (); ui_process_keypad ();
} }
static void menu_Bandscope_cb ( int item )
static void menu_RX_sweep_cb ( int item )
{ {
ui_mode_keypad ( item + KM_BANDSCOPESTART - 1 ); // item = 1 -> KM_BANDSCOPESTART, 2 -> KM_BANDSCOPESPAN ui_mode_keypad ( item + KM_RXSPAN - 1 ); // item = 1 -> KM_RXSPAN, 2 -> KM_RXSIG
ui_process_keypad (); ui_process_keypad ();
} }
/*
* *********************************************
* BANDSCOPE MENU ITEMS
* *********************************************
*/
/*
* Handles the Bandscope RBW menu item.
*
*/
static void menu_BandscopeRbw_cb ( int item )
{
const int rbwsel[] = { 0, 26, 28, 31, 37 , 42, 54 }; // Resolution bandwidth choices (in KHz * 10)
SetBandscopeRBW ( rbwsel[item] );
menu_move_back ();
ui_mode_normal ();
}
/*
* Set the span for the bandscope
*/
static void menu_BandscopeSpan_cb ( int item )
{
switch ( item )
{
case 0: // 200kHz
setting.BandscopeSpan = 200000;
break;
case 1: // 400kHz
setting.BandscopeSpan = 400000;
break;
}
WriteSettings ();
menu_move_back ();
ui_mode_normal ();
}
/*
* Set the level of the top of the bandscope scale
*/
static void menu_BandscopeLevel_cb ( int item )
{
ui_mode_keypad ( KM_BANDSCOPELEVEL );
ui_process_keypad ();
}
/*
* Set the min for the bandscope waterfall colours
*/
static void menu_WaterfallMin_cb ( int item )
{
ui_mode_keypad ( KM_WFMIN );
ui_process_keypad ();
}
/*
* Set the gain for the bandscope waterfall colours
*/
static void menu_WaterfallGain_cb ( int item )
{
ui_mode_keypad ( KM_WFGAIN );
ui_process_keypad ();
}
/*
* Set the start frequency of the bandscope sweep
*/
static void menu_BandscopeStart_cb ( int item )
{
ui_mode_keypad ( KM_BANDSCOPESTART );
ui_process_keypad ();
}
/* /*
* "ensure_selection" - Validates that a menu selection is valid * "ensure_selection" - Validates that a menu selection is valid
*/ */
@ -1982,7 +2124,13 @@ static const keypads_t * const keypads_mode_tbl[] =
keypads_level, // KM_TGLO_DRIVE.....Tracking generator LO drive keypads_level, // KM_TGLO_DRIVE.....Tracking generator LO drive
keypads_level, // KM_TGIF_DRIVE.....Tracking generator IF drive keypads_level, // KM_TGIF_DRIVE.....Tracking generator IF drive
keypads_freq, // KM_BANDSCOPESTART.IF Sweep start frequency keypads_freq, // KM_BANDSCOPESTART.IF Sweep start frequency
keypads_freq // KM_BANDSCOPESPAN..IF Sweep stop frequency keypads_integer, // KM_WFMIN..........Waterfall min level (RSSI)
keypads_level, // KM_WFGAIN.........Waterfall Gain
keypads_level, // KM_BANDSCOPELEVEL.Grid reference level
keypads_freq, // KM_RXSPAN.........RX Sweep span frequency
keypads_freq, // KM_RXSIG..........RX Sweep signal frequency
keypads_freq // KM_RBW............RX Sweep RBW
}; };
@ -2373,10 +2521,30 @@ static void fetch_numeric_target ( void )
uistat.value = setting.BandscopeStart; uistat.value = setting.BandscopeStart;
break; break;
case KM_BANDSCOPESPAN: case KM_WFMIN:
uistat.value = setting.BandscopeSpan; uistat.value = setting.WaterfallMin;
break;
case KM_WFGAIN:
uistat.value = setting.WaterfallGain;
break;
case KM_BANDSCOPELEVEL:
uistat.value = setting.BandscopeMaxGrid;
break;
case KM_RXSPAN:
uistat.value = GetRXsweepSpan();
break; break;
case KM_RXSIG:
uistat.value = GetRXsweepSigFreq();
break;
case KM_RBW:
uistat.value = (double)setting.Bandwidth10 / 10.0;
break;
} }
uint32_t x = uistat.value; uint32_t x = uistat.value;
@ -2388,7 +2556,7 @@ static void fetch_numeric_target ( void )
uistat.digit = n; uistat.digit = n;
uistat.previous_value = uistat.value; uistat.previous_value = uistat.value;
Serial.printf("uistat previous value %f\n", uistat.previous_value ); // Serial.printf("uistat previous value %f\n", uistat.previous_value );
} }
@ -2792,11 +2960,51 @@ static int keypad_click ( int key )
break; break;
case KM_BANDSCOPESPAN: // Bandscope span entered? case KM_WFMIN: // Bandscope span entered?
SetBandscopeSpan (( int32_t ) value ); if ( (value >= 0) && (value <= 200 ) )
setting.WaterfallMin = ( int16_t )value ;
else
{
DisplayError ( ERR_WARN,
"Invalid minimum level!",
"(0 - 200)",
NULL,
NULL );
}
break; break;
case KM_WFGAIN: // Bandscope span entered?
if ( (value >= 0.1) && (value <= 4.0 ) )
setting.WaterfallGain = value ;
else
{
DisplayError ( ERR_WARN,
"Invalid gain!",
"(0.1 - 4)",
NULL,
NULL );
}
break;
case KM_BANDSCOPELEVEL: // Bandscope level entered?
SetBandscopeLevel (( int32_t ) value );
break;
case KM_RXSPAN: // RX Span frequency entered?
Serial.printf( "Value = %f\n", value );
SetRXsweepSpan (( int32_t ) value );
break;
case KM_RXSIG: // RX Signal frequency entered?
SetRXsweepSigFreq (( int32_t ) value );
break;
case KM_RBW: // RBW
SetRBW (( int32_t ) (value * 10) );
break;
} // End of "switch" } // End of "switch"
return KP_DONE; // Indicate finished with the keypad return KP_DONE; // Indicate finished with the keypad
@ -2990,41 +3198,46 @@ void UiProcessTouch ( void )
Serial.printf("x:%i y:%i\n", touch_x, touch_y); Serial.printf("x:%i y:%i\n", touch_x, touch_y);
touch_wait_release (); touch_wait_release ();
/*
// test to see if the touch is in the marker area * Some touchscreen areas have fast access to the submenu levels, depends on the mode
if ( (touch_y < 10) && (touch_x < 160) ) */
{ if ( setting.Mode == SA_LOW_RANGE )
marker[0].Toggle(); // marker 1 {
return; // test to see if the touch is in the marker area
} if ( (touch_y < 10) && (touch_x < 160) )
else if ( (touch_y < 10) && (touch_x > 160) ) {
{ marker[0].Toggle(); // marker 1
marker[2].Toggle(); // marker 3 return;
return; }
} else if ( (touch_y < 10) && (touch_x > 160) )
else if ( (touch_y < 20) && (touch_x < 160) ) {
{ marker[2].Toggle(); // marker 3
marker[1].Toggle(); // marker 2 return;
return; }
} else if ( (touch_y < 20) && (touch_x < 160) )
else if ( (touch_y < 20) && (touch_x > 160) ) {
{ marker[1].Toggle(); // marker 2
marker[3].Toggle(); // marker 4 return;
return; }
} else if ( (touch_y < 20) && (touch_x > 160) )
else if ( (touch_y < 40) && (touch_x > 30) ) {
StartMarkerMenu(); marker[3].Toggle(); // marker 4
else if ( (touch_y > 210) && ( setting.Mode == SA_LOW_RANGE ) ) return;
StartSweepMenu(); }
else if ( (touch_x < 30) && (touch_y < 60) && ( setting.Mode == SA_LOW_RANGE ) ) else if ( (touch_y < 40) && (touch_x > 30) )
StartRBWMenu(); StartMarkerMenu();
else if ( (touch_x < 30) && (touch_y > CHAR_HEIGHT * 20 ) && ( touch_y < CHAR_HEIGHT * 22 ) && ( setting.Mode == SA_LOW_RANGE ) ) else if ( touch_y > 210 )
{ StartSweepMenu();
SetSpur (!setting.Spur); else if ( (touch_x < 30) && (touch_y < 60) )
return; StartRBWMenu();
} else if ( (touch_x < 30) && (touch_y > CHAR_HEIGHT * 20 ) && ( touch_y < CHAR_HEIGHT * 22 ) )
else if ( (touch_x < 30) && ( setting.Mode == SA_LOW_RANGE ) ) {
StartDisplayMenu(); SetSpur (!setting.Spur);
return;
}
else if ( (touch_x < 30) )
StartDisplayMenu();
}
selection = -1; // Switch menu mode selection = -1; // Switch menu mode
bg = BLACK; // black background bg = BLACK; // black background
@ -3160,6 +3373,21 @@ void ResetIFsweepMenuStack (void)
ui_mode_normal (); ui_mode_normal ();
} }
/*
* Resets the menu stack to root level for RX_SWEEP mode
*/
void ResetRXsweepMenuStack (void)
{
tft.unloadFont();
selection = -1; // Switch menu mode
menu_current_level = 0;
menu_stack[0] = menu_RXsweep_top;
menu_stack[1] = NULL;
menu_stack[2] = NULL;
menu_stack[3] = NULL;
if (ui_mode != UI_NORMAL)
ui_mode_normal ();
}
/* /*