simpleSA/cmd.cpp
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

3482 lines
101 KiB
C++

/*
* "Cmd.cpp" was added in Version 2.0 by John Price (WA2FZW)
*
* This file contains the functions to read a command and the associated data
* from the serial input and all the functions necessary to process the
* commands.
*
* The functions to read and interpret the commands were stripped out of the
* "loop" function in the main program file. As it currently exists, the command
* structure has been modified to be more human friendly, and will no longer work
* with Erik's "TintSA.exe" program, which has been replaced with a web page
* interface which is much more flexibile.
*
* The other functions in here all support the main command interpreter and are
* also used by the "ui.cpp" and "TinySA_wifi.cpp" modules. Eventually, we hope
* that those modules will also be able to use the "ProcessCommand" function in
* here in place of much redundant code in those modules.
*/
#include "cmd.h" // Our associated header
#include "preferences.h" // For "SAVE" & "RECALL"
#include "marker.h" // Marker class definition
/*
* These are the objects for the PE4302 and Si4432 modules. They are created in the
* main program file:
*/
extern PE4302 att; // PE4302 Attenuator object
extern Si4432 rcvr; // Si4432 Receiver object
extern Si4432 xmit; // Si4432 Transmitter object
#ifdef TG_IF_INSTALLED
extern Si4432 tg_if; // Si4432 Transmitter object
#endif
#ifdef TG_LO_INSTALLED
extern Si4432 tg_lo; // Si4432 Transmitter object
#endif
/*
* Global variables defined in the main program file:
*/
extern settings_t setting; // Structure to track & save settings
extern bandpassFilter_t bandpassFilters[]; // RBW Setting data
extern int bpfCount; // Number of entries in "bandpassFilters"
extern int changedSetting; // Something in "setting" changed
extern uint16_t bpfCalibrate; // set true if a SI4432 bandpass filter calibration run is taking place
extern uint16_t tinySA_mode;
/*
* 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 unsigned long delaytime; // In microseconds
extern unsigned long offsetDelayTime; // In microseconds
extern unsigned long wiFiTargetTime;
extern uint16_t wiFiPoints;
extern unsigned long websocketInterval; // time in microseconds between websocket polls if no wifi client
extern int VFO; // TX/RX Si4432 selector
extern uint16_t steps; // Number of frequency steps in the sweep (not really used)
extern bool setActualPowerRequested;
extern float actualPower;
extern uint16_t oldPeakLevel; // Current maximum signal level (was peakLevel) G3ZQC
int32_t frequency0 = 0; // Used in setting sweep configuration
int32_t frequency1 = 100000000;
extern uint32_t lastStart; // Last value of the sweep start frequency
extern uint32_t lastStop; // Last value of the sweep end frequency
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 RX sweep start frequency
extern uint32_t stopFreq_RX; // Last value of the RX sweep end frequency
extern uint32_t sigFreq_RX; // Last value of the RX 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;
extern uint32_t colourTest;
extern Marker marker[MARKER_COUNT]; // Array of marker objects
extern uint16_t sigGenOutputOn; // signal generator state - 0 = off, 1 = on
/*
* Functions still in the main program file. Maybe some need to move here?
*/
extern void DisplayInfo ();
extern void DisplayError ( uint8_t severity, const char *line1, const char *line2, const char *line3, const char *line4 );
extern void RedrawHisto ();
extern void pushSettings ();
extern void pushBandscopeSettings ();
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.
*/
msg_t msgTable[] =
{
{ "START", MSG_START }, // Set sweep start frequency
{ "STOP", MSG_STOP }, // Set sweep stop frequency
{ "CENTER", MSG_CENTER }, // Set sweep center frequency
{ "SPAN", MSG_SPAN }, // Set sweep range
{ "FOCUS", MSG_FOCUS }, // Set sweep center frequency with narrow bandwidth
{ "PREAMP", MSG_PREAMP }, // Set the receiver preamp gain
{ "GAIN", MSG_PREAMP }, // Alternate receiver preamp gain
{ "DRIVE", MSG_DRIVE }, // Set transmitter (LO) output level
{ "FREQ", MSG_VFO_FREQ }, // Set the frequency for the selected VFO
{ "ATTEN", MSG_ATTEN }, // Set the PE4301 attenuation
{ "HELP", MSG_HELP }, // Display the command menu
{ "?", MSG_HELP }, // Display the command menu
{ "STEPS", MSG_STEPS }, // Set or get number of sweep points (not used)
{ "DELAY", MSG_DELAY }, // Set or get delay time between sweep readings
{ "VFO", MSG_VFO }, // Set or get the currently selected VFO
{ "RBW", MSG_RBW }, // Set or get the current resolution bandwidth
{ "REGDUMP", MSG_REG_DUMP }, // Print register values for the selected VFO
{ "RSSI", MSG_RSSI }, // Show RSSI values
{ "QUIT", MSG_QUIT }, // Stop RSSI readings
{ "REGISTER", MSG_SET_REG }, // Set or get the value of a specific register for the selected VFO
{ "SAVE", MSG_SAVE }, // Save scan configuration
{ "RECALL", MSG_RECALL }, // Recall a saved scan configuration
{ "REF_FREQ", MSG_GPIO2 }, // Set transmitter GPIO2 frequency
{ "TUNE", MSG_TUNE }, // Tune the selected Si4431 (VFO)
{ "CONFIG_SAVE", MSG_CONFIG }, // Save "config" structure
{ "ACTUAL_PWR", MSG_ACT_PWR }, // Calibrate the indicated power level
{ "IF_FREQ", MSG_IF_FREQ }, // Change the IF (receiver) frequency
{ "TRACES", MSG_TRACES }, // Turn traces on the display on or off
{ "GRIDREF", MSG_GRID }, // Set dB value for the top line of the grid
{ "SCALE", MSG_SCALE }, // Set dB/horizontal line value for the grid
{ "PAUSE", MSG_PAUSE }, // Pause at end of sweep - toggled
{ "SALO", MSG_SWEEPLO }, // Sweep low range mode
{ "SGLO", MSG_SIGLO }, // Signal generator low range mode
{ "IFSWEEP", MSG_IF_SWEEP }, // IF sweep mode
{ "BANDSCOPE", MSG_BANDSCOPE }, // BANDSCOPE mode
{ "MARKER", MSG_MARKER }, // Set marker status and color
{ "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
{ "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!
{ "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
{ "EXTGAIN", MSG_EXTGAIN }, // External gain or attenuation
{ "SGON", MSG_SGON }, // turn on signal generator output
{ "SGOFF", MSG_SGOFF }, // turn off signal generator output
{ "SGFREQ", MSG_SGFREQ }, // Set Signal Generator Frequency
{ "TRACKON", MSG_TGON }, // turn on tracking generator output
{ "TRACKOFF", MSG_TGOFF }, // turn off tracking generator output
{ "TRACKSIG", MSG_TGSIG }, // turn off tracking generator output
{ "TGFREQ", MSG_TGFREQ }, // Set Track gen freq for sig gen mode
{ "BPFCAL", MSG_BPFCAL }, // Start bandpass filter calibration
{ "", MSG_NONE } // Unrecognized command
};
int msgCount = ELEMENTS ( msgTable);
/*
* "ShowMenu" displays the command menu on the USB output (Arduino IDE's Serial
* Monitor, or some other terminal emulator program).
*/
void ShowMenu ()
{
Serial.printf ( "\nsimpleSA Spectrum Analyzer %s - User Commands:\n\n", PROGRAM_VERSION );
Serial.println ( "Sweep Settings:\n" );
Serial.print ( " START.........Sweep start frequency, currently: " );
Serial.println ( FormatFrequency ( setting.ScanStart ));
Serial.print ( " STOP..........Sweep stop frequency, currently: " );
Serial.println ( FormatFrequency ( setting.ScanStop ));
Serial.print ( " CENTER........Sweep center frequency, currently: " );
Serial.println ( FormatFrequency ( GetSweepCenter() ));
Serial.print ( " SPAN..........Sweep frequency span, currently: " );
Serial.println ( FormatFrequency ( setting.ScanStop - setting.ScanStart ));
Serial.println ( " MARKER........It's complicated; read the documentation!" );
Serial.println ( " FOCUS.........Set single frequency mode with narrow bandwidth" );
Serial.printf ( " RBW...........Set or get resolution bandwidth (RBW); currently: %.1f KHz\n", bandwidth );
Serial.printf ( " ATTEN.........Set or get the attenuator setting; currently: %ddB\n",
setting.Attenuate );
Serial.printf ( " EXTGAIN.......Set or get the external gain setting; currently: %ddB\n",
setting.ExternalGain );
Serial.printf ( " SPUR......... Turn Spur Reduction 'ON' or 'OFF'; currently: %s\n",
setting.Spur ? "ON" : "OFF" );
Serial.println ( " PAUSE.........Pause or resume the sweep" );
Serial.println ( "\nDisplay Options:\n" );
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']" );
Serial.printf ( " PREAMP/GAIN...Set or get the receiver preamp gain\n" );
Serial.println ( " See documentation for allowed values" );
Serial.printf ( " GRIDREF.......Set the grid reference level; currently: %ddB\n", setting.MaxGrid );
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 ( "\nSignal Generator Commands:\n" );
Serial.println ( " SGON..........Turn on signal generator output" );
Serial.print ( " SGOFF.........Turn off signal generator output; currently " );
Serial.println ( sigGenOutputOn );
Serial.print ( " SGFREQ........Signal Generator frequency, currently: " );
Serial.println ( FormatFrequency ( sigGenSetting.Frequency ));
Serial.print ( " SGLODRIVE.....Local oscillator drive level in signal generator mode [0 to 7]; currently: " );
Serial.println ( sigGenSetting.LO_Drive );
Serial.print ( " SGRXDRIVE.....RX SI4432 oscillator drive level in signal generator mode [0 to ");
Serial.print ( RX_SI4432_MAX_DRIVE );
Serial.print ( "]; currently: " );
Serial.println ( sigGenSetting.RX_Drive );
#ifdef TG_IF_INSTALLED
Serial.println ( "\nTracking Generator Commands:\n" );
Serial.println ( " TRACKON.......Turn on tracking generator output" );
#ifdef TG_LO_INSTALLED
Serial.println ( " TRACKSIG......Turn on tracking generator signal generator output" );
#endif
Serial.print ( " TRACKOFF......Turn off tracking generator output; currently " );
Serial.println ( trackGenSetting.Mode );
#ifdef TG_LO_INSTALLED
Serial.print ( " TGLODRIVE.....Local oscillator drive level in tracking generator mode [0 to 7]; currently: " );
Serial.println ( trackGenSetting.LO_Drive );
#endif
Serial.print ( " TGIFDRIVE.....Local oscillator drive level in tracking generator mode [0 to 7]; currently: " );
Serial.println ( trackGenSetting.IF_Drive );
#ifdef TG_LO_INSTALLED
Serial.print ( " TGOFFSET......Tracking generator offset from SA IF; currently: " );
Serial.println ( trackGenSetting.Offset );
Serial.print ( " TGFREQ........Tracking generator signal generator frequency; currently: " );
Serial.println ( trackGenSetting.Frequency );
#endif
#endif
Serial.println ( "\nOther Commands:\n" );
Serial.print ( " DRIVE.........Local oscillator drive level [0 to 7]; currently: " );
Serial.println ( setting.Drive );
Serial.println ( " SAVE .........Save the current scan configuration [0 to 4]" );
Serial.println ( " RECALL........Recall a saved scan configuration [0 to 4]" );
Serial.println ( " FREQ..........Set or get the frequency for the selected VFO" );
Serial.println ( " HELP (or ?)...Show this menu" );
Serial.printf ( " STEPS.........Sweep samples; currently: %u\n", steps );
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: " );
if ( VFO == RX_4432 )
Serial.println ( "RX (0)" );
else if (VFO == TX_4432 )
Serial.println ( "LO (1)" );
else if (VFO == TGIF_4432 )
Serial.println ( "TGIF (2)" );
else if (VFO == TGLO_4432 )
Serial.println ( "TGLO (3)" );
Serial.println ( "\nDebugging and Troubleshooting Commands:\n" );
Serial.printf ( " IF_FREQ.......Set the IF (receiver) frequency [433 to 435MHz]; currently: %s\n",
FormatFrequency ( setting.IF_Freq ));
Serial.println ( " REGDUMP.......Display a dump of all the registers for the selected VFO" );
Serial.println ( " RSSI..........Output RSSI readings until the 'Q' command is entered" );
Serial.println ( " or one time only ['C' or '1']; the default is once.");
Serial.println ( " QUIT (or Q)...Terminate RSSI output" );
Serial.println ( " REGISTER......Read or write a Si4432 register for the selected VFO" );
Serial.println ( " REF_FREQ......Set or get the transmitter GPIO2 reference frequency" );
Serial.println ( " TUNE..........Tune the frequency of the selected VFO" );
Serial.println ( " ACTUAL_PWR....Calibrate the indicated power level" );
Serial.println ( " CONFIG_SAVE...Save the hardware configuration parameters" );
Serial.println ( " WiFiTIME......Target web page Chart Update time interval in ms" );
Serial.println ( " WiFiPTS.......No of points in each data chunk pushed to the web clients" );
Serial.println ( " SKTINT........Interval between check for websocket messages if no client connected" );
Serial.println ( " IFSIGNAL......Frequency of signal injected for IF Sweep (External or Ref)" );
Serial.println ( " BPFCAL........Start SI4432 bandpass filter calibration - RXSweep mode only");
Serial.println ();
}
/*
* "CheckCommand" checks the serial input for any messages. If there is nothing
* to read, it returns "false".
*
* If there is something to be read, the complete line of data is read into a
* buffer. The read loop is set up to recognize either a return or newline character
* as the line terminator and handles the case where both are used (as is possible
* when using the Arduino IDE's Serial Monitor. The function also translates all
* alpha characters to upper case.
*
* Once the command is read, the buffer is passed to "ProcessCommand" which does
* whatever was requested. If the command is successfully executed, the function
* returns "true" and returns "false" if the command was not successful.
*/
bool CheckCommand ()
{
char inBuff[80]; // Input buffer
char c = 0; // Just one character
int16_t index = 0; // Index to the buffer
if ( !Serial.available() ) // Any input?
return false; // Nope!
while ( Serial.available () ) // While data to read
{
c = toupper ( Serial.read () ); // Get next character
if (( c == '\r' ) || ( c == '\n' )) // End of the line?
inBuff[index++] = '\0'; // Replace with a null
else // Not the end of the line
inBuff[index++] = c; // Save the character
inBuff[index] = '\0'; // Make next character a null
}
return ParseCommand ( inBuff ); // Process the command and return result
}
/*
* "ParseCommand" Translates the ASCII command text into a numerical value based
* on the contents of the "msgTable" and separates the data portion of the message
* (if any) into the "dataBuff".
*
* The process is a bit clever! The user only need to enter wnough characters of the
* command to make it unique! For example, if the user simply entered the command as
* "D", that could be interpreted as meaning "DRIVE" or "DELAY" (based on the contents
* of the "msgTable" as I write this). But entering "DE" or "DR" is enough to distinguish
* between the two.
*
* If the command is not found or is not unique, appropriate error messages are
* diaplayed and the function returns false.
*
* If the command is identified uniquely, the "ProcessCommand" function is invoked
* to complete the processing.
*/
bool ParseCommand ( char* inBuff ) // inBuff has full message
{
int messageLength; // Length of entire message
int cmdLength; // Length of command string
uint8_t cmdNumber; // Command translated to a number
int cmdCount = 0; // Number of commands matching the input
int inputIx = 0; // Input buffer index
int cmdIx = 0; // Command string index
int dataIx = 0; // Data string index
char cmdString[10]; // Will contain the command string
char dataBuff[20]; // Will contain the data portion of the message
char fullCmd[20]; // Full command from table
char c; // Just one character
memset ( cmdString, 0, sizeof ( cmdString )); // Clear the command buffer
memset ( dataBuff, 0, sizeof ( dataBuff )); // Clear the data buffer
messageLength = strlen ( inBuff ); // Length of input
if ( messageLength == 0 )
return false;
while ( inputIx < messageLength )
{
c = toupper ( inBuff[inputIx++] ); // Get a character
if (( c != ' ' ) && ( c != ',' )) // If it's not a space or a comma
cmdString[cmdIx++] = c; // Add to commmand string
else // If it is a space or comma
break; // On to the data part of the message
}
while ( inputIx < messageLength )
dataBuff[dataIx++] = inBuff[inputIx++]; // Copy rest of message to the data buffer
cmdLength = strlen ( cmdString ); // Length of the command string
cmdNumber = MSG_NONE; // No message found so far
for ( inputIx = 0; inputIx < msgCount; inputIx++ ) // Search the msgTable
{
if ( strncmp ( cmdString, msgTable[inputIx].Name, cmdLength ) == 0 )
{
cmdNumber = msgTable[inputIx].ID; // Get translation
strcpy ( fullCmd, msgTable[inputIx].Name ); // And remember full command string
cmdCount++; // Found at least one match
if ( cmdCount > 1 ) // But did we find more than one?
{
Serial.printf ( "'%s' Is ambiguous!\n", cmdString );
return false;
}
}
}
if ( cmdNumber != MSG_NONE )
{
// Serial.print ( "Command: " ); Serial.print ( fullCmd );
// Serial.print ( " = " ); Serial.println ( cmdNumber );
// Serial.print ( "Data: " ); Serial.println ( dataBuff );
return ( ProcessCommand ( cmdNumber, dataBuff ));
}
else
Serial.printf ( "Command: '%s' not found!\n", cmdString );
return false;
}
/*
* "ProcessCommand" parses the input buffer appropriately for the command, which is
* (for now) a single letter in the 1st byte of the buffer.
*
* If the command is executed correctly, the function returns a "true" indication
* and returns "false" if some error occurred.
*/
bool ProcessCommand ( uint8_t command, char* dataBuff )
{
uint16_t dataIx = 0; // Data buffer index
uint8_t dataLen; // Length of data buffer
char tempBuff[40]; // Buffer for formatting responses
int16_t tempIx = 0; // tempBuff index
char c = 0; // A single character from the input
int16_t addr; // Si4432 register address
int16_t tempValue; // Temporary value of something
int32_t tempFreq; // Temporary frequency
float tempFloat;
uint32_t freq1; // Several other temporary frequencies
uint32_t freq2; // used in computing the center
uint32_t freq3; // frequency and span range
uint32_t freq4;
int32_t signedFreq;
int16_t attenuation; // Attenuation setting for the PE4302
uint8_t driveLevel[] = { 1, 2, 5, 8, 11, 14, 17, 20 }; // Drive level to dB translation
uint8_t fTranslate[7] = { 30, 15, 10, 4, 3, 2, 1 }; // GPIO2 frequency table
bool agcOn; // Used by "GetPreampGain"
uint8_t reg69; // Ditto
dataLen = strlen ( dataBuff ); // Length of the data string
// Serial.print ( "Command: " ); Serial.println ( command );
// Serial.print ( "Data: " ); Serial.println ( dataBuff );
/*
* The original code had a plethora of "if" statements to try to figure out what
* command was entered; modified to a "switch" statement in Version 1.7.
*/
switch ( command )
{
/*
* The marker command is a bit more complicated than most. The format of the command
* is:
*
* MARKER # ACTION
*
* where '#' is the marker number (1 to 4) and 'ACTION' is one of the following:
*
* ENABLE Turns it on
* DISABLE Turns it off
* WHITE Set the color to white
* RED Set the color to red
* BLUE etc.
* GREEN
* YELLOW
* ORANGE
*
* Only the first letter of the 'ACTION' is necessary, so the command "MARKER 2 R"
* would set the color of marker #2 to red.
*/
case MSG_MARKER: // Set marker status or color
if ( dataLen < 1 ) // Less than 1; no number specified
{
Serial.println ( "No marker number specified" );
return false;
}
if ( !isDigit ( dataBuff[0] )) // Should be the marker number
{
Serial.println ( "No marker number specified" );
return false;
}
if ( dataLen < 3 ) // Has to be at least 3 characters
{
Serial.println ( "No marker action specified" );
return false;
}
tempValue = dataBuff[0] - 0x30 - 1; // Quick atoi conversion
if ( tempValue >= MARKER_COUNT ) // Range check
{
Serial.printf ( "Illegal marker number: %i\n", tempValue + 1);
return false;
}
if ( UpdateMarker ( tempValue, dataBuff[2] )) // Update the status
return true;
else
{
Serial.printf ( "Illegal marker action: %s\n",
&dataBuff[2] );
return false;
}
/*
* The "START" command sets the scan START frequency. and the "STOP" command
* sets the scan STOP frequency. These are fairly straightforward!
*/
case MSG_START: // Set the start frequency
if ( dataLen != 0 ) // Frequency specified?
{
tempFreq = ParseFrequency ( dataBuff ); // Then get the new frequency
SetSweepStart ( tempFreq ); // Set it
RedrawHisto();
//DisplayInfo (); // Restart scan?
}
Serial.printf ( "Scan start frequency: %s\n",
FormatFrequency ( GetSweepStart () ));
return true;
case MSG_STOP: // Set the stop frequency
if ( dataLen != 0 ) // Frequency specified?
{
tempFreq = ParseFrequency ( dataBuff ); // Get new frequency
SetSweepStop ( tempFreq ); // And set it
RedrawHisto ();
}
Serial.printf ( "Scan stop frequency: %s\n",
FormatFrequency ( GetSweepStop () ));
return true;
/*
* When the user sets the "CENTER" frequency, we're also going to set the "SPAN". But,
* it's a bit tricky!
*
* The basic assumption is that we will set the "SPAN" equal to the selected "CENTER"
* frequency. But if the "SPAN" limits would cause the "START" frequency to go negative
* or cause the "STOP" frequency to exceed "STOP_MAX", the difference between the
* "CENTER" frequency and whichever limit is exceeded will become 1/2 of the "SPAN".
*
* Of course, setting the "CENTER" frequency at either one of the limits is going to
* result in a zero span.
*/
case MSG_CENTER: // Set the center frequency
if ( dataLen != 0 ) // Frequency specified?
{
tempFreq = ParseFrequency ( dataBuff ); // Yes, get new frequency
SetSweepCenter ( tempFreq, WIDE ); // Set it
RedrawHisto ();
}
Serial.printf ( "Scan center frequency: %s\n",
FormatFrequency ( GetSweepCenter ( ) ));
return true;
case MSG_SPAN: // Set the frequency span
if ( dataLen != 0 ) // Frequency specified?
{
tempFreq = ParseFrequency ( dataBuff ); // Yes, get new frequency
SetSweepSpan ( tempFreq ); // and set it
RedrawHisto ();
}
Serial.printf ( "Sweep frequency span: %s\n",
FormatFrequency ( GetSweepSpan ( ) ));
return true;
case MSG_FOCUS: // Set single frequency mode
if ( dataLen != 0 ) // Frequency specified?
{
tempFreq = ParseFrequency ( dataBuff ); // Yes, get new frequency
SetSweepCenter ( tempFreq, NARROW ); // Set it
RedrawHisto ();
}
Serial.printf ( "Scan center frequency: %s\n",
FormatFrequency ( GetSweepCenter () ));
return true;
/*
* The "GRID" command sets the decibel value for the top grid line.
*
* If no number is entered, we report the current setting.
*/
case MSG_GRID:
if ( dataLen != 0 ) // Value specified?
{
tempValue = atoi ( dataBuff ); // Yes, get grid reference value
SetRefLevel ( tempValue ); // Set it
DisplayInfo (); // Re display the scan
}
Serial.printf ( "Grid reference value: %ddB\n", setting.MaxGrid );
return true;
/*
* The "SCALE" command sets the decibel value spacing for each horizontal line on the grid.
* Unlike the touch-screen equivalent command, here you can set any value you like as opposed
* to chosing from a list of standard settings.
*
* If no number is entered, we report the current setting.
*/
case MSG_SCALE:
if ( dataLen != 0 ) // Value specified?
{
tempValue = atoi ( dataBuff ); // Yes, get scale setting
SetPowerGrid ( tempValue ); // Set it
DisplayInfo (); // Re display the scan
}
Serial.printf ( "Grid scale: %ddB/div\n", setting.PowerGrid );
return true;
/*
* The "PREAMP" command sets the receiver Si4432 preamp gain. The way this works is pretty
* strange and is explained in A440, but not very clearly!
*
* There are two ranges; low and high. The low range values are 5dB to 29dB in 3dB steps.
* The high range goes from 25dB to 49dB in 3dB steps. Note there is some overlap! You can
* also specify "AUTO" mode which turns on the AGC in the module.
*
* Rather than go through the hassle of figuring out if the value entered by the user is
* one of the specific values allowed, we will allow the user to enter any value between
* 5dB and 49dB and if it's not one of the legal values, we will use the next lower value.
*
* Note the ranges overlap, so if the requested value is greater than 23dB, we'll just use
* the high range (in other words, we won't use 26dB or 29dB).
*/
case MSG_PREAMP:
if ( dataLen != 0 ) // Any data?
{
if ( dataBuff[0] == 'A' ) // "AUTO" mode requested?
tempValue = AGC_ON; // Turn on the AGC
else if ( isDigit ( dataBuff[0] )) // It should be a number
tempValue = atoi ( dataBuff ); // Convert it to a number
SetPreampGain ( tempValue ); // Set the new value
} // End of if ( dataLen != 0 )
tempValue = GetPreampGain ( &agcOn, &reg69 );
if ( agcOn ) // If AGC is on
Serial.println ( "Preamp AGC is on" );
else // No AGC, report real gain
Serial.printf ( "Preamp gain is %ddB\n", tempValue );
pushSettings ();
return true;
/*
* The "DRIVE" command sets the local oscillator "drive" level.
*/
case MSG_DRIVE:
if ( dataLen != 0 ) // Drive level specified?
{
tempValue = atoi ( dataBuff ); // Yes, convert to a number
if (( tempValue < MIN_DRIVE ) || ( tempValue > MAX_DRIVE ))
Serial.printf ( "Invalid drive specification: %d\n", tempValue );
else // Set the transmitter drive level
SetLoDrive ( tempValue ); // "SetLoDrive" saves the settings
}
Serial.printf ( "Drive: %d (+%udBm)\n", setting.Drive, driveLevel[setting.Drive] );
pushSettings ();
return true;
/*
* Signal Generator On/Off commands
*
*/
case MSG_SGON:
SetSGState(1);
return true;
case MSG_SGOFF:
SetSGState(0);
return true;
case MSG_SGFREQ:
if ( dataLen != 0 ) // Frequency specified?
{
freq1 = ParseFrequency ( dataBuff ); // Yes, get new frequency
SetSGFreq (freq1);
}
Serial.printf ( "Signal Generator frequency: %i\n", FormatFrequency ( freq1 ));
return true;
/*
* The "SGLODRIVE" command sets the local oscillator "drive" level in signal generator mode.
*/
case MSG_SG_LO_DRIVE:
if ( dataLen != 0 ) // Drive level specified?
{
tempValue = atoi ( dataBuff ); // Yes, convert to a number
if (( tempValue < MIN_DRIVE ) || ( tempValue > MAX_DRIVE ))
Serial.printf ( "Invalid LO drive: %d\n", tempValue );
else // Set the transmitter drive level
SetSGLoDrive ( tempValue ); // "SetSGLoDrive" saves the settings
}
Serial.printf ( "SigGen LO Drive: %d (+%udBm)\n", sigGenSetting.LO_Drive, driveLevel[sigGenSetting.LO_Drive] );
//pushSettings ();
return true;
/*
* The "SGRXDRIVE" command sets the receiver (IF) oscillator "drive" level in signal generator mode.
*/
case MSG_SG_RX_DRIVE:
if ( dataLen != 0 ) // Drive level specified?
{
tempValue = atoi ( dataBuff ); // Yes, convert to a number
if (( tempValue < MIN_DRIVE ) || ( tempValue > RX_SI4432_MAX_DRIVE ))
Serial.printf ( "Invalid RX drive: %d - (%d-%d)\n", tempValue, MIN_DRIVE, RX_SI4432_MAX_DRIVE );
else // Set the transmitter drive level
SetSGRxDrive ( tempValue ); // "SetSGRxDrive" saves the settings
}
Serial.printf ( "SigGen RX Drive: %d (+%udBm)\n", sigGenSetting.RX_Drive, driveLevel[sigGenSetting.RX_Drive] );
//pushSettings ();
return true;
/*
* Tracking Generator On/Off commands
*
*/
case MSG_TGON:
SetTracking(1);
return true;
case MSG_TGOFF:
SetTracking(0);
return true;
#ifdef TG_LO_INSTALLED
case MSG_TGSIG:
SetTracking(2);
return true;
/*
* The "TGLODRIVE" command sets the local oscillator "drive" level in tracking generator mode.
* (only valid if two SI4432 used for the tracking generator)
*/
case MSG_TG_LO_DRIVE:
if ( dataLen != 0 ) // Drive level specified?
{
tempValue = atoi ( dataBuff ); // Yes, convert to a number
if (( tempValue < MIN_DRIVE ) || ( tempValue > MAX_DRIVE ))
Serial.printf ( "Invalid drive specification: %d\n", tempValue );
else // Set the transmitter drive level
SetTGLoDrive ( tempValue ); // "SetTGLoDrive" saves the settings
}
Serial.printf ( "Tracking Generator LO Drive: %d (+%udBm)\n", trackGenSetting.LO_Drive, driveLevel[trackGenSetting.LO_Drive] );
//pushSettings ();
return true;
#endif
#ifdef TG_IF_INSTALLED
/*
* The "TGRXDRIVE" command sets the receiver (IF) oscillator "drive" level in tracking generator mode.
*/
case MSG_TG_IF_DRIVE:
if ( dataLen != 0 ) // Drive level specified?
{
tempValue = atoi ( dataBuff ); // Yes, convert to a number
if (( tempValue < MIN_DRIVE ) || ( tempValue > MAX_DRIVE ))
Serial.printf ( "Invalid drive specification: %d\n", tempValue );
else // Set the transmitter drive level
SetTGIfDrive ( tempValue ); // "SetTGLoDrive" saves the settings
}
Serial.printf ( "Tracking Generator IF Drive: %d (+%udBm)\n", trackGenSetting.IF_Drive, driveLevel[trackGenSetting.IF_Drive] );
//pushSettings ();
return true;
#endif
case MSG_SAVE: // Save the scan configuration
if ( dataLen != 0 ) // Location specified?
{
Save ( atoi ( dataBuff )); // Save the settings
return true;
}
else
{
Serial.println ( "No save location specified!" );
return false;
}
case MSG_RECALL: // Retrieve saved scan configuration
if ( dataLen != 0 ) // Location specified?
{
Recall ( atoi ( dataBuff )); // Save the settings
return true;
}
else
{
Serial.println ( "No save location specified!" );
return false;
}
/*
* "FREQ" - Get or set the frequency for selected VFO
*/
case MSG_VFO_FREQ:
if ( dataLen != 0 ) // Frequency specified?
{
freq1 = ParseFrequency ( dataBuff ); // Yes, get new frequency
if ( VFO == RX_4432 ) // Receiver?
rcvr.SetFrequency ( freq1 );
if ( VFO == TX_4432 ) // Transmitter?
xmit.SetFrequency ( freq1 );
#ifdef TG_IF_INSTALLED
if ( VFO == TGIF_4432 ) // Track generator IF?
tg_if.SetFrequency ( freq1 );
#endif
#ifdef TG_LO_INSTALLED
if ( VFO == TGLO_4432 ) // Track generator IF?
tg_lo.SetFrequency ( freq1 );
#endif
}
if ( VFO == RX_4432 ) // Receiver?
Serial.printf ( "RX frequency: %s\n", FormatFrequency ( rcvr.GetFrequency() ));
if ( VFO == TX_4432 ) // Local Oscillator?
Serial.printf( "LO frequency: %s\n", FormatFrequency ( xmit.GetFrequency() ));
#ifdef TG_IF_INSTALLED
if ( VFO == TGIF_4432 ) // Local Oscillator?
Serial.printf( "TG IF frequency: %s\n", FormatFrequency ( tg_if.GetFrequency() ));
#endif
#ifdef TG_LO_INSTALLED
if ( VFO == TGLO_4432 ) // Local Oscillator?
Serial.printf( "TG LO frequency: %s\n", FormatFrequency ( tg_lo.GetFrequency() ));
#endif
return true;
/*
* The "ATTEN" command sets the PE4302 attenuation
*/
case MSG_ATTEN: // Set attenuation
if ( dataLen != 0 ) // Anything entered?
{
attenuation = atoi ( dataBuff ); // Get the value
if (( attenuation < 0 )
|| ( attenuation > PE4302_MAX )) // Range check
{
Serial.printf ( "Illegal attenuator setting: %ddB\n", attenuation );
return false;
}
SetAttenuation ( attenuation );
}
Serial.printf ( "Attenuator setting: %ddB\n", setting.Attenuate );
pushSettings ();
return true;
case MSG_HELP: // "HELP" or "?"
ShowMenu ();
return true;
case MSG_STEPS: // Set number of scan points
if ( dataLen != 0 ) // Anything entered?
steps = atoi ( &dataBuff[dataIx] );
Serial.print ( "Steps: " );
Serial.println ( steps );
return true;
/*
* The "DELAY" command sets the timestep in uS.
*/
case MSG_DELAY: // Set the timestep in uS, currently 2000
if ( dataLen != 0 ) // Anything entered?
{
delaytime = atol ( dataBuff ); // Yes, set new delay time
DisplayInfo ();
}
Serial.printf ( "Time per scan step = %uuS\n", delaytime );
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
*/
case MSG_VFO:
if ( dataLen != 0 ) // Anything entered?
{
if ( dataBuff[0] == 'R' ) // Receiver?
VFO = RX_4432;
else if (( dataBuff[0] == 'L' )
|| ( dataBuff[0] == 'T' )) // Transmitter?
VFO = TX_4432;
#ifdef TG_IF_INSTALLED
else if ( dataBuff[0] == 'I' ) // track gen IF
VFO = TGIF_4432;
#endif
#ifdef TG_LO_INSTALLED
else if ( dataBuff[0] == 'G' ) // track gen LO
VFO = TGLO_4432;
#endif
else // Illegal entry
{
VFO = RX_4432; // No, default to receiver
Serial.println ( "Illegal VFO specification! ");
}
}
Serial.print ( "Selected VFO is: " ); // Confirm setting
switch ( VFO )
{
case RX_4432: // Receiver
Serial.printf ( " Receiver (%u)\n", VFO );
break;
case TX_4432: // Transmitter (LO)
Serial.printf ( " Local Oscillator (%u)\n", VFO );
break;
case TGIF_4432: // Transmitter (LO)
Serial.printf ( " Tracking Generator IF (%u)\n", VFO );
break;
case TGLO_4432: // Transmitter (LO)
Serial.printf ( " Tracking Generator LO (%u)\n", VFO );
break;
}
return true;
/*
* The "RBW" command sets the resolution bandwidth (RBW).
*
* Entering "RBW A" (or "RBW AUTO") puts the RBW into automatic mode.
*/
case MSG_RBW:
if ( dataLen != 0 ) // Anything entered?
{
if ( dataBuff[0] == 'A' ) // Auto RBW requested?
bandwidth = 0; // Set bandwidth to '0'
else if ( isDigit ( dataBuff[0] )) // Was a number entered?
{
bandwidth = atof ( dataBuff ); // Yes, then get entered value
if ( bandwidth < 2.7 )
bandwidth = 2.7;
}
// Serial.print ( "RBW: " ); Serial.println ( bandwidth );
// setting.Bandwidth10 = (int) ( bandwidth * 10 ); // Here also
SetRBW ( (int) ( bandwidth * 10 ));
}
Serial.print ( "RBW = " );
if ( setting.Bandwidth10 == 0 )
Serial.printf ( "AUTO (%.1fHKz)\n", bandwidth );
else
Serial.printf ( "%.1fHKz\n", bandwidth );
return true; // End of 'W' command
case MSG_TRACES: // Only works on 'GAIN' trace for now
if ( dataLen != 0 ) // Anything entered?
{
if ( dataBuff[0] == 'G' ) // Gain trace requested?
{
setting.ShowGain = !setting.ShowGain; // Toggle the setting
WriteSettings (); // Save setting
return true; // Processed successfully
}
if ( dataBuff[0] == 'D' ); // Manin scan requested?
{
setting.ShowSweep = !setting.ShowSweep; // Toggle the setting
WriteSettings (); // Save setting
return true; // Processed successfully
}
}
else // Nothing specified
{
Serial.println ( "No 'TRACES' item specified!" );
return false; // Unsuccessful
}
case MSG_IF_FREQ:
if ( dataLen != 0 ) // Anything entered?
{
if ( dataBuff[0] == '+' ) // User wants to add an increment?
{
strcpy ( tempBuff, &dataBuff[1] ); // Copy the number to "tempBuff"
tempValue = 1; // We'll multiply by '+1'
}
else if ( dataBuff[0] == '-' ) // User wants to add a decrement?
{
strcpy ( tempBuff, &dataBuff[1] ); // Copy the number to "tempBuff"
tempValue = -1; // We'll multiply by '1'
}
else // No sign entered
{
strcpy ( tempBuff, dataBuff ); // Copy the number to "tempBuff"
tempValue = 0; // Indicate full frequency in the buffer
}
tempFreq = ParseFrequency ( tempBuff ); // Convert frequency string to a number
if ( tempValue != 0 ) // Increment or decrement?
tempFreq = ( tempFreq * tempValue ) + setting.IF_Freq;
if ( !SetIFFrequency ( tempFreq )) // Returns result of range check
Serial.println ( "Requested IF frequency exceeds limits; not changed!" );
}
Serial.printf ( "IF Frequency set to: %s\n", FormatFrequency ( setting.IF_Freq ));
return true;
/*
* "REGDUMP" Print the registers for selected VFO
*/
case MSG_REG_DUMP:
if ( VFO == RX_4432 ) // Receiver?
{
Serial.println ( "\nReceiver registers:\n" );
rcvr.PrintRegs();
}
if ( VFO == TX_4432 ) // Transmitter?
{
Serial.println ( "\nTransmitter (LO) registers:\n" );
xmit.PrintRegs();
}
#ifdef TG_IF_INSTALLED
if ( VFO == TGIF_4432 ) // Track generator IF
{
Serial.println ( "\nTracking Generator IF registers:\n" );
tg_if.PrintRegs();
}
#endif
#ifdef TG_LO_INSTALLED
if ( VFO == TGLO_4432 ) // Track generator Lo
{
Serial.println ( "\nTracking Generator LO registers:\n" );
tg_lo.PrintRegs();
}
#endif
return true;
/*
* The "RSSI" and "QUIT" commands turn the displaying of the RSSI (Receiver Signal
* Strength Indicator) readings in the scan.
*
* Notice "showRSSI" gets set to '2'. I tried making it an option to only show one
* pass through the sweep, but somehow, the output starts mid-sweep. Until I figure
* that out, continuois is the onlt option. The hooks for that are still in the
* sweep loop.
*/
case MSG_RSSI:
if ( dataLen != 0 ) // Anything entered?
{
if ( dataBuff[0] == 'C' ) // Continuous reporting requested?
showRSSI = 2; // Yes
else if ( dataBuff[0] == '1' ) // Or just one time?
showRSSI = 1; // Set it
}
else // No specification
showRSSI = 1; // Then the default is one time
initSweep = true; // Restart the sweep
return true;
case MSG_QUIT:
showRSSI = 0; // Turn RSSI display off
return true;
/*
* The "REGISTER" command can read or write any of the Si4432 registers; it's pretty clever!
*
* The register designation and (optional) data to be written are in HEX. If you enter
* something like "REGISTER 2F", the response will be the contents of the specified register
* (in this case, "2F").
*
* If you enter something like "REGISTER 69 2", it will write 0x02 into register 0x69.
*
* Note, the current setting of "VFO" ("VFO" command) must be either the transmitter
* or receiver modules.
*/
case MSG_SET_REG:
tempBuff[0] = 0; // Start with null terminator
tempIx = 0; // And zero out the index;
if (( VFO != RX_4432 ) && ( VFO != TX_4432 ) && ( VFO != TGIF_4432 ) && ( VFO != TGLO_4432 ) )
{
Serial.print ( "Illegal Si4432 module specified; 'VFO' = " );
Serial.println ( VFO );
return false;
}
if ( dataLen == 0 ) // Any data?
{
Serial.println ( "No register specified!" );
return false;
}
/*
* Ok, there is some data there so let's extract the register specification.
*/
while ( dataIx < dataLen )
{
c = dataBuff[dataIx++]; // Next character from input
if ((( c != ' ' ) && ( c != ',' )) && ( isHex ( c )))
{
tempBuff[tempIx++] = c; // Copy to temporary buffer
tempBuff[tempIx] = 0; // Add null terminator
}
else if (( c != ' ' ) || ( c != ',' )) // If it's a space or a comma
break; // On to the next step
if ( !isHex ( c )) // If not a valid hex digit
{
Serial.printf ( "'%c' is not a hexadecimal digit!\n", c );
return false;
}
}
addr = xtoi ( tempBuff ) & 0xFF; // Convert buffer to address
tempIx = 0; // Start over for data if any
tempBuff[0] = 0; // Null terminate the buffer
/*
* If we got this far, we have a register address. Now we need to determine whether
* or not the user also specified a value to set in that register or if they just
* want a readout of what's in the register.
*/
while ( dataIx < dataLen )
{
c = dataBuff[dataIx++]; // Next character from input
if ( !isHex ( c )) // Check for non hexadecimal digit
{
Serial.printf ( "'%c' is not a hexadecimal digit!\n", c );
return false;
}
tempBuff[tempIx++] = c; // Copy to other buffer
tempBuff[tempIx] = 0; // Null terminator
}
if ( strlen ( tempBuff ) > 0 ) // If something in the buffer
{
tempValue = xtoi ( tempBuff ) & 0xFF; // Convert it to a hex number
if ( VFO == RX_4432 ) // Receiver?
rcvr.WriteByte ( addr, tempValue ); // Send data to the device
if ( VFO == TX_4432 ) // Transmitter?
xmit.WriteByte ( addr, tempValue ); // Send data to the device
#ifdef TG_IF_INSTALLED
if ( VFO == TGIF_4432 ) // Track gen IF
tg_if.WriteByte ( addr, tempValue ); // Send data to the device
#endif
#ifdef TG_LO_INSTALLED
if ( VFO == TGLO_4432 ) // Track gen LO
tg_lo.WriteByte ( addr, tempValue ); // Send data to the device
#endif
}
/*
* So far, so good! When we get here, either the user just wanted to see what's in
* the register or we have already changed it. Now just report the contents of the
* selected register.
*/
if ( VFO == RX_4432 ) // Receiver?
Serial.printf ( "RX Reg[0x%02X] = 0x%02X\n", addr, rcvr.ReadByte ( addr ));
else if ( VFO == TX_4432 ) // Transmitter?
Serial.printf ( "TX Reg[0x%02X] = 0x%02X\n", addr, xmit.ReadByte ( addr ));
#ifdef TG_IF_INSTALLED
else if ( VFO == TGIF_4432 ) // Transmitter?
Serial.printf ( "TGIF Reg[0x%02X] = 0x%02X\n", addr, tg_if.ReadByte ( addr ));
#endif
#ifdef TG_LO_INSTALLED
else if ( VFO == TGLO_4432 ) // Transmitter?
Serial.printf ( "TGLO Reg[0x%02X] = 0x%02X\n", addr, tg_lo.ReadByte ( addr ));
#endif
return true;
/*
* 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
* 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:
if ( dataLen != 0 ) // Value specified?
{
tempValue = xtoi ( dataBuff ); // Yes, get new frequency (in hex)
if ( VFO == RX_4432 ) // Receiver?
{
rcvr.Tune ( tempValue ); // Tune it
config.RX_capacitance = tempValue; // And save it
}
if ( VFO == TX_4432 ) // Transmitter?
{
xmit.Tune ( tempValue ); // Tune it
config.TX_capacitance = tempValue; // And save it
}
#ifdef TG_IF_INSTALLED
if ( VFO == TGIF_4432 ) // Trackgen IF
{
tg_if.Tune ( tempValue ); // Tune it
config.tgIF_capacitance = tempValue; // And save it
}
#endif
#ifdef TG_LO_INSTALLED
if ( VFO == TGLO_4432 ) // Track Gen LO
{
tg_lo.Tune ( tempValue ); // Tune it
config.tgLO_capacitance = tempValue; // And save it
}
#endif
}
if ( VFO == RX_4432 ) // Receiver?
{
tempValue = rcvr.ReadByte ( REG_COLC ); // Get current setting
Serial.printf ( "RX capacitance: 0x%2X\n", tempValue );
}
if ( VFO == TX_4432 ) // Local Oscillator?
{
tempValue = xmit.ReadByte ( REG_COLC ); // Get current setting
Serial.printf ( "TX capacitance: 0x%2X\n", tempValue );
}
#ifdef TG_IF_INSTALLED
if ( VFO == TGIF_4432 ) // Tracking gen IF
{
tempValue = tg_if.ReadByte ( REG_COLC ); // Get current setting
Serial.printf ( "TGIF capacitance: 0x%2X\n", tempValue );
}
#endif
#ifdef TG_LO_INSTALLED
if ( VFO == TGLO_4432 ) // Tracking gen LO
{
tempValue = tg_lo.ReadByte ( REG_COLC ); // Get current setting
Serial.printf ( "TGLO capacitance: 0x%2X\n", tempValue );
}
#endif
return true;
/*
* The "REF_FREQ" ("MSG_GPIO2") command sets or gets the current frequency setting for
* the GPIO2 calibration frequency for the transmitter Si4432.
*
* The legal values are:
*
* 30MHz, 15MHz, 10MHz, 4MHz, 3MHz, 2MHz, 1MHz
*
* WA2FZW - For whatever reason, when a value is entered and the register is set, the
* readback of register "REG_MCOC" is correct, but when no data is entered the readback
* is always zero. Needs further investigation!
*/
case MSG_GPIO2:
if ( dataLen != 0 ) // Value specified?
{
if ( dataBuff[0] == 'O' ) // Turn it off?
tempIx = 7; // This will do it!
else
{
tempValue = atoi ( dataBuff ); // Yes, get new frequency
for ( tempIx = 0; tempIx < 7; tempIx++ ) // Loop through the table
if ( tempValue == fTranslate[tempIx] ) // A match?
break; // Get out of the loop
if ( tempIx > 6 ) // Illegal entry
{
Serial.printf ( "'%u' is an illegal GPIO2 frequency setting!\n", tempValue );
return false;
}
}
SetRefOutput ( tempIx ); // Set the frequency
}
tempValue = xmit.ReadByte ( REG_GPIO2 ); // Read the GPIO2 register
if ( tempValue == 0x1F )
Serial.println ( "Transmitter GPIO2 reference frequency is off!" );
else
{
tempValue = xmit.ReadByte ( REG_MCOC ); // Read the clock register
Serial.printf ( "Transmitter GPIO2 reference frequency: %uMHz\n", fTranslate[tempValue] );
}
return true;
case MSG_ACT_PWR: // Calibrate the indicated power level
if ( dataLen != 0 ) // Value specified?
{
tempValue = atof ( dataBuff ); // Yes, get new power reading
RequestSetPowerLevel ( tempValue );
Serial.printf ( "Indicated power set to: %f\n", tempValue );
return true;
}
else
{
Serial.println ( "No indicated power specified!" );
return false;
}
case MSG_WIFI_UPDATE: // Set WiFi update target time
if ( dataLen != 0 ) // Value specified?
{
tempValue = atoi ( dataBuff ); // Yes, get new value in milliSeconds
// check valid
if ( (tempValue > 10) && (tempValue < 2000) )
{
wiFiTargetTime = tempValue * 1000; // convert to microSeconds
Serial.printf ( "Target wifi Update time set to: %d\n", tempValue );
return true;
}
else
{
Serial.println("Invalid - must be between 10ms and 2000ms");
return false;
}
}
else
{
Serial.printf ( "WiFi update target time is %ums\n", wiFiTargetTime/1000 );
return false;
}
case MSG_WEBSKT_INTERVAL: // Set WiFi websocket interval time
if ( dataLen != 0 ) // Value specified?
{
tempValue = atoi ( dataBuff ); // Yes, get new value in microSeconds
// check valid
if ( (tempValue > 50) && (tempValue < 10000) )
{
websocketInterval = tempValue;
Serial.printf ( "websocket poll interval set to: %uus\n", tempValue );
return true;
}
else
{
Serial.println("Invalid - must be between 50us and 10,000us");
return false;
}
}
else
{
Serial.printf ( "Websocket poll interval is %uus\n", websocketInterval );
return false;
}
case MSG_WIFI_POINTS: // Set WiFi chunk size
if ( dataLen != 0 ) // Value specified?
{
tempValue = atoi ( dataBuff ); // Yes, get new value
// check valid
if ( (tempValue > 10) && (tempValue < displayPoints * OVERLAP) )
{
wiFiPoints = tempValue;
Serial.printf ( "Chunk size set to: %u\n", tempValue );
return true;
}
else
{
Serial.printf("Invalid - must be between 10 and %u\n", displayPoints * OVERLAP);
return false;
}
}
else
{
Serial.printf ( "Chunk size is %u\n", wiFiPoints );
return false;
}
case MSG_CONFIG: // Save configuration settings
WriteConfig ();
return true;
case MSG_PAUSE: // Pause Scan (toggle)
paused = ! paused;
if (paused)
Serial.println ( "Sweep paused" );
else
Serial.println ( "Sweep resumed" );
return true;
case MSG_SWEEPLO:
setMode(SA_LOW_RANGE);
return true;
case MSG_SIGLO:
setMode(SIG_GEN_LOW_RANGE);
return true;
case MSG_BANDSCOPE:
setMode(BANDSCOPE);
return true;
case MSG_IFSWEEP:
setMode(IF_SWEEP);
return true;
case MSG_RX_SWEEP:
setMode(RX_SWEEP);
return true;
case MSG_TGOFFSET:
if ( dataLen != 0 ) // Frequency specified?
{
signedFreq = ParseSignedFrequency ( dataBuff ); // Yes, get new frequency
SetTGOffset (signedFreq);
}
Serial.printf ( "Tracking Generator IF offset: %i\n", trackGenSetting.Offset ); // sort out signed version of format frequency!!!!!!!!!!!!!!!!!!!
return true;
case MSG_TGFREQ:
if ( dataLen != 0 ) // Frequency specified?
{
freq1 = ParseFrequency ( dataBuff ); // Yes, get new frequency
SetTGFreq (freq1);
}
Serial.printf ( "Tracking Signal Generator frequency: %i\n", FormatFrequency ( trackGenSetting.Frequency ));
return true;
/*
* "FREQ" - Get or set the frequency for IF sweep injected signal
*/
case MSG_IFSIGNAL:
if ( dataLen != 0 ) // Frequency specified?
{
freq1 = ParseFrequency ( dataBuff ); // Yes, get new frequency
SetIFsweepSigFreq (freq1);
}
Serial.printf ( "IF Sweep Signal frequency: %s\n", FormatFrequency ( GetIFsweepSigFreq() ));
return true;
case MSG_SPUR:
if ( dataLen != 0 ) // Anything entered?
{
// if ( dataBuff[0] == '1' ) // Turn Spur reduction on
if ( strcmp ( dataBuff, "ON" ) == 0 )
{
SetSpur (1);
return true; // Processed successfully
}
if ( strcmp ( dataBuff, "OFF" ) == 0 )
// if ( dataBuff[0] == '0' ); // Turn Spur reduction off
{
SetSpur (0);
return true; // Processed successfully
}
Serial.println( "Invalid argument for Spur command - use 1(on) or 0(off)" );
return false;
}
else // Nothing specified
{
Serial.printf ( "Spur reduction: %s\n", setting.Spur ? "ON" : "OFF" );
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;
/*
* Set external gain or attenuation
*/
case MSG_EXTGAIN:
if ( dataLen != 0 ) // Value specified?
{
tempFloat = atof ( dataBuff ); // Yes
SetExtGain( tempFloat );
DisplayInfo (); // Re display the scan
}
Serial.printf ( "External gain: %4.1f\n", setting.ExternalGain );
return true;
/*
* Start calibration of the SI4432 bandpass filters (ie different RBW)
* Valid only if already in RX Sweep mode
*/
case MSG_BPFCAL:
if ( setting.Mode == RX_SWEEP )
{
bpfCalibrate = true;
Serial.println("Starting bpf calibration");
return true;
}
return false;
default:
return false;
} // End of "switch"
} // End of "ProcessCommand"
/*
* "ParseFrequency" expects a frequency in the "freqString". The format of the string
* is designed to be consistent with the way frequencies are entered using the touch
* screen menus. That is one can enter something like "100M" (or "100m") to specify
* 100MHz or "100k" (or "100K") to specify 100KHz. if you want to enter the
* value in pure Hertz, you can use commas (or dots) for clarity; for example,
* "100,000,000" would specify 100MHz.
*/
uint32_t ParseFrequency ( char* freqString )
{
uint32_t freq = 0; // Used to build the frequency
uint8_t index = 0; // Input buffer index
uint8_t digit; // Next digit
for ( index = 0; index < strlen ( freqString ); index++ )
{
digit = freqString[index]; // Get a digit or something else
if ( isDigit ( digit )) // If it is a number
freq = ( freq * 10 ) + ( digit - 0x30 ); // Cheap atoi conversion
else if ( digit == 'M' )
{
freq *= 1000000; // Make it MHz
return freq; // And send it back
}
else if ( digit == 'K' )
{
freq *= 1000; // Make it KHz
return freq; // And send it back
}
}
return freq;
}
int32_t ParseSignedFrequency ( char* freqString )
{
int32_t freq = 0; // Used to build the frequency
uint8_t index = 0; // Input buffer index
uint8_t digit; // Next digit
int32_t negative = 1;
for ( index = 0; index < strlen ( freqString ); index++ )
{
digit = freqString[index]; // Get a digit or something else
if ( isDigit ( digit )) // If it is a number
freq = ( freq * 10 ) + ( digit - 0x30 ); // Cheap atoi conversion
else if ( digit == 'M' )
{
freq *= 1000000; // Make it MHz
return freq * negative; // And send it back
}
else if ( digit == 'K' )
{
freq *= 1000; // Make it KHz
return freq * negative; // And send it back
}
else if ( digit == '-' )
{
negative = -1;
}
}
return freq * negative;
}
/*
* "FormatFrequency" breaks down the frequency into a format of "nnn,nnn,nnn" in the
* frequency buffer.
*/
char* FormatFrequency ( uint32_t freq )
{
uint32_t MHz; // MHz part of frequency
uint32_t KHz; // KHz part of frequency
uint32_t remainder; // Leftover pieces
static char freqBuff[20] = {0}; // The formatted frequency
MHz = freq / 1000000; // Isolate MHz
remainder = freq % 1000000; // The remainder of the frequency
KHz = remainder / 1000; // Isolate KHz
remainder = remainder % 1000; // Low order 3 digits
if ( freq >= 1000000 )
sprintf ( freqBuff, "%lu,%03lu,%03lu Hz", MHz, KHz, remainder );
else if ( freq >= 1000 )
sprintf ( freqBuff, "%lu,%03lu Hz", KHz, remainder );
else
sprintf ( freqBuff, "%lu Hz", remainder );
return freqBuff;
}
/*
* "FormatSignedFrequency" breaks down the frequency into a format of "nnn,nnn,nnn" in the
* frequency buffer.
*/
char* FormatSignedFrequency ( int32_t freq )
{
uint32_t uFreq; // turned positive
uint32_t MHz; // MHz part of frequency
uint32_t KHz; // KHz part of frequency
uint32_t remainder; // Leftover pieces
static char freqBuff[20] = {0}; // The formatted frequency
static char firstChar = ' ';
if (freq < 0)
uFreq = -freq;
else
uFreq = freq;
if (freq < 0)
firstChar = '-';
MHz = uFreq / 1000000; // Isolate MHz
remainder = uFreq % 1000000; // The remainder of the frequency
KHz = remainder / 1000; // Isolate KHz
remainder = remainder % 1000; // Low order 3 digits
if ( uFreq >= 1000000 )
sprintf ( freqBuff, "%c%lu,%03lu,%03lu Hz", firstChar, MHz, KHz, remainder );
else if ( uFreq >= 1000 )
sprintf ( freqBuff, "%c%lu,%03lu Hz", firstChar, KHz, remainder );
else
sprintf ( freqBuff, "%c%lu Hz", firstChar, remainder );
return freqBuff;
}
/*
* "DisplayFrequency" breaks down the frequency into a format of "nnn,nnn,nnn" in the
* frequency buffer, with leading zeros and no unit
*/
char* DisplayFrequency ( uint32_t freq )
{
uint32_t MHz; // MHz part of frequency
uint32_t KHz; // KHz part of frequency
uint32_t remainder; // Leftover pieces
static char freqBuff[20] = {0}; // The formatted frequency
MHz = freq / 1000000; // Isolate MHz
remainder = freq % 1000000; // The remainder of the frequency
KHz = remainder / 1000; // Isolate KHz
remainder = remainder % 1000; // Low order 3 digits
sprintf ( freqBuff, "%03lu,%03lu,%03lu", MHz, KHz, remainder );
return freqBuff;
}
/*
* "xtoi()" works similar to "atoi()" except the string is assumed to be hex numbers. It will
* convert characters in the string as long as the next character in the string is a valid hex
* digit. In other words it stops converting if it sees a null or a ';', or any thing else that
* is not a valid hex digit. The only exception is the character "X" (or "x"), which is ignored.
* This allows it to correctly convert a string in the "0xNN..." format.
*/
uint16_t xtoi ( char* hexString )
{
int ix; // Loop index
char c; // Single character
uint16_t answer = 0; // The answer
for ( ix = 0; ix < strlen ( hexString ); ix++ )
{
c = toupper ( hexString[ix] ); // Get next character
if ( c == 'X' )
continue;
if ( isdigit ( c )) // Normal base 10 digit?
answer = ( answer * 16 ) + ( c - '0' ); // shift answer and add new number
else if ( c >= 'A' && c <= 'F' ) // Valid hex digit?
answer = ( answer * 16 ) + ( c - 'A' + 10 ); // Shift answer and add new number
else
break;
}
return answer;
}
/*
* "isHex" returns true if the character is a valid hexadecimal digit or the letter 'X'
* (or 'x') as used in formatting a hexadecimal number as "0xNN".
*/
bool isHex ( char c )
{
char myC = toupper ( c ); // Make uppercase
if (( myC >= '0' ) && ( myC <= '9' )) // standard digits
return true;
if (( myC >= 'A' ) && ( myC <= 'F' )) // More digits
return true;
if ( myC == 'X' ) // Special hex formatting
return true;
return false; // If none of the above
}
/*
* "UpdateMarker" changes the enabled/disabled status of a marker or its color.
* The "mkr" argument is the index to the marker, not its number.
*
* The "action" argument is (for now) a single character from the following list:
*
* [E]NABLE Turn the marker on
* [D]ISABLE Turn the marker off
* [W]HITE Set the color to white
* [R]ED Set the color to red
* [B]LUE etc.
* [G]REEN
* [Y]ELLOW
* [O]RANGE
*/
bool UpdateMarker ( uint8_t mkr, char action )
{
uint8_t oldMkrStatus;
oldMkrStatus = marker[mkr].Status (); // Remember marker'scurrent status
switch ( action ) // What are we supposed to change
{
case 'E': // Enable the marker
marker[mkr].Enable ();
break; // Return success
case 'D': // Disable the marker
marker[mkr].Disable ();
break; // Return success
case 'W': // Set color to white
marker[mkr].Color ( WHITE );
break; // Return success
case 'R': // Set color to red
marker[mkr].Color ( RED );
break; // Return success
case 'B': // Set color to blue
marker[mkr].Color ( BLUE );
break; // Return success
case 'G': // Set color to green
marker[mkr].Color ( GREEN );
break; // Return success
case 'Y': // Set color to yellow
marker[mkr].Color ( YELLOW );
break; // Return success
case 'O': // Set color to orange
marker[mkr].Color ( ORANGE );
break; // Return success
default: // If none of the above
return false; // Illegal action/color
}
/*
* If we get here, the marker number and action were good. Now see if something
* actually changes and if so, update and save the "setting" structure.
*/
if ( oldMkrStatus != marker[mkr].Status () )
{
setting.MkrStatus[mkr] = marker[mkr].Status ();
WriteSettings ();
}
return true; // Marker command processed ok
}
/*
* "SetPreampGain" sets the preamp gain, which was added to the "setting" structure in
* Version 2.5. "GetPreampGain" reads it from the Si4432 and reports the current setting.
*/
void SetPreampGain ( uint8_t gain )
{
uint8_t oldGain = setting.PreampGain; // Remember current setting
// Serial.printf ( "Cmd.cpp - SetPreampGain %i \n", gain );
if (( gain == 0 ) || ( gain == 0x60 )) // AGC on request?
gain = 0x60; // That will be the setting
else if ( gain < 25 ) // If < 25 use low range
{
gain = ( gain - 5 ) / 3; // Value for the register
gain &= PGAGAIN; // Just the low 4 bits
}
else if ( gain >= 25 ) // If we found it above
{
gain = ( gain - 25 ) / 3; // Value for the register
gain &= PGAGAIN; // Just the low 4 bits
gain |= LNAGAIN; // Plus the high range bit
}
setting.PreampGain = gain; // Set new value in the "setting" structure
rcvr.SetPreampGain ( gain ); // and in the receiver module
if ( oldGain != setting.PreampGain ) // Did it actually change?
{
changedSetting = true; // Yes, need to update the display
WriteSettings (); // and save the "setting" structure
}
}
uint8_t GetPreampGain ( bool* agc, uint8_t* reg )
{
uint8_t Reg69 = rcvr.GetPreampGain (); // Read the register
int lnaGain = 5; // Assume low gain
if ( Reg69 & LNAGAIN ) // If LNA is turned on
lnaGain = 25; // Add 25 dB to total gain
int pgaGain = ( Reg69 & PGAGAIN ) * 3; // 3dB per bit
*agc = (bool) ( Reg69 & AGCEN ); // Set true if AGC is enabled
*reg = Reg69 & ( PGAGAIN | LNAGAIN );
return lnaGain + pgaGain; // Total gain
}
/*
* "SetRefOutput" - Sets the GPIO2 frequency for the LO Si4432 module
*/
void SetRefOutput ( int freq )
{
int oldFreq = setting.ReferenceOut;
setting.ReferenceOut = freq;
xmit.SetPowerReference ( freq );
if ( oldFreq != setting.ReferenceOut )
{
changedSetting = true;
WriteSettings ();
}
}
/*
* "SetRefLevel" - Sets the decibel level for the top line of the graph.
*/
void SetRefLevel ( int ref )
{
int oldMin = setting.MinGrid;
int oldMax = setting.MaxGrid;
setting.MaxGrid = ref;
setting.MinGrid = ref - yGrid * setting.PowerGrid;
if (( oldMin != setting.MinGrid ) || ( oldMax != setting.MaxGrid ))
{
changedSetting = true;
WriteSettings ();
}
}
/*
* "SetGenerate" - Puts the unit into Signal Generator Mode
* Will be superceded by changing setting.mode
*/
void SetGenerate ( int8_t g )
{
int oldG = setting.Generate;
setting.Generate = g;
if (g) {
tinySA_mode = SIG_GEN_LOW_RANGE;
} else {
tinySA_mode = SA_LOW_RANGE;
}
if ( oldG != setting.Generate )
{
changedSetting = true;
WriteSettings ();
}
}
/*
* "SetTracking" - sets the Tracking Generator Mode
* 0 = off, 1 = track, 2 = sig gen
*/
void SetTracking ( int8_t m )
{
int oldM = trackGenSetting.Mode;
trackGenSetting.Mode = m;
if ( oldM != trackGenSetting.Mode )
{
changedSetting = true;
WriteTrackGenSettings ();
pushSettings ();
// Serial.println("SetTracking - pushSettings");
}
}
/*
* "SetPowerGrid" - Sets the dB/vertical divison on the grid
*/
void SetPowerGrid ( int g )
{
int oldGrid = setting.PowerGrid;
setting.PowerGrid = g;
setting.MinGrid = setting.MaxGrid - yGrid * g;
if ( oldGrid != setting.PowerGrid )
{
changedSetting = true;
WriteSettings ();
}
}
/*
* Set the IF frequency - M0WID addition
*/
bool SetIFFrequency ( int32_t freq )
{
int32_t oldFreq = setting.IF_Freq;
// Serial.print ( "SetIF: freq = " ); Serial.println ( freq );
if (( freq < MIN_IF_FREQ ) || ( freq > MAX_IF_FREQ )) // If out of limits
return false; // Don't change it
// Serial.println ( "SetIF: Past range check" );
setting.IF_Freq = freq;
if ( oldFreq != setting.IF_Freq )
{
changedSetting = true;
WriteSettings ();
}
return true; // Frequency was good
}
/*
* "SetAttenuation" - Modified by WA2FZW:
*
* "setting.Attenuate" is now stored as a positive number as displaying
* an attenuation of "-nn" (as was the case in the original software)
* would imply a gain value. All other places where it is used have been
* modified accordingly.
*
* Note, we don't actually set the attenuation in the PE4302 as that is done at
* the start of each sweep cycle.
*/
void SetAttenuation ( int a )
{
int oldAtten = setting.Attenuate;
setting.Attenuate = a; // Put value in the setting structure
if ( oldAtten != setting.Attenuate )
{
changedSetting = true;
WriteSettings ();
}
}
/*
* Set the external gain value
* +ve for gain, -ve for attenuation
*/
void SetExtGain ( double a )
{
setting.ExternalGain = a;
changedSetting = true;
WriteSettings ();
pushSettings();
}
double GetExtGain (void )
{
return setting.ExternalGain;
}
/*
* "SetStorage" - Saves the results of a scan for comparison to an active one.
*/
void SetStorage ( void )
{
for ( int i = 0; i < displayPoints; i++ )
myStorage[i] = myData[i];
setting.ShowStorage = true;
}
/*
* "SetClearStorage" - Logically clears the saved scan data
*/
void SetClearStorage ( void )
{
setting.ShowStorage = false;
setting.SubtractStorage = false;
}
/*
* "SetSubtractStorage"
*/
void SetSubtractStorage ( void )
{
if ( !setting.ShowStorage )
SetStorage ();
setting.SubtractStorage = true;
}
/*
* "RequestSetPowerLevel" will be called from the menu or WiFi, possibly Serial to request
* the offset is calculated so that peak power measured in the sweep matches the input value
* the Request function sets the flag to initiate the calibration at the start of the next sweep
* using the input value.
*/
void RequestSetPowerLevel ( float o )
{
actualPower = o;
setActualPowerRequested = true;
// Serial.printf ( "Request set Power Level %f \n", o );
}
/*
* "SetPowerLevel"
* Adjusts displayed power to match the actual input signal power - calibration function
*/
void SetPowerLevel ( double o )
{
double oldOffset = setting.LevelOffset;
if ( o != 100.0 )
setting.LevelOffset = ( o - ( (double)oldPeakLevel / 2.0 + setting.Attenuate - setting.ExternalGain - 120.0) ); // WA2FZW
else
setting.LevelOffset = 0.0;
// Serial.printf ( "Peak level: %i, Actual: %f, Level offset: %f \n",
// oldPeakLevel, o, setting.LevelOffset );
if ( oldOffset != setting.LevelOffset )
{
changedSetting = true;
RedrawHisto (); // Redraw labels and restart sweep with new settings
WriteSettings ();
pushSettings();
// Serial.println("Power level changed");
}
}
/*
* "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 SetRBW ( int bw )
{
int16_t oldRBW = setting.Bandwidth10;
int16_t tempBw;
if ( bw == 0 ) // "AUTO" mode requested
tempBw = ( setting.ScanStop - setting.ScanStart ) / 30000;
else
tempBw = bw;
setting.Bandwidth10 = bw;
// bandwidth = rcvr.SetRBW ( tempBw, &delaytime ); // Not needed as will be done at start of sweep
RedrawHisto (); // Redraw labels and restart sweep with new settings
if ( setting.Bandwidth10 != oldRBW )
{
changedSetting = true;
WriteSettings ();
}
}
/*
* "SetSpur" - Turns spurious signal supression on or off
* Also turns on the MIN trace which is needed for the spur reduction to be seen.
* Only turns the average trace off of it is still set to min
* Also turns off/on the dB trace
*/
void SetSpur ( int v )
{
int oldSpur = setting.Spur;
setting.Spur = v;
if (setting.Spur) {
setting.Average = AV_MIN;
setting.ShowSweep = false;
} else {
if (setting.Average == AV_MIN)
setting.Average = AV_OFF;
setting.ShowSweep = true;
}
if ( oldSpur != setting.Spur )
{
changedSetting = true;
WriteSettings ();
pushSettings ();
}
}
/*
* "SetAverage" - Sets the number of readings to average for each display point
*/
void SetAverage ( int v )
{
int oldAvg = setting.Average;
setting.Average = v;
if ( oldAvg != setting.Average )
{
changedSetting = true;
WriteSettings ();
}
}
/*
* "SetLoDrive" sets the transmitter output level.
*
* The drive value can be from '0' to '7' and the output power will be set
* according to the following table (from page 39 of the Si4432 datasheet):
*
* 0 => +1dBm 4 => +11dBm
* 1 => +2dBm 5 => +14dBm
* 2 => +5dBm 6 => +17dBm
* 3 => +8dBm 7 => +20dBm
*/
void SetLoDrive ( uint8_t level )
{
int oldLevel = setting.Drive;
if (( level < 0 ) || ( level > 7 ))
return;
else
{
setting.Drive = level;
xmit.SetDrive ( level ); // Set transmitter power level
if ( oldLevel != setting.Drive )
{
changedSetting = true;
WriteSettings ();
pushSettings ();
}
}
}
/* Set SGFreq sets the signal generator Frequency
* Signal generator frequency may change frequently, so don't save the setting
* Settings are saved when leaving the mode
* parameter is in Hz
*/
bool SetSGFreq (uint32_t f)
{
if ( ( f >= MIN_SIGLO_FREQ ) && ( f <= MAX_SIGLO_FREQ ) )
{
sigGenSetting.Frequency = f;
return true;
}
else
return false;
}
/*
* Sig gen state is not saved - always want to start in off state
*/
void SetSGState (uint16_t s)
{
sigGenOutputOn = s;
}
/*
* "SetSGLoDrive" sets the transmitter max output level in signal generator mode.
*
* The drive value can be from '0' to '7' and the output power will be set
* according to the following table (from page 39 of the Si4432 datasheet):
*
* 0 => +1dBm 4 => +11dBm
* 1 => +2dBm 5 => +14dBm
* 2 => +5dBm 6 => +17dBm
* 3 => +8dBm 7 => +20dBm
*/
void SetSGLoDrive ( uint8_t level )
{
int oldLevel = sigGenSetting.LO_Drive;
if (( level < 0 ) || ( level > 7 ))
return;
else
{
sigGenSetting.LO_Drive = level;
if (setting.Mode == SIG_GEN_LOW_RANGE)
xmit.SetDrive ( level ); // Set transmitter power level
if ( oldLevel != sigGenSetting.LO_Drive )
{
changedSetting = true;
WriteSigGenSettings ();
}
}
}
/*
* "SetSGRxDrive" sets the receiver output level in signal generator mode
*
* The drive value can be from '0' to '7' and the output power will be set
* according to the following table (from page 39 of the Si4432 datasheet):
*
* 0 => +1dBm 4 => +11dBm
* 1 => +2dBm 5 => +14dBm
* 2 => +5dBm 6 => +17dBm
* 3 => +8dBm 7 => +20dBm
* Note the power limits of the B3555 SAW filter is 10dBm
* RX_SI4432_MAX_DRIVE is set in my_SA.h to suit the attenuator pad and losses
* in your build.
*/
void SetSGRxDrive ( uint8_t level )
{
int oldLevel = sigGenSetting.RX_Drive;
if (( level < 0 ) || ( level > RX_SI4432_MAX_DRIVE ))
return;
else
{
sigGenSetting.RX_Drive = level;
if (setting.Mode == SIG_GEN_LOW_RANGE)
rcvr.SetDrive ( level ); // Set transmitter power level
if ( oldLevel != sigGenSetting.RX_Drive )
{
changedSetting = true;
WriteSigGenSettings ();
}
}
}
void SetSGPower ( int16_t dBm )
{
// Add algorithm to set attenuator and IF drive level
// initially limit to just set attenuator
if ( dBm > sigGenSetting.Calibration )
dBm = sigGenSetting.Calibration;
if ( dBm < (sigGenSetting.Calibration - ATTENUATOR_RANGE - RX_SI4432_MAX_DRIVE * 3) )
dBm = sigGenSetting.Calibration - ATTENUATOR_RANGE;
sigGenSetting.Power = dBm;
changedSetting = true;
}
#ifdef TG_LO_INSTALLED
/*
* "SetTGLoDrive" sets the transmitter max output level for the tracking generator.
*
* The drive value can be from '0' to '7' and the output power will be set
* according to the following table (from page 39 of the Si4432 datasheet):
*
* 0 => +1dBm 4 => +11dBm
* 1 => +2dBm 5 => +14dBm
* 2 => +5dBm 6 => +17dBm
* 3 => +8dBm 7 => +20dBm
*/
void SetTGLoDrive ( uint8_t level )
{
int oldLevel = trackGenSetting.LO_Drive;
if (( level < 0 ) || ( level > 7 ))
return;
else
{
trackGenSetting.LO_Drive = level;
tg_lo.SetDrive ( level ); // Set transmitter power level
if ( oldLevel != trackGenSetting.LO_Drive )
{
changedSetting = true;
WriteTrackGenSettings ();
}
}
}
#endif
#ifdef TG_IF_INSTALLED
/*
* "SetTGIfDrive" sets the transmitter output level for the tracking generator IF.
*
* The drive value can be from '0' to '7' and the output power will be set
* according to the following table (from page 39 of the Si4432 datasheet):
*
* 0 => +1dBm 4 => +11dBm
* 1 => +2dBm 5 => +14dBm
* 2 => +5dBm 6 => +17dBm
* 3 => +8dBm 7 => +20dBm
*/
void SetTGIfDrive ( uint8_t level )
{
int oldLevel = trackGenSetting.IF_Drive;
if (( level < 0 ) || ( level > 7 ))
return;
else
{
trackGenSetting.IF_Drive = level;
tg_if.SetDrive ( level ); // Set transmitter power level
if ( oldLevel != trackGenSetting.IF_Drive )
{
changedSetting = true;
WriteTrackGenSettings ();
pushSettings ();
}
}
}
#endif
#ifdef TG_IF_INSTALLED
void SetTGPower ( int16_t dBm )
{
// Add algorithm to set attenuator and IF drive level
// initially limit to just set drive levels
// TG Output = IF output - mixer loss - attenuator pads
int16_t pathLoss = 10; //6.9 mixer + 3 pad;
int16_t maxOut = 20 - pathLoss;
int16_t minOut = 1 - pathLoss;
if (dBm > maxOut)
dBm = maxOut;
if (dBm < minOut)
dBm = minOut;
uint8_t level = ( dBm + pathLoss + 1 ) / 3;
Serial.printf("Set TG power - req dBm %i - level set to %i\n", dBm, level);
trackGenSetting.Power = dBm; // saved in SetTGIFDrive
SetTGIfDrive ( level );
}
#endif
/*
* Set Tracking generator IF offset from main SA IF
* Ideally TG IF should be outside bandwidth of main RX
*/
bool SetTGOffset ( int32_t offset)
{
// check valid
if ( (offset > -1000000 ) && (offset < 1000000 ) )
trackGenSetting.Offset = offset;
changedSetting = true;
WriteTrackGenSettings ();
}
/*
* Get Tracking generator IF offset from main SA IF
*/
int32_t GetTGOffset ( )
{
return trackGenSetting.Offset;
}
/*
* Set the Track Gen frequency - M0WID addition
*/
bool SetTGFreq ( int32_t freq )
{
int32_t oldFreq = trackGenSetting.Frequency;
if (( freq < MIN_SIGLO_FREQ ) || ( freq > MAX_SIGLO_FREQ )) // If out of limits
return false; // Don't change it
trackGenSetting.Frequency = freq;
if ( oldFreq != trackGenSetting.Frequency )
{
changedSetting = true;
WriteTrackGenSettings ();
}
return true; // Frequency was good
}
/*
* "SetSweepStart" and "GetSweepStart" were added in Version 2.3. They used to be in
* "switch" statements in "SetSweepFrequency" and "GetSweepFrequency". By making them
* separate function, they can be used by the "SetSweepCenter", "SetSweepSpan", etc.
* functions, thus eliminating a bunch of redundent code.
*/
void SetSweepStart ( uint32_t startFreq )
{
uint32_t lastStart; // Old ssweep start frequency
uint32_t lastStop; // and old stop frequency
// Serial.print ( "Requested start = " ); Serial.println ( FormatFrequency ( startFreq ));
lastStart = setting.ScanStart; // Save the current start frequency
lastStop = setting.ScanStop; // and the current stop frequency
if ( startFreq < START_MIN ) // Range check
startFreq = START_MIN;
if ( startFreq > STOP_MAX ) // Range check
startFreq = STOP_MAX;
if ( startFreq > setting.ScanStop ) // Start can't be more than current stop
setting.ScanStop = STOP_MAX; // So set stop at maximum
setting.ScanStart = startFreq; // Set new start frequency
if (( setting.ScanStart != lastStart )
|| ( setting.ScanStop != 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 GetSweepStart ( void )
{
return setting.ScanStart;
}
/*
* "SetSweepStop" and "GetSweepStop" were also added in Version 2.3.
*/
void SetSweepStop ( uint32_t stopFreq )
{
uint32_t lastStart; // Old ssweep start frequency
uint32_t lastStop; // and old stop frequency
// Serial.print ( "Requested stop = " ); Serial.println ( FormatFrequency ( stopFreq ));
lastStart = setting.ScanStart; // Save the current start frequency
lastStop = setting.ScanStop; // and the current stop frequency
if ( stopFreq < START_MIN ) // Range check
stopFreq = START_MIN;
if ( stopFreq > STOP_MAX ) // Range check
stopFreq = STOP_MAX;
if ( stopFreq < setting.ScanStart ) // Stop can't be less than current start
setting.ScanStart = START_MIN; // so set start to minimum frequency
setting.ScanStop = stopFreq; // Set new stop frequency
if (( setting.ScanStart != lastStart )
|| ( setting.ScanStop != 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 GetSweepStop ( void )
{
return setting.ScanStop;
}
/*
* Specific start/stop frequency setting for IF Sweep mode
*/
void SetIFsweepStart ( 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_IF; // Save the current start frequency
lastStop = stopFreq_IF; // and the current stop frequency
if ( startFreq < IF_START_MIN ) // Range check
startFreq = IF_START_MIN;
if ( startFreq > IF_STOP_MAX ) // Range check
startFreq = IF_STOP_MAX;
if ( startFreq > stopFreq_IF ) // Start can't be more than current stop
stopFreq_IF = IF_STOP_MAX; // So set stop at maximum
startFreq_IF = startFreq; // Set new start frequency
if (( startFreq_IF != lastStart )
|| ( stopFreq_IF != 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 GetIFsweepStart ( void )
{
return startFreq_IF;
}
void SetIFsweepStop ( 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_IF; // Save the current start frequency
lastStop = stopFreq_IF; // and the current stop frequency
if ( stopFreq < IF_START_MIN ) // Range check
stopFreq = IF_START_MIN;
if ( stopFreq > IF_STOP_MAX ) // Range check
stopFreq = IF_STOP_MAX;
if ( stopFreq < startFreq_IF ) // Stop can't be less than current start
startFreq_IF = IF_START_MIN; // so set start to minimum frequency
stopFreq_IF = stopFreq; // Set new stop frequency
if (( startFreq_IF != lastStart )
|| ( stopFreq_IF != 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 GetIFsweepStop ( void )
{
return stopFreq_IF;
}
void SetIFsweepSigFreq ( uint32_t sigFreq )
{
uint32_t lastSigFreq;
// Serial.print ( "Requested if sweep signal = " ); Serial.println ( FormatFrequency ( sigFreq_IF ));
lastSigFreq = sigFreq_IF;
// 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_IF = sigFreq;
if ( sigFreq_IF != lastSigFreq )
changedSetting = true;
}
RedrawHisto (); // Redraw labels and restart sweep with new settings
}
uint32_t GetIFsweepSigFreq ( void )
{
return sigFreq_IF;
}
/*
* 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
*/
void SetBandscopeStart ( uint32_t startFreq )
{
uint32_t lastStart; // Old sweep start frequency
// Serial.print ( "Requested start = " ); Serial.println ( FormatFrequency ( startFreq ));
lastStart = setting.BandscopeStart; // Save the current start frequency
if ( startFreq < START_MIN ) // Range check
startFreq = START_MIN;
if ( startFreq > STOP_MAX ) // Range check
startFreq = STOP_MAX;
setting.BandscopeStart = startFreq; // Set new start frequency
if ( setting.BandscopeStart != lastStart )
{
changedSetting = true; // Yes it did
initSweep = true;
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 ();
}
}
uint32_t GetBandscopeStart ( void )
{
return setting.BandscopeStart;
}
/*
* Specific start/stop frequency setting for Bandscope mode
*/
void SetBandscopeSpan ( uint32_t spanFreq )
{
uint32_t lastSpan; // Old sweep span
// Serial.print ( "Requested bandscope span = " ); Serial.println ( FormatFrequency ( spanFreq ));
lastSpan = setting.BandscopeSpan; // Save the current start frequency
if ( spanFreq < 100000 ) // Range check
spanFreq = 100000;
if ( spanFreq > 350000 ) // Range check
spanFreq = 350000;
setting.BandscopeSpan = spanFreq; // Set new start frequency
if ( setting.BandscopeSpan != lastSpan )
{
changedSetting = true; // Yes it did
initSweep = true;
// RedrawHisto (); // Redraw labels and restart sweep with new settings
// WriteSettings (); // and save the setting structure
}
}
uint32_t GetBandscopeSpan ( void )
{
return setting.BandscopeSpan;
}
/*
* "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 ();
}
}
/*
* When the user sets the "CENTER" frequency, we're also going to set the "SPAN". But,
* it's a bit tricky!
*
* The basic assumption is that we will set the "SPAN" equal to the selected "CENTER"
* frequency. But if the "SPAN" limits would cause the "START" frequency to go negative
* or cause the "STOP" frequency to exceed "STOP_MAX", the difference between the
* "CENTER" frequency and whichever limit is exceeded will become 1/2 of the "SPAN".
*
* Of course, setting the "CENTER" frequency at either one of the limits is going to
* result in a zero span.
*/
void SetSweepCenter ( uint32_t freq, uint8_t span )
{
uint32_t startFreq; // These are used in computing the
uint32_t stopFreq; // Scan limits
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 center = " ); Serial.println ( FormatFrequency ( freq ));
lastStart = setting.ScanStart; // Save the current start frequency
lastStop = setting.ScanStop; // and the current stop frequency
/*
* Range check the requested frequency against the sweep limits and adjust accordingly:
*/
if ( freq > STOP_MAX ) // Range check requested frequency
freq = STOP_MAX;
if ( freq < START_MIN ) // Range check
freq = START_MIN;
/*
* Compute new start and stop frequencies based on the requested frequenct and whether
* a "WIDE" span or a "NARROW" span was requested.
*/
if ( span == WIDE ) // Using normal span?
halfSpan = freq / 2; // Then halfSpan is half the frequency
else // Must be a "NARROW" span
halfSpan = freq / FOCUS_FACTOR / 2; // Span is banse on the "FOCUS_FACTOR"
startFreq = freq - halfSpan; // New computed start frequency
stopFreq = freq + halfSpan; // New computed stop frequency
/*
* From the Department of Redundency Department: Now we have to range check the computed
* start and stop frequencies and if either of those is out of bounds, we need to adjust the
* computed span accordingly. We'll maintain the requested center frequency though.
*/
if ( stopFreq > STOP_MAX ) // Range check the stop frequency
{
stopFreq = STOP_MAX; // Set the stop frequency at the upper limit
halfSpan = stopFreq - freq; // Re-compute half span
startFreq = freq - halfSpan; // and recompute start frequency
}
if ( startFreq < START_MIN ) // Range check the start frequency
{
startFreq = START_MIN; // Set start at the limit
halfSpan = freq - startFreq; // Re-compute half span
stopFreq = freq + halfSpan; // and re-compute stop frequency
}
startFreq = freq - halfSpan; // Re-compute start and
stopFreq = freq + 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 ));
setting.ScanStart = startFreq; // Set new start frequency
setting.ScanStop = stopFreq; // Set new stop frequency
if (( setting.ScanStart != lastStart )
|| ( setting.ScanStop != 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 GetSweepCenter ()
{
return ( setting.ScanStart + setting.ScanStop ) / 2;
}
/*
* "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 SetSweepSpan ( 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 = setting.ScanStart; // Save the current start frequency
lastStop = setting.ScanStop; // and the current stop frequency
halfSpan = spanRange / 2; // Half the requested span
centerFreq = GetSweepCenter (); // Current center frequency
startFreq = spanRange - halfSpan; // New computed start frequency
stopFreq = spanRange + 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 > STOP_MAX ) // Range check the stop frequency
{
stopFreq = 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 < START_MIN ) // Range check the start frequency
{
startFreq = 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 ));
setting.ScanStart = startFreq; // Set new start frequency
setting.ScanStop = stopFreq; // Set new stop frequency
if (( setting.ScanStart != lastStart )
|| ( setting.ScanStop != 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 GetSweepSpan ()
{
return setting.ScanStop - setting.ScanStart;
}
/*
* "SetFreq" is used by the serial command handler to set a specific frequency
* for one of the Si4432 modules.
*/
void SetFreq ( int vfo, uint32_t freq )
{
if ( vfo == RX_4432 ) // Receiver?
rcvr.SetFrequency ( freq );
if ( vfo == TX_4432 ) // Transmitter?
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();
}