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.
|
|
|
|
*/
|
|
|
|
|
2020-08-17 05:24:08 +08:00
|
|
|
#include "cmd.h" // Our associated header
|
2020-08-16 02:03:43 +08:00
|
|
|
#include "preferences.h" // For "SAVE" & "RECALL"
|
2020-08-17 05:24:08 +08:00
|
|
|
#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;
|
|
|
|
|
2020-08-17 05:24:08 +08:00
|
|
|
/*
|
|
|
|
* 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 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 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 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);
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
{ "", 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 ( "\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 ( " 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 ( " 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.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.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
|
|
|
|
|
|
|
|
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, ®69 );
|
|
|
|
|
|
|
|
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 "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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
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 = atoi ( dataBuff ); // Yes, get new power reading
|
|
|
|
RequestSetPowerLevel ( tempValue );
|
|
|
|
Serial.printf ( "Indicated power set to: %d\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
|
2020-08-17 05:24:08 +08:00
|
|
|
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
|
|
|
|
{
|
2020-08-17 05:24:08 +08:00
|
|
|
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_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 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2020-08-17 05:24:08 +08:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
void SetTracking ( int8_t m )
|
|
|
|
{
|
|
|
|
int oldM = trackGenSetting.Mode;
|
|
|
|
|
|
|
|
trackGenSetting.Mode = m;
|
|
|
|
|
|
|
|
if ( oldM != trackGenSetting.Mode )
|
|
|
|
{
|
|
|
|
changedSetting = true;
|
|
|
|
WriteTrackGenSettings ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "SetPowerGrid" - Sets the dB/vertical divison on the grid
|
|
|
|
*/
|
|
|
|
|
|
|
|
void SetPowerGrid ( int g )
|
|
|
|
{
|
|
|
|
int oldGrid = setting.PowerGrid;
|
|
|
|
|
|
|
|
setting.PowerGrid = g;
|
2020-08-17 05:24:08 +08:00
|
|
|
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 ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "SetStorage" - Saves the results of a scan for comparison to an active one.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void SetStorage ( void )
|
|
|
|
{
|
2020-08-17 05:24:08 +08:00
|
|
|
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 ( int o )
|
|
|
|
{
|
|
|
|
int oldOffset = setting.LevelOffset;
|
|
|
|
|
|
|
|
if ( o != 100 )
|
|
|
|
setting.LevelOffset = (int) ( o - (( oldPeakLevel / 2.0 + setting.Attenuate ) - 120.0 )); // WA2FZW
|
|
|
|
else
|
|
|
|
setting.LevelOffset = 0;
|
|
|
|
|
|
|
|
// Serial.printf ( "Peak level: %i, Actual: %i, Level offset: %i \n",
|
|
|
|
// oldPeakLevel, o, setting.LevelOffset );
|
|
|
|
|
|
|
|
if ( oldOffset != setting.LevelOffset )
|
|
|
|
{
|
|
|
|
changedSetting = true;
|
|
|
|
RedrawHisto (); // Redraw labels and restart sweep with new settings
|
|
|
|
WriteSettings ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "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 );
|
|
|
|
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 ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "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 ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "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 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;
|
|
|
|
// RedrawHisto (); // Redraw labels and restart sweep with new settings
|
|
|
|
// WriteSettings (); // and save the setting structure
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 );
|
|
|
|
}
|