simpleSA/cmd.cpp

3315 lines
96 KiB
C++
Raw Normal View History

2020-08-16 02:03:43 +08:00
/*
* "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
2020-08-16 02:03:43 +08:00
#include "preferences.h" // For "SAVE" & "RECALL"
#include "marker.h" // Marker class definition
2020-08-16 02:03:43 +08:00
/*
* 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 SI_TG_IF_CS
extern Si4432 tg_if; // Si4432 Transmitter object
#endif
#ifdef SI_TG_LO_CS
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 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];
2020-08-16 02:03:43 +08:00
extern float bandwidth; // The current bandwidth (not * 10)
extern unsigned long delaytime; // In microseconds
extern unsigned long offsetDelayTime; // In microseconds
2020-08-16 02:03:43 +08:00
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 IF sweep start frequency
extern uint32_t stopFreq_RX; // Last value of the IF sweep end frequency
extern uint32_t sigFreq_RX; // Last value of the IF sweep end frequency
2020-08-16 02:03:43 +08:00
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;
2020-08-16 02:03:43 +08:00
extern Marker marker[MARKER_COUNT]; // Array of marker objects
/*
* 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 );
2020-08-16 02:03:43 +08:00
/*
* 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
2020-08-16 02:03:43 +08:00
{ "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
{ "", MSG_NONE } // Unrecognized command
2020-08-16 02:03:43 +08:00
};
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 ( "\nTiny Spectrum Analyzer Version %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 );
2020-08-16 02:03:43 +08:00
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" );
2020-08-16 02:03:43 +08:00
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 );
2020-08-16 02:03:43 +08:00
Serial.println ( "\nOther Commands:\n" );
Serial.print ( " DRIVE.........Local oscillator drive level [0 to 7]; currently: " );
Serial.println ( setting.Drive );
Serial.print ( " SGLODRIVE.....Local oscillator drive level in signal generator mode [0 to 7]; currently: " );
Serial.println ( sigGenSetting.LO_Drive );
Serial.print ( " SGRXDRIVE.....Local oscillator drive level in signal generator mode [0 to 7]; currently: " );
Serial.println ( sigGenSetting.RX_Drive );
Serial.print ( " TGLODRIVE.....Local oscillator drive level in tracking generator mode [0 to 7]; currently: " );
Serial.println ( trackGenSetting.LO_Drive );
Serial.print ( " TGIFDRIVE.....Local oscillator drive level in tracking generator mode [0 to 7]; currently: " );
Serial.println ( trackGenSetting.IF_Drive );
Serial.print ( " TGOFFSET......Tracking generator offset from SA IF; currently: " );
Serial.println ( trackGenSetting.Offset );
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 );
2020-08-16 02:03:43 +08:00
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 ();
}
/*
* "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;
2020-08-16 02:03:43 +08:00
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.
2020-08-16 02:03:43 +08:00
*/
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;
/*
* 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 drive specification: %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 "SGLODRIVE" 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 > MAX_DRIVE ))
Serial.printf ( "Invalid drive specification: %d\n", tempValue );
else // Set the transmitter drive level
SetSGRxDrive ( tempValue ); // "SetTGRxDrive" saves the settings
}
Serial.printf ( "SigGen RX Drive: %d (+%udBm)\n", sigGenSetting.RX_Drive, driveLevel[sigGenSetting.RX_Drive] );
//pushSettings ();
return true;
#ifdef SI_TG_LO_CS
/*
* 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 SI_TG_IF_CS
/*
* 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 SI_TG_IF_CS
if ( VFO == TGIF_4432 ) // Track generator IF?
tg_if.SetFrequency ( freq1 );
#endif
#ifdef SI_TG_LO_CS
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 SI_TG_IF_CS
if ( VFO == TGIF_4432 ) // Local Oscillator?
Serial.printf( "TG IF frequency: %s\n", FormatFrequency ( tg_if.GetFrequency() ));
#endif
#ifdef SI_TG_LO_CS
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;
2020-08-16 02:03:43 +08:00
/*
* 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 SI_TG_IF_CS
else if ( dataBuff[0] == 'I' ) // track gen IF
VFO = TGIF_4432;
#endif
#ifdef SI_TG_LO_CS
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 SI_TG_IF_CS
if ( VFO == TGIF_4432 ) // Track generator IF
{
Serial.println ( "\nTracking Generator IF registers:\n" );
tg_if.PrintRegs();
}
#endif
#ifdef SI_TG_LO_CS
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 SI_TG_IF_CS
if ( VFO == TGIF_4432 ) // Track gen IF
tg_if.WriteByte ( addr, tempValue ); // Send data to the device
#endif
#ifdef SI_TG_LO_CS
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 SI_TG_IF_CS
else if ( VFO == TGIF_4432 ) // Transmitter?
Serial.printf ( "TGIF Reg[0x%02X] = 0x%02X\n", addr, tg_if.ReadByte ( addr ));
#endif
#ifdef SI_TG_LO_CS
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
*
2020-08-16 02:03:43 +08:00
*/
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 SI_TG_IF_CS
if ( VFO == TGIF_4432 ) // Trackgen IF
{
tg_if.Tune ( tempValue ); // Tune it
config.tgIF_capacitance = tempValue; // And save it
}
#endif
#ifdef SI_TG_LO_CS
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 SI_TG_IF_CS
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 SI_TG_LO_CS
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
2020-08-16 02:03:43 +08:00
RequestSetPowerLevel ( tempValue );
Serial.printf ( "Indicated power set to: %f\n", tempValue );
2020-08-16 02:03:43 +08:00
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) )
2020-08-16 02:03:43 +08:00
{
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);
2020-08-16 02:03:43 +08:00
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;
2020-08-16 02:03:43 +08:00
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;
/*
* "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() ));
2020-08-16 02:03:43 +08:00
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;
2020-08-16 02:03:43 +08:00
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;
2020-08-16 02:03:43 +08:00
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
2020-08-16 02:03:43 +08:00
*/
void SetTracking ( int8_t m )
{
int oldM = trackGenSetting.Mode;
trackGenSetting.Mode = m;
if ( oldM != trackGenSetting.Mode )
{
changedSetting = true;
WriteTrackGenSettings ();
pushSettings ();
// Serial.println("SetTracking - pushSettings");
2020-08-16 02:03:43 +08:00
}
}
/*
* "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;
2020-08-16 02:03:43 +08:00
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;
}
2020-08-16 02:03:43 +08:00
/*
* "SetStorage" - Saves the results of a scan for comparison to an active one.
*/
void SetStorage ( void )
{
for ( int i = 0; i < displayPoints; i++ )
2020-08-16 02:03:43 +08:00
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"
*/
void SetPowerLevel ( double o )
2020-08-16 02:03:43 +08:00
{
double oldOffset = setting.LevelOffset;
2020-08-16 02:03:43 +08:00
if ( o != 100.0 )
setting.LevelOffset = ( o - ( (double)oldPeakLevel / 2.0 + setting.Attenuate - setting.ExternalGain - 120.0) ); // WA2FZW
2020-08-16 02:03:43 +08:00
else
setting.LevelOffset = 0.0;
2020-08-16 02:03:43 +08:00
// Serial.printf ( "Peak level: %i, Actual: %f, Level offset: %f \n",
2020-08-16 02:03:43 +08:00
// 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");
2020-08-16 02:03:43 +08:00
}
}
/*
* "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
2020-08-16 02:03:43 +08:00
RedrawHisto (); // Redraw labels and restart sweep with new settings
if ( setting.Bandwidth10 != oldRBW )
{
changedSetting = true;
SetRXsweepSpan (setting.Bandwidth10 * 1000); // 10 * bandwidth
2020-08-16 02:03:43 +08:00
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 ();
2020-08-16 02:03:43 +08:00
}
}
}
/*
* "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
*/
void SetSGRxDrive ( uint8_t level )
{
int oldLevel = sigGenSetting.RX_Drive;
if (( level < 0 ) || ( level > 7 ))
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 ();
}
}
}
#ifdef SI_TG_LO_CS
/*
* "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 SI_TG_IF_CS
/*
* "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 ();
2020-08-16 02:03:43 +08:00
}
}
}
#endif
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 );
}
2020-08-16 02:03:43 +08:00
/*
* 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;
}
/*
* "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;
}
2020-08-16 02:03:43 +08:00
/*
* 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
2020-08-16 02:03:43 +08:00
}
}
2020-08-21 19:33:06 +08:00
/*
* "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 ();
}
}
2020-08-16 02:03:43 +08:00
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;
}
2020-08-21 19:33:06 +08:00
/*
* "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 ();
}
}
2020-08-16 02:03:43 +08:00
/*
* 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();
}