simpleSA/IFsweep.ino
M0WID 1746058f31 Fixed TG CS pin define compile error
Corrected problem with CS pins floating if module installed but not initialised.
Also corrected compiler error if neither TG SI4432 defined
2020-10-09 21:14:58 +01:00

507 lines
16 KiB
C++

/*
* IF sweep sweeps the IF Frequency from a start value to an stop value to
* measure the SAW and RX low pass filter response.
* It requires a fixed strength and frequency signal, and this is provided
* by setting the reference output to 30MHz.
* The input should be connected to the reference signal output with an external cable
*/
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();
tinySA_mode = IF_SWEEP;
setting.Mode = tinySA_mode;
// Turn off track gen
#ifdef TG_IF_INSTALLED
tg_if.RxMode();
#endif
#ifdef TG_LO_INSTALLED
tg_lo.RxMode();
#endif
ResetIFsweepMenuStack(); // Put menu stack back to root level
}
void doIF_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 IFSweep or changedSetting");
autoSweepFreqStep = ( stopFreq_IF - startFreq_IF ) / displayPoints;
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution
bandwidth = rcvr.SetRBW ( 106.0, &delaytime ); // Set it in the receiver Si4432. delaytime is returned
sweepPoints = displayPoints; // At least the right number of points for the display
sweepFreqStep = ( stopFreq_IF - startFreq_IF ) / sweepPoints; // Step for each reading
att.SetAtten ( 0 ); // Set the internal attenuator
// pre-calculate adjustment for RSSI values
dBadjust = -120.0 + setting.LevelOffset - setting.ExternalGain;
Serial.printf("IFSweep dBadjust = %f; leveloffset = %f; attenuate = %i, ext gain = %f\n",
dBadjust, setting.LevelOffset, setting.Attenuate, setting.ExternalGain);
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
sweepStep = 0;
autoSweepFreq = startFreq_IF; // 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 IF frequency
setFreqMicros = micros(); // Store the time the frequency was changed
xmit.SetFrequency ( sigFreq_IF + autoSweepFreq ); // set the LO frequency to the IF plus 30Mhz ref out
// Serial.printf("autoSweepFreq init: %u\n", autoSweepFreq);
#ifdef USE_WIFI
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
{
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
else
jsonDocInitialised = false;
#endif // #ifdef USE_WIFI
startFreq = startFreq_IF + sigFreq_IF; // Start freq for the LO
stopFreq = stopFreq_IF + sigFreq_IF; // Stop freq for the LO
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 > MIN_DELAY_WEBSOCKETS ) &&
( (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.
*/
uint32_t f = sigFreq_IF + autoSweepFreq;
setFreqMicros = micros(); // Store the time the LO frequency was changed
rcvr.SetFrequency ( autoSweepFreq ); // Set the RX Si4432 to the IF frequency
xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read
// Serial.printf("Required: %i Actual %i\n", tempIF+autoSweepFreq, xmit.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;
initChunkSweepDoc (sweepStep);
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 )"
}