From 6479c6050b23a14e9c55d859e649a873e9339bd7 Mon Sep 17 00:00:00 2001 From: M0WID Date: Fri, 21 Aug 2020 22:02:22 +0100 Subject: [PATCH] RXSweep mode added Also scale for bandscope --- Bandscope.ino | 8 +- RXsweep.ino | 508 ++++++++++++++++++++++++++++++++++++++++++++++++++ SweepLo.ino | 33 ++-- cmd.cpp | 223 +++++++++++++++++++++- cmd.h | 21 ++- my_SA.h | 12 ++ si4432.cpp | 49 +++-- simpleSA.h | 8 +- simpleSA.ino | 99 +++++++++- ui.cpp | 97 ++++++++-- 10 files changed, 989 insertions(+), 69 deletions(-) create mode 100644 RXsweep.ino diff --git a/Bandscope.ino b/Bandscope.ino index 96c79a6..5ee0bf5 100644 --- a/Bandscope.ino +++ b/Bandscope.ino @@ -54,7 +54,7 @@ void initBandscope() /* * Create and draw the sprite for the gain scale */ - CreateGainScale (); + CreateGridScale (); // Make sure everything will be reset old_settingAttenuate = -1000; @@ -457,10 +457,10 @@ static uint16_t chunkIndex; if ( setting.ShowStorage ) DisplayPoint ( myStorage, tmp, STORAGE_COLOR ); - // If in the last few points and gain trace is displayed show the gain scale - if ( setting.ShowGain && (oldSweepStep > setting.BandscopePoints - 2 * CHAR_WIDTH) ) + // If in the first few points show the scale + if ( ( tmp < 4 * CHAR_WIDTH ) && (tmp > 0) ) { - int16_t scaleX = setting.BandscopePoints - 2 * CHAR_WIDTH - tmp + 1; // relative to the img sprite + int16_t scaleX = -tmp + 1; // relative to the img sprite img.setPivot( scaleX, 0); gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour } diff --git a/RXsweep.ino b/RXsweep.ino new file mode 100644 index 0000000..c28d0b6 --- /dev/null +++ b/RXsweep.ino @@ -0,0 +1,508 @@ + +/* + * 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(); + + 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 )" + + +} diff --git a/SweepLo.ino b/SweepLo.ino index 6a2d701..cc67e7e 100644 --- a/SweepLo.ino +++ b/SweepLo.ino @@ -70,6 +70,7 @@ static bool resetAverage; // Flag to indicate a setting has changed and avera static bool jsonDocInitialised = false; static uint16_t chunkIndex; +static uint32_t offsetIF; // IF frequency offset by half the bandwidth to position in the centre of the filter @@ -111,6 +112,12 @@ static uint16_t chunkIndex; 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. * Work out how many points needed for the whole sweep: @@ -186,11 +193,13 @@ static uint16_t chunkIndex; uint32_t IF_Shift = ownrbw * 1000; // bandwidth in Hz if (IF_Shift > MAX_IF_SHIFT) IF_Shift = MAX_IF_SHIFT; - tempIF = setting.IF_Freq - IF_Shift; + tempIF = offsetIF - IF_Shift; } else { - tempIF = setting.IF_Freq; + tempIF = offsetIF; } + + spurToggle = !spurToggle; //Serial.printf("tempIF %u, spurOffset=%i, spur:%i, Toggle:%i\n", tempIF, tempIF - setting.IF_Freq, setting.Spur, spurToggle); @@ -514,15 +523,15 @@ static uint16_t chunkIndex; if (( autoSweepFreq > MARKER_MIN_FREQUENCY ) && (oldSweepStep > 0)) { -// if ( maxRSSI <= myActual[oldSweepStep] ) -// { -// maxIndex = oldSweepStep; -// maxRSSI = myActual[oldSweepStep]; -// maxFreq = oldSweepFreq; -// -//// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep); -//// displayPeakData (); -// } + if ( maxRSSI <= myActual[oldSweepStep] ) + { + maxIndex = oldSweepStep; + maxRSSI = myActual[oldSweepStep]; + maxFreq = oldSweepFreq; + +// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep); +// displayPeakData (); + } /* @@ -629,7 +638,7 @@ static uint16_t chunkIndex; // Serial.printf("MaxRSSI = %i, freq = %i\n", maxRSSI, maxFreq); - oldPeakLevel = peakLevel; //Save value of peak level for use by the "SetPowerLevel" function + 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 { diff --git a/cmd.cpp b/cmd.cpp index 46f8af3..4f1a0f6 100644 --- a/cmd.cpp +++ b/cmd.cpp @@ -94,6 +94,10 @@ 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 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 int initSweep; // Set by the "RSSI" command to restart the sweep extern bool paused; @@ -116,6 +120,9 @@ extern void initSweepLow (); extern void initSigLow (); extern void initIF_Sweep (); 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 * number. Needs to be re-ordered based on expected usage. @@ -162,17 +169,19 @@ extern void setMode (uint16_t newMode); { "SPUR", MSG_SPUR }, // Set Spur reduction on/off { "WIFITIME", MSG_WIFI_UPDATE }, // Set WiFi Update target time { "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 { "SGLODRIVE", MSG_SG_LO_DRIVE }, // Set Signal Generator LO Drive level { "TGIFDRIVE", MSG_TG_IF_DRIVE }, // Set Track Generator IF Drive level { "TGLODRIVE", MSG_TG_LO_DRIVE }, // Set Track Generator LO Drive level { "IFSIGNAL", MSG_IFSIGNAL }, // IF sweep signal frequency { "TGOFFSET", MSG_TGOFFSET }, // Offset TG IF from SA IF to reduce pass through - { "CTEST", MSG_COLOURTEST }, // Work out how colours work! + { "CTEST", MSG_COLOURTEST}, // Work out how colours work! { "OFFDEL", MSG_OFFDELAY }, // Offset tuning delay - { "WFMIN", MSG_WFMIN }, // Min level for waterfall colours + { "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 }; @@ -220,6 +229,7 @@ void ShowMenu () Serial.println ( " SALO..........Set to analyse 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 ( " RXSWEEP.......Set to RX Sweep mode to analyse the TinySA FIR filters" ); Serial.println ( " BANDSCOPE.....Set to BANDSCOPE mode" ); Serial.println ( " TRACES........Turn display traces on or off ['GAIN' or 'dB']" ); @@ -1551,6 +1561,10 @@ uint8_t reg69; // Ditto setMode(IF_SWEEP); return true; + case MSG_RX_SWEEP: + setMode(RX_SWEEP); + return true; + case MSG_TGOFFSET: if ( dataLen != 0 ) // Frequency specified? { @@ -1572,7 +1586,7 @@ uint8_t reg69; // Ditto freq1 = ParseFrequency ( dataBuff ); // Yes, get new frequency SetIFsweepSigFreq (freq1); } - Serial.printf ( "If Signal frequency: %s\n", FormatFrequency ( GetIFsweepSigFreq() )); + Serial.printf ( "IF Sweep Signal frequency: %s\n", FormatFrequency ( GetIFsweepSigFreq() )); return true; @@ -1622,6 +1636,7 @@ uint8_t reg69; // Ditto /* * 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. */ @@ -1653,6 +1668,17 @@ uint8_t reg69; // Ditto 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: @@ -2277,6 +2303,7 @@ int16_t tempBw; if ( setting.Bandwidth10 != oldRBW ) { changedSetting = true; + SetRXsweepSpan (setting.Bandwidth10 * 1000); // 10 * bandwidth WriteSettings (); } } @@ -2720,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 */ @@ -3024,8 +3234,9 @@ void SetFreq ( int vfo, uint32_t freq ) void SetWFMin (int16_t level) { - if ( (level >=0) && (level < 255) ) - setting.WaterfallMin = level; + if ( (level >=-130) && (level < -50) ) + setting.WaterfallMin = dBmToRSSI( (double)level ); + Serial.printf("Watterfall min set to %i \n", setting.WaterfallMin ); WriteSettings(); } diff --git a/cmd.h b/cmd.h index 3ad0f3d..1b90a97 100644 --- a/cmd.h +++ b/cmd.h @@ -75,6 +75,9 @@ #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! @@ -168,9 +171,6 @@ uint32_t GetSweepStart ( void ); void SetSweepStop ( uint32_t freq ); // Added in Version 2.3 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 uint32_t GetBandscopeStart ( void ); @@ -181,12 +181,27 @@ 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 ); void SetIFsweepSigFreq ( uint32_t freq ); // Added in Version 3.0c 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 ); uint32_t GetSweepCenter ( void ); diff --git a/my_SA.h b/my_SA.h index a46f941..2161645 100644 --- a/my_SA.h +++ b/my_SA.h @@ -92,6 +92,18 @@ #define IF_STOP_MAX 500000000 // 500MHz #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 diff --git a/si4432.cpp b/si4432.cpp index 61a1595..461cd29 100644 --- a/si4432.cpp +++ b/si4432.cpp @@ -25,29 +25,35 @@ bandpassFilter_t Si4432::_bandpassFilters[] { // bw*10, settle, dwn3, ndec, filset - { 26, 7500, 0, 5, 1 }, // 0 "AUTO" selection possibility - { 28, 7300, 0, 5, 2 }, // 1 "AUTO" selection possibility - { 31, 7000, 0, 5, 3 }, // 2 If user selects 3KHz -> 3.1KHz actual - { 32, 6800, 0, 5, 4 }, // 3 "AUTO" selection possibility - { 37, 6500, 0, 5, 5 }, // 4 "AUTO" selection possibility - { 42, 6000, 0, 5, 6 }, // 5 "AUTO" selection possibility + { 26, 6800, 0, 5, 1 }, // 0 "AUTO" selection possibility + { 28, 6600, 0, 5, 2 }, // 1 "AUTO" selection possibility + { 31, 6600, 0, 5, 3 }, // 2 If user selects 3KHz -> 3.1KHz actual + { 32, 6600, 0, 5, 4 }, // 3 "AUTO" selection possibility + { 37, 6600, 0, 5, 5 }, // 4 "AUTO" selection possibility + { 42, 6600, 0, 5, 6 }, // 5 "AUTO" selection possibility { 45, 5500, 0, 5, 7 }, // 6 "AUTO" selection possibility { 49, 5000, 0, 4, 1 }, // 7 "AUTO" selection possibility { 54, 4200, 0, 4, 2 }, // 8 "AUTO" selection possibility { 59, 3700, 0, 4, 3 }, // 9 "AUTO" selection possibility - { 72, 3200, 0, 4, 5 }, // 10 "AUTO" selection possibility + { 72, 3300, 0, 4, 5 }, // 10 "AUTO" selection possibility { 106, 2500, 0, 3, 2 }, // 11 If user selects 10KHz -> 10.6KHz actual - { 322, 1000, 0, 2, 6 }, // 12 If user selects 30KHz -> 32.2KHz actual - { 377, 1000, 0, 1, 1 }, // 13 "AUTO" selection possibility - { 562, 700, 0, 1, 5 }, // 14 "AUTO" selection possibility - { 832, 600, 0, 0, 2 }, // 15 "AUTO" selection possibility - { 1121, 500, 0, 0, 5 }, // 16 If user selects 100KHz -> 112.1KHz actual - { 1811, 500, 1, 1, 9 }, // 17 "AUTO" selection possibility - { 2488, 450, 1, 0, 2 }, // 18 "AUTO" selection possibility - { 3355, 400, 1, 0, 8 }, // 19 If user selects 300KHz -> 335.5KHz actual - { 3618, 300, 1, 0, 9 }, // 20 "AUTO" selection possibility - { 4685, 300, 1, 0, 11 }, // 21 "AUTO" selection possibility - { 6207, 300, 1, 0, 14 } // 22 "AUTO" selection possibility + { 162, 2000, 0, 3, 6 }, // 12 "AUTO" selection possibility + { 210, 1600, 0, 2, 2 }, // 13 "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 }; @@ -550,8 +556,11 @@ float Si4432::SetRBW ( float reqBandwidth10, unsigned long* delaytime_p ) // "re */ if ( reqBandwidth10 <= _bandpassFilters[0].bandwidth10 ) + { filter = 0; - + Serial.print ("Minimum RBW"); + } + /* * 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. @@ -560,7 +569,7 @@ float Si4432::SetRBW ( float reqBandwidth10, unsigned long* delaytime_p ) // "re while (( _bandpassFilters[filter-1].bandwidth10 > reqBandwidth10 - 0.01 ) && ( filter > 0 )) filter--; -// Serial.print ( "filter = " ); Serial.println ( filter ); + Serial.printf ( "Required = %f, filter = %i\n", reqBandwidth10, filter); Serial.println ( filter ); /* diff --git a/simpleSA.h b/simpleSA.h index 3edf04b..9dc31e0 100644 --- a/simpleSA.h +++ b/simpleSA.h @@ -31,8 +31,8 @@ #include -#define PROGRAM_NAME "TinySA" // These are for the WiFi interface -#define PROGRAM_VERSION "3.0" // Current version is 3.0 +#define PROGRAM_NAME "simpleSA" // These are for the WiFi interface +#define PROGRAM_VERSION "0.01" // Current version is 0.01 - beta! /* @@ -41,7 +41,7 @@ * 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 /* @@ -179,7 +179,7 @@ */ 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 }; /* diff --git a/simpleSA.ino b/simpleSA.ino index 3c2e49f..349cf42 100644 --- a/simpleSA.ino +++ b/simpleSA.ino @@ -380,7 +380,7 @@ int16_t minGrid; * Some varibales for the various operating modes */ uint16_t tinySA_mode = SA_LOW_RANGE; // Low frequency range -const char *modeText[] = { "SALo", "SAHi", "SGLo", "SGHi", "IFSw", "0SpL", "0SpH", "TrGn", "BScp" }; // 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) @@ -420,6 +420,7 @@ extern void StartSigGenMenu ( void ); // Function to launch sig gen menu extern void StartSigGenFreq ( void ); // Function to set frequency extern void ResetSAMenuStack ( void ); // Reinitialise stack for SA 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 ShowSplash ( void ); // Displays the splash screen @@ -434,8 +435,14 @@ extern void pushBandscopeSettings (); */ uint32_t startFreq_IF = IF_SWEEP_START; 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: @@ -891,6 +898,10 @@ loopStartMicros = micros(); doIF_Sweep(); // IF Sweep break; + case RX_SWEEP: + doRX_Sweep(); // RX Sweep + break; + case BANDSCOPE: doBandscope(); // Bandscope Sweep break; @@ -930,14 +941,19 @@ void setMode ( uint16_t newMode ) // initSigHigh(); // break; + case BANDSCOPE: + initBandscope(); + break; + case IF_SWEEP: initIF_Sweep(); break; - case BANDSCOPE: - initBandscope(); + case RX_SWEEP: + initRX_Sweep(); break; + default: DisplayError ( ERR_WARN, "Invalid Mode!", @@ -983,6 +999,13 @@ void menuExit() initIF_Sweep(); break; + case RX_SWEEP: + if ( setting.Mode == RX_SWEEP ) + RedrawHisto(); + else + initRX_Sweep(); + break; + case BANDSCOPE: if ( setting.Mode == BANDSCOPE ) RedrawHisto(); @@ -999,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() { @@ -1392,7 +1415,7 @@ 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 }; +// 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.setTextColor ( WHITE ); @@ -1535,6 +1558,11 @@ double fStop; fCenter = (double) ((( startFreq_IF + stopFreq_IF ) / 2.0 ) / 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 { fStart = (( (double)setting.ScanStart )/ 1000000.0); // Start freq @@ -1721,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. * @@ -1837,6 +1875,55 @@ void CreateGainScale () } } + +/* + * "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. * diff --git a/ui.cpp b/ui.cpp index 608389b..152fee8 100644 --- a/ui.cpp +++ b/ui.cpp @@ -94,8 +94,8 @@ typedef struct { // to '6' below! int8_t digit_mode; int8_t current_trace; // 0 to 3 ??? - int32_t value; // For editing at numeric input area - int32_t previous_value; + double value; // For editing at numeric input area + double previous_value; } uistat_t; @@ -107,8 +107,8 @@ uistat_t uistat = { 6, // digit - See note above 0, // digit_mode 0, // current_trace - 0, // value - 0 // previous_value + 0.0, // value + 0.0 // previous_value }; @@ -198,7 +198,7 @@ enum { KM_START, KM_STOP, KM_CENTER, KM_SPAN, KM_FOCUS, KM_REFPOS, KM_ATTENUATION, KM_ACTUALPOWER, KM_IFFREQ, KM_PREAMP, 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_WFMIN, KM_WFGAIN, KM_BANDSCOPELEVEL }; + KM_WFMIN, KM_WFGAIN, KM_BANDSCOPELEVEL, KM_RXSPAN, KM_RXSIG, KM_RBW }; /* @@ -216,7 +216,7 @@ static const char * const keypad_mode_label[] = "REFPOS", "ATTEN", "POWER", "IF FREQ", "PREAMP", "XTAL CAL", "SG FREQ", "dBm", "Max dBm", "IF START", "IF STOP", "IF Sig Freq", "TG OFFSET", "TG LO Drive", "TG IF Drive", "START", - "WF MIN", "WF GAIN", "REF LEVEL" + "WF MIN", "WF GAIN", "REF LEVEL", "RX SPAN", "RX Sig Freq", "RBW" }; @@ -274,6 +274,7 @@ static void menu_format_cb ( int item ); static void menu_scale_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_RX_sweep_cb ( int item ); static void menu_recall_cb ( int item ); static void menu_version_cb (int item ); static void menu_generate_cb(int item); // WA2FZW - Added in M0WID's Version 05 @@ -442,8 +443,9 @@ static Menuitem menu_mode[] = // Select mode menu { Menuitem ( MT_FUNC, "\2SWEEP\0LOW", 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, "\2IF\0SWEEP", menu_mode_cb ), + Menuitem ( MT_FUNC, "\2RX\0SWEEP", menu_mode_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; @@ -739,6 +741,17 @@ static Menuitem menu_IFsweep_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_FUNC, "\2SWEEP\0SPAN", menu_RX_sweep_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 +}; + + static Menuitem menu_BandscopeSpan[] = // Badscope Span settings { Menuitem ( MT_FUNC, "200kHz", menu_BandscopeSpan_cb ), @@ -1145,13 +1158,13 @@ void ShowSplash ( void ) tft.setTextColor ( MAGENTA ); 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.setFreeFont ( &FreeSansBold9pt7b ); // Select Free Serif 9 point font tft.drawString ( "By WA2FZW, M0WID,", 160, 60 ); tft.drawString ( "VK3PE and G3ZQC", 160, 80 ); - tft.drawString ( "Version 3.0", 160, 100 ); - tft.drawString ( "Original by Erik (PD0EK)", 160, 120 ); + tft.drawString ( "Version 0.01", 160, 100 ); + tft.drawString ( "Original tinySA by Erik (PD0EK)", 160, 120 ); tft.setTextDatum ( TL_DATUM ); // Back to default top left @@ -1195,14 +1208,20 @@ void menu_mode_cb ( int item ) break; case 2: // Set IF Sweep mode - setMode(IF_SWEEP); + setMode(BANDSCOPE); ui_mode_normal (); // No menu displayed break; case 3: // Set IF Sweep mode - setMode(BANDSCOPE); + setMode(IF_SWEEP); ui_mode_normal (); // No menu displayed break; + + case 4: // Set RX Sweep mode + setMode(RX_SWEEP); + ui_mode_normal (); // No menu displayed + break; + } changedSetting = true; @@ -1775,6 +1794,14 @@ static void menu_IF_sweep_cb ( int item ) } +static void menu_RX_sweep_cb ( int item ) +{ + ui_mode_keypad ( item + KM_RXSPAN - 1 ); // item = 1 -> KM_RXSPAN, 2 -> KM_RXSIG + ui_process_keypad (); +} + + + /* * ********************************************* * BANDSCOPE MENU ITEMS @@ -2099,7 +2126,10 @@ static const keypads_t * const keypads_mode_tbl[] = keypads_freq, // KM_BANDSCOPESTART.IF Sweep start frequency keypads_integer, // KM_WFMIN..........Waterfall min level (RSSI) keypads_level, // KM_WFGAIN.........Waterfall Gain - keypads_level // KM_BANDSCOPELEVEL.Grid reference level + 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 }; @@ -2503,6 +2533,18 @@ static void fetch_numeric_target ( void ) uistat.value = setting.BandscopeMaxGrid; break; + case KM_RXSPAN: + uistat.value = GetRXsweepSpan(); + break; + + case KM_RXSIG: + uistat.value = GetRXsweepSigFreq(); + break; + + case KM_RBW: + uistat.value = (double)setting.Bandwidth10 / 10.0; + break; + } uint32_t x = uistat.value; @@ -2514,7 +2556,7 @@ static void fetch_numeric_target ( void ) uistat.digit = n; 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 ); } @@ -2950,6 +2992,18 @@ static int keypad_click ( int key ) 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" @@ -3319,6 +3373,21 @@ void ResetIFsweepMenuStack (void) 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 (); +} /*