9 Commits

26 changed files with 2094 additions and 489 deletions

1
Raduino/Debug.h Symbolic link
View File

@@ -0,0 +1 @@
../TeensyDSP/Debug.h

View File

@@ -337,7 +337,7 @@ byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWK
return 1; return 1;
//Check PTT while auto Sending //Check PTT while auto Sending
autoSendPTTCheck(); //autoSendPTTCheck();
//Check_Cat(3); //Check_Cat(3);
} }
@@ -1449,6 +1449,7 @@ void setup()
factory_alignment(); factory_alignment();
#endif #endif
rigState.begin();
} }
//Auto save Frequency and Mode with Protected eeprom life by KD8CEC //Auto save Frequency and Mode with Protected eeprom life by KD8CEC
@@ -1473,16 +1474,6 @@ void checkAutoSaveFreqMode()
saveCheckTime = 0; //for reduce cpu use rate saveCheckTime = 0; //for reduce cpu use rate
} }
} }
rigState.vfo[0] = vfoA;
rigState.vfo[1] = vfoB;
rigState.rit = ritRxFrequency - frequency;
rigState.flags = 0;
rigState.flags |= (vfoActive == VFO_B ? UBITX_VFOB_FLAG : 0);
rigState.flags |= (cwMode != 0 ? UBITX_CW_FLAG : 0);
rigState.flags |= (isUSB != 0 ? UBITX_USB_FLAG : 0);
rigState.flags |= (splitOn != 0 ? UBITX_SPLIT_FLAG : 0);
rigState.flags |= (ritOn != 0 ? UBITX_RIT_FLAG : 0);
} }
void loop(){ void loop(){

1
Raduino/RigState.cpp Symbolic link
View File

@@ -0,0 +1 @@
../TeensyDSP/RigState.cpp

View File

@@ -255,11 +255,11 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter #define I2CMETER_CALCR 0x55 //Calculated SWR Meter
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter #define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
// Raduino provides updated data to TeensyDSP // Raduino<=>TeensyDSP data exchange
#define I2CMETER_RIGINF 0x50 #define I2CMETER_RIGINF 0x50
// Raduino requests any CAT updates from TeensyDSP // Raduino requests any CAT updates from TeensyDSP
#define I2CMETER_REQCAT 0x51 //#define I2CMETER_REQCAT 0x51
//============================================================================== //==============================================================================
// for public, Variable, functions // for public, Variable, functions
@@ -340,10 +340,4 @@ extern void DisplayVersionInfo(const char* fwVersionInfo);
//I2C Signal Meter, Version 1.097 //I2C Signal Meter, Version 1.097
extern int GetI2CSmeterValue(int valueType); //ubitx_ui.ino extern int GetI2CSmeterValue(int valueType); //ubitx_ui.ino
extern void doRaduinoToTeensy(UBitxRigState* r);
extern void updateStateFromRaduino(UBitxRigState& r);
extern void updateRaduinoFromState(UBitxRigState& r);
extern UBitxRigState rigState;
#endif //end of if header define #endif //end of if header define

View File

@@ -296,7 +296,7 @@ void cwKeyer(void){
return; //Tx stop control by Main Loop return; //Tx stop control by Main Loop
} }
Check_Cat(2); //Check_Cat(2);
} //end of while } //end of while
// } //end of elese // } //end of elese
} }

View File

@@ -990,16 +990,16 @@ void SWS_Process(void)
char checkCount = 0; char checkCount = 0;
char checkCountSMeter = 0; char checkCountSMeter = 0;
UBitxRigState rigState;
UBitxRigState catState;
//execute interval : 0.25sec //execute interval : 0.25sec
void idle_process() void idle_process()
{ {
// KC4UPR 2021-02-05 added update process for Raduino-TeensyDSP coordination // KC4UPR 2021-02-05 added update process for Raduino-TeensyDSP coordination
updateStateFromRaduino(rigState); rigState.send_RIGINF();
doRaduinoToTeensy(&rigState); delay(1);
updateRaduinoFromState(rigState); rigState.receive_RIGINF();
//updateStateFromRaduino(rigState);
//doRaduinoToTeensy(&rigState);
//updateRaduinoFromState(rigState);
//S-Meter Display //S-Meter Display
if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency)) if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency))

View File

@@ -263,7 +263,7 @@ void menuCHMemory(int btn, byte isMemoryToVfo){
} }
} }
Check_Cat(0); //To prevent disconnections //Check_Cat(0); //To prevent disconnections
} //end of while (knob) } //end of while (knob)
if (selectChannel < 20 && selectChannel >= 0) if (selectChannel < 20 && selectChannel >= 0)
@@ -697,7 +697,7 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob
} }
} }
Check_Cat(0); //To prevent disconnections //Check_Cat(0); //To prevent disconnections
} }
return targetValue; return targetValue;
@@ -1290,7 +1290,7 @@ void doMenu(){
default : default :
menuExit(btnState); break; menuExit(btnState); break;
} //end of switch } //end of switch
Check_Cat(0); //To prevent disconnections //Check_Cat(0); //To prevent disconnections
} //end of while } //end of while
//**************************************************************************** //****************************************************************************
@@ -1690,7 +1690,7 @@ void menuSetupCarrier(int btn){
si5351bx_setfreq(0, usbCarrier); si5351bx_setfreq(0, usbCarrier);
printCarrierFreq(usbCarrier); printCarrierFreq(usbCarrier);
Check_Cat(0); //To prevent disconnections //Check_Cat(0); //To prevent disconnections
delay(100); delay(100);
} }

View File

@@ -18,7 +18,7 @@ const PROGMEM uint8_t meters_bitmap[] = {
}; };
*/ */
#include "RigState.h" //#include "RigState.h"
//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA //SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA
@@ -299,94 +299,3 @@ int GetI2CSmeterValue(int valueType)
} }
//====================================================================== //======================================================================
void doRaduinoToTeensy(UBitxRigState* r) {
uint8_t* ptr = (uint8_t*)r;
Wire.beginTransmission(I2CMETER_ADDR);
Wire.write(I2CMETER_RIGINF);
//for (size_t i = 0; i < sizeof(UBitxRigState); i++) {
// Wire.write(ptr[i]);
//}
//Note, I can switch this back...
Wire.write(ptr, sizeof(UBitxRigState));
Wire.endTransmission();
Serial.println("BEFORE:");
Serial.print("VFO A: ");
Serial.print(r->vfo[0]);
Serial.print(", VFO B: ");
Serial.print(r->vfo[1]);
Serial.print(", Data Size: ");
Serial.print(sizeof(UBitxRigState));
Serial.println();
// First we need to see if there's any updated state.
Wire.requestFrom(I2CMETER_ADDR, sizeof(uint8_t));
int len = 0;
int readflag = Wire.read();
if (readflag != 0) {
Wire.requestFrom(I2CMETER_ADDR, sizeof(UBitxRigState));
UBitxRigState tmp;
//int len = 0;
//ptr = (uint8_t*)&tmp;
while (Wire.available() > 0) {
uint8_t b = Wire.read();
if (len < sizeof(UBitxRigState)) {
ptr[len++] = b;
}
}
}
Serial.println("AFTER:");
Serial.print("VFO A: ");
Serial.print(r->vfo[0]);
Serial.print(", VFO B: ");
Serial.print(r->vfo[1]);
Serial.print(", Data Size: ");
Serial.print(len);
Serial.println();
}
void updateStateFromRaduino(UBitxRigState& r) {
// Note, we really need to be checking a dirty flag for this. But, I don't have a dirty flag in this version of the data type...
if (vfoActive == VFO_A) {
rigState.vfo[0] = frequency;
rigState.flags &= ~UBITX_VFOB_FLAG;
} else if (vfoActive == VFO_B) {
rigState.vfo[1] = frequency;
rigState.flags |= UBITX_VFOB_FLAG;
}
rigState.rit = ritRxFrequency - frequency;
rigState.flags = 0;
rigState.flags |= (vfoActive == VFO_B ? UBITX_VFOB_FLAG : 0);
rigState.flags |= (cwMode != 0 ? UBITX_CW_FLAG : 0);
rigState.flags |= (isUSB != 0 ? UBITX_USB_FLAG : 0);
rigState.flags |= (splitOn != 0 ? UBITX_SPLIT_FLAG : 0);
rigState.flags |= (ritOn != 0 ? UBITX_RIT_FLAG : 0);
}
void updateRaduinoFromState(UBitxRigState& r) {
vfoActive = rigState.flags & UBITX_VFOB_FLAG ? VFO_B : VFO_A;
if (vfoActive == VFO_A) {
if (rigState.vfo[0] != frequency) {
setFrequency(rigState.vfo[0]);
}
} else if (vfoActive == VFO_B) {
if (rigState.vfo[1] != frequency) {
setFrequency(rigState.vfo[1]);
}
}
ritRxFrequency = frequency + rigState.rit;
splitOn = rigState.flags & UBITX_SPLIT_FLAG ? 1 : 0;
ritOn = rigState.flags & UBITX_RIT_FLAG ? 1 : 0;
isUSB = rigState.flags & UBITX_USB_FLAG ? 1 : 0;
if (rigState.flags & UBITX_CW_FLAG) {
cwMode = isUSB ? 2 : 1; // 2 = cwu / 1 = cwl
} else {
cwMode = 0;
}
}

Binary file not shown.

View File

@@ -4,7 +4,6 @@
#include "DSP.h" #include "DSP.h"
#include <Audio.h>
#include <i2c_t3.h> #include <i2c_t3.h>
//#include <Wire.h> //#include <Wire.h>
//#include <SPI.h> //#include <SPI.h>
@@ -13,35 +12,60 @@
#define RX_AUDIO_CH 0 #define RX_AUDIO_CH 0
#define TX_MIC_IN_CH 0 const int txMicInChannel = TX_MIC_IN_CH;
#define TX_LINE_IN_CH 0 const int txLineInChannel = TX_LINE_IN_CH;
#define TX_USB_IN_CH1 1 const int txUSBInChannel = TX_USB_IN_CH;
#define TX_USB_IN_CH2 2 const int txNumChannels = TX_NUM_CHANNELS;
const int txLineInVOX = TX_LINE_IN_VOX;
const int txUSBInLVOX = TX_USB_IN_L_VOX;
const int txUSBInRVOX = TX_USB_IN_R_VOX;
const int txNumVOX = TX_NUM_VOX;
UBitxDSP DSP; UBitxDSP DSP;
//static struct { //static struct {
// GUItool: begin automatically generated code // GUItool: begin automatically generated code
AudioInputI2S lineIn; //xy=385.9371643066406,1001.9600830078125 AudioInputUSB usbIn; //xy=153,341
AudioInputUSB usbIn; //xy=390.9371643066406,1107.9600830078125 AudioInputI2S lineIn; //xy=161,233
AudioMixer4 rxAudio; //xy=642.9371643066406,915.9600830078125 AudioAnalyzeRMS usbInRMS_R; //xy=276,431
AudioMixer4 txAudio; //xy=646.9371643066406,1103.9600830078125 AudioAnalyzeRMS usbInRMS_L; //xy=335,392
AudioOutputUSB usbOut; //xy=1022.9371643066406,919.9600830078125 AudioAnalyzeRMS lineInRMS; //xy=387,273
AudioOutputI2S lineOut; //xy=1024.9371643066406,1017.9600830078125 AudioMixer4 rxAudio; //xy=418,147
AudioConnection patchCord1(lineIn, 0, rxAudio, 0); AudioMixer4 txAudio; //xy=422,335
AudioConnection patchCord2(lineIn, 1, txAudio, 0); AudioFilterFIR rxFilter; //xy=583,139
AudioConnection patchCord3(usbIn, 0, txAudio, 1); AudioAmplifier usbOutAmp; //xy=748,135
AudioConnection patchCord4(usbIn, 1, txAudio, 2); AudioAmplifier lineOutAmp; //xy=749,198
AudioConnection patchCord5(rxAudio, 0, lineOut, 0); AudioAmplifier usbBypassAmp; //xy=756,261
AudioConnection patchCord6(rxAudio, 0, usbOut, 0); AudioOutputI2S lineOut; //xy=966,330
AudioConnection patchCord7(rxAudio, 0, usbOut, 1); AudioOutputUSB usbOut; //xy=968,291
AudioConnection patchCord8(txAudio, 0, lineOut, 1); AudioConnection patchCord1(usbIn, 0, txAudio, 1);
AudioControlSGTL5000 audioCtrl; //xy=651.9371643066406,1244.9600830078125 AudioConnection patchCord2(usbIn, 0, usbInRMS_L, 0);
AudioConnection patchCord3(usbIn, 1, usbInRMS_R, 0);
AudioConnection patchCord4(lineIn, 0, rxAudio, 0);
AudioConnection patchCord5(lineIn, 1, txAudio, 0);
AudioConnection patchCord6(lineIn, 1, lineInRMS, 0);
AudioConnection patchCord7(rxAudio, rxFilter);
AudioConnection patchCord8(rxAudio, usbBypassAmp);
AudioConnection patchCord9(txAudio, 0, lineOut, 1);
AudioConnection patchCord10(rxFilter, usbOutAmp);
AudioConnection patchCord11(rxFilter, lineOutAmp);
AudioConnection patchCord12(usbOutAmp, 0, usbOut, 0);
AudioConnection patchCord13(lineOutAmp, 0, lineOut, 0);
AudioConnection patchCord14(usbBypassAmp, 0, usbOut, 1);
AudioControlSGTL5000 audioCtrl; //xy=427,476
// GUItool: end automatically generated code // GUItool: end automatically generated code
//} audio; //} audio;
UBitxDSP::UBitxDSP() {
voxRMS[txLineInVOX] = &lineInRMS;
voxRMS[txUSBInLVOX] = &usbInRMS_L;
voxRMS[txUSBInRVOX] = &usbInRMS_R;
}
void UBitxDSP::begin() { void UBitxDSP::begin() {
AudioMemory(16); AudioMemory(16);
audioCtrl.enable(); audioCtrl.enable();
@@ -77,17 +101,59 @@ void UBitxDSP::begin() {
// SETUP THE AUDIO OUTPUTS // SETUP THE AUDIO OUTPUTS
// Line Output (RX) // Line Output (RX)
lineOutAmp.gain(1.0);
// USB Output (RX) // USB Output (RX)
usbOutAmp.gain(1.0);
usbBypassAmp.gain(1.0);
// Rig (Line) Output (TX) // Rig (Line) Output (TX)
// Default to RX. // Default to RX.
rx(); rx();
// Setup the VOX - clean this up
state.voxActive[TX_LINE_IN_VOX] = false;
state.voxThresh[TX_LINE_IN_VOX] = TX_LINE_IN_VOX_THRESH;
state.voxDelay[TX_LINE_IN_VOX] = TX_LINE_IN_VOX_DELAY;
state.voxTimeout[TX_LINE_IN_VOX] = 0;
state.voxActive[TX_USB_IN_L_VOX] = false;
state.voxThresh[TX_USB_IN_L_VOX] = TX_USB_IN_L_VOX_THRESH;
state.voxDelay[TX_USB_IN_L_VOX] = TX_USB_IN_L_VOX_DELAY;
state.voxTimeout[TX_USB_IN_L_VOX] = 0;
state.voxActive[TX_USB_IN_R_VOX] = false;
state.voxThresh[TX_USB_IN_R_VOX] = TX_USB_IN_R_VOX_THRESH;
state.voxDelay[TX_USB_IN_R_VOX] = TX_USB_IN_R_VOX_DELAY;
state.voxTimeout[TX_USB_IN_R_VOX] = 0;
// Setup the RX Filter.
setRxFilter(300, 3000);
sinceLastUpdate = 0;
} }
void UBitxDSP::update() { void UBitxDSP::update() {
// Only going to adjust the USB volume periodically.
if (sinceLastUpdate > DSP_MILLIS_PER_UPDATE) {
float vol = usbIn.volume();
setTxInputLevel(txUSBInChannel, vol);
sinceLastUpdate = 0;
}
// Update the VOX switches.
// TODO: Move the enable logic in here, so we don't process unnecessarily.
for (int i = 0; i < txNumVOX; i++) {
if (voxRMS[i]->available()) {
float lvl = voxRMS[i]->read();
if (lvl > state.voxThresh[i]) {
state.voxTimeout[i] = millis() + state.voxDelay[i];
state.voxActive[i] = true;
}
}
if (millis() > state.voxTimeout[i]) {
state.voxActive[i] = false;
}
}
} }
void UBitxDSP::end() { void UBitxDSP::end() {
@@ -138,13 +204,157 @@ void UBitxDSP::txUSBIn() {
// restore the tx usb in audio // restore the tx usb in audio
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN); audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (i == TX_USB_IN_CH1 || i == TX_USB_IN_CH2) if (i == TX_USB_IN_CH)
txAudio.gain(i, 0.1); txAudio.gain(i, 0.1);
else else
txAudio.gain(i, 0.0); txAudio.gain(i, 0.0);
} }
} }
/**********************************************************************/
// RX filter settings
const float minRxFilterLo = MIN_RX_FILTER_LO;
const float maxRxFilterHi = MAX_RX_FILTER_HI;
const float minRxFilterWidth = MIN_RX_FILTER_WIDTH;
const float maxRxFilterWidth = MAX_RX_FILTER_WIDTH;
const float minRxFilterCenter = MIN_RX_FILTER_CENTER;
const float maxRxFilterCenter = MAX_RX_FILTER_CENTER;
/*!
* @brief Bypass the RX audio filter.
*/
void UBitxDSP::bypassRxFilter() {
rxFilter.begin(FIR_PASSTHRU, NUM_COEFFICIENTS);
}
/*!
* @brief Update the RX audio filter using the currently set low and
* high frequencies. This is called by each of the public
* filter methods to update the filter with new frequencies.
*/
void UBitxDSP::updateRxFilter() {
audioFilter(coefficients, NUM_COEFFICIENTS, ID_BANDPASS, W_HAMMING, double(state.rxFilterLo), double(state.rxFilterHi));
rxFilter.begin(coefficients, NUM_COEFFICIENTS);
}
void UBitxDSP::setRxFilter(float lo, float hi) {
if (hi < lo + minRxFilterWidth) {
hi = lo + minRxFilterWidth;
}
if (hi > maxRxFilterHi) {
hi = maxRxFilterHi;
}
if (lo > hi - minRxFilterWidth) {
lo = hi - minRxFilterWidth;
}
if (lo < minRxFilterLo) {
lo = minRxFilterLo;
}
state.rxFilterHi = hi;
state.rxFilterLo = lo;
updateRxFilter();
}
void UBitxDSP::setRxFilterLo(float lo) {
if (lo > state.rxFilterHi - minRxFilterWidth) {
lo = state.rxFilterHi - minRxFilterWidth;
}
if (lo < minRxFilterLo) {
lo = minRxFilterLo;
}
state.rxFilterLo = lo;
updateRxFilter();
}
void UBitxDSP::setRxFilterHi(float hi) {
if (hi < state.rxFilterLo + minRxFilterWidth) {
hi = state.rxFilterLo + minRxFilterWidth;
}
if (hi > maxRxFilterHi) {
hi = maxRxFilterHi;
}
state.rxFilterHi = hi;
updateRxFilter();
}
void UBitxDSP::setRxFilterWidth(float width) {
if (width < minRxFilterWidth) {
width = minRxFilterWidth;
} else if (width > maxRxFilterWidth) {
width = maxRxFilterWidth;
}
float center = (state.rxFilterHi + state.rxFilterLo) / 2;
float lo = center - (width / 2);
float hi = center + (width / 2);
setRxFilter(lo, hi);
}
void UBitxDSP::setRxFilterCenter(float center) {
if (center < minRxFilterCenter) {
center = minRxFilterCenter;
} else if (center > maxRxFilterCenter) {
center = maxRxFilterCenter;
}
float width = state.rxFilterHi - state.rxFilterLo;
float lo = center - (width / 2);
float hi = center + (width / 2);
setRxFilter(lo, hi);
}
/**********************************************************************/
// TX audio input settings
void UBitxDSP::setTxInputLevel(int ch, float lvl) {
if ((ch > -1) && (ch < txNumChannels)) {
state.txInLvl[ch] = lvl;
float vol = lvl * float(state.txInEnable[ch] * state.txInTx[ch]);
txAudio.gain(ch, vol);
}
}
void UBitxDSP::enableTxInput(int ch) {
if ((ch > -1) && (ch < txNumChannels)) {
state.txInEnable[ch] = 1;
float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]);
txAudio.gain(ch, vol);
}
}
void UBitxDSP::disableTxInput(int ch) {
if ((ch > -1) && (ch < txNumChannels)) {
state.txInEnable[ch] = 0;
float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]);
txAudio.gain(ch, vol);
}
}
void UBitxDSP::startTxInput(int ch) {
if ((ch > -1) && (ch < txNumChannels)) {
state.txInTx[ch] = 1;
float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]);
txAudio.gain(ch, vol);
}
}
void UBitxDSP::stopTxInput(int ch) {
if ((ch > -1) && (ch < txNumChannels)) {
state.txInTx[ch] = 0;
float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]);
txAudio.gain(ch, vol);
}
}
// VOX settings
bool UBitxDSP::isVoxActive(int vox) {
if ((vox > -1) && (vox < 3)) {
return state.voxActive[vox];
} else {
return false;
}
}
//====================================================================== //======================================================================
// EOF // EOF
//====================================================================== //======================================================================

View File

@@ -5,8 +5,57 @@
#ifndef __DSP_h__ #ifndef __DSP_h__
#define __DSP_h__ #define __DSP_h__
#include <Audio.h>
#include <dynamicFilters.h>
#include "Debug.h" #include "Debug.h"
#define MIN_RX_FILTER_LO 0.0
#define MAX_RX_FILTER_HI 5000.0
#define MIN_RX_FILTER_WIDTH 0.0
#define MAX_RX_FILTER_WIDTH 5000.0
#define MIN_RX_FILTER_CENTER 0.0
#define MAX_RX_FILTER_CENTER 5000.0
#define DSP_MILLIS_PER_UPDATE 100
#define TX_MIC_IN_CH 0
#define TX_LINE_IN_CH 0
#define TX_USB_IN_CH 1
#define TX_NUM_CHANNELS 2
#define TX_LINE_IN_VOX 0
#define TX_USB_IN_L_VOX 1
#define TX_USB_IN_R_VOX 2
#define TX_NUM_VOX 3
#define TX_LINE_IN_VOX_THRESH 0.25
#define TX_USB_IN_L_VOX_THRESH 0.25
#define TX_USB_IN_R_VOX_THRESH 0.25
#define TX_LINE_IN_VOX_DELAY 500
#define TX_USB_IN_L_VOX_DELAY 500
#define TX_USB_IN_R_VOX_DELAY 500
struct DSPState {
// RX audio output settings
// RX filter settings
float rxFilterLo = 300.0;
float rxFilterHi = 3000.0;
// TX audio input settings
float txInLvl[4] = {0.5, 0.5, 0.0, 0.0};
int txInEnable[4] = {1, 1, 0, 0};
int txInTx[4] = {0, 0, 0, 0};
// VOX settings
bool voxActive[3];
float voxThresh[3];
unsigned voxDelay[3];
unsigned voxTimeout[3];
};
enum TRState { enum TRState {
TRANSMIT, TRANSMIT,
RECEIVE RECEIVE
@@ -24,7 +73,7 @@ enum TxAudioIn {
class UBitxDSP { class UBitxDSP {
public: public:
UBitxDSP() {}; UBitxDSP();
void begin(); void begin();
void update(); void update();
void end(); void end();
@@ -32,6 +81,59 @@ class UBitxDSP {
void txMicIn(); void txMicIn();
void txLineIn(); void txLineIn();
void txUSBIn(); void txUSBIn();
// RX audio output settings
// RX filter settings
void bypassRxFilter();
void setRxFilter(float lo, float hi);
void setRxFilterLo(float lo);
void setRxFilterHi(float hi);
void setRxFilterWidth(float width);
void setRxFilterCenter(float center);
/*!
* @brief Get the current low frequency bound of the RX band pass filter.
* @return The low frequency bound.
*/
inline int getRxFilterLo() { return state.rxFilterLo; }
/*!
* @brief Get the current high frequency bound of the RX band pass filter.
* @return The high frequency bound.
*/
inline int getRxFilterHi() { return state.rxFilterHi; }
/*!
* @brief Get the current width of the RX band pass filter.
* @return The filter width.
*/
inline int getRxFilterWidth() { return state.rxFilterHi - state.rxFilterLo; }
/*!
* @brief Get the current center frequency of the RX band pass filter.
* @return The center frequency.
*/
inline int getRxFilterCenter() { return (state.rxFilterHi + state.rxFilterLo) / 2; }
// TX audio input settings
void setTxInputLevel(int ch, float lvl);
void enableTxInput(int ch);
void disableTxInput(int ch);
void startTxInput(int ch);
void stopTxInput(int ch);
// VOX settings
bool isVoxActive(int vox);
private:
void updateRxFilter();
DSPState state;
short coefficients[NUM_COEFFICIENTS];
elapsedMillis sinceLastUpdate;
AudioAnalyzeRMS* voxRMS[3];
}; };
extern UBitxDSP DSP; extern UBitxDSP DSP;

View File

@@ -8,11 +8,13 @@
#define DBGPRINTLN(MSG) do { Serial.print("DBG: "); Serial.println(MSG); } while (0) #define DBGPRINTLN(MSG) do { Serial.print("DBG: "); Serial.println(MSG); } while (0)
#define DBGNEWLINE() do { Serial.println(); } while (0) #define DBGNEWLINE() do { Serial.println(); } while (0)
#define DBGCMD(CMD) do { Serial.print("DBG: "); Serial.println(#CMD); CMD; } while (0) #define DBGCMD(CMD) do { Serial.print("DBG: "); Serial.println(#CMD); CMD; } while (0)
#define IFDEBUG(CMD) do { CMD; } while (0)
#else #else
#define DBGPRINT(MSG) do {} while (0) #define DBGPRINT(MSG) do {} while (0)
#define DBGPRINTLN(MSG) do {} while (0) #define DBGPRINTLN(MSG) do {} while (0)
#define DBGNEWLINE() do {} while (0) #define DBGNEWLINE() do {} while (0)
#define DBGCMD(CMD) do { CMD; } while (0) #define DBGCMD(CMD) do { CMD; } while (0)
#define IFDEBUG(CMD) do {} while (0)
#endif #endif
#endif #endif

View File

@@ -61,7 +61,7 @@ speed(wpm), symWeight(weight)
// Calculate the length of dot, dash and silence // Calculate the length of dot, dash and silence
void UBitxKeyer::calcRatio() void UBitxKeyer::calcRatio()
{ {
float w = (1 + symWeight) / (symWeight -1); float w = (1.0 + symWeight) / (symWeight - 1.0);
spaceLen = (1200 / speed); spaceLen = (1200 / speed);
dotLen = spaceLen * (w - 1); dotLen = spaceLen * (w - 1);
dashLen = (1 + w) * spaceLen; dashLen = (1 + w) * spaceLen;
@@ -73,6 +73,12 @@ void UBitxKeyer::setWPM(int wpm)
calcRatio(); calcRatio();
} }
void UBitxKeyer::setWeight(float weight)
{
symWeight = weight;
calcRatio();
}
//====================================================================== //======================================================================
// Latch paddle press // Latch paddle press
//====================================================================== //======================================================================
@@ -107,10 +113,9 @@ bool UBitxKeyer::doPaddles()
if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW) || (keyerControl & 0x03)) { if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW) || (keyerControl & 0x03)) {
updatePaddleLatch(); updatePaddleLatch();
keyerState = CHK_DIT; keyerState = CHK_DIT;
// letting this fall through // return true; return true;
} else {
return false;
} }
return false;
//break; //break;
case CHK_DIT: // See if the dit paddle was pressed case CHK_DIT: // See if the dit paddle was pressed
@@ -119,15 +124,15 @@ bool UBitxKeyer::doPaddles()
ktimer = dotLen; ktimer = dotLen;
keyerState = KEYED_PREP; keyerState = KEYED_PREP;
return true; return true;
} else { // fall through
keyerState = CHK_DAH;
} }
// fall through
keyerState = CHK_DAH;
case CHK_DAH: // See if dah paddle was pressed case CHK_DAH: // See if dah paddle was pressed
if (keyerControl & DAH_L) { if (keyerControl & DAH_L) {
ktimer = dashLen; ktimer = dashLen;
keyerState = KEYED_PREP; keyerState = KEYED_PREP;
// letting this fall through // return true; return true;
} else { } else {
keyerState = IDLE; keyerState = IDLE;
return false; return false;
@@ -140,7 +145,7 @@ bool UBitxKeyer::doPaddles()
ktimer += millis(); // set ktimer to interval end time ktimer += millis(); // set ktimer to interval end time
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
keyerState = KEYED; // next state keyerState = KEYED; // next state
// letting this fall through // return true; return true;
//break; //break;
case KEYED: // Wait for timer to expire case KEYED: // Wait for timer to expire
@@ -148,12 +153,11 @@ bool UBitxKeyer::doPaddles()
keyDown = false; keyDown = false;
ktimer = millis() + spaceLen; // inter-element time ktimer = millis() + spaceLen; // inter-element time
keyerState = INTER_ELEMENT; // next state keyerState = INTER_ELEMENT; // next state
// letting this fall through // return true; return true;
} else if (keyMode == IAMBICB) { // Iambic B Mode ? } else if (keyMode == IAMBICB) { // Iambic B Mode ?
updatePaddleLatch(); // yes, early paddle latch in Iambic B mode updatePaddleLatch(); // yes, early paddle latch in Iambic B mode
} else {
return true;
} }
return true;
// break; // break;
case INTER_ELEMENT: // Insert time between dits/dahs case INTER_ELEMENT: // Insert time between dits/dahs
@@ -168,9 +172,8 @@ bool UBitxKeyer::doPaddles()
keyerState = IDLE; // go idle keyerState = IDLE; // go idle
return false; return false;
} }
} else {
return true;
} }
return true;
//break; //break;
} }

View File

@@ -44,10 +44,12 @@ public:
//void cw_pin(int pin); //void cw_pin(int pin);
//void ptt_pin(int pin); //void ptt_pin(int pin);
void setWPM(int wpm); void setWPM(int wpm);
inline int getWPM() { return speed; }
void setWeight(float weight);
inline float getWeight() { return symWeight; }
inline void setMode(int mode) { keyMode = mode; } inline void setMode(int mode) { keyMode = mode; }
inline int getMode() { return keyMode; } inline int getMode() { return keyMode; }
inline bool isDown() { return keyDown; } inline bool isDown() { return keyDown; }
// void setWeight();
bool doPaddles(); bool doPaddles();

View File

@@ -3,143 +3,122 @@
#include "RigState.h" #include "RigState.h"
enum UpdateSource { #define DEFAULT_SSB_LO_CUT 300.0
NoSource, #define DEFAULT_SSB_HI_CUT 3000.0
RaduinoSource, #define DEFAULT_CW_WIDTH 500.0
CATSource #define DEFAULT_LOW_USB false
#define DEFAULT_LOW_CWU true
#define DEFAULT_HIGH_USB true
#define DEFAULT_HIGH_CWU true
enum HamBand {
BAND_80M = 0,
BAND_60M,
BAND_40M,
BAND_30M,
BAND_20M,
BAND_17M,
BAND_15M,
BAND_12M,
BAND_10M,
NUM_BANDS
};
struct ModeConfig {
bool isUpper;
float dspLo;
float dspHi;
};
struct BandConfig {
ModeConfig cw;
ModeConfig ssb;
};
struct RigConfig {
//bool isData = false;
bool useUSBInput = true; // whether or not to use the USB input for data
}; };
class UBitxRig { class UBitxRig {
public: public:
UBitxRig();
inline void begin() {} inline void begin() {}
inline void update() {} inline void update() {}
inline unsigned long getFreqA() const { return state.vfo[0]; }
inline unsigned long getFreqB() const { return state.vfo[1]; }
inline long getRIT() const { return state.rit; }
inline long getXIT() const { return state.xit; }
inline bool isVFOA() const { return (state.flags & UBITX_VFOB_FLAG) != UBITX_VFOB_FLAG; }
inline bool isVFOB() const { return (state.flags & UBITX_VFOB_FLAG) == UBITX_VFOB_FLAG; }
inline bool isSplit() const { return (state.flags & UBITX_SPLIT_FLAG) == UBITX_SPLIT_FLAG; }
inline bool isRITOn() const { return (state.flags & UBITX_RIT_FLAG) == UBITX_RIT_FLAG; }
inline bool isXITOn() const { return (state.flags & UBITX_XIT_FLAG) == UBITX_XIT_FLAG; }
inline bool isCW() const { return (state.flags & UBITX_CW_FLAG) == UBITX_CW_FLAG; }
inline bool isLSB() const { return (state.flags & UBITX_USB_FLAG) != UBITX_USB_FLAG; }
inline bool isUSB() const { return (state.flags & UBITX_USB_FLAG) == UBITX_USB_FLAG; }
inline bool getAI() const { return autoInfo; }
inline bool updatedByCAT() const { return lastUpdatedBy == CATSource; }
inline bool updatedByRaduino() const { return lastUpdatedBy == RaduinoSource; }
inline void clearUpdate() { lastUpdatedBy = NoSource; } inline unsigned getFreqA() const { return radState.getFreqA(); }
inline void setRaduinoUpdate() { lastUpdatedBy = RaduinoSource; } inline unsigned getFreqB() const { return radState.getFreqB(); }
inline void setCATUpdate() { lastUpdatedBy = CATSource; }
inline void setFreqA(unsigned long freq, bool isCAT = false) { inline int getRIT() const { return radState.getRIT(); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline int getXIT() const { return radState.getXIT(); }
state.vfo[0] = freq;
}
inline void setFreqB(unsigned long freq, bool isCAT = false) { inline bool isVFOA() const { return radState.isVFOA(); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline bool isVFOB() const { return radState.isVFOB(); }
state.vfo[1] = freq; inline bool isSplit() const { return radState.isSplit(); }
} inline bool isRIT() const { return radState.isRIT(); }
inline bool isXIT() const { return radState.isXIT(); }
inline bool isModeCWAny() const { return radState.isModeCWAny(); }
inline bool isModeCW() const { return radState.isModeCW(); }
inline bool isModeCWR() const { return radState.isModeCWR(); }
inline bool isModeUSB() const { return radState.isModeUSB(); }
inline bool isModeLSB() const { return radState.isModeLSB(); }
inline void setRIT(short offset, bool isCAT = false) { inline float getCWSidetone() const { return static_cast<float>(radState.getSidetone()); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.rit = offset;
}
inline void setXIT(short offset, bool isCAT = false) { inline bool isUSBInput() const { return conf.useUSBInput; }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline bool isLineInput() const { return !conf.useUSBInput; }
state.xit = offset;
}
inline void selectVFOA(bool isCAT = false) { inline bool isAI() const { return autoInfo; }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_VFOB_FLAG;
}
inline void selectVFOB(bool isCAT = false) { inline void setFreqA(unsigned freq) { catState.setFreqA(freq); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline void setFreqB(unsigned freq) { catState.setFreqB(freq); }
state.flags |= UBITX_VFOB_FLAG;
}
inline void toggleVFO(bool isCAT = false) { inline void setRIT(int freq) { catState.setRIT(freq); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline void setXIT(int freq) { catState.setXIT(freq); }
state.flags ^= UBITX_VFOB_FLAG;
}
inline void splitOn(bool isCAT = false) { inline void setVFOA() { catState.setVFOA(); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline void setVFOB() { catState.setVFOB(); }
state.flags |= UBITX_SPLIT_FLAG; inline void setSplitOn() { catState.setSplitOn(); }
} inline void setSplitOff() { catState.setSplitOff(); }
inline void setRITOn() { catState.setRITOn(); }
inline void setRITOff() { catState.setRITOff(); }
inline void setXITOn() { catState.setXITOn(); }
inline void setXITOff() { catState.setXITOff(); }
inline void setModeCW() { catState.setModeCW(); }
inline void setModeCWR() { catState.setModeCWR(); }
inline void setModeUSB() { catState.setModeUSB(); }
inline void setModeLSB() { catState.setModeLSB(); }
inline void splitOff(bool isCAT = false) { inline void setCWSidetone(float f) { catState.setSidetone(static_cast<uint16_t>(f)); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_SPLIT_FLAG;
}
inline void ritOn(bool isCAT = false) { inline void setUSBInput() { conf.useUSBInput = true; }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline void setLineInput() { conf.useUSBInput = false; }
state.flags |= UBITX_RIT_FLAG;
}
inline void ritOff(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_RIT_FLAG;
}
inline void xitOn(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_XIT_FLAG;
}
inline void xitOff(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_XIT_FLAG;
}
inline void cwOn(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_CW_FLAG;
}
inline void cwOff(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_CW_FLAG;
}
inline void selectLSB(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_USB_FLAG;
}
inline void selectUSB(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_USB_FLAG;
}
inline void aiOn() { autoInfo = true; } inline void aiOn() { autoInfo = true; }
inline void aiOff() { autoInfo = false; } inline void aiOff() { autoInfo = false; }
uint8_t* const stateAsBytes() const { return (uint8_t* const)&state; } inline UBitxRigState& cat() { return catState; }
inline UBitxRigState& rad() { return radState; }
inline void updateState(UBitxRigState& r, bool isCAT = false) { /********************************************************************/
if ((r.vfo[0] == state.vfo[0]) && // New functional/mode-based Rig methods
(r.vfo[1] == state.vfo[1]) &&
(r.rit == state.rit) && // AG
(r.xit == state.xit) && //void setVolOut(uint8_t level);
(r.flags == state.flags)) { //uint8_t getVolOut();
return;
} else { // BD/BU
state = r; //void setBand();
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; //void getBand();
}
}
private: private:
RigConfig conf;
UBitxRigState catState;
UBitxRigState radState;
bool autoInfo = false; bool autoInfo = false;
UpdateSource lastUpdatedBy = NoSource;
UBitxRigState state; BandConfig band[NUM_BANDS];
}; };
extern UBitxRig Rig; extern UBitxRig Rig;

447
TeensyDSP/RigState.cpp Normal file
View File

@@ -0,0 +1,447 @@
/*!
* @file RigState.cpp
*
* @mainpage uBITX V5X Software - RigState
*
* @section introsec Introduction
*
* TBD
*
* @section dependencies Dependencies
*
* TBD
*
* @section author Author
*
* Written by Rob "Scrape" French, KC4UPR
*
* @section license License
*
* TBD
*/
#include "Debug.h"
#include "RigState.h"
/***********************************************************************
* COMMON FUNCTIONS
*
* The following are all common to RigState objects, whether on the
* Raduino or on the TeensyDSP.
**********************************************************************/
static uint32_t zeroes[1] = {0}; // used to transmit zeroes
/*!
* @brief Begin using the RigState object. In order to force an
* initial update (i.e. sending current state to the remote
* device), all fields are initially marked dirty.
*/
void UBitxRigState::begin() {
setDirty();
}
/***********************************************************************
* RADUINO FUNCTIONS
*
* The following are specific to the Raduino implementation. Note that
* this depends on the use of the TEENSYDUINO #define, which may result
* in a fragile implementation for other development environments (e.g.
* if the normal Arduino IDE is not being used).
**********************************************************************/
#ifndef TEENSYDUINO
#include <Wire.h>
#include "ubitx.h"
#include "ubitx_eemap.h"
extern unsigned long frequency, ritRxFrequency, ritTxFrequency, sideTone;
extern unsigned long vfoA;
extern unsigned long vfoB;
extern char cwMode;
extern char isUSB;
extern char vfoActive;
extern char ritOn;
extern char splitOn;
extern char inTx;
void setFrequency(unsigned long);
/*!
* @brief Send the RigState from the Raduino to the TeensyDSP. The
* basic process is: (1) read in any updated (dirty) data
* from the Raduino's state variables; (2) transmit the dirty
* data to the TeensyDSP; (2a) for clean data, zeroes are
* transmitted; (3) mark all data as clean.
*/
void UBitxRigState::send_RIGINF() {
readDirty();
Wire.beginTransmission(I2CMETER_ADDR);
Wire.write(I2CMETER_RIGINF);
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS; i++) {
if (i == DIRTY_WORD || isDirty(i)) {
// always send the current dirty bits
// or, bytes for updated (dirty) fields
Wire.write((byte*)&data[i], sizeof(uint32_t));
} else {
// otherwise, send out zeroes
Wire.write((byte*)&zeroes, sizeof(uint32_t));
//----------------------------------------------------------------
// NOTE: I am sending these zeroed out fields under a possibly
// mistaken assumption that in doing so, I will be sending a
// constant voltage on the SDA line most of the time, i.e. no
// bit changes, and so this will help reduce noise generated by
// I2C traffic (since most of the time there will be no updates.)
//----------------------------------------------------------------
}
}
Wire.endTransmission();
IFDEBUG( serialHexState("Sent") );
//IFDEBUG( serialPrettyState("Sent") );
setClean();
}
// delay(1); // 1ms - some delay required between ending transmission and requesting?
/*!
* @brief Receive the RigState from the TeensyDSP. This generally
* reflects changes due to CAT transmission to the TeensyDSP.
* @param numBytes
* Number of bytes received from the TeensyDSP.
*/
void UBitxRigState::receive_RIGINF(int numBytes) {
// Retrieve all of the deltas. Mark any received fields as dirty. It
// is assumed that send_RIGINF() was called immedaitely before this,
// so the fields are already clean.
byte* ptr = (byte*)&data;
Wire.requestFrom(I2CMETER_ADDR, sizeof(data));
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS && Wire.available(); i++) {
for (size_t j = 0; j < sizeof(uint32_t) && Wire.available(); j++) {
byte incomingByte = Wire.read();
if (i == DIRTY_WORD || isDirty(i)) {
// always overwrite the dirty bits
// and, update bytes for fields marked dirty
*ptr = incomingByte;
}
ptr++;
}
}
writeDirty();
IFDEBUG( serialHexState("Rcvd") );
//IFDEBUG( serialPrettyState("Rcvd") );
setClean(); // They get marked dirty as req'd during readDirty().
}
/*!
* @brief Write dirty fields from the RigState out to the Raduino
* variables.
*/
void UBitxRigState::writeDirty() {
// VFO A frequency
if (isDirty(VFOA_WORD)) {
if (vfoActive == VFO_A) {
setFrequency(getFreqA());
} else {
vfoA = getFreqA();
}
}
// VFO B frequency
if (isDirty(VFOB_WORD)) {
if (vfoActive == VFO_B) {
setFrequency(getFreqB());
} else {
vfoB = getFreqB();
}
}
// RIT and XIT frequencies
if (isDirty(OFFSETS_WORD)) {
// RIT
ritRxFrequency = getRIT() + ritTxFrequency;
if (ritOn == 1) {
if (inTx == 0) {
setFrequency(ritRxFrequency);
} else {
setFrequency(ritTxFrequency);
}
}
// XIT - TODO
}
// Various flags
if (isDirty(FLAGS_WORD)) {
// VFO A/B selection
char prev = vfoActive;
vfoActive = isVFOA() ? VFO_A : VFO_B;
if (vfoActive != prev) {
if (vfoActive == VFO_A) {
if (vfoA != frequency) {
setFrequency(vfoA);
}
} else if (vfoActive == VFO_B) {
if (vfoB != frequency) {
setFrequency(vfoB);
}
}
}
// Split on/off
splitOn = isSplit() ? 1 : 0;
// RIT on/off
prev = ritOn;
ritOn = isRIT() ? 1 : 0;
if (ritOn != prev) {
if ((ritOn == 1) && (inTx == 0)) {
setFrequency(ritRxFrequency);
}
}
// XIT on/off
// TODO
// Mode
prev = (cwMode << 1) | isUSB;
isUSB = isModeUSB() ? 1 : 0;
if (isModeCW()) {
cwMode = 2; // 2 = cwu
} else if (isModeCWR()) {
cwMode = 1; // 1 = cwl
} else {
cwMode = 0; // 0 = no cw
}
if ((cwMode << 1) | isUSB != prev) {
setFrequency(frequency);
}
}
// Keyer information
if (isDirty(KEYER_WORD)) {
// Sidetone frequency
sideTone = static_cast<unsigned long>(getSidetone());
}
}
/*!
* @brief Read current Raduino variables into the RigState
* (if they are changed) and set the appropriate dirty flags.
* @param r
* RigState reference to put the values into.
*/
void UBitxRigState::readDirty() {
unsigned long freq;
short offset;
// VFO A frequency
freq = (vfoActive == VFO_A) ? frequency : vfoA;
if (getFreqA() != freq) {
setFreqA(freq);
}
// VFO B frequency
freq = (vfoActive == VFO_B) ? frequency : vfoB;
if (getFreqB() != freq) {
setFreqB(freq);
}
// RIT frequency
if (inTx) {
offset = ritRxFrequency - ritTxFrequency;
} else {
offset = frequency - ritTxFrequency;
}
if (getRIT() != offset) {
setRIT(offset);
}
// XIT frequency
offset = 0; // xitRxFrequency - frequency;
if (getXIT() != offset) {
setXIT(offset);
}
// VFO A/B selection
if (isVFOA() && vfoActive == VFO_B) {
setVFOB();
} else if (isVFOB() && vfoActive == VFO_A) {
setVFOA();
}
// Split selection
if (isSplit() && splitOn == 0) {
setSplitOff();
} else if (!isSplit() && splitOn != 0) {
setSplitOn();
}
// RIT selection
if (isRIT() && ritOn == 0) {
setRITOff();
} else if (!isRIT() && ritOn != 0) {
setRITOn();
}
// XIT selection
//setXITOff();
// TODO
// Mode
char prev = (isModeCW() ? 4 : 0) | (isModeCWR() ? 2 : 0) | (isModeUSB() ? 1 : 0);
char curr = (cwMode << 1) | isUSB;
if (curr != prev) {
if (cwMode == 2) {
setModeCW();
} else if (cwMode == 1) {
setModeCWR();
} else {
if (isUSB) {
setModeUSB();
} else {
setModeLSB();
}
}
}
// Sidetone
if (getSidetone() != static_cast<uint16_t>(sideTone)) {
setSidetone(static_cast<uint16_t>(sideTone));
}
}
/***********************************************************************
* TEENSYDSP FUNCTIONS
*
* The following are specific to the TeensyDSP implementation. Note
* that this depends on the use of the TEENSYDUINO #define, which may
* result in a fragile implementation for other development environments
* (e.g. if the normal Arduino IDE is not being used).
**********************************************************************/
#else
#include <i2c_t3.h>
/*!
* @brief Receive RIGINF data from the Raduino. This method should
* be called on the TeensyDSP 'radState' (Raduino state)
* instance, when a RIGINF signal is received via I2C. It
* receives the incoming data from the Raduino and updates the
* state.
*/
void UBitxRigState::receive_RIGINF(int numBytes) {
byte* ptr = (byte*)&data;
setClean(); // we'll get new dirty bits via the I2C message
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS && Wire1.available(); i++) {
for (size_t j = 0; j < sizeof(uint32_t) && Wire1.available(); j++) {
byte incomingByte = Wire1.read();
if (i == DIRTY_WORD || isDirty(i)) {
// always overwrite the dirty bits
// and, update bytes for fields marked dirty
*ptr = incomingByte;
}
ptr++;
}
}
//--------------------------------------------------------------------
// Do anything that needs to happen when something is updated by the
// Raduino... this would be due to e.g. changes through the menu.
// Current (as of 2/19/2021) things that DON'T need to have any
// updates: frequency (those just get requested by CAT as needed).
// Current things that do need to get updated: sidetone (used to
// update CW filter values).
//--------------------------------------------------------------------
processDirty();
IFDEBUG( serialHexState("Rcvd") );
IFDEBUG( serialPrettyState("Rcvd") );
}
/**********************************************************************/
/*!
* @brief Handle a RIGINF signal from the Raduino. This method should
* be called on the TeensyDSP 'catState' (CAT state)
* instance, when a RIGINF signal is received via I2C. It
* sends a response to the Raduino via I2C, using the Wire1
* interface.
*/
void UBitxRigState::send_RIGINF() {
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS; i++) {
if (i == DIRTY_WORD || isDirty(i)) {
// always send the current dirty bits
// or, bytes for updated (dirty) fields
Wire1.write((byte*)&data[i], sizeof(uint32_t));
} else {
// otherwise, send out zeroes
Wire1.write((byte*)&zeroes, sizeof(uint32_t));
//----------------------------------------------------------------
// NOTE: I am sending these zeroed out fields under a possibly
// mistaken assumption that in doing so, I will be sending a
// constant voltage on the SDA line most of the time, i.e. no
// bit changes, and so this will help reduce noise generated by
// I2C traffic (since most of the time there will be no updates.)
//----------------------------------------------------------------
}
}
IFDEBUG( serialHexState("Sent") );
IFDEBUG( serialPrettyState("Sent") );
setClean(); // now that we've sent them, they're clean
//--------------------------------------------------------------------
// TODO: Need to look at possibly merging the two states together at
// this point. The purpose would be to minimize the turnaround time
// for getting the most recent data to a CAT response.
//--------------------------------------------------------------------
}
/*!
* @brief Perform required actions based on any dirty bits set.
*/
void UBitxRigState::processDirty() {
}
#endif
#ifdef DEBUG
char debugString[81] = {'\0'};
void UBitxRigState::serialHexState(const char* label = "RigState") {
Serial.print(label);
sprintf(debugString, ": %#010lx, %#010lx, %#010lx, %#010lx, %#010lx",
data[DIRTY_WORD], data[VFOA_WORD], data[VFOB_WORD], data[OFFSETS_WORD], data[FLAGS_WORD]);
Serial.println(debugString);
}
void UBitxRigState::serialPrettyState(const char* label = "RigState") {
Serial.println(label);
sprintf(debugString, "VFO A : %011ld %1c / VFO B : %011ld %1c",
getFreqA(), isDirty(VFOA_WORD) ? 'D' : ' ', getFreqB(), isDirty(VFOB_WORD) ? 'D' : ' ');
Serial.println(debugString);
sprintf(debugString, "RIT : %011ld %1c / XIT : %011ld %1c",
getRIT(), isDirty(OFFSETS_WORD) ? 'D' : ' ', getXIT(), isDirty(OFFSETS_WORD) ? 'D' : ' ');
Serial.println(debugString);
sprintf(debugString, "Split? %1c / VFO? %1c / RIT? %1c / XIT? %1c / Mode? %3s",
isSplit() ? 'Y' : 'N', isVFOA() ? 'A' : 'B', isRIT() ? 'Y' : 'N', isXIT() ? 'Y' : 'N',
isModeUSB() ? "USB" : (isModeLSB() ? "LSB" : (isModeCW() ? "CW " : (isModeCWR() ? "CWR" : " "))));
Serial.println(debugString);
}
#endif
/**********************************************************************/
#ifndef TEENSYDUINO
UBitxRigState _rigState;
UBitxRigState& rigState = _rigState;
#endif
/***********************************************************************
* EOF
**********************************************************************/

View File

@@ -1,14 +1,12 @@
/*!
* @file RigState.h
*/
#ifndef __RigState_h__ #ifndef __RigState_h__
#define __RigState_h__ #define __RigState_h__
#include <Arduino.h> #include <Arduino.h>
#define UBITX_VFOA_UPDATE 0x00000001
#define UBITX_VFOB_UPDATE 0x00000002
#define UBITX_RIT_UPDATE 0x00000004
#define UBITX_XIT_UPDATE 0x00000008
#define UBITX_FLAGS_UPDATE 0x00000010
#define UBITX_VFOB_FLAG 0x00000001 #define UBITX_VFOB_FLAG 0x00000001
#define UBITX_SPLIT_FLAG 0x00000002 #define UBITX_SPLIT_FLAG 0x00000002
#define UBITX_RIT_FLAG 0x00000004 #define UBITX_RIT_FLAG 0x00000004
@@ -17,126 +15,341 @@
#define UBITX_USB_FLAG 0x00000020 #define UBITX_USB_FLAG 0x00000020
#define UBITX_TX_FLAG 0x00000040 #define UBITX_TX_FLAG 0x00000040
#define UBITX_SIDETONE_MASK 0x000007FF
#define UBITX_KEYER_MODE_MASK 0x00003800
#ifdef TEENSYDUINO
#define DISABLEINTS(CMD) do { noInterrupts(); CMD; interrupts(); } while (0)
#else
#define DISABLEINTS(CMD) do { CMD; } while (0)
#endif
enum RigStateWord {
DIRTY_WORD = 0,
VFOA_WORD,
VFOB_WORD,
OFFSETS_WORD,
FLAGS_WORD,
KEYER_WORD,
NUM_WORDS
};
inline RigStateWord& operator++(RigStateWord& orig) {
orig = static_cast<RigStateWord>(orig + 1);
// NOTE: Will overflow...
return orig;
}
inline RigStateWord operator++(RigStateWord& orig, int) {
RigStateWord rVal = orig;
++orig;
return rVal;
}
struct UBitxRigState { struct UBitxRigState {
uint32_t header = 0; volatile uint32_t data[NUM_WORDS] = {0};
uint32_t vfo[2];
int32_t rit;
int32_t xit;
uint32_t flags = 0;
};
/**********************************************************************/ void begin();
template<typename T, int ID> void send_RIGINF();
struct Field { void receive_RIGINF(int numBytes = sizeof(data));
byte id = ID;
/*!
* @brief Set the dirty bit for the specified word.
*
* @param w
* The word to mark as dirty.
*/
inline void setDirty(RigStateWord w) {
data[DIRTY_WORD] |= w < NUM_WORDS ? 1 << w : 0;
}
/*!
* @brief Set the dirty bits for all words.
*/
inline void setDirty() { DISABLEINTS( data[DIRTY_WORD] = 0xFFFFFFFF ); }
/*!
* @brief Clear the dirty bit for the specified word.
*
* @param w
* The word to mark as clean.
*/
inline void setClean(RigStateWord w) {
data[DIRTY_WORD] &= ~(w < NUM_WORDS ? 1 << w : 0);
}
/*!
* @brief Clear the dirty bits for all words.
*/
inline void setClean() { DISABLEINTS( data[DIRTY_WORD] = 0 ); }
/*!
* @brief Check whether the specified word is clean.
*
* @param w
* The word to check for clean status.
*
* @return True if the word is clean.
*/
inline bool isClean(RigStateWord w) {
bool clean;
DISABLEINTS( clean = ((1 << w) & data[DIRTY_WORD]) > 0 ? false : true );
return clean;
}
/*!
* @brief Check whether the data is clean (as a whole).
*
* @return True if the data is clean (no dirty fields).
*/
inline bool isClean() {
bool clean;
DISABLEINTS( clean = data[DIRTY_WORD] == 0 );
return clean;
}
/*!
* @brief Check whether the specified word is dirty.
*
* @param w
* The word to check for dirty status.
*
* @return True if the word is dirty.
*/
inline bool isDirty(RigStateWord w) {
bool dirty; bool dirty;
T data; DISABLEINTS( dirty = ((1 << w) & data[DIRTY_WORD]) > 0 ? true : false );
return dirty;
inline size_t sizeOfWrite() { return dirty ? sizeof(byte) + sizeof(T) : 0; }
template<typename STREAM> void writeChanges() {
if (dirty) {
STREAM().write(id);
STREAM().write((byte*)&data, sizeof(T));
}
} }
template<typename STREAM> int read() { /*!
size_t len = 0; * @brief Check whether the data is dirty (as a whole).
byte* ptr = (byte*)&data; *
while (STREAM().available() && len < sizeof(T)) { * @return True if the data is dirty (at least one dirty field).
ptr[len++] = STREAM().read(); */
} inline bool isDirty() {
return len; bool dirty;
DISABLEINTS( dirty = data[DIRTY_WORD] != 0 );
return dirty;
} }
inline void merge(Field<T,ID>& f) { /*!
if (dirty) { * @brief Set the VFO A frequency.
f.data = data; *
f.dirty = true; * @param freq
} else if (f.dirty) { * The new frequency in Hz.
data = f.data; */
dirty = true; inline void setFreqA(uint32_t freq, bool mark = true) {
} DISABLEINTS( data[VFOA_WORD] = freq;
if (mark) setDirty(VFOA_WORD) );
} }
inline void markClean() { dirty = false; } inline uint32_t getFreqA() const {
uint32_t result;
DISABLEINTS( result = data[VFOA_WORD] );
return result;
}
/*!
* @brief Set the VFO B frequency.
*
* @param freq
* The new frequency in Hz.
*/
inline void setFreqB(uint32_t freq, bool mark = true) {
DISABLEINTS( data[VFOB_WORD] = freq );
}
inline uint32_t getFreqB() const {
uint32_t result;
DISABLEINTS( result = data[VFOB_WORD] );
return result;
}
inline void setRIT(int16_t offset, bool mark = true) {
DISABLEINTS( data[OFFSETS_WORD] = (int32_t(offset) << 16) | (0x0000FFFF & data[OFFSETS_WORD]);
if (mark) setDirty(OFFSETS_WORD) );
}
inline int16_t getRIT() const {
int16_t result;
DISABLEINTS( result = data[OFFSETS_WORD] >> 16 );
return result;
}
inline void setXIT(int16_t offset, bool mark = true) {
DISABLEINTS( data[OFFSETS_WORD] = (0xFFFF0000 & data[OFFSETS_WORD]) | offset;
if (mark) setDirty(OFFSETS_WORD) );
}
inline int16_t getXIT() const {
int16_t result;
DISABLEINTS( result = 0x0000FFFF & data[OFFSETS_WORD] );
return result;
}
inline void setVFOA(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_VFOB_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setVFOB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_VFOB_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline bool isVFOA() const {
bool result;
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_VFOB_FLAG ? false : true );
return result;
}
inline bool isVFOB() const {
bool result;
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_VFOB_FLAG ? true : false );
return result;
}
inline void setSplitOn(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_SPLIT_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setSplitOff(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_SPLIT_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline bool isSplit() const {
bool result;
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_SPLIT_FLAG ? true : false );
return result;
}
inline void setRITOn(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_RIT_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setRITOff(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_RIT_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline bool isRIT() const {
bool result;
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_RIT_FLAG ? true : false );
return result;
}
inline void setXITOn(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_XIT_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setXITOff(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_XIT_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline bool isXIT() const {
bool result;
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_XIT_FLAG ? true : false );
return result;
}
inline void setModeUSB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setModeLSB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setModeCW(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
data[FLAGS_WORD] |= UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setModeCWR(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
data[FLAGS_WORD] |= UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline bool isModeUSB() const {
bool result;
DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) > 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) == 0) );
return result;
}
inline bool isModeLSB() const {
bool result;
DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) == 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) == 0) );
return result;
}
inline bool isModeCWAny() const {
bool result;
DISABLEINTS( result = (data[FLAGS_WORD] & UBITX_CW_FLAG) > 0 );
return result;
}
inline bool isModeCW() const {
bool result;
DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) > 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) > 0) );
return result;
}
inline bool isModeCWR() const {
bool result;
DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) == 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) > 0) );
return result;
}
inline void setSidetone(uint16_t f, bool mark = true) {
DISABLEINTS( data[KEYER_WORD] &= ~UBITX_SIDETONE_MASK;
data[KEYER_WORD] |= (uint32_t(f) & UBITX_SIDETONE_MASK);
if (mark) setDirty(KEYER_WORD) );
}
inline uint16_t getSidetone() {
uint32_t result;
DISABLEINTS( result = data[KEYER_WORD] & UBITX_SIDETONE_MASK );
return uint16_t(result);
}
#ifdef DEBUG
void serialHexState(const char* label);
void serialPrettyState(const char* label);
#endif
#ifndef TEENSYDUINO
// These methods are only defined in the Raduino (Arduino) case of the
// RigState, not in the TeensyDSP (Teensy) case.
void writeDirty(); // write fields FROM RigState TO Raduino
void readDirty(); // read variables FROM Raduino TO RigState
#else
// These methods are only defined (currently) in the TeensyDSP case.
void processDirty();
#endif
}; };
struct RigState { #ifndef TEENSYDUINO
Field<uint32_t, 0> vfoA;
Field<uint32_t, 1> vfoB;
Field<int32_t, 2> rit;
Field<int32_t, 3> xit;
Field<uint32_t, 4> flags;
inline size_t sizeOfWrite() { extern UBitxRigState& rigState;
size_t size = 0;
size += vfoA.sizeOfWrite();
size += vfoB.sizeOfWrite();
size += rit.sizeOfWrite();
size += xit.sizeOfWrite();
size += flags.sizeOfWrite();
return size;
}
template<typename STREAM> void writeChanges() { #endif
vfoA.writeChanges<STREAM>();
vfoB.writeChanges<STREAM>();
rit.writeChanges<STREAM>();
xit.writeChanges<STREAM>();
flags.writeChanges<STREAM>();
}
template<typename STREAM> void readChanges(size_t size) {
size_t len = 0;
while (STREAM().available() && len < size) {
switch(STREAM().read()) {
case 0:
len += vfoA.read<STREAM>();
break;
case 1:
len += vfoB.read<STREAM>();
break;
case 2:
len += rit.read<STREAM>();
break;
case 3:
len += xit.read<STREAM>();
break;
case 4:
len += flags.read<STREAM>();
break;
default:
;
}
}
}
inline void merge(RigState& r) {
vfoA.merge(r.vfoA);
vfoB.merge(r.vfoB);
rit.merge(r.rit);
xit.merge(r.xit);
flags.merge(r.flags);
}
inline void markClean(RigState& r) {
vfoA.markClean();
vfoB.markClean();
rit.markClean();
xit.markClean();
flags.markClean();
}
};
/* /*
NOTE: This is all currently OBE, leaving it here for reference/future cleanup.
Protocol discussion: Protocol discussion:
- I2C master: Raduino - I2C master: Raduino
- I2C slave: TeensyDSP - I2C slave: TeensyDSP
@@ -172,3 +385,7 @@ TeensyDSP state:
*/ */
#endif #endif
/***********************************************************************
* EOF
**********************************************************************/

View File

@@ -5,7 +5,8 @@
#include <Arduino.h> #include <Arduino.h>
#include "TR.h" #include "TR.h"
UBitxTR TR(DSP); UBitxTR _tr(DSP);
UBitxTR& TR = _tr;
void UBitxTR::update(bool cw, bool extKey) { void UBitxTR::update(bool cw, bool extKey) {
updateKey(); updateKey();

View File

@@ -19,6 +19,12 @@ const int uBitxTRPttPin = UBITX_TR_PTT_PIN;
const int uBitxTRVoxPin = UBITX_TR_VOX_PIN; const int uBitxTRVoxPin = UBITX_TR_VOX_PIN;
const int uBitxTRKeyPin = UBITX_TR_KEY_PIN; const int uBitxTRKeyPin = UBITX_TR_KEY_PIN;
struct TxSource {
MIC_SOURCE = 0,
LINE_SOURCE,
USB_SOURCE,
};
class UBitxTR { class UBitxTR {
public: public:
UBitxTR(UBitxDSP& d, int out = uBitxTROutPin, int p = uBitxTRPttPin, int v = uBitxTRVoxPin, int k = uBitxTRKeyPin): UBitxTR(UBitxDSP& d, int out = uBitxTROutPin, int p = uBitxTRPttPin, int v = uBitxTRVoxPin, int k = uBitxTRKeyPin):
@@ -63,9 +69,10 @@ class UBitxTR {
inline bool catActivated() { return (L_catActive != catActive) && L_catActive; } inline bool catActivated() { return (L_catActive != catActive) && L_catActive; }
inline bool catDeactivated() { return (L_catActive != catActive) && catActive; } inline bool catDeactivated() { return (L_catActive != catActive) && catActive; }
inline void catTX() { inline void catTX(TxSource src) {
L_catActive = catActive; L_catActive = catActive;
catActive = true; catActive = true;
txSource = src;
} }
inline void catRX() { inline void catRX() {
@@ -73,10 +80,11 @@ class UBitxTR {
catActive = false; catActive = false;
} }
//====================================================================== //====================================================================
inline bool transmitting() { return isTX; } inline bool transmitting() { return isTX; }
inline bool receiving() { return !isTX; } inline bool receiving() { return !isTX; }
inline TxSource source() const { return txSource; }
/*! /*!
* @brief Check if any of the PTT's have been pressed or released * @brief Check if any of the PTT's have been pressed or released
@@ -145,6 +153,7 @@ class UBitxTR {
bool L_keyDown = false; bool L_keyDown = false;
bool catActive = false; bool catActive = false;
bool L_catActive = false; bool L_catActive = false;
TxSource txSource = MIC_SOURCE;
}; };
extern UBitxTR TR; extern UBitxTR TR;

View File

@@ -41,8 +41,6 @@ TS590Command::TS590Command(const char* pre) {
} }
} }
TS590Command::~TS590Command() {}
/*! /*!
* @brief Determine whether this is a Read command or not. by * @brief Determine whether this is a Read command or not. by
* default, if it's a 2-letter command, it's a Read. * default, if it's a 2-letter command, it's a Read.
@@ -77,7 +75,7 @@ void TS590Command::process(const char* cmd) {
DBGCMD( handleCommand(cmd) ); DBGCMD( handleCommand(cmd) );
switch(theError) { switch(theError) {
case NoError: case NoError:
if (theRig->getAI()) { if (theRig->isAI()) {
DBGCMD( sendResponse(cmd) ); DBGCMD( sendResponse(cmd) );
} }
break; break;
@@ -130,7 +128,27 @@ void TS590Command::setRig(UBitxRig* r) {
theRig = r; theRig = r;
} }
/*!
* @brief Set the DSP that will be used to process commands.
* @param d
* Pointer to the UBitxDSP object.
*/
void TS590Command::setDSP(UBitxDSP* d) {
theDSP = d;
}
/*!
* @brief Set the T/R that will be used to process commands.
* @param t
* Pointer to the UBitxTR object.
*/
void TS590Command::setDSP(UBitxTR* t) {
theTR = t;
}
UBitxRig* TS590Command::theRig = &Rig; UBitxRig* TS590Command::theRig = &Rig;
UBitxDSP* TS590Command::theDSP = &DSP;
UBitxTR* TR590Command::theTR = &TR;
TS590Error TS590Command::theError = NoError; TS590Error TS590Command::theError = NoError;
/**********************************************************************/ /**********************************************************************/
@@ -139,13 +157,13 @@ void TS590_FR::handleCommand(const char* cmd) {
if (strlen(cmd) == 3) { if (strlen(cmd) == 3) {
switch (cmd[2]) { switch (cmd[2]) {
case '0': case '0':
rig()->selectVFOA(true); rig()->setVFOA();
rig()->splitOff(true); rig()->setSplitOff();
break; break;
case '1': case '1':
rig()->selectVFOB(true); rig()->setVFOB();
rig()->splitOff(true); rig()->setSplitOff();
break; break;
case '2': case '2':
@@ -177,9 +195,9 @@ void TS590_FT::handleCommand(const char* cmd) {
switch (cmd[2]) { switch (cmd[2]) {
case '0': case '0':
if (rig()->isVFOA()) { if (rig()->isVFOA()) {
rig()->splitOff(true); rig()->setSplitOff();
} else if (rig()->isVFOB()) { } else if (rig()->isVFOB()) {
rig()->splitOn(true); rig()->setSplitOn();
} else { } else {
setSyntaxError(); setSyntaxError();
} }
@@ -187,9 +205,9 @@ void TS590_FT::handleCommand(const char* cmd) {
case '1': case '1':
if (rig()->isVFOA()) { if (rig()->isVFOA()) {
rig()->splitOn(true); rig()->setSplitOn();
} else if (rig()->isVFOB()) { } else if (rig()->isVFOB()) {
rig()->splitOff(true); rig()->setSplitOff();
} else { } else {
setSyntaxError(); setSyntaxError();
} }
@@ -228,23 +246,19 @@ void TS590_MD::handleCommand(const char* cmd) {
break; break;
case '1': // LSB case '1': // LSB
rig()->selectLSB(true); rig()->setModeLSB();
rig()->cwOff(true);
break; break;
case '2': // USB case '2': // USB
rig()->selectUSB(true); rig()->setModeUSB();
rig()->cwOff(true);
break; break;
case '3': // CW case '3': // CW
rig()->selectUSB(true); rig()->setModeCW();
rig()->cwOn(true);
break; break;
case '7': // CW-R case '7': // CW-R
rig()->selectLSB(true); rig()->setModeCWR();
rig()->cwOn(true);
break; break;
default: default:
@@ -256,19 +270,176 @@ void TS590_MD::handleCommand(const char* cmd) {
} }
void TS590_MD::sendResponse(const char* cmd) { void TS590_MD::sendResponse(const char* cmd) {
if (rig()->isCW()) { if (rig()->isModeCW()) {
if (rig()->isUSB()) {
ts590SendCommand("MD3"); ts590SendCommand("MD3");
} else { } else if (rig()->isModeCWR()) {
ts590SendCommand("MD7"); ts590SendCommand("MD7");
} } else if (rig()->isModeUSB()) {
} else {
if (rig()->isUSB()) {
ts590SendCommand("MD2"); ts590SendCommand("MD2");
} else { } else if (rig()->isModeLSB()) {
ts590SendCommand("MD1"); ts590SendCommand("MD1");
} else {
ts590SendCommand("MD0");
} }
} }
/**********************************************************************/
int ssbHiCut[14] = {1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3400, 4000, 5000};
int ssbLoCut[12] = {0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
int ssbWidth[14] = {50, 80, 100, 150, 200, 250, 300, 400, 500, 600, 1000, 1500, 2000, 2500};
int ssbCenter[14] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1750, 1800, 1900, 2000, 2100, 2210};
void TS590_SH::handleCommand(const char* cmd) {
if (strlen(cmd) == 4) {
index = strtoul(&cmd[2], NULL, 10);
if (index < sizeof(ssbHiCut) / sizeof(ssbHiCut[0])) {
dsp()->setRxFilterHi(ssbHiCut[index]);
} else {
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_SH::sendResponse(const char* cmd) {
ts590SendCommand("SH%02u", index);
}
void TS590_SL::handleCommand(const char* cmd) {
if (strlen(cmd) == 4) {
index = strtoul(&cmd[2], NULL, 10);
if (index < sizeof(ssbLoCut) / sizeof(ssbLoCut[0])) {
dsp()->setRxFilterLo(ssbLoCut[index]);
} else {
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_SL::sendResponse(const char* cmd) {
ts590SendCommand("SL%02u", index);
}
/**********************************************************************/
void TS590_TX::handleCommand(const char* cmd) {
if (strlen(cmd) == 3) {
switch (cmd[2]) {
case '0':
tr.catTX(MIC_SOURCE);
break;
case '1':
tr.catTX(rig.isUSBInput() ? USB_INPUT : LINE_INPUT);
break;
case '2':
// TODO: Need to implement w/ Teensy Audio Tool.
//tr.catTX();
break;
default:
setSyntaxError();
}
} else if (strlen(cmd) == 2) {
tr.catTX(MIC_SOURCE);
} else {
setSyntaxError();
}
}
void TS590_TX::sendResponse(const char* cmd) {
char src;
switch (tr.source()) {
case MIC_SOURCE:
src = '0';
break;
case LINE_SOURCE:
case USB_SOURCE:
src = '1';
break;
}
ts590SeondCommand("TX%1c", src);
}
/**********************************************************************/
/*!
* @brief Create a new CAT EX command. It should be initialized with
* a 3-character P1 parameter (command number).
* @param pre
* A 3-character command prefix. If more than 3 characters
* are supplied, only the first two will be used. If less
* than three are supplied, then the command will be
* initialized with a null prefix.
*/
TS590EXCommand::TS590EXCommand(const char* P1):
TS590Command("EX") {
if (strlen(P1) >= 3) {
strncpy(myMenu, P1, 3);
}
}
/*!
* @brief Determine whether this is a Read command or not. by
* default, if it's a 2-letter command, it's a Read.
* @return True if a Read command; false otherwise.
*/
bool TS590EXCommand::isReadCommand(const char* cmd) const {
if (strlen(cmd) == 9) {
return true;
} else {
return false;
}
}
void TS590EXCommand::sendResponse(const char* cmd) {
ts590sendCommand("%2c%3c0000%s", prefix(), menu(), getReturnValue());
}
/**********************************************************************/
void TS590_EX034::handleCommand(const char* cmd) {
if (strlen(cmd) == 10 || strlen(cmd) == 11) {
index = (uint8_t)atol(&cmd[9]);
if (index < 15) {
rig().setCWSidetone(300.0 + (float)(index * 50));
} else {
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_EX034::sendResponse(const char* cmd) {
ts590SendCommand("EX0340000%02d", index % 15);
}
/**********************************************************************/
void TS590_EX063::handleCommand(const char* cmd) {
if (strlen(cmd) == 10) {
if (cmd[9] == '0') {
rig().setLineInput();
} else if (cmd[9] == '1') {
rig().setUSBInput();
} else {
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_EX063::sendResponse(const char* cmd) {
ts590SendCommand("EX0630000%1c", rig().isUSBInput() ? '1' : '0');
} }
/**********************************************************************/ /**********************************************************************/
@@ -278,13 +449,17 @@ TS590_FB cmdFB;
TS590_FR cmdFR; TS590_FR cmdFR;
TS590_FT cmdFT; TS590_FT cmdFT;
TS590_MD cmdMD; TS590_MD cmdMD;
TS590_SH cmdSH;
TS590_SL cmdSL;
TS590Command* catCommands[] = { TS590Command* catCommands[] = {
&cmdFA, &cmdFA,
&cmdFB, &cmdFB,
&cmdFR, &cmdFR,
&cmdFT, &cmdFT,
&cmdMD &cmdMD,
&cmdSH,
&cmdSL
}; };
int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]); int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]);
@@ -343,8 +518,27 @@ int compareCATCommands(const void* a, const void* b) {
return cmp; return cmp;
} }
int compareCATEXCommands(const void* a, const void* b) {
TS590Command const *B = *(TS590Command const **)b;
int cmp = strncmp((char*)a, (char*)B->prefix(), 5);
#ifdef DEBUG
Serial.print("Comparison: ");
Serial.print((char*)a);
Serial.print(" ? ");
Serial.print((char*)B->prefix());
Serial.print(" --> ");
Serial.println(cmp);
#endif
return cmp;
}
void UBitxTS590::processCommand() { void UBitxTS590::processCommand() {
TS590Command** cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands); TS590Command** cmd;
if (strncmp(buf, "EX", 2) == 0) {
cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATEXCommands);
} else {
cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands);
}
if (cmd == NULL) { if (cmd == NULL) {
ts590SyntaxError(); ts590SyntaxError();
} else { } else {

View File

@@ -2,10 +2,15 @@
#define __TS590_h__ #define __TS590_h__
#include <Arduino.h> #include <Arduino.h>
#include "DSP.h"
#include "Rig.h" #include "Rig.h"
#include "TR.h"
/**********************************************************************/ /**********************************************************************/
// uncomment to use TS-590SG / comment to use TS-590S
#define USE_TS590SG
#define TS590_COMMAND_MAX_LENGTH 50 // including terminator (which will get converted to null) #define TS590_COMMAND_MAX_LENGTH 50 // including terminator (which will get converted to null)
const int ts590CommandMaxLength = TS590_COMMAND_MAX_LENGTH; const int ts590CommandMaxLength = TS590_COMMAND_MAX_LENGTH;
@@ -56,6 +61,16 @@ class TS590Command {
*/ */
inline UBitxRig* rig() const { return theRig; } inline UBitxRig* rig() const { return theRig; }
/*!
* @brief Return the DSP that this command will be used to control.
*/
inline UBitxDSP* dsp() const { return theDSP; }
/*!
* @brief Return the T/R that this command will be used to control.
*/
inline UBitxDSP* tr() const { return theTR; }
/*! /*!
* @brief Handle the provided Set command. If the Set command * @brief Handle the provided Set command. If the Set command
* results in an error, then set the appropriate flag with * results in an error, then set the appropriate flag with
@@ -81,11 +96,14 @@ class TS590Command {
static void setCommError(); static void setCommError();
static void setProcessError(); static void setProcessError();
static void setRig(UBitxRig* r); static void setRig(UBitxRig* r);
static void setDSP(UBitxDSP* d);
static void setTR(UBitxTR* t);
private: private:
char myPrefix[3] = "\0\0"; char myPrefix[3] = "\0\0";
static TS590Error theError; static TS590Error theError;
static UBitxRig* theRig; static UBitxRig* theRig;
static UBitxDSP* theDSP;
}; };
/**********************************************************************/ /**********************************************************************/
@@ -102,9 +120,9 @@ class TS590_FAB : public TS590Command {
if (strlen(cmd) == 13) { if (strlen(cmd) == 13) {
unsigned long freq = strtoul(&cmd[2], NULL, 10); unsigned long freq = strtoul(&cmd[2], NULL, 10);
if (VFOA) { if (VFOA) {
rig()->setFreqA(freq, true); rig()->setFreqA(freq);
} else { } else {
rig()->setFreqB(freq, true); rig()->setFreqB(freq);
} }
} else { } else {
setSyntaxError(); setSyntaxError();
@@ -160,6 +178,159 @@ class TS590_MD : public TS590Command {
/**********************************************************************/ /**********************************************************************/
/*!
* @brief CAT command for setting the receiver high-cut frequency.
*/
class TS590_SH : public TS590Command {
public:
TS590_SH(): TS590Command("SH") {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
unsigned index;
};
/*!
* @brief CAT command for setting the receiver low-cut frequency.
*/
class TS590_SL : public TS590Command {
public:
TS590_SL(): TS590Command("SL") {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
unsigned index;
};
/*!
* @brief CAT command to start transmitting.
*/
class TS590_TX : public TS590Command {
public:
TS590_TX(): TS590Command("TX") {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
};
/**********************************************************************/
class TS590EXCommand : public TS590Command {
public:
TS590EXCommand(const char *P1);
virtual ~TS590EXCommand() = 0;
inline const char* menu() const { return &myMenu[0]; }
virtual const char* getReturnValue() const = 0;
virtual void sendResponse(const char* cmd);
private:
char myMenu[4] = "\0\0\0";
};
/**********************************************************************/
/*!
* @brief CAT command for setting the sidetone pitch/frequency.
*/
class TS590_EX034 : public TS590EXCommand {
public:
TS590_EX034(): TS590EXCommand("034") {}
virtual void handleCommand(const char* cmd);
virtual const char* getReturnValue();
private:
uint8_t index;
};
/*!
* @brief CAT command for selecting the data input line.
*/
class TS590_EX063 : public TS590EXCommand {
public:
TS590_EX063(): TS590EXCommand("063") {}
virtual void handleCommand(const char* cmd);
virtual const char* getReturnValue();
};
/**********************************************************************/
/*!
* @brief CAT command for setting USB/Line audio input levels.
*/
template<bool USB, bool SG>
class TS590_EXDataAudioInLevel : public TS590EXCommand {
public:
TS590_EXDataAudioInLevel(): TS590EXCommand(USB ? (SG ? "071" : "064") : (SG ? "073" : "066")) {}
virtual void handleCommand(const char* cmd) {
if (strlen(cmd) == 10) {
uint8_t val = (cmd[9] - 48) % 10;
if (USB) {
// set USB input level
} else {
// set Line input level
}
} else {
setSyntaxError();
}
}
virtual const char* getReturnValue() {
static char str[2] = "\0";
// get input level - decimal 0 to 9 ... str[1] = ...
return str;
}
};
#ifdef USE_TS590SG
typedef TS590_EXDataAudioInLevel<true, true> TS590_EX071;
typedef TS590_EXDataAudioInLevel<false, true> TS590_EX073;
#else
typedef TS590_EXDataAudioInLevel<true, false> TS590_EX064;
typedef TS590_EXDataAudioInLevel<true, false> TS590_EX066;
#endif
/**********************************************************************/
/*!
* @brief CAT command for setting USB/Line audio output levels.
*/
template<bool USB, bool SG>
class TS590_EXDataAudioOutLevel : public TS590EXCommand {
public:
TS590_EXDataAudioOutLevel(): TS590EXCommand(USB ? (SG ? "072" : "065") : (SG ? "074" : "067")) {}
virtual void handleCommand(const char* cmd) {
if (strlen(cmd) == 10) {
uint8_t val = (cmd[9] - 48) % 10;
if (USB) {
// set USB output level
} else {
// set Line output level
}
} else {
setSyntaxError();
}
}
virtual const char* getReturnValue() {
static char str[2] = "\0";
// get output level - decimal 0 to 9 ... str[1] = ...
return str;
}
};
#ifdef USE_TS590SG
typedef TS590_EXDataAudioOutLevel<true, true> TS590_EX072;
typedef TS590_EXDataAudioOutLevel<false, true> TS590_EX074;
#else
typedef TS590_EXDataAudioOutLevel<true, false> TS590_EX065;
typedef TS590_EXDataAudioOutLevel<true, false> TS590_EX067;
#endif
/**********************************************************************/
class UBitxTS590 { class UBitxTS590 {
public: public:
UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {} UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {}

View File

@@ -68,8 +68,8 @@ extern int magnitudelimit_low;
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter #define I2CMETER_CALCR 0x55 //Calculated SWR Meter
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter #define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
// Raduino provides updated data to TeensyDSP // Raduino<=>TeensyDSP data exchange
#define I2CMETER_RIGINF 0x50 #define I2CMETER_RIGINF 0x50
// Raduino requests any CAT updates from TeensyDSP // Raduino requests any CAT updates from TeensyDSP
#define I2CMETER_REQCAT 0x51 //#define I2CMETER_REQCAT 0x51

View File

@@ -363,8 +363,6 @@ void setup()
//Serial1.println("Start..."); //Serial1.println("Start...");
} }
bool sentRigInfFlag = false;
/*! /*!
@brief Receive a command via I2C. The most recent command will be received, which will @brief Receive a command via I2C. The most recent command will be received, which will
indicate which data the DSP should be preparing to return. indicate which data the DSP should be preparing to return.
@@ -375,20 +373,12 @@ void i2cReceiveEvent(size_t numBytes)
{ {
int readCommand = 0; int readCommand = 0;
bool exitLoop = false; bool exitLoop = false;
UBitxRigState tmpState;
// Does this really need to be a while loop? Don't we know the number of bytes?
while (Wire1.available() > 0 && !exitLoop) { while (Wire1.available() > 0 && !exitLoop) {
readCommand = Wire1.read(); readCommand = Wire1.read();
if (readCommand == I2CMETER_RIGINF) { if (readCommand == I2CMETER_RIGINF) {
size_t len = 0; Rig.rad().receive_RIGINF(numBytes - 1);
uint8_t* const ptr = (uint8_t* const)&tmpState;
while ((Wire1.available() > 0) && (len < sizeof(UBitxRigState))) {
ptr[len++] = Wire1.read();
}
if (!Rig.updatedByCAT()) {
Rig.updateState(tmpState);
}
sentRigInfFlag = false; // so we know that we need to send the flag first
exitLoop = true; exitLoop = true;
} }
} }
@@ -424,43 +414,35 @@ void i2cRequestEvent(void)
case I2CMETER_CALCS: case I2CMETER_CALCS:
// Returns an already-calculated S-meter value. // Returns an already-calculated S-meter value.
Wire1.write(scaledSMeter); Wire1.write(scaledSMeter);
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break; break;
case I2CMETER_UNCALCS: case I2CMETER_UNCALCS:
// Returns a raw signal strength value. // Returns a raw signal strength value.
Wire1.write(Sensors.sMeterUnscaled() >> 2); // divided by 4... do we want this? Wire1.write(Sensors.sMeterUnscaled() >> 2); // divided by 4... do we want this?
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break; break;
case I2CMETER_CALCP: case I2CMETER_CALCP:
// Returns a raw forward power value. // Returns a raw forward power value.
Wire1.write(int(fwdPower * 100.0)); Wire1.write(int(fwdPower * 100.0));
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break; break;
case I2CMETER_CALCR: case I2CMETER_CALCR:
// Returns a raw reverse power value. // Returns a raw reverse power value.
Wire1.write(int(revPower * 100.0)); Wire1.write(int(revPower * 100.0));
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break; break;
case I2CMETER_RIGINF: case I2CMETER_RIGINF:
// Receive current rig state; transmit any CAT updates, if required. // Receive current rig state; transmit any CAT updates, if required.
Rig.cat().send_RIGINF();
//Wire1.write(catState.header); // temporary - just writing a single, null byte //Wire1.write(catState.header); // temporary - just writing a single, null byte
//break; // NEEDS TO GET UPDATED
break;
/*
case I2CMETER_REQCAT: case I2CMETER_REQCAT:
// Provide latest CAT updates, if any. // Provide latest CAT updates, if any.
//Wire1.write(catState.header); // temporary - just writing a single, null byte //Wire1.write(catState.header); // temporary - just writing a single, null byte
// NEEDS TO GET UPDATED
if (Rig.updatedByCAT()) { if (Rig.updatedByCAT()) {
if (sentRigInfFlag) { if (sentRigInfFlag) {
DBGPRINTLN("I2CMETER_REQCAT -- updated by CAT"); DBGPRINTLN("I2CMETER_REQCAT -- updated by CAT");
@@ -475,14 +457,18 @@ void i2cRequestEvent(void)
//Wire1.write(Rig.stateAsBytes(), sizeof(uint8_t)); //Wire1.write(Rig.stateAsBytes(), sizeof(uint8_t));
Wire1.write(0); Wire1.write(0);
} }
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break; break;
*/
default: default:
break; break;
} }
#ifdef DEBUG
if (0x50 <= i2cCommand && i2cCommand <= 0x59)
{
i2cRespCounter[i2cCommand - 0x50]++;
}
#endif
} }
//extern void Decode_Morse(float magnitude); //extern void Decode_Morse(float magnitude);
@@ -541,9 +527,9 @@ void loop()
} }
// If CW mode, we need to update keying a lot... // If CW mode, we need to update keying a lot...
if (Rig.isCW()) { if (Rig.isModeCWAny()) {
if (Rig.isCW()) Keyer.doPaddles(); if (Rig.isModeCWAny()) Keyer.doPaddles();
TR.update(Rig.isCW(), Keyer.isDown()); TR.update(Rig.isModeCWAny(), Keyer.isDown());
//if (TR.transmitting()) return; //if (TR.transmitting()) return;
} }
@@ -558,11 +544,11 @@ void loop()
// Update each of the subsystems, beginning with CAT control. // Update each of the subsystems, beginning with CAT control.
TS590.update(); TS590.update();
TR.update(Rig.isCW(), Keyer.isDown()); TR.update(Rig.isModeCWAny(), Keyer.isDown());
Rig.update(); Rig.update();
DSP.update(); DSP.update();
//if (Rig.isCW()) return; //if (Rig.isModeCWAny()) return;
#ifdef DEBUG #ifdef DEBUG
// For debugging, output some debug info every 1.0" (40 frames @ 40 Hz). // For debugging, output some debug info every 1.0" (40 frames @ 40 Hz).
@@ -685,7 +671,7 @@ void loop()
forwardData(); forwardData();
} }
if (Rig.isCW()) return; // In CW, the ADC measurement messes with the timing. So need to use interrupts on the Keyer, and/or continuous ADC. if (Rig.isModeCWAny()) return; // In CW, the ADC measurement messes with the timing. So need to use interrupts on the Keyer, and/or continuous ADC.
if (sinceADCMillis > adcIntervalMillis) { if (sinceADCMillis > adcIntervalMillis) {
// Do stuff that we do once per ADC interval--ADC colllection. // Do stuff that we do once per ADC interval--ADC colllection.
@@ -703,7 +689,7 @@ void loop()
//forwardData(); //forwardData();
} }
//if (Rig.isCW()) return; //if (Rig.isModeCWAny()) return;
// Check Response Command // Check Response Command
if (responseCommand > 0 && sinceForward > LAST_TIME_INTERVAL) if (responseCommand > 0 && sinceForward > LAST_TIME_INTERVAL)

386
TeensyDSP/temp.h Normal file
View File

@@ -0,0 +1,386 @@
const int rxLoCutSSB[] = { 0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
const int rxHiCutSSB[] = {1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3400, 4000, 5000};
const int rxDataWidth[] = { 50, 80, 100, 150, 200, 250, 300, 400, 500, 600, 1000, 1500, 2000, 2500};
#ifdef USE_TS590SG
const int rxDataShift[] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1750, 1800, 1900, 2000, 2100, 2210};
#else
const int rxDataShift[] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2210};
#endif
const int txLowCutFilter[] = { 10, 100, 200, 300, 400, 500};
const int txHighCutFilter[] = {2500, 2600, 2700, 2800, 2900, 3000};
const int timeOutMinutes[] = {3, 5, 10, 20, 30};
#define MAX_MENU_TITLE_LEN 13
#define MAX_MENU_OPTION_LEN 13
typedef void (*toggleFunc)(UBitxRig&);
typedef bool (*boolStatus)(UBitxRig&);
#define KEYER_MIN_SPEED 4
#define KEYER_MAX_SPEED 60
/**********************************************************************/
/*!
* @brief Abstract base class for a switch/toggle (on/off, A/B, etc.)
* option. Derived classes must provide set() and get()
* methods for controlling the state of the switch.
*/
struct ConfigSwitch {
virtual ~ConfigSwitch() = 0;
virtual void set(bool on) = 0;
virtual bool get() const = 0;
};
/*!
* @brief Abstract base class for an option that supports a linear
* sequence of integer values. There can be a minimum and a
* maximum value, but otherwise, all integers between those
* should be acceptable. The base class takes care of checking
* min/max bounds, so the derived class must provide an get()
* and onSet() methods. In addition, derived classes can
* optionally override the onTooHigh() and onTooLow(), which
* by default clamp any out-of-band inputs to the min/max.
*/
struct ConfigInteger {
ConfigInteger(int min, int max)
: myMin(min), myMax(max) {}
virtual ~ConfigInteger() = 0;
inline void set(int val) {
if (val > myMax) val = onTooHigh(val);
if (val < myMin) val = onTooLow(val);
onSet(val);
}
virtual int get() const = 0;
virtual void onSet() = 0;
virtual int onTooHigh(int val) const { return myMax; }
virtual int onTooLow(int val) const { return myMin; }
int myMin, myMax;
};
/*!
* @brief Abstract base class for an option that supports a set
* of integer values stored in an array. The derived
* class must provide get() and onSet() methods. It can
* optionally provide an onTooHigh() method, which by
* default will clamp indices to the highest allowable.
*/
struct ConfigArray {
ConfigArray(int len, int* data)
: myLen(len), myData(data) {}
virtual ~ConfigArray() = 0;
inline void set(int i) {
if (i < 0) i = 0;
if (i > myLen - 1) i = onTooHigh(i);
onSet(myData[i]);
}
virtual int get() const = 0;
virtual void onSet(int val) = 0;
virtual int onTooHigh(int i) const { return myLen - 1; }
inline int getData(int i) { return myData[i]; }
int myLen;
int *myData;
};
/**********************************************************************/
/*!
* @brief Option class to set the configuration of the RX DSP filter.
*/
template<bool isHIGH>
struct ConfigRXFilter : public ConfigArray {
ConfigFilter(UBitxDSP& d, int default)
: ConfigArray(0, NULL), dsp(d), useData(false) {
if (isHIGH) {
current[0] = 10; // SSB - hi cut
current[1] = 05; // Data - center (shift)
data[0] = rxHiCutSSB;
data[1] = rxDataShift;
length[0] = sizeof(rxHiCutSSB)/sizeof(rxHiCutSSB[0]);
length[1] = sizeof(rxDataShift)/sizeof(rxDataShift[0]);
} else {
current[0] = 04; // SSB - lo cut
current[1] = 12; // Data - width
data[0] = rxLoCutSSB;
data[1] = rxDataWidth;
length[0] = sizeof(rxLoCutSSB)/sizeof(rxLoCutSSB[0]);
length[1] = sizeof(rxDataWidth)/sizeof(rxDataWidth[0]);
}
}
// TODO - A TON MORE TO DO HERE TO MAKE IT CONSISTENT WITH CONSTRUCTION
inline void setSSB() {
useData = false;
}
inline void setData() { useData = true; }
virtual void get() { return current; }
virtual void onSet(int i) {
current = i;
float value = static_cast<float>(getData(i));
if (isHIGH) {
if (useCENTER) {
dsp.setRxFilterCenter(value);
} else {
dsp.setRxFilterHi(value);
}
} else {
if (useCENTER) {
dsp.setRxFilterWidth(value);
} else {
dsp.setRxFilterLo(static_cast<float>(getData(i)));
}
}
}
private:
UBitxDSP& dsp;
int* data[2];
int length[2];
int current[2];
int mode; // 0 = SSB, 1 = Data
};
typedef ConfigRXFilter<false, false> ConfigRxLoCutSSB;
typedef ConfigRXFilter<true, false> ConfigRxHiCutSSB;
typedef ConfigRXFilter<false, true > ConfigRxLoCutData;
typedef ConfigRXFilter<true, true > ConfigRxHiCutData;
struct DSPConfigurator {
DSPConfigurator(UBitxDSP& d)
: dsp(d) {}
ConfigRxLoCutSSB ssbRxLoCut;
ConfigRxHiCutSSB ssbRxHiCut;
ConfigRxLoCutData dataRxLoCut;
ConfigRxHiCutData dataRxHiCut;
private:
UBitxDSP& dsp;
} dspConfigurator(DSP);
/**********************************************************************/
/**********************************************************************/
/*!
* @brief A configuration class for the Keyer. Since the Keyer has
* its own state that is not 100% compatible with the way way
* the Rig will interact with it (primarily in terms of CAT
* commands), this class provides the glue. (This is mostly
* used because, while the configuration objects are generally
* all separate, there is some state that needs to be
* communicated between e.g. the iambic mode configuration and
* the bug mode configuration.)
*/
struct KeyerConfigurator {
KeyerConfigurator(UBitxKeyer& k): keyer(k) { iambicAB = keyer.getMode(); }
/*!
* @brief An inner class that supports switching between Iambic A & B.
*/
struct IambicMode : public ConfigSwitch {
IambicMode(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
virtual void set(bool on) {
config.iambicAB = on;
if (config.keyer.getMode() != STRAIGHT) {
config.keyer.setMode(config.iambicAB ? IAMBICB : IAMBICA);
}
}
virtual bool get() const { return config.iambicAB; }
private: KeyerConfigurator& config;
} iambicMode;
/*!
* @brief An inner class that supports switching bug mode on/off.
*/
struct BugMode : public ConfigSwitch {
BugMode(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
virtual void set(bool on) {
config.isBug = on;
if (config.isBug && (config.keyer.getMode() != STRAIGHT)) {
config.keyer.setMode(STRAIGHT);
} else if (!config.isBug && (config.keyer.getMode() == STRAIGHT)) {
config.keyer.setMode(config.iambicAB ? IAMBICB : IAMBICA);
}
}
virtual bool get() const { return config.isBug; }
private: KeyerConfigurator& config;
} bugMode;
/*!
* @brief An inner class that supports switching the left and right
* paddles. TODO: This currently does nothing.
*/
struct PaddleSwap : public ConfigSwitch {
PaddleSwap(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
virtual void set(bool on) { ; }
virtual bool get() const { return false(); }
private: KeyerConfigurator& config;
} paddleSwap;
/*!
* @brief An inner class that supports changing the keyer speed.
*/
struct KeyerSpeed : public ConfigInteger {
KeyerSpeed(KeyerConfigurator& c): ConfigInteger(KEYER_MIN_SPEED, KEYER_MAX_SPEED), config(c) {}
virtual void onSet(int val) { config.keyer.setWPM(val); }
virtual int get { return config.keyer.getWPM(); }
private: KeyerConfigurator& config;
} keyerSpeed;
private:
Keyer& keyer;
bool iambicAB;
bool isBug;
} keyerConfigurator(Keyer);
/**********************************************************************/
/**********************************************************************/
class MenuItem {
public:
MenuItem(int idno, const char* title, int numOpt, int defOpt, ): myID(idno), myTitle(title), numOptions(numOpt), selected(defOpt) {}
virtual ~MenuItem() = 0;
inline void id() { return myID; }
inline void writeTitle(char* outbuf, int maxlen) { strncpy(outbuf, myTitle, maxlen); }
inline char* writeOption(char* outbuf, int o = -1, int maxlen = MAX_MENU_OPTION_LEN) {
if (o == -1) {
setOptionText(selected);
} else if ((o >= 0) && (o < numOptions)) {
setOptionTitle(o);
} else {
memset(myOption, '\0', MAX_MENU_OPTION_LEN);
}
return strncpy(outbuf, myOption, maxlen);
}
inline int getNumOptions() { return numOptions; }
inline int getSelected() { return selected; }
inline int nextOption(bool doUpdate = true) {
selected = (selected + 1) % numOptions;
if (doUpdate) update();
return selected;
}
inline int prevOption(bool doUpdate = true) {
selected = (selected - 1) % numOptions;
if (doUpdate) update();
return selected;
}
inline int gotoOption(int o, bool doUpdate = true) {
selected = o >= numOptions ? numOptions : (o < 0 ? 0 : o);
if (doUpdate) update();
return selected;
}
virtual void update() = 0;
private:
int myID;
char myTitle[MAX_MENU_TITLE_LEN] = {'\0'};
char myOption[MAX_MENU_OPTION_LEN] = {'\0'};
int numOptions;
int selected;
};
class StepItem : public MenuItem {
public:
StepItem(int idno, const char* title, int numOpt, int defOpt): MenuItem(idno, title, numOpt, defOpt) {}
};
static const char off_str[] = "OFF";
static const char on_str[] = "ON";
static const char line_str[] = "LINE";
static const char usb_str[] = "USB";
static const char key_a_str[] = "A";
static const char key_b_str[] = "B";
class ToggleItem : public MenuItem {
public:
ToggleItem(int idno, const char* title, const char* off, const char* on, int defOpt): MenuItem(idno, title, 2, defOpt) {}
};
/**********************************************************************/
struct MenuConfig {
byte sidetoneVolume = 9;
byte ssbLoCut = 0;
byte ssbHiCut = 5;
byte dataLoCut = 0;
byte dataHiCut = 0;
byte keyerAB = 0;
byte sidetonePitch = 6;
};
// CAT COMMANDS - BASIC
// KS - Sets and reads the Keying speed.
IntegerItem ("KS", "KEYER SPEED", 0, 2, keyerConfigurator.keyerSpeed),
ArrayItem ("SH", "RX HI CUT ", 0, 2, dspConfigurator.ssbRxHiCut),
ArrayItem ("SL", "RX LO CUT ", 0, 2, dspConfigurator.ssbRxLoCut),
// CAT COMMANDS - EX MENU
// Sidetone volume
Steps {004, "ST VOL ", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},
// SSB/AM Low Cut transmit filter (Hz)
Steps {025, "SSB TX LO ", 6, [](auto x) { return txLowCutFilter[x]; }},
// SSB/AM High Cut transmit filter (Hz)
Steps {026, "SSB TX HI ", 6, [](auto x) { return txHighCutFilter[x]; }},
// SSB-DATA Low Cut transmit filter (Hz)
Steps {027, "DATA TX LO ", 6, [](auto x) { return txLowCutFilter[x]; }},
// SSB-DATA High Cut transmit filter (Hz)
Steps {028, "DATA TX HI ", 6, [](auto x) { return txHighCutFilter[x]; }},
// Electronic keyer operation mode
ToggleItem (032, "KEYER A/B ", "A ", "B ", keyerConfigurator.iambicMode),
// Sidetone/ pitch frequency setting (Hz)
Steps {034, "ST PITCH ", 15, [](auto x) { return 300+(x*50); }},
// Keying weight ratio
StepsSpec {036, "KEYER WEIGHT", 17, ...},
// Bug key function
ToggleItem (038, "KEYER BUG ", "OFF ", "ON ", keyerConfigurator.bugMode),
// Paddle dot/dash replacement setting
ToggleItem (039, "KEYER SWAP ", "OFF ", "ON ", keyerConfigurator.paddleSwap),
// Auto CW TX in SSB mode
//ToggleItem (041, "AUTO CW TX ", "OFF ", "ON ", [&Rig](bool on) { Rig.setKeyerAutoTransmitCW(on); } ),
// Time-out Timer
//StepsSpec {049, "TIMEOUT ", 6, ... },
// Transmit inhibit
ToggleItem (060, "TX INHIBIT ", "OFF ", "ON ", [&Rig](bool on) { Rig.setTransmitInhibit(on); } ),
// DATA moduleation line
ToggleItem (063, "DATA LINE ", "LINE", "USB ", [&Rig](bool usb) { Rig.setDataInputLine(usb); } ),
// USB audio input level
Steps {064, "USB IN LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
// USB audio output level
Steps {065, "USB OUT LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
// ACC2 terminal AF input level
Steps {066, "LINE IN LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
// ACC2 terminal AF output level
Steps {067, "LINE OUT LVL", [](auto x) { return static_cast<double>(x-48)/9.0; }},
// DATA VOX
ToggleItem (069, "DATA VOX ", "OFF ", "ON ", [&Rig](bool on) { Rig.setDataVoxOn(on); } ),
// DATA VOX delay
Steps {070, "DATA VOX DEL", 20, ...},
// DATA VOX gain for USB audio input
Steps {071, "USB VOX LVL ", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},
// DATA VOX gain for ACC2 terminal input
Steps {072, "LINE VOX LVL", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},