/* * 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; 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, &bpfIndex ); // 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 + bpfCalibrations[bpfIndex] ; 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 )" }