16 Commits

Author SHA1 Message Date
295b99ef36 making sure any outstanding updates are committed 2025-02-15 09:48:00 -06:00
Rob French
e6009989db Reorganizing DSP and TR. Thoroughly broken ATM, just committing for backup. 2021-03-30 12:31:44 -05:00
Rob French
1f3585d8e4 Various changes. Compiles. Hasn't been tested. 2021-03-19 15:30:30 -05:00
Rob French
c8aecdfb0d Further updates to DSP, and significant changes to TS590. Implementing templated CAT commands using the delegate template from the ETL. Compiles, but not tested. 2021-03-16 23:11:17 -05:00
Rob French
f724142fca Updated DSP. Compiles, no warnings/errors. Going to do some work on CAT. 2021-03-16 18:37:14 -05:00
Rob French
869e47d430 Significant updates to DSP. Compiles. Likely doesn't work at the moment. Next step should be to get it running again on the rig, NOT to try any more fancy updates to CAT etc. 2021-03-14 23:18:24 -05:00
Rob French
20b475dace Updates to the DSP. One issue that needs to be resolved: in order to use the VOX the way it is currently setup, muting of the overall TX chain needs to be at the END, not the beginning; whatever is setup as the default TX audio source, needs to be unmuted even during RX. 2021-03-04 23:12:41 -06:00
Rob French
962a3ce80f Revert "Updated with some of the changes I made on travel. Will probably break everything..."
This reverts commit 86ae1ddb2f.
2021-03-04 07:16:14 -06:00
Rob French
86ae1ddb2f Updated with some of the changes I made on travel. Will probably break everything... 2021-03-03 10:44:25 -06:00
Rob French
119902b1e0 Significant updates. Compiles and works, though not test significantly nor assumed to be particularly robust. I2C comms between Raduino and TeensyDSP. Some amount of functioning CAT. Haven't tried with any applications e.g. WSJT-X. 2021-02-19 01:39:25 -06:00
Rob French
1bca18c3e1 Updates to RigState. 2021-02-17 11:05:09 -06:00
Rob French
f3887e7950 commit before I totally change the RigState architecture 2021-02-15 22:38:05 -06:00
Rob French
2cbc9abae8 Updated files list. 2021-02-15 08:06:31 -06:00
Rob French
8a416608a1 Long way from being compilable on either the Raduino or the TeensyDSP. Lot of changes in progress. 2021-02-14 23:04:29 -06:00
Rob French
e5de516633 Significant revamp of RigState to only send changes. Not done yet. 2021-02-14 00:35:38 -06:00
Rob French
5b395cd922 Added multiple new audio (DSP) functions. Minor updates to the rig. Added additional CAT command (filter hi- and lo-cut frequencies, SH/SL). 2021-02-12 22:19:14 -06:00
26 changed files with 1929 additions and 583 deletions

0
Raduino/Debug.h Executable file
View File

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(){

0
Raduino/RigState.cpp Executable file
View File

View File

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

0
Raduino/RigState.h Executable file
View File

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;
}
}

BIN
References/B5A-0180-20.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -4,147 +4,477 @@
#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>
//#include <SD.h> //#include <SD.h>
//#include <SerialFlash.h> //#include <SerialFlash.h>
#define RX_AUDIO_CH 0
#define TX_MIC_IN_CH 0
#define TX_LINE_IN_CH 0
#define TX_USB_IN_CH1 1
#define TX_USB_IN_CH2 2
UBitxDSP DSP;
//static struct {
// GUItool: begin automatically generated code // GUItool: begin automatically generated code
AudioInputI2S lineIn; //xy=385.9371643066406,1001.9600830078125 AudioInputUSB usbIn; //xy=63,305
AudioInputUSB usbIn; //xy=390.9371643066406,1107.9600830078125 AudioInputI2S lineIn; //xy=71,197
AudioMixer4 rxAudio; //xy=642.9371643066406,915.9600830078125 AudioSynthWaveformSine tone1; //xy=111,369
AudioMixer4 txAudio; //xy=646.9371643066406,1103.9600830078125 AudioSynthWaveformSine tone2; //xy=111,410
AudioOutputUSB usbOut; //xy=1022.9371643066406,919.9600830078125 AudioMixer4 rxAudio; //xy=328,111
AudioOutputI2S lineOut; //xy=1024.9371643066406,1017.9600830078125 AudioMixer4 txAudio; //xy=332,299
AudioConnection patchCord1(lineIn, 0, rxAudio, 0); AudioAnalyzeRMS txVoxLevel; //xy=490,340
AudioConnection patchCord2(lineIn, 1, txAudio, 0); AudioFilterFIR rxFilter; //xy=493,103
AudioConnection patchCord3(usbIn, 0, txAudio, 1); AudioAmplifier usbOutAmp; //xy=658,99
AudioConnection patchCord4(usbIn, 1, txAudio, 2); AudioAmplifier lineOutAmp; //xy=659,162
AudioConnection patchCord5(rxAudio, 0, lineOut, 0); AudioAmplifier usbBypassAmp; //xy=666,225
AudioConnection patchCord6(rxAudio, 0, usbOut, 0); AudioAmplifier txOutAmp; //xy=713,301
AudioConnection patchCord7(rxAudio, 0, usbOut, 1); AudioOutputI2S lineOut; //xy=876,294
AudioConnection patchCord8(txAudio, 0, lineOut, 1); AudioOutputUSB usbOut; //xy=878,255
AudioControlSGTL5000 audioCtrl; //xy=651.9371643066406,1244.9600830078125 AudioConnection patchCord1(usbIn, 0, txAudio, 1);
AudioConnection patchCord2(lineIn, 0, rxAudio, 0);
AudioConnection patchCord3(lineIn, 1, txAudio, 0);
AudioConnection patchCord4(tone1, 0, txAudio, 2);
AudioConnection patchCord5(tone1, 0, rxAudio, 2);
AudioConnection patchCord6(tone2, 0, txAudio, 3);
AudioConnection patchCord7(tone2, 0, rxAudio, 3);
AudioConnection patchCord8(rxAudio, rxFilter);
AudioConnection patchCord9(rxAudio, usbBypassAmp);
AudioConnection patchCord10(txAudio, txVoxLevel);
AudioConnection patchCord11(txAudio, txOutAmp);
AudioConnection patchCord12(rxFilter, usbOutAmp);
AudioConnection patchCord13(rxFilter, lineOutAmp);
AudioConnection patchCord14(usbOutAmp, 0, usbOut, 0);
AudioConnection patchCord15(lineOutAmp, 0, lineOut, 0);
AudioConnection patchCord16(usbBypassAmp, 0, usbOut, 1);
AudioConnection patchCord17(txOutAmp, 0, lineOut, 1);
AudioControlSGTL5000 audioCtrl; //xy=337,440
// GUItool: end automatically generated code // GUItool: end automatically generated code
//} audio;
void UBitxDSP::begin() { void UBitxDSP::begin() {
AudioMemory(16);
// Basic audio setup
AudioMemory(16); // TODO: optimize this
audioCtrl.enable(); audioCtrl.enable();
audioCtrl.volume(0.0); // headphone volume... audioCtrl.volume(0.0); // headphone volume...
audioCtrl.muteHeadphone(); // ...not used by UBitxDSP audioCtrl.muteHeadphone(); // ...not used by UBitxDSP
for (int i = 0; i < 4; i++) { setupRxAudio();
if (i == RX_AUDIO_CH) setupTxAudio();
// Default to RX.
muteRxIn(); // redundant?
muteTxIn(); // redundant?
isTx = true; // so that rx() call works
rx();
// Setup the VOX - TBD
// Setup the RX Filter.
setRxFilter(300.0, 3000.0);
sinceLastUpdate = 0;
}
void UBitxDSP::update() {
// Update the USB volume (level of TX USB output) periodically.
if (sinceLastUpdate > DSP_MILLIS_PER_UPDATE) {
float vol = usbIn.volume();
if (vol != usbVol) {
setTxInLevel(TX_USB, vol);
usbVol = vol;
}
sinceLastUpdate = 0;
}
}
void UBitxDSP::end() {
bypassRxFilter();
}
/**********************************************************************
* Transmit/Receive switching
**********************************************************************/
/*!
* Return to receive (RX) mode from transmit (TX) mode.
* First the transmit audio output is muted, to ensure that no more
* audio goes to the rig. Then we check to see if the latched TX audio
* source was different than the selected TX audio source; this happens
* if the radio is currently set for a particular input (which
* determines what is monitored by the VOX), but then is commanded to
* transmit a different source (e.g. based on a CAT command). The
* actual transmit audio source is latched during transmit, but upon
* returning to receive, we restore the selected transmit audio.
*/
void UBitxDSP::rx() {
if (isTx) {
muteTxOut();
if (txSrcLatched != txSrc) {
setTxAudioIn(txSrc);
}
if (txSrcLatched == MIC_IN) {
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
}
unmuteRxIn(RX_AUDIO);
isTx = false;
}
}
/*!
* Enter transmit (TX) mode from receive (RX) mode.
*/
void UBitxDSP::tx(TxAudioIn src) {
if (!isTx) {
muteRxIn(RX_AUDIO);
txSrcLatched = src;
if (txSrcLatched != txSrc) {
setTxAudioIn(txSrcLatched, true);
}
if (txSrcLatched == MIC_IN) {
audioCtrl.inputSelect(AUDIO_INPUT_MIC);
audioCtrl.micGain(micGain);
}
unmuteTxOut();
isTx = true;
}
}
/**********************************************************************
* General audio setup -- called via begin()
**********************************************************************/
void UBitxDSP::setupRxAudio() {
for (int i = 0; i < NUM_RX_AUDIO_CH; i++) {
if (i == RX_AUDIO)
rxAudio.gain(i, 1.0); rxAudio.gain(i, 1.0);
else else
rxAudio.gain(i, 0.0); rxAudio.gain(i, 0.0);
} }
for (int i = 0; i < 4; i++) {
txAudio.gain(i, 0.0);
}
// SETUP THE AUDIO INPUTS
// Rig (Line) Input (RX) // Rig (Line) Input (RX)
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN); audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
audioCtrl.unmuteLineout(); audioCtrl.unmuteLineout();
audioCtrl.lineInLevel(9, 5); // RX, TX audioCtrl.lineInLevel(9, 5); // RX, TX
audioCtrl.lineOutLevel(29, 31); //RX, TX audioCtrl.lineOutLevel(29, 31); // RX, TX
// Line Output (RX)
setLineOutLevel(1.0);
// USB Output (RX)
setUSBOutLevel(1.0);
}
void UBitxDSP::setupTxAudio() {
for (int i = 0; i < NUM_TX_AUDIO_CH; i++) {
txAudio.gain(i, 0.0);
}
// Mic Input (TX) // Mic Input (TX)
audioCtrl.micGain(0); // TODO: set value audioCtrl.micGain(0); // TODO: set value
// Line Input (TX) // Line Input (TX)
// USB Input (TX) // USB Input (TX)
// SETUP THE AUDIO OUTPUTS
// Line Output (RX)
// USB Output (RX)
// Rig (Line) Output (TX) // Rig (Line) Output (TX)
txOutAmp.gain(1.0);
// Default to RX. tone1.amplitude(1.0); // TODO - just do this once.
rx(); tone1.frequency(1500); // TODO: Make this dynamic based on CW (sidetone freq) versus data (1500 Hz)
tone1.amplitude(1.0); // TODO - just do this once.
tone1.amplitude(1.0); // TODO - just do this once.
tone1.frequency(700);
tone2.frequency(1900);
} }
void UBitxDSP::update() { /**********************************************************************
* Receive audio chain
**********************************************************************/
void UBitxDSP::setRxInLevel(RxAudioCh ch, float level) {
if (ch < NUM_RX_AUDIO_CH) {
state.rxIn[ch].level = level;
rxAudio.gain(ch, state.rxIn[ch].mute ? 0.0 : state.rxIn[ch].level);
}
} }
void UBitxDSP::end() { void UBitxDSP::muteRxIn() {
for (int i = 0; i < NUM_RX_AUDIO_CH; i++) {
state.rxIn[RxAudioCh(i)].mute = true;
rxAudio.gain(i, 0.0);
}
} }
void UBitxDSP::rx() { void UBitxDSP::muteRxIn(RxAudioCh ch) {
// mute all tx audio if (ch < NUM_RX_AUDIO_CH) {
audioCtrl.micGain(0); if (!state.rxIn[ch].mute) {
for (int i = 0; i < 4; i++) { state.rxIn[ch].mute = true;
rxAudio.gain(ch, 0.0);
}
}
}
void UBitxDSP::unmuteRxIn(RxAudioCh ch) {
if (ch < NUM_RX_AUDIO_CH) {
if (state.rxIn[ch].mute) {
state.rxIn[ch].mute = false;
rxAudio.gain(ch, state.rxIn[ch].level);
}
}
}
void UBitxDSP::setLineOutLevel(float level) {
state.rxOut[LINE_OUT].level = level;
lineOutAmp.gain(state.rxOut[LINE_OUT].mute ? 0.0 : state.rxOut[LINE_OUT].level);
}
void UBitxDSP::setUSBOutLevel(float level) {
state.rxOut[USB_OUT].level = level;
usbOutAmp.gain(state.rxOut[USB_OUT].mute ? 0.0 : state.rxOut[USB_OUT].level);
usbBypassAmp.gain(state.rxOut[USB_OUT].mute ? 0.0 : state.rxOut[USB_OUT].level);
}
/**********************************************************************
* Transmit audio chain
**********************************************************************/
void UBitxDSP::setTxInLevel(TxAudioCh ch, float level) {
if (ch < NUM_TX_AUDIO_CH) {
state.txIn[ch].level = level;
txAudio.gain(ch, state.txIn[ch].mute ? 0.0 : state.txIn[ch].level);
}
}
void UBitxDSP::muteTxIn() {
for (int i = 0; i < NUM_TX_AUDIO_CH; i++) {
state.txIn[TxAudioCh(i)].mute = true;
txAudio.gain(i, 0.0); txAudio.gain(i, 0.0);
} }
// select line in for rx
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
// restore rx audio
rxAudio.gain(RX_AUDIO_CH, 1.0); // restore the RX audio
} }
void UBitxDSP::txMicIn() { void UBitxDSP::muteTxIn(TxAudioCh ch) {
// mute the rx audio if (ch < NUM_TX_AUDIO_CH) {
rxAudio.gain(RX_AUDIO_CH, 0.0); if (!state.txIn[ch].mute) {
// restore the tx mic audio state.txIn[ch].mute = true;
audioCtrl.inputSelect(AUDIO_INPUT_MIC); txAudio.gain(ch, 0.0);
audioCtrl.micGain(12); }
for (int i = 0; i < 4; i++) {
if (i == TX_MIC_IN_CH)
txAudio.gain(i, 0.1);
else
txAudio.gain(i, 0.0);
} }
} }
void UBitxDSP::txLineIn() { void UBitxDSP::unmuteTxIn(TxAudioCh ch) {
// mute the rx audio if (ch < NUM_TX_AUDIO_CH) {
rxAudio.gain(RX_AUDIO_CH, 0.0); if (state.txIn[ch].mute) {
// restore the tx line in audio state.txIn[ch].mute = false;
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN); rxAudio.gain(ch, state.txIn[ch].level);
for (int i = 0; i < 4; i++) { }
if (i == TX_LINE_IN_CH)
txAudio.gain(i, 0.1);
else
txAudio.gain(i, 0.0);
} }
} }
void UBitxDSP::txUSBIn() { void UBitxDSP::setTxOutLevel(float level) {
// mute the rx audio state.txOut.level = level;
rxAudio.gain(RX_AUDIO_CH, 0.0); txOutAmp.gain(state.txOut.mute ? 0.0 : state.txOut.level);
// restore the tx usb in audio }
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
for (int i = 0; i < 4; i++) { void UBitxDSP::muteTxOut() {
if (i == TX_USB_IN_CH1 || i == TX_USB_IN_CH2) if (!state.txOut.mute) {
txAudio.gain(i, 0.1); state.txOut.mute = true;
else txOutAmp.gain(0.0);
txAudio.gain(i, 0.0);
} }
} }
void UBitxDSP::unmuteTxOut() {
if (state.txOut.mute) {
state.txOut.mute = false;
txOutAmp.gain(state.txOut.level);
}
}
void UBitxDSP::setLineInLevel(float level) {
state.txIn[TX_LINE].level = level;
txAudio.gain(TX_LINE, state.txIn[TX_LINE].mute ? 0.0 : state.txIn[TX_LINE].level);
}
void UBitxDSP::setUSBInLevel(float level) {
state.txIn[TX_USB].level = level;
txAudio.gain(TX_USB, state.txIn[TX_USB].mute ? 0.0 : state.txIn[TX_USB].level);
}
void UBitxDSP::setTxAudioIn(TxAudioIn src, bool isTemp) {
if (!isTemp) {
txSrc = src;
}
if (!isTx) {
muteTxIn(); // Mute all channels, then unmute the desired ones.
switch (src) { // Don't switch inputs while transmitting.
case MIC_IN:
// Note that we can't actually use the VOX code on the mic input,
// because we can't make the actual mic input active without
// losing our receive audio. So, mic input is never actually
// selected until it is time for it to transmit, which makes the
// VOX moot. The caller must make use of an external, analog VOX
// circuit driving a GPIO pin, or something similar (or the PTT of
// course) to begin actually using the mic input. So this case
// just falls through to the line input.
case LINE_IN:
unmuteTxIn(TX_LINE);
break;
case USB_IN:
unmuteTxIn(TX_USB);
break;
case TUNE_IN:
tone1.amplitude(1.0); // TODO - just do this once.
tone1.frequency(1500); // TODO: Make this dynamic based on CW (sidetone freq) versus data (1500 Hz)
unmuteTxIn(TX_TONE1);
break;
case TWO_TONE_IN:
tone1.amplitude(1.0); // TODO - just do this once.
tone1.amplitude(1.0); // TODO - just do this once.
tone1.frequency(700);
tone2.frequency(1900);
unmuteTxIn(TX_TONE1);
unmuteTxIn(TX_TONE2);
break;
default:
// should never happen
break;
}
}
}
/**********************************************************************
* Receive audio filter (band pass)
**********************************************************************/
const int minRxFilterLo = MIN_RX_FILTER_LO;
const int maxRxFilterHi = MAX_RX_FILTER_HI;
const int minRxFilterWidth = MIN_RX_FILTER_WIDTH;
const int maxRxFilterWidth = MAX_RX_FILTER_WIDTH;
const int minRxFilterCenter = MIN_RX_FILTER_CENTER;
const int 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(int lo, int 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(int lo) {
if (lo > state.rxFilterHi - minRxFilterWidth) {
lo = state.rxFilterHi - minRxFilterWidth;
}
if (lo < minRxFilterLo) {
lo = minRxFilterLo;
}
state.rxFilterLo = lo;
updateRxFilter();
}
void UBitxDSP::setRxFilterHi(int hi) {
if (hi < state.rxFilterLo + minRxFilterWidth) {
hi = state.rxFilterLo + minRxFilterWidth;
}
if (hi > maxRxFilterHi) {
hi = maxRxFilterHi;
}
state.rxFilterHi = hi;
updateRxFilter();
}
void UBitxDSP::setRxFilterWidth(int width) {
if (width < minRxFilterWidth) {
width = minRxFilterWidth;
} else if (width > maxRxFilterWidth) {
width = maxRxFilterWidth;
}
int center = (state.rxFilterHi + state.rxFilterLo) / 2;
int lo = center - (width / 2);
int hi = center + (width / 2);
setRxFilter(lo, hi);
}
void UBitxDSP::setRxFilterCenter(int center) {
if (center < minRxFilterCenter) {
center = minRxFilterCenter;
} else if (center > maxRxFilterCenter) {
center = maxRxFilterCenter;
}
int width = state.rxFilterHi - state.rxFilterLo;
int lo = center - (width / 2);
int hi = center + (width / 2);
setRxFilter(lo, hi);
}
/**********************************************************************
* Transmit Voice-Operated-Switch (VOX)
**********************************************************************/
float UBitxDSP::getVoxLevel() const {
if (return txVoxLevel.available()) {
prevVox = txVoxLevel.read();
}
return prevVox;
}
/**********************************************************************
* Singleton - the DSP instance
**********************************************************************/
// TODO: Fix this. This won't work... this compilation unit won't be
// able to instantiate a class it doesn't know about.
#ifndef UBITXDSP_CLASS
#define UBITXDSP_CLASS UBitxDSP
#endif
UBITXDSP_CLASS theDSP;
UBitxDSP& DSP = theDSP;
/*
NOTES
Major functions:
- tx() - start transmitting / pause receiving
- rx() - stop transmitting / resume receiving
- setTxSource() - set the TX audio source to MIC_IN, LINE_IN, or USB_IN
- also sets the relevant VOX source/parameters (as applicable)
Receive audio chain:
-
*/
//====================================================================== //======================================================================
// EOF // EOF
//====================================================================== //======================================================================

View File

@@ -5,36 +5,276 @@
#ifndef __DSP_h__ #ifndef __DSP_h__
#define __DSP_h__ #define __DSP_h__
#include <Audio.h>
#include <dynamicFilters.h>
#include "Debug.h" #include "Debug.h"
enum TRState { /**********************************************************************
TRANSMIT, * Macros
RECEIVE **********************************************************************/
#define MIN_RX_FILTER_LO (0.0) //! Min allowable value of the RX filter low-cut frequency
#define MAX_RX_FILTER_HI (5000.0) //! Max allowable value of the RX filter hi-cut frequency
#define MIN_RX_FILTER_WIDTH (0.0) //! Min allowable value of the RX filter bandwidth
#define MAX_RX_FILTER_WIDTH (5000.0) //! Max allowable value of the RX filter bandwidth
#define MIN_RX_FILTER_CENTER (0.0) //! Min allowable value of the RX filter center frequency
#define MAX_RX_FILTER_CENTER (5000.0) //! Max allowable value of the RX filter center frequency
#define DSP_MILLIS_PER_UPDATE (100) //! Number of milliseconds between update of the DSP object
#define TX_VOX_MIC_THRESH (0.0) //! Threshold for mic VOX (not implemented, since mic requires special handling)
#define TX_VOX_LINE_THRESH (0.25) //! Threshold for line in VOX
#define TX_VOX_USB_THRESH (0.25) //! Threshold for USB VOX
#define TX_VOX_TUNE_THRESH (0.0) //! Threshold for tune (single tone) VOX (not expected to be used)
#define TX_VOX_TT_THRESH (0.0) //! Threshold for two-tone VOX (not expected to be used)
#define TX_VOX_DELAY (500) //! VOX delay in milliseconds
/**********************************************************************
* Enumerations
**********************************************************************/
//! Defines the four separate RX audio input channels available.
enum RxAudioCh {
RX_AUDIO = 0, // Normal receiver audio input channel
RX_SPARE, // Not used
RX_TONE1 , // Optional tone #1 input channel (currently not used)
RX_TONE2, // Optional tone #2 input channel (currently not used)
NUM_RX_AUDIO_CH // Total number of channels
}; };
//! Defines the different RX audio inputs (not channels).
enum RxAudioIn { enum RxAudioIn {
RIG_AUDIO RIG_IN = 0, // Normal rig input (receiver audio)
NUM_RX_AUDIO_IN // Total number of inputs
}; };
//! Defines the different RX audio outputs.
enum RxAudioOut {
LINE_OUT = 0, // Line audio out (and speaker)
USB_OUT, // USB audio out
NUM_RX_AUDIO_OUT
};
//! Defines the four separate TX audio input channels available.
enum TxAudioCh {
TX_LINE = 0, // Line and/or mic audio input channel
TX_USB, // USB audio input channel
TX_TONE1, // Audio tone #1 input channel
TX_TONE2, // Audio tone #2 input channel
NUM_TX_AUDIO_CH // Toal number of channels
};
//! Defines the different TX audio input sources (not channels!).
enum TxAudioIn { enum TxAudioIn {
MIC_IN, MIC_IN = 0, // Microphone transmit audio input
LINE_IN, LINE_IN, // Line ("AUX") transmit audio input
USB_IN USB_IN, // USB transmit audio input
TUNE_IN, // Tune input (transmits a single tone)
TWO_TONE_IN, // Two tone audio input (transmits two tones)
NUM_TX_AUDIO_IN // Total number of inputs
}; };
/**********************************************************************
* Classes
**********************************************************************/
//! Defines parameters for a simple audio channel that can be muted.
struct AudioChannel {
bool mute = false;
float level = 0.0;
};
/*!
* Contains the current 'persistent' state of the DSP.
* This includes all audio-specific state that can be saved to, or
* restored from, EEPROM. It does not include 'transient' state (such
* as whether we're currently transmitting or receiving).
*/
struct DSPState {
//! Receiver audio inputs; all default to muted.
AudioChannel rxIn[NUM_RX_AUDIO_CH] = {
{true, 1.0}, // audio
{true, 0.0}, // spare 1
{true, 0.0}, // spare 2
{true, 0.0} // spare 3
};
//! Receiver audio output; defaults to un
muted.
AudioChannel rxOut[NUM_RX_AUDIO_OUT] = {
{false, 1.0}, // line
{false, 1.0} // USB
};
//! Transmitter audio inputs; all default to muted.
AudioChannel txIn[NUM_TX_AUDIO_CH] = {
{true, 0.1}, // line
{true, 0.1}, // USB
{true, 0.1}, // tone 1
{true, 0.1} // tone 2
};
//! Tranmitter audio output; defaults to muted.
AudioChannel txOut = {true, 1.0};
//! Current RX filter settings
float rxFilterLo = 300.0;
float rxFilterHi = 3000.0;
};
/*!
* Defines the DSP subsystem of the UBitx V5X.
* The DSP subsystem, which relies on the Teensy Audio Library, is
* responsible for setting up the audio inputs and outputs for both
* receive (RX) and transmit (TX) audio, maintaining the correct path
* between inputs and outputs based on current TX/RX state, and setting
* up audio filters and other audio-based modules for the RX and TX
* audio paths.
*/
class UBitxDSP { class UBitxDSP {
/********************************************************************
* Object creation/deletion
********************************************************************/
public:
UBitxDSP() {}
/********************************************************************
* Basic administration
********************************************************************/
public: public:
UBitxDSP() {};
void begin(); void begin();
void update(); void update();
void end(); void end();
/********************************************************************
* Transmit/Receive switching
********************************************************************/
public:
void rx(); void rx();
void txMicIn(); inline void tx() { tx(txSrc); }
void txLineIn(); void tx(TxAudioIn src);
void txUSBIn();
/********************************************************************
* General audio setup -- called via begin()
********************************************************************/
protected:
virtual void setupRxAudio();
virtual void setupTxAudio();
/********************************************************************
* Receive audio chain
********************************************************************/
// Basic control of RX audio inputs and outputs.
public:
void setRxInLevel(RxAudioCh ch, float level); // Set the audio input level for a given channel.
void muteRxIn(); // Mute all RX audio input channels.
void muteRxIn(RxAudioCh ch); // Mute a specific RX audio input channel.
void unmuteRxIn(RxAudioCh ch); // Un-mute a specific RX audio input channel.
void setLineOutLevel(float level); // Set the line output level (0.0 - 1.0).
void setUSBOutLevel(float level); // Set the USB output level (0.0 - 1.0).
/********************************************************************
* Transmit audio chain
********************************************************************/
// Basic control of TX audio inputs and outputs.
public:
void setTxInLevel(TxAudioCh ch, float level); // Set the audio input level for a given channel.
void muteTxIn(); // Mute all TX audio input channels.
void muteTxIn(TxAudioCh ch); // Mute a specific TX audio input channel.
void unmuteTxIn(TxAudioCh ch); // Un-mute a specific TX audio input channel.
void setTxOutLevel(float level); // Set the TX audio output level.
void muteTxOut(); // Mute the TX audio output.
void unmuteTxOut(); // Un-mute the TX audio output.
void setLineInLevel(float level); // Set the line input level (0.0 - 1.0).
void setUSBInLevel(float level); // Set the USB input level (0.0 - 1.0).
// Transmit audio selection (may be overriden at actual transmit time).
public:
void setTxAudioIn(TxAudioIn src, bool isTemp = false); // Select a specific TX audio input path, and identify it as permanent or temporary.
inline TxAudioIn getTxAudioIn() const { return txSrc; } // Return the current TX audio input.
// Mic input controls.
public:
inline void setMicGain(float level) { micGain = static_cast<unsigned>(level * 63.0); } // Set the mic gain.
/********************************************************************
* Receive audio filter (band pass)
********************************************************************/
public:
void bypassRxFilter();
void updateRxFilter();
void setRxFilter(float lo, float hi);
void setRxFilterLo(float lo);
void setRxFilterHi(float hi);
void setRxFilterWidth(float width);
void setRxFilterCenter(float center);
/*!
* Get the current low frequency bound of the RX band pass filter.
* @return The low frequency bound.
*/
inline float getRxFilterLo() const { return state.rxFilterLo; }
/*!
* Get the current high frequency bound of the RX band pass filter.
* @return The high frequency bound.
*/
inline float getRxFilterHi() const { return state.rxFilterHi; }
/*!
* Get the current width of the RX band pass filter.
* @return The filter width.
*/
inline float getRxFilterWidth() const { return state.rxFilterHi - state.rxFilterLo; }
/*!
* Get the current center frequency of the RX band pass filter.
* @return The center frequency.
*/
inline float getRxFilterCenter() const { return (state.rxFilterHi + state.rxFilterLo) / 2.0; }
/********************************************************************
* Transmit Voice-Operated-Switch (VOX)
********************************************************************/
public:
float getVoxLevel() const;
/********************************************************************
* Private state
********************************************************************/
private:
DSPState state;
bool isTx = false;
TxAudioIn txSrc = MIC_IN;
TxAudioIn txSrcLatched = MIC_IN;
short coefficients[NUM_COEFFICIENTS] = {0};
elapsedMillis sinceLastUpdate = 0;
float usbVol = 0.0;
unsigned micGain = 0;
float prevVox = 0.0;
}; };
extern UBitxDSP DSP; extern UBitxDSP& DSP;
#endif #endif

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

@@ -3,143 +3,69 @@
#include "RigState.h" #include "RigState.h"
enum UpdateSource { struct RigState {
NoSource,
RaduinoSource,
CATSource
}; };
class UBitxRig { class UBitxRig {
public: public:
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 int getRIT() const { return radState.getRIT(); }
inline int getXIT() const { return radState.getXIT(); }
inline bool isVFOA() const { return radState.isVFOA(); }
inline bool isVFOB() const { return radState.isVFOB(); }
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 bool isAI() const { return autoInfo; }
inline void setFreqA(unsigned long freq, bool isCAT = false) { inline void setFreqA(unsigned freq) { catState.setFreqA(freq); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline void setFreqB(unsigned freq) { catState.setFreqB(freq); }
state.vfo[0] = freq; inline void setRIT(int freq) { catState.setRIT(freq); }
} inline void setXIT(int freq) { catState.setXIT(freq); }
inline void setVFOA() { catState.setVFOA(); }
inline void setFreqB(unsigned long freq, bool isCAT = false) { inline void setVFOB() { catState.setVFOB(); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline void setSplitOn() { catState.setSplitOn(); }
state.vfo[1] = freq; inline void setSplitOff() { catState.setSplitOff(); }
} inline void setRITOn() { catState.setRITOn(); }
inline void setRITOff() { catState.setRITOff(); }
inline void setRIT(short offset, bool isCAT = false) { inline void setXITOn() { catState.setXITOn(); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource; inline void setXITOff() { catState.setXITOff(); }
state.rit = offset; inline void setCW() { catState.setCW(); }
} inline void setCWR() { catState.setCWR(); }
inline void setUSB() { catState.setUSB(); }
inline void setXIT(short offset, bool isCAT = false) { inline void setLSB() { catState.setLSB(); }
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.xit = offset;
}
inline void selectVFOA(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_VFOB_FLAG;
}
inline void selectVFOB(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_VFOB_FLAG;
}
inline void toggleVFO(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags ^= UBITX_VFOB_FLAG;
}
inline void splitOn(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_SPLIT_FLAG;
}
inline void splitOff(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_SPLIT_FLAG;
}
inline void ritOn(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
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 setAI(bool on) { autoInfo = on; }
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:
bool autoInfo = false; UBitxRigState catState;
UpdateSource lastUpdatedBy = NoSource; UBitxRigState radState;
UBitxRigState state; bool autoInfo = false; // TODO: Move this to rig state struct
}; };
extern UBitxRig Rig; extern UBitxRig Rig;

416
TeensyDSP/RigState.cpp Normal file
View File

@@ -0,0 +1,416 @@
/*!
* @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;
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
}
// VFO A/B selection
if (isDirty(FLAGS_WORD)) {
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);
}
}
}
/*!
* @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) {
setCW();
} else if (cwMode == 1) {
setCWR();
} else {
if (isUSB) {
setUSB();
} else {
setLSB();
}
}
}
}
/***********************************************************************
* 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++;
}
}
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.
//--------------------------------------------------------------------
}
#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,322 @@
#define UBITX_USB_FLAG 0x00000020 #define UBITX_USB_FLAG 0x00000020
#define UBITX_TX_FLAG 0x00000040 #define UBITX_TX_FLAG 0x00000040
#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,
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; void begin();
int32_t xit;
uint32_t flags = 0; void send_RIGINF();
void receive_RIGINF(int numBytes = sizeof(data));
/*!
* @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;
DISABLEINTS( dirty = ((1 << w) & data[DIRTY_WORD]) > 0 ? true : false );
return dirty;
}
/*!
* @brief Check whether the data is dirty (as a whole).
*
* @return True if the data is dirty (at least one dirty field).
*/
inline bool isDirty() {
bool dirty;
DISABLEINTS( dirty = data[DIRTY_WORD] != 0 );
return dirty;
}
/*!
* @brief Set the VFO A frequency.
*
* @param freq
* The new frequency in Hz.
*/
inline void setFreqA(uint32_t freq, bool mark = true) {
DISABLEINTS( data[VFOA_WORD] = freq;
if (mark) setDirty(VFOA_WORD) );
}
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 setUSB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setLSB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setCW(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
data[FLAGS_WORD] |= UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setCWR(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;
}
#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
#endif
}; };
/**********************************************************************/ #ifndef TEENSYDUINO
template<typename T, int ID> extern UBitxRigState& rigState;
struct Field {
byte id = ID;
bool dirty;
T data;
inline size_t sizeOfWrite() { return dirty ? sizeof(byte) + sizeof(T) : 0; } #endif
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;
byte* ptr = (byte*)&data;
while (STREAM().available() && len < sizeof(T)) {
ptr[len++] = STREAM().read();
}
return len;
}
inline void merge(Field<T,ID>& f) {
if (dirty) {
f.data = data;
f.dirty = true;
} else if (f.dirty) {
data = f.data;
dirty = true;
}
}
inline void markClean() { dirty = false; }
};
struct RigState {
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() {
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() {
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 +366,7 @@ TeensyDSP state:
*/ */
#endif #endif
/***********************************************************************
* EOF
**********************************************************************/

View File

@@ -8,7 +8,7 @@
UBitxTR TR(DSP); UBitxTR TR(DSP);
void UBitxTR::update(bool cw, bool extKey) { void UBitxTR::update(bool cw, bool extKey) {
updateKey(); updateLinePTT();
if (cw) { if (cw) {
if ((keyEnable && keyDown) || extKey) { if ((keyEnable && keyDown) || extKey) {
@@ -19,33 +19,35 @@ void UBitxTR::update(bool cw, bool extKey) {
return; return;
} }
updatePTT(); updateMicPTT();
updateVOX(); updateMicVOX();
updateDataVOX();
if (isTX) { if (isTX) {
// If we are currently transmitting, then ANY T/R release (key // If we are currently transmitting, then ANY T/R release (key
// release) will result in exitting transmit... except for VOX // release) will result in exiting transmit... except for VOX
// and CAT which can only function as a release if it was enabled. // and CAT which can only function as a release if it was enabled.
if (pttReleased() || keyReleased() || if (micPTTReleased() || linePTTReleased() ||
(voxEnable && voxDeactivated()) || (micVOXEnabled() && micVOXDeactivated()) ||
(catEnable && catDeactivated())) { (catEnabled() && catDeactivated()) ||
(dataVOXEnabled() && dataVOXDeactivated())) {
// first, stop transmitting; then, setup RX audio // first, stop transmitting; then, setup RX audio
DBGCMD( setRX() ); DBGCMD( setRX() );
DBGCMD( dsp.rx() ); DBGCMD( dsp.rx() );
} }
} else { } else {
if ((pttEnable && pttPressed()) || (voxEnable && voxActivated())) { if ((micPTTEnabled() && micPTTPressed()) || (micVOXEnabled() && micVOXActivated())) {
// first, setup TX audio; then, start transmitting (from Mic) // first, setup TX audio; then, start transmitting (from Mic)
DBGCMD( dsp.txMicIn() ); DBGCMD( dsp.tx(MIC_IN) );
DBGCMD( setTX() ); DBGCMD( setTX() );
} else if (keyEnable && keyPressed()) { } else if ((linePTTEnabled() && linePTTPressed())) {
// first, setup TX audio; then, start transmitting (from Line In) // first, setup TX audio; then, start transmitting (from Line In)
DBGCMD( dsp.txLineIn() ); DBGCMD( dsp.tx(LINE_IN) );
DBGCMD( setTX() ); DBGCMD( setTX() );
} else if (catEnable && catActivated()) { } else if (catEnable && catActivated()) {
// first, setup TX audio; then, start transmitting (USB) // first, setup TX audio; then, start transmitting (USB)
DBGCMD( dsp.txUSBIn() ); DBGCMD( dsp.tx(USB_IN) );
DBGCMD( setTX() ); DBGCMD( setTX() );
} }
} }
} }

View File

@@ -32,34 +32,40 @@ class UBitxTR {
ptt.interval(5); ptt.interval(5);
// default configuration: PTT, key, and CAT enabled; VOX disabled // default configuration: PTT, key, and CAT enabled; VOX disabled
DBGCMD( enablePTT() ); DBGCMD( enableMicPTT() );
DBGCMD( disableVOX() ); DBGCMD( disableMicVOX() );
DBGCMD( enableKey() ); DBGCMD( enableLinePTT() );
DBGCMD( enableCAT() ); DBGCMD( enableCAT() );
DBGCMD( setRX() ); DBGCMD( setRX() );
} }
inline void enablePTT() { pttEnable = true; } inline void enableMicPTT() { pttEnable = true; }
inline void enableVOX() { voxEnable = true; } inline void enableLinePTT() { keyEnable = true; }
inline void enableKey() { keyEnable = true; } inline void enableMicVOX() { voxEnable = true; }
inline void enableDataVOX() { dvoxEnable = true; }
inline void enableCAT() { catEnable = true; } inline void enableCAT() { catEnable = true; }
inline void disablePTT() { pttEnable = false; }
inline void disableVOX() { voxEnable = false; } inline void disableMicPTT() { pttEnable = false; }
inline void disableKey() { keyEnable = false; } inline void disableLinePTT() { keyEnable = false; }
inline void disableMicVOX() { voxEnable = false; }
inline void disableDataVOX() { dvoxEnable = false; }
inline void disableCAT() { catEnable = false; } inline void disableCAT() { catEnable = false; }
inline bool pttEnabled() { return pttEnable; } inline bool micPTTEnabled() const { return pttEnable; }
inline bool voxEnabled() { return voxEnable; } inline bool linePTTEnabled() const { return keyEnable; }
inline bool keyEnabled() { return keyEnable; } inline bool micVOXEnabled() const { return voxEnable; }
inline bool catEnabled() { return catEnable; } inline bool dataVOXEnabled() const { return dvoxEnable; }
inline bool catEnabled() const { return catEnable; }
inline bool pttPressed() { return ptt.fell(); } inline bool micPTTPressed() { return ptt.fell(); }
inline bool pttReleased() { return ptt.rose(); } inline bool micPTTReleased() { return ptt.rose(); }
inline bool voxActivated() { return (L_voxActive != voxActive) && L_voxActive; } inline bool linePTTPressed() { return (L_keyDown != keyDown) && L_keyDown; }
inline bool voxDeactivated() { return (L_voxActive != voxActive) && voxActive; } inline bool linePTTReleased() { return (L_keyDown != keyDown) && keyDown; }
inline bool keyPressed() { return (L_keyDown != keyDown) && L_keyDown; } inline bool micVOXActivated() { return (L_voxActive != voxActive) && L_voxActive; }
inline bool keyReleased() { return (L_keyDown != keyDown) && keyDown; } inline bool micVOXDeactivated() { return (L_voxActive != voxActive) && voxActive; }
inline bool dataVOXActivated() { return (L_dvoxActive != dvoxActive) && L_dvoxActive; }
inline bool dataVOXDeactivated() { return (L_dvoxActive != dvoxActive) && dvoxActive; }
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; }
@@ -109,42 +115,58 @@ class UBitxTR {
digitalWrite(outPin, HIGH); digitalWrite(outPin, HIGH);
} }
inline void updatePTT() { inline void updateMicPTT() {
ptt.update(); ptt.update();
} }
inline void updateVOX() { inline void updateLinePTT() {
L_keyDown = keyDown;
keyDown = (digitalRead(keyPin) == LOW);
}
inline void updateMicVOX() {
L_voxActive = voxActive; L_voxActive = voxActive;
voxActive = (digitalRead(voxPin) == LOW); voxActive = (digitalRead(voxPin) == LOW);
} }
inline void updateKey() { inline void updateDataVOX() {
L_keyDown = keyDown; L_dvoxActive = dvoxActive;
keyDown = (digitalRead(keyPin) == LOW); if (dsp.getVoxLevel() > dvoxThreshold) {
dvoxActive = true;
dvoxElapsed = 0;
} else if (dvoxActive && (dvoxElapsed > dvoxDelay)) {
dvoxActive = false;
}
} }
UBitxDSP& dsp; UBitxDSP& dsp;
Bounce ptt; Bounce ptt;
int outPin;
int pttPin; int pttPin;
int voxPin; int voxPin;
int keyPin; int keyPin;
int outPin;
bool isTX = false; bool isTX = false;
bool pttEnable = false; bool pttEnable = false;
bool voxEnable = false; bool voxEnable = false;
bool dvoxEnable = false;
bool keyEnable = false; bool keyEnable = false;
bool catEnable = false; bool catEnable = false;
bool voxActive = false; bool voxActive = false;
bool L_voxActive = false; bool L_voxActive = false;
bool dvoxActive = false;
bool L_dvoxActive = false;
bool keyDown = false; bool keyDown = false;
bool L_keyDown = false; bool L_keyDown = false;
bool catActive = false; bool catActive = false;
bool L_catActive = false; bool L_catActive = false;
elapsedMillis dvoxElapsed = 0;
unsigned dvoxDelay = 250; // TODO: make dynamic
}; };
extern UBitxTR TR; extern UBitxTR TR;

View File

@@ -34,12 +34,9 @@ void ts590SendCommand(const char* format, ...) {
* than two are supplied, then the command will be * than two are supplied, then the command will be
* initialized with a null prefix. * initialized with a null prefix.
*/ */
TS590Command::TS590Command(const char* pre) { TS590Command::TS590Command(const char* pre)
if (strlen(pre) >= 2) { : myPrefix(pre), prefixLength(strlen(pre))
myPrefix[0] = pre[0]; {}
myPrefix[1] = pre[1];
}
}
TS590Command::~TS590Command() {} TS590Command::~TS590Command() {}
@@ -49,7 +46,7 @@ TS590Command::~TS590Command() {}
* @return True if a Read command; false otherwise. * @return True if a Read command; false otherwise.
*/ */
bool TS590Command::isReadCommand(const char* cmd) const { bool TS590Command::isReadCommand(const char* cmd) const {
if (strlen(cmd) == 2) { if (strlen(cmd) == prefixLength) {
return true; return true;
} else { } else {
return false; return false;
@@ -77,7 +74,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,22 +127,65 @@ 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;
}
UBitxRig* TS590Command::theRig = &Rig; UBitxRig* TS590Command::theRig = &Rig;
UBitxDSP* TS590Command::theDSP = &DSP;
TS590Error TS590Command::theError = NoError; TS590Error TS590Command::theError = NoError;
/**********************************************************************/ /**********************************************************************/
void TS590Command_Bool::handleCommand(const char* cmd) {
setter(cmd[length()] == '0' ? false : true);
}
void TS590Command_Bool::sendResponse(const char* cmd) {
ts590SendCommand("%s%s", prefix(), getter() ? "1" : "0");
}
/**********************************************************************/
void TS590Command_UL::handleCommand(const char* cmd) {
unsigned val = static_cast<unsigned>(strtoul(&cmd[length()], NULL, 10));
if (val < myMin) {
val = myMin;
} else if (val > myMax) {
val = myMax;
}
val = (val * mySlope) + myIntercept;
setter(val);
}
void TS590Command_UL::sendResponse(const char* cmd) {
unsigned val = getter();
val = (val - myIntercept) / mySlope;
if (val < myMin) {
val = myMin;
} else if (val > myMax) {
val = myMax;
}
ts590SendCommand("%s%0*u", prefix(), myWidth, getter());
}
/**********************************************************************/
void TS590_FR::handleCommand(const char* cmd) { 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 +217,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 +227,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 +268,19 @@ void TS590_MD::handleCommand(const char* cmd) {
break; break;
case '1': // LSB case '1': // LSB
rig()->selectLSB(true); rig()->setLSB();
rig()->cwOff(true);
break; break;
case '2': // USB case '2': // USB
rig()->selectUSB(true); rig()->setUSB();
rig()->cwOff(true);
break; break;
case '3': // CW case '3': // CW
rig()->selectUSB(true); rig()->setCW();
rig()->cwOn(true);
break; break;
case '7': // CW-R case '7': // CW-R
rig()->selectLSB(true); rig()->setCWR();
rig()->cwOn(true);
break; break;
default: default:
@@ -256,41 +292,157 @@ 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 if (rig()->isModeCWR()) {
} else { ts590SendCommand("MD7");
ts590SendCommand("MD7"); } else if (rig()->isModeUSB()) {
} ts590SendCommand("MD2");
} else if (rig()->isModeLSB()) {
ts590SendCommand("MD1");
} else { } else {
if (rig()->isUSB()) { ts590SendCommand("MD0");
ts590SendCommand("MD2");
} else {
ts590SendCommand("MD1");
}
} }
} }
/**********************************************************************/ /**********************************************************************/
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_VX::handleCommand(const char* cmd) {
}
void TS590_VX::sendResponse(const char* cmd) {
}
/**********************************************************************/
void nullSetFunc(unsigned x) { return; }
unsigned getIDFunc() {
#ifndef USE_TS590SG_CAT
return 021;
#else
return 023;
#endif
}
SetUL setAG = SetUL::create<UBitxDSP, &UBitxDSP::setLineOut255>(DSP);
GetUL getAG = GetUL::create<UBitxDSP, &UBitxDSP::getLineOut255>(DSP);
SetUL setAI = [](unsigned v) -> void { v == 0 ? Rig.aiOff() : Rig.aiOn(); };
GetUL getAI = []() -> unsigned { return Rig.isAI() ? 4 : 0; };
//SetUL setSidetone = SetUL::create<UBitxDSP, ...>(...);
//GetUL getSidetone = GetUL::create<UBitxDSP, ...>(...);
SetUL setUSBin = SetUL::create<UBitxDSP, &UBitxDSP::setUSBIn9>(DSP);
GetUL getUSBin = GetUL::create<UBitxDSP, &UBitxDSP::getUSBIn9>(DSP);
SetUL setUSBout = SetUL::create<UBitxDSP, &UBitxDSP::setUSBOut9>(DSP);
GetUL getUSBout = GetUL::create<UBitxDSP, &UBitxDSP::getUSBOut9>(DSP);
SetUL setACC2in = SetUL::create<UBitxDSP, &UBitxDSP::setLineIn9>(DSP);
GetUL getACC2in = GetUL::create<UBitxDSP, &UBitxDSP::getLineIn9>(DSP);
SetUL setACC2out = SetUL::create<UBitxDSP, &UBitxDSP::setLineOut9>(DSP);
GetUL getACC2out = GetUL::create<UBitxDSP, &UBitxDSP::getLineOut9>(DSP);
SetUL setVoxDelay = SetUL::create<UBitxDSP, &UBitxDSP::setDataVoxDelay>(DSP);
GetUL getVoxDelay = GetUL::create<UBitxDSP, &UBitxDSP::getDataVoxDelay>(DSP);
SetUL setUSBvox = SetUL::create<UBitxDSP, &UBitxDSP::setUSBVOXThresh9>(DSP);
GetUL getUSBvox = GetUL::create<UBitxDSP, &UBitxDSP::getUSBVOXThresh9>(DSP);
SetUL setACC2vox = SetUL::create<UBitxDSP, &UBitxDSP::setLineVOXThresh9>(DSP);
GetUL getACC2vox = GetUL::create<UBitxDSP, &UBitxDSP::getLineVOXThresh9>(DSP);
SetUL setID = SetUL::create<nullSetFunc>();
GetUL getID = GetUL::create<getIDFunc>();
TS590Command_UL TS590_AG("AG0", 3, 0, 255, setAG, getAG);
TS590Command_UL TS590_AI("AI", 1, 0, 4, setAI, getAI);
// TS590_AS
// TS590_BD
// TS590_BU
// TS590_CA
// TS590_CD0
// TS590_CD1
// TS590_CD2
// TS590_CH
#ifndef USE_TS590SG_CAT
//TS590Command_UL TS590_EX034("EX0340000", 2, 0, 14, 50, 300, setSideTone, getSideTone);
TS590Command_UL TS590_EX064("EX0640000", 1, 0, 9, setUSBin, getUSBin);
TS590Command_UL TS590_EX065("EX0650000", 1, 0, 9, setUSBout, getUSBout);
TS590Command_UL TS590_EX066("EX0660000", 1, 0, 9, setACC2in, getACC2in);
TS590Command_UL TS590_EX067("EX0670000", 1, 0, 9, setACC2out, getACC2out);
TS590Command_UL TS590_EX070("EX0700000", 2, 0, 20, 5, 0, setVoxDelay, getVoxDelay);
TS590Command_UL TS590_EX071("EX0710000", 1, 0, 9, setUSBvox, getUSBvox);
TS590Command_UL TS590_EX072("EX0720000", 1, 0, 9, setACC2vox, getACC2vox);
#else
//TS590Command_UL TS590_EX040("EX0400000", 2, 0, 14, 50, 300, setSideTone, getSideTone);
TS590Command_UL TS590_EX071("EX0710000", 1, 0, 9, setUSBin, getUSBin);
TS590Command_UL TS590_EX072("EX0720000", 1, 0, 9, setUSBout, getUSBout);
TS590Command_UL TS590_EX073("EX0730000", 1, 0, 9, setACC2in, getACC2in);
TS590Command_UL TS590_EX074("EX0740000", 1, 0, 9, setACC2out, getACC2out);
TS590Command_UL TS590_EX077("EX0770000", 2, 0, 20, 5, 0, setVoxDelay, getVoxDelay);
TS590Command_UL TS590_EX078("EX0780000", 1, 0, 9, setUSBvox, getUSBvox);
TS590Command_UL TS590_EX079("EX0790000", 1, 0, 9, setACC2vox, getACC2vox);
#endif
TS590Command_UL TS590_ID("ID", 3, 21, 23, setID, getID);
TS590_FA cmdFA; TS590_FA cmdFA;
TS590_FB cmdFB; 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]);
/**********************************************************************/ /**********************************************************************/
void UBitxTS590::begin() { void UBitxTS590::begin() {
Serial.begin(9600); // USB is always 12 Mbit/sec Serial.begin(9600); // USB is always 12 Mbit/sec
#ifdef DEBUG #ifdef DEBUG
@@ -331,7 +483,7 @@ typedef class TS590Command* PCmd;
int compareCATCommands(const void* a, const void* b) { int compareCATCommands(const void* a, const void* b) {
TS590Command const *B = *(TS590Command const **)b; TS590Command const *B = *(TS590Command const **)b;
int cmp = strncmp((char*)a, (char*)B->prefix(), 2); int cmp = strncmp((char*)a, (char*)B->prefix(), B->length());
#ifdef DEBUG #ifdef DEBUG
Serial.print("Comparison: "); Serial.print("Comparison: ");
Serial.print((char*)a); Serial.print((char*)a);

View File

@@ -2,6 +2,9 @@
#define __TS590_h__ #define __TS590_h__
#include <Arduino.h> #include <Arduino.h>
#include <Embedded_Template_Library.h>
#include <etl/delegate.h>
#include "DSP.h"
#include "Rig.h" #include "Rig.h"
/**********************************************************************/ /**********************************************************************/
@@ -34,6 +37,72 @@ enum TS590Error {
ProcessError ProcessError
}; };
/**********************************************************************
**********************************************************************/
/*
class TS590BaseCommand {
public:
TS590BaseCommand(const char* prefix)
: myPrefix(prefix), prefixLength(strlen(prefix))
{}
virtual ~TS590BaseCommand() = 0;
inline const char* prefix() { return &myPrefix[0]; }
inline size_t length() { return prefixLength; }
virtual void handleCommand(const char* cmd) = 0;
virtual void sendResponse(const char* cmd) = 0;
virtual bool isReadCommand(const char* cmd) const;
inline void process(const char* cmd) {
theError = NoError;
if (isReadCommand(cmd)) {
DBGCMD( sendResponse(cmd) );
} else {
DBGCMD( handleCommand(cmd) );
switch(theError) {
case NoError:
if (theRig->isAI()) {
DBGCMD( sendResponse(cmd) );
}
break;
case SyntaxError:
DBGCMD( ts590SyntaxError() );
break;
case CommError:
DBGCMD( ts590CommError() );
break;
case ProcessError:
DBGCMD( ts590ProcessError() );
break;
}
}
}
private:
const char* myPrefix;
size_t prefixLength;
};
*/
typedef etl::delegate<bool(const char*)> ValidateFunc;
typedef etl::delegate<bool(const char*)> IsReadFunc;
typedef etl::delegate<void(bool)> ToggleFunc;
/*
class TS590ToggleCommand : public TS590BaseCommand {
TS590ToggleCommand(const char* prefix, ToggleFunc& setter, ToggleFunc& getter)
: TS590BaseCommand(prefix)
{}
private:
ToggleFunc& mySetter;
ToggleFunc& myGetter;
};
*/
/**********************************************************************/ /**********************************************************************/
/*! /*!
@@ -43,7 +112,7 @@ enum TS590Error {
class TS590Command { class TS590Command {
public: public:
TS590Command(const char* pre); TS590Command(const char* pre);
virtual ~TS590Command() = 0; virtual ~TS590Command();
/*! /*!
* @brief Return the 2-character prefix for the command. * @brief Return the 2-character prefix for the command.
@@ -51,11 +120,18 @@ class TS590Command {
*/ */
inline const char* prefix() const { return &myPrefix[0]; } inline const char* prefix() const { return &myPrefix[0]; }
inline size_t length() const { return prefixLength; }
/*! /*!
* @brief Return the rig that this command will be used to control. * @brief Return the rig that this command will be used to control.
*/ */
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 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,15 +157,80 @@ 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);
private: private:
char myPrefix[3] = "\0\0"; const char* myPrefix;
size_t prefixLength;
static TS590Error theError; static TS590Error theError;
static UBitxRig* theRig; static UBitxRig* theRig;
static UBitxDSP* theDSP;
}; };
/**********************************************************************/ /**********************************************************************/
typedef etl::delegate<void(bool)> SetBool;
typedef etl::delegate<bool(void)> GetBool;
class TS590Command_Bool : public TS590Command {
public:
TS590Command_Bool(const char* prefix, SetBool set, GetBool get)
: TS590Command(prefix), setter(set), getter(get) {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
SetBool setter;
GetBool getter;
};
/**********************************************************************/
typedef etl::delegate<void(unsigned)> SetUL;
typedef etl::delegate<unsigned(void)> GetUL;
class TS590Command_UL : public TS590Command {
public:
TS590Command_UL(const char* prefix, size_t width, unsigned min, unsigned max, SetUL set, GetUL get)
: TS590Command(prefix), myWidth(width), myMin(min), myMax(max), mySlope(1), myIntercept(0), setter(set), getter(get) {}
TS590Command_UL(const char* prefix, size_t width, unsigned min, unsigned max, unsigned slope, unsigned intercept, SetUL set, GetUL get)
: TS590Command(prefix), myWidth(width), myMin(min), myMax(max), mySlope(slope), myIntercept(intercept), setter(set), getter(get) {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
size_t myWidth;
unsigned myMin;
unsigned myMax;
unsigned mySlope;
unsigned myIntercept;
SetUL setter;
GetUL getter;
};
/**********************************************************************/
/*
typedef etl::delegate<void(bool)> SetULArray;
typedef etl::delegate<bool(void)> GetULArray;
class TS590Command_ULArray : public TS590Command {
public:
TS590Command_ULArray(const char* prefix, SetUL set, GetUL get)
: TS590Command(prefix), setter(set), getter(get) {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
SetUL setter;
GetUL getter;
};
*/
/**********************************************************************/
/*! /*!
* @brief CAT command for setting or reading the VFO A/B frequency. * @brief CAT command for setting or reading the VFO A/B frequency.
*/ */
@@ -102,9 +243,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 +301,44 @@ 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;
};
/*!
* CAT command for enabling or disabling the mic VOX.
*/
class TS590_VX : public TS590Command {
public:
TS590_VX(): TS590Command("VX") {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
unsigned index;
};
/**********************************************************************/
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).
@@ -607,11 +593,11 @@ void loop()
Serial.println(AudioMemoryUsageMax()); Serial.println(AudioMemoryUsageMax());
Serial.println("----------------------------------------------------------------------"); Serial.println("----------------------------------------------------------------------");
Serial.print("Enabled/Active: PTT: "); Serial.print("Enabled/Active: PTT: ");
Serial.print(TR.pttEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.pttPressed() ? "Y" : "N"); Serial.print(TR.micPTTEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.micPTTPressed() ? "Y" : "N");
Serial.print(", VOX: "); Serial.print(", VOX: ");
Serial.print(TR.voxEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.voxActivated() ? "Y" : "N"); Serial.print(TR.micVOXEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.micVOXActivated() ? "Y" : "N");
Serial.print(", Key: "); Serial.print(", Key: ");
Serial.print(TR.keyEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.keyPressed() ? "Y" : "N"); Serial.print(TR.linePTTEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.linePTTPressed() ? "Y" : "N");
Serial.print(", CAT: "); Serial.print(", CAT: ");
Serial.print(TR.catEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.catActivated() ? "Y" : "N"); Serial.print(TR.catEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.catActivated() ? "Y" : "N");
Serial.println(); Serial.println();
@@ -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)