Compare commits
16 Commits
integratio
...
new-main-d
Author | SHA1 | Date | |
---|---|---|---|
295b99ef36 | |||
|
e6009989db | ||
|
1f3585d8e4 | ||
|
c8aecdfb0d | ||
|
f724142fca | ||
|
869e47d430 | ||
|
20b475dace | ||
|
962a3ce80f | ||
|
86ae1ddb2f | ||
|
119902b1e0 | ||
|
1bca18c3e1 | ||
|
f3887e7950 | ||
|
2cbc9abae8 | ||
|
8a416608a1 | ||
|
e5de516633 | ||
|
5b395cd922 |
0
Raduino/Debug.h
Executable file
0
Raduino/Debug.h
Executable file
@@ -337,7 +337,7 @@ byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWK
|
||||
return 1;
|
||||
|
||||
//Check PTT while auto Sending
|
||||
autoSendPTTCheck();
|
||||
//autoSendPTTCheck();
|
||||
|
||||
//Check_Cat(3);
|
||||
}
|
||||
@@ -1449,6 +1449,7 @@ void setup()
|
||||
factory_alignment();
|
||||
#endif
|
||||
|
||||
rigState.begin();
|
||||
}
|
||||
|
||||
//Auto save Frequency and Mode with Protected eeprom life by KD8CEC
|
||||
@@ -1473,16 +1474,6 @@ void checkAutoSaveFreqMode()
|
||||
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(){
|
||||
|
0
Raduino/RigState.cpp
Executable file
0
Raduino/RigState.cpp
Executable file
@@ -1 +0,0 @@
|
||||
../TeensyDSP/RigState.h
|
0
Raduino/RigState.h
Executable file
0
Raduino/RigState.h
Executable 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_UNCALCR 0x54 //Uncalculated SWR Meter
|
||||
|
||||
// Raduino provides updated data to TeensyDSP
|
||||
// Raduino<=>TeensyDSP data exchange
|
||||
#define I2CMETER_RIGINF 0x50
|
||||
|
||||
// Raduino requests any CAT updates from TeensyDSP
|
||||
#define I2CMETER_REQCAT 0x51
|
||||
//#define I2CMETER_REQCAT 0x51
|
||||
|
||||
//==============================================================================
|
||||
// for public, Variable, functions
|
||||
@@ -340,10 +340,4 @@ extern void DisplayVersionInfo(const char* fwVersionInfo);
|
||||
//I2C Signal Meter, Version 1.097
|
||||
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
|
||||
|
@@ -296,7 +296,7 @@ void cwKeyer(void){
|
||||
return; //Tx stop control by Main Loop
|
||||
}
|
||||
|
||||
Check_Cat(2);
|
||||
//Check_Cat(2);
|
||||
} //end of while
|
||||
// } //end of elese
|
||||
}
|
||||
|
@@ -990,16 +990,16 @@ void SWS_Process(void)
|
||||
char checkCount = 0;
|
||||
char checkCountSMeter = 0;
|
||||
|
||||
UBitxRigState rigState;
|
||||
UBitxRigState catState;
|
||||
|
||||
//execute interval : 0.25sec
|
||||
void idle_process()
|
||||
{
|
||||
// KC4UPR 2021-02-05 added update process for Raduino-TeensyDSP coordination
|
||||
updateStateFromRaduino(rigState);
|
||||
doRaduinoToTeensy(&rigState);
|
||||
updateRaduinoFromState(rigState);
|
||||
rigState.send_RIGINF();
|
||||
delay(1);
|
||||
rigState.receive_RIGINF();
|
||||
//updateStateFromRaduino(rigState);
|
||||
//doRaduinoToTeensy(&rigState);
|
||||
//updateRaduinoFromState(rigState);
|
||||
|
||||
//S-Meter Display
|
||||
if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency))
|
||||
|
@@ -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)
|
||||
|
||||
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;
|
||||
@@ -1290,7 +1290,7 @@ void doMenu(){
|
||||
default :
|
||||
menuExit(btnState); break;
|
||||
} //end of switch
|
||||
Check_Cat(0); //To prevent disconnections
|
||||
//Check_Cat(0); //To prevent disconnections
|
||||
} //end of while
|
||||
|
||||
//****************************************************************************
|
||||
@@ -1690,7 +1690,7 @@ void menuSetupCarrier(int btn){
|
||||
si5351bx_setfreq(0, usbCarrier);
|
||||
printCarrierFreq(usbCarrier);
|
||||
|
||||
Check_Cat(0); //To prevent disconnections
|
||||
//Check_Cat(0); //To prevent disconnections
|
||||
delay(100);
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
||||
@@ -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
BIN
References/B5A-0180-20.pdf
Normal file
Binary file not shown.
BIN
References/ts_590_g_pc_command_e.pdf
Normal file
BIN
References/ts_590_g_pc_command_e.pdf
Normal file
Binary file not shown.
BIN
Schematics/SW_Architecture.odg
Normal file
BIN
Schematics/SW_Architecture.odg
Normal file
Binary file not shown.
@@ -4,147 +4,477 @@
|
||||
|
||||
#include "DSP.h"
|
||||
|
||||
#include <Audio.h>
|
||||
#include <i2c_t3.h>
|
||||
//#include <Wire.h>
|
||||
//#include <SPI.h>
|
||||
//#include <SD.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
|
||||
AudioInputI2S lineIn; //xy=385.9371643066406,1001.9600830078125
|
||||
AudioInputUSB usbIn; //xy=390.9371643066406,1107.9600830078125
|
||||
AudioMixer4 rxAudio; //xy=642.9371643066406,915.9600830078125
|
||||
AudioMixer4 txAudio; //xy=646.9371643066406,1103.9600830078125
|
||||
AudioOutputUSB usbOut; //xy=1022.9371643066406,919.9600830078125
|
||||
AudioOutputI2S lineOut; //xy=1024.9371643066406,1017.9600830078125
|
||||
AudioConnection patchCord1(lineIn, 0, rxAudio, 0);
|
||||
AudioConnection patchCord2(lineIn, 1, txAudio, 0);
|
||||
AudioConnection patchCord3(usbIn, 0, txAudio, 1);
|
||||
AudioConnection patchCord4(usbIn, 1, txAudio, 2);
|
||||
AudioConnection patchCord5(rxAudio, 0, lineOut, 0);
|
||||
AudioConnection patchCord6(rxAudio, 0, usbOut, 0);
|
||||
AudioConnection patchCord7(rxAudio, 0, usbOut, 1);
|
||||
AudioConnection patchCord8(txAudio, 0, lineOut, 1);
|
||||
AudioControlSGTL5000 audioCtrl; //xy=651.9371643066406,1244.9600830078125
|
||||
AudioInputUSB usbIn; //xy=63,305
|
||||
AudioInputI2S lineIn; //xy=71,197
|
||||
AudioSynthWaveformSine tone1; //xy=111,369
|
||||
AudioSynthWaveformSine tone2; //xy=111,410
|
||||
AudioMixer4 rxAudio; //xy=328,111
|
||||
AudioMixer4 txAudio; //xy=332,299
|
||||
AudioAnalyzeRMS txVoxLevel; //xy=490,340
|
||||
AudioFilterFIR rxFilter; //xy=493,103
|
||||
AudioAmplifier usbOutAmp; //xy=658,99
|
||||
AudioAmplifier lineOutAmp; //xy=659,162
|
||||
AudioAmplifier usbBypassAmp; //xy=666,225
|
||||
AudioAmplifier txOutAmp; //xy=713,301
|
||||
AudioOutputI2S lineOut; //xy=876,294
|
||||
AudioOutputUSB usbOut; //xy=878,255
|
||||
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
|
||||
|
||||
//} audio;
|
||||
|
||||
void UBitxDSP::begin() {
|
||||
AudioMemory(16);
|
||||
|
||||
// Basic audio setup
|
||||
AudioMemory(16); // TODO: optimize this
|
||||
audioCtrl.enable();
|
||||
audioCtrl.volume(0.0); // headphone volume...
|
||||
audioCtrl.muteHeadphone(); // ...not used by UBitxDSP
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (i == RX_AUDIO_CH)
|
||||
setupRxAudio();
|
||||
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);
|
||||
else
|
||||
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)
|
||||
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
|
||||
audioCtrl.unmuteLineout();
|
||||
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)
|
||||
audioCtrl.micGain(0); // TODO: set value
|
||||
|
||||
// Line Input (TX)
|
||||
|
||||
// USB Input (TX)
|
||||
|
||||
// SETUP THE AUDIO OUTPUTS
|
||||
|
||||
// Line Output (RX)
|
||||
|
||||
// USB Output (RX)
|
||||
|
||||
// Rig (Line) Output (TX)
|
||||
txOutAmp.gain(1.0);
|
||||
|
||||
// Default to RX.
|
||||
rx();
|
||||
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)
|
||||
|
||||
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() {
|
||||
// mute all tx audio
|
||||
audioCtrl.micGain(0);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
void UBitxDSP::muteRxIn(RxAudioCh ch) {
|
||||
if (ch < NUM_RX_AUDIO_CH) {
|
||||
if (!state.rxIn[ch].mute) {
|
||||
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);
|
||||
}
|
||||
// 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() {
|
||||
// mute the rx audio
|
||||
rxAudio.gain(RX_AUDIO_CH, 0.0);
|
||||
// restore the tx mic audio
|
||||
audioCtrl.inputSelect(AUDIO_INPUT_MIC);
|
||||
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::muteTxIn(TxAudioCh ch) {
|
||||
if (ch < NUM_TX_AUDIO_CH) {
|
||||
if (!state.txIn[ch].mute) {
|
||||
state.txIn[ch].mute = true;
|
||||
txAudio.gain(ch, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UBitxDSP::txLineIn() {
|
||||
// mute the rx audio
|
||||
rxAudio.gain(RX_AUDIO_CH, 0.0);
|
||||
// restore the tx line in audio
|
||||
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
|
||||
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::unmuteTxIn(TxAudioCh ch) {
|
||||
if (ch < NUM_TX_AUDIO_CH) {
|
||||
if (state.txIn[ch].mute) {
|
||||
state.txIn[ch].mute = false;
|
||||
rxAudio.gain(ch, state.txIn[ch].level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UBitxDSP::txUSBIn() {
|
||||
// mute the rx audio
|
||||
rxAudio.gain(RX_AUDIO_CH, 0.0);
|
||||
// restore the tx usb in audio
|
||||
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (i == TX_USB_IN_CH1 || i == TX_USB_IN_CH2)
|
||||
txAudio.gain(i, 0.1);
|
||||
else
|
||||
txAudio.gain(i, 0.0);
|
||||
void UBitxDSP::setTxOutLevel(float level) {
|
||||
state.txOut.level = level;
|
||||
txOutAmp.gain(state.txOut.mute ? 0.0 : state.txOut.level);
|
||||
}
|
||||
|
||||
void UBitxDSP::muteTxOut() {
|
||||
if (!state.txOut.mute) {
|
||||
state.txOut.mute = true;
|
||||
txOutAmp.gain(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
|
||||
//======================================================================
|
||||
|
264
TeensyDSP/DSP.h
264
TeensyDSP/DSP.h
@@ -5,36 +5,276 @@
|
||||
#ifndef __DSP_h__
|
||||
#define __DSP_h__
|
||||
|
||||
#include <Audio.h>
|
||||
#include <dynamicFilters.h>
|
||||
#include "Debug.h"
|
||||
|
||||
enum TRState {
|
||||
TRANSMIT,
|
||||
RECEIVE
|
||||
/**********************************************************************
|
||||
* Macros
|
||||
**********************************************************************/
|
||||
|
||||
#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 {
|
||||
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 {
|
||||
MIC_IN,
|
||||
LINE_IN,
|
||||
USB_IN
|
||||
MIC_IN = 0, // Microphone transmit audio input
|
||||
LINE_IN, // Line ("AUX") transmit audio input
|
||||
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 {
|
||||
|
||||
/********************************************************************
|
||||
* Object creation/deletion
|
||||
********************************************************************/
|
||||
|
||||
public:
|
||||
UBitxDSP() {}
|
||||
|
||||
/********************************************************************
|
||||
* Basic administration
|
||||
********************************************************************/
|
||||
|
||||
public:
|
||||
UBitxDSP() {};
|
||||
void begin();
|
||||
void update();
|
||||
void end();
|
||||
|
||||
/********************************************************************
|
||||
* Transmit/Receive switching
|
||||
********************************************************************/
|
||||
|
||||
public:
|
||||
void rx();
|
||||
void txMicIn();
|
||||
void txLineIn();
|
||||
void txUSBIn();
|
||||
inline void tx() { tx(txSrc); }
|
||||
void tx(TxAudioIn src);
|
||||
|
||||
/********************************************************************
|
||||
* 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
|
||||
|
||||
|
@@ -8,11 +8,13 @@
|
||||
#define DBGPRINTLN(MSG) do { Serial.print("DBG: "); Serial.println(MSG); } while (0)
|
||||
#define DBGNEWLINE() do { Serial.println(); } while (0)
|
||||
#define DBGCMD(CMD) do { Serial.print("DBG: "); Serial.println(#CMD); CMD; } while (0)
|
||||
#define IFDEBUG(CMD) do { CMD; } while (0)
|
||||
#else
|
||||
#define DBGPRINT(MSG) do {} while (0)
|
||||
#define DBGPRINTLN(MSG) do {} while (0)
|
||||
#define DBGNEWLINE() do {} while (0)
|
||||
#define DBGCMD(CMD) do { CMD; } while (0)
|
||||
#define IFDEBUG(CMD) do {} while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
170
TeensyDSP/Rig.h
170
TeensyDSP/Rig.h
@@ -3,143 +3,69 @@
|
||||
|
||||
#include "RigState.h"
|
||||
|
||||
enum UpdateSource {
|
||||
NoSource,
|
||||
RaduinoSource,
|
||||
CATSource
|
||||
struct RigState {
|
||||
};
|
||||
|
||||
class UBitxRig {
|
||||
public:
|
||||
inline void begin() {}
|
||||
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 void setRaduinoUpdate() { lastUpdatedBy = RaduinoSource; }
|
||||
inline void setCATUpdate() { lastUpdatedBy = CATSource; }
|
||||
inline unsigned getFreqA() const { return radState.getFreqA(); }
|
||||
inline unsigned getFreqB() const { return radState.getFreqB(); }
|
||||
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) {
|
||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
||||
state.vfo[0] = freq;
|
||||
}
|
||||
|
||||
inline void setFreqB(unsigned long freq, bool isCAT = false) {
|
||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
||||
state.vfo[1] = freq;
|
||||
}
|
||||
|
||||
inline void setRIT(short offset, bool isCAT = false) {
|
||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
||||
state.rit = offset;
|
||||
}
|
||||
|
||||
inline void setXIT(short offset, bool isCAT = false) {
|
||||
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 setFreqA(unsigned freq) { catState.setFreqA(freq); }
|
||||
inline void setFreqB(unsigned freq) { catState.setFreqB(freq); }
|
||||
inline void setRIT(int freq) { catState.setRIT(freq); }
|
||||
inline void setXIT(int freq) { catState.setXIT(freq); }
|
||||
inline void setVFOA() { catState.setVFOA(); }
|
||||
inline void setVFOB() { catState.setVFOB(); }
|
||||
inline void setSplitOn() { catState.setSplitOn(); }
|
||||
inline void setSplitOff() { catState.setSplitOff(); }
|
||||
inline void setRITOn() { catState.setRITOn(); }
|
||||
inline void setRITOff() { catState.setRITOff(); }
|
||||
inline void setXITOn() { catState.setXITOn(); }
|
||||
inline void setXITOff() { catState.setXITOff(); }
|
||||
inline void setCW() { catState.setCW(); }
|
||||
inline void setCWR() { catState.setCWR(); }
|
||||
inline void setUSB() { catState.setUSB(); }
|
||||
inline void setLSB() { catState.setLSB(); }
|
||||
|
||||
inline void setAI(bool on) { autoInfo = on; }
|
||||
inline void aiOn() { autoInfo = true; }
|
||||
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]) &&
|
||||
(r.vfo[1] == state.vfo[1]) &&
|
||||
(r.rit == state.rit) &&
|
||||
(r.xit == state.xit) &&
|
||||
(r.flags == state.flags)) {
|
||||
return;
|
||||
} else {
|
||||
state = r;
|
||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
||||
}
|
||||
}
|
||||
/********************************************************************/
|
||||
// New functional/mode-based Rig methods
|
||||
|
||||
// AG
|
||||
//void setVolOut(uint8_t level);
|
||||
//uint8_t getVolOut();
|
||||
|
||||
// BD/BU
|
||||
//void setBand();
|
||||
//void getBand();
|
||||
|
||||
private:
|
||||
bool autoInfo = false;
|
||||
UpdateSource lastUpdatedBy = NoSource;
|
||||
UBitxRigState state;
|
||||
UBitxRigState catState;
|
||||
UBitxRigState radState;
|
||||
bool autoInfo = false; // TODO: Move this to rig state struct
|
||||
};
|
||||
|
||||
extern UBitxRig Rig;
|
||||
|
416
TeensyDSP/RigState.cpp
Normal file
416
TeensyDSP/RigState.cpp
Normal 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
|
||||
**********************************************************************/
|
@@ -1,14 +1,12 @@
|
||||
/*!
|
||||
* @file RigState.h
|
||||
*/
|
||||
|
||||
#ifndef __RigState_h__
|
||||
#define __RigState_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_SPLIT_FLAG 0x00000002
|
||||
#define UBITX_RIT_FLAG 0x00000004
|
||||
@@ -17,126 +15,322 @@
|
||||
#define UBITX_USB_FLAG 0x00000020
|
||||
#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 {
|
||||
uint32_t header = 0;
|
||||
uint32_t vfo[2];
|
||||
int32_t rit;
|
||||
int32_t xit;
|
||||
uint32_t flags = 0;
|
||||
volatile uint32_t data[NUM_WORDS] = {0};
|
||||
|
||||
void begin();
|
||||
|
||||
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>
|
||||
struct Field {
|
||||
byte id = ID;
|
||||
bool dirty;
|
||||
T data;
|
||||
extern UBitxRigState& rigState;
|
||||
|
||||
inline size_t sizeOfWrite() { return dirty ? sizeof(byte) + sizeof(T) : 0; }
|
||||
|
||||
template<typename STREAM> void writeChanges() {
|
||||
if (dirty) {
|
||||
STREAM().write(id);
|
||||
STREAM().write((byte*)&data, sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename STREAM> int read() {
|
||||
size_t len = 0;
|
||||
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();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
NOTE: This is all currently OBE, leaving it here for reference/future cleanup.
|
||||
|
||||
Protocol discussion:
|
||||
- I2C master: Raduino
|
||||
- I2C slave: TeensyDSP
|
||||
@@ -172,3 +366,7 @@ TeensyDSP state:
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
* EOF
|
||||
**********************************************************************/
|
||||
|
@@ -8,7 +8,7 @@
|
||||
UBitxTR TR(DSP);
|
||||
|
||||
void UBitxTR::update(bool cw, bool extKey) {
|
||||
updateKey();
|
||||
updateLinePTT();
|
||||
|
||||
if (cw) {
|
||||
if ((keyEnable && keyDown) || extKey) {
|
||||
@@ -19,33 +19,35 @@ void UBitxTR::update(bool cw, bool extKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
updatePTT();
|
||||
updateVOX();
|
||||
updateMicPTT();
|
||||
updateMicVOX();
|
||||
updateDataVOX();
|
||||
|
||||
if (isTX) {
|
||||
// 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.
|
||||
if (pttReleased() || keyReleased() ||
|
||||
(voxEnable && voxDeactivated()) ||
|
||||
(catEnable && catDeactivated())) {
|
||||
if (micPTTReleased() || linePTTReleased() ||
|
||||
(micVOXEnabled() && micVOXDeactivated()) ||
|
||||
(catEnabled() && catDeactivated()) ||
|
||||
(dataVOXEnabled() && dataVOXDeactivated())) {
|
||||
// first, stop transmitting; then, setup RX audio
|
||||
DBGCMD( setRX() );
|
||||
DBGCMD( dsp.rx() );
|
||||
}
|
||||
} else {
|
||||
if ((pttEnable && pttPressed()) || (voxEnable && voxActivated())) {
|
||||
if ((micPTTEnabled() && micPTTPressed()) || (micVOXEnabled() && micVOXActivated())) {
|
||||
// first, setup TX audio; then, start transmitting (from Mic)
|
||||
DBGCMD( dsp.txMicIn() );
|
||||
DBGCMD( setTX() );
|
||||
} else if (keyEnable && keyPressed()) {
|
||||
DBGCMD( dsp.tx(MIC_IN) );
|
||||
DBGCMD( setTX() );
|
||||
} else if ((linePTTEnabled() && linePTTPressed())) {
|
||||
// first, setup TX audio; then, start transmitting (from Line In)
|
||||
DBGCMD( dsp.txLineIn() );
|
||||
DBGCMD( setTX() );
|
||||
DBGCMD( dsp.tx(LINE_IN) );
|
||||
DBGCMD( setTX() );
|
||||
} else if (catEnable && catActivated()) {
|
||||
// first, setup TX audio; then, start transmitting (USB)
|
||||
DBGCMD( dsp.txUSBIn() );
|
||||
DBGCMD( setTX() );
|
||||
DBGCMD( dsp.tx(USB_IN) );
|
||||
DBGCMD( setTX() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -32,34 +32,40 @@ class UBitxTR {
|
||||
ptt.interval(5);
|
||||
|
||||
// default configuration: PTT, key, and CAT enabled; VOX disabled
|
||||
DBGCMD( enablePTT() );
|
||||
DBGCMD( disableVOX() );
|
||||
DBGCMD( enableKey() );
|
||||
DBGCMD( enableMicPTT() );
|
||||
DBGCMD( disableMicVOX() );
|
||||
DBGCMD( enableLinePTT() );
|
||||
DBGCMD( enableCAT() );
|
||||
|
||||
DBGCMD( setRX() );
|
||||
}
|
||||
|
||||
inline void enablePTT() { pttEnable = true; }
|
||||
inline void enableVOX() { voxEnable = true; }
|
||||
inline void enableKey() { keyEnable = true; }
|
||||
inline void enableMicPTT() { pttEnable = true; }
|
||||
inline void enableLinePTT() { keyEnable = true; }
|
||||
inline void enableMicVOX() { voxEnable = true; }
|
||||
inline void enableDataVOX() { dvoxEnable = true; }
|
||||
inline void enableCAT() { catEnable = true; }
|
||||
inline void disablePTT() { pttEnable = false; }
|
||||
inline void disableVOX() { voxEnable = false; }
|
||||
inline void disableKey() { keyEnable = false; }
|
||||
|
||||
inline void disableMicPTT() { pttEnable = false; }
|
||||
inline void disableLinePTT() { keyEnable = false; }
|
||||
inline void disableMicVOX() { voxEnable = false; }
|
||||
inline void disableDataVOX() { dvoxEnable = false; }
|
||||
inline void disableCAT() { catEnable = false; }
|
||||
|
||||
inline bool pttEnabled() { return pttEnable; }
|
||||
inline bool voxEnabled() { return voxEnable; }
|
||||
inline bool keyEnabled() { return keyEnable; }
|
||||
inline bool catEnabled() { return catEnable; }
|
||||
inline bool micPTTEnabled() const { return pttEnable; }
|
||||
inline bool linePTTEnabled() const { return keyEnable; }
|
||||
inline bool micVOXEnabled() const { return voxEnable; }
|
||||
inline bool dataVOXEnabled() const { return dvoxEnable; }
|
||||
inline bool catEnabled() const { return catEnable; }
|
||||
|
||||
inline bool pttPressed() { return ptt.fell(); }
|
||||
inline bool pttReleased() { return ptt.rose(); }
|
||||
inline bool voxActivated() { return (L_voxActive != voxActive) && L_voxActive; }
|
||||
inline bool voxDeactivated() { return (L_voxActive != voxActive) && voxActive; }
|
||||
inline bool keyPressed() { return (L_keyDown != keyDown) && L_keyDown; }
|
||||
inline bool keyReleased() { return (L_keyDown != keyDown) && keyDown; }
|
||||
inline bool micPTTPressed() { return ptt.fell(); }
|
||||
inline bool micPTTReleased() { return ptt.rose(); }
|
||||
inline bool linePTTPressed() { return (L_keyDown != keyDown) && L_keyDown; }
|
||||
inline bool linePTTReleased() { return (L_keyDown != keyDown) && keyDown; }
|
||||
inline bool micVOXActivated() { return (L_voxActive != voxActive) && L_voxActive; }
|
||||
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 catDeactivated() { return (L_catActive != catActive) && catActive; }
|
||||
|
||||
@@ -109,42 +115,58 @@ class UBitxTR {
|
||||
digitalWrite(outPin, HIGH);
|
||||
}
|
||||
|
||||
inline void updatePTT() {
|
||||
inline void updateMicPTT() {
|
||||
ptt.update();
|
||||
}
|
||||
|
||||
inline void updateVOX() {
|
||||
inline void updateLinePTT() {
|
||||
L_keyDown = keyDown;
|
||||
keyDown = (digitalRead(keyPin) == LOW);
|
||||
}
|
||||
|
||||
inline void updateMicVOX() {
|
||||
L_voxActive = voxActive;
|
||||
voxActive = (digitalRead(voxPin) == LOW);
|
||||
}
|
||||
|
||||
inline void updateKey() {
|
||||
L_keyDown = keyDown;
|
||||
keyDown = (digitalRead(keyPin) == LOW);
|
||||
inline void updateDataVOX() {
|
||||
L_dvoxActive = dvoxActive;
|
||||
if (dsp.getVoxLevel() > dvoxThreshold) {
|
||||
dvoxActive = true;
|
||||
dvoxElapsed = 0;
|
||||
} else if (dvoxActive && (dvoxElapsed > dvoxDelay)) {
|
||||
dvoxActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
UBitxDSP& dsp;
|
||||
|
||||
Bounce ptt;
|
||||
|
||||
int outPin;
|
||||
int pttPin;
|
||||
int voxPin;
|
||||
int keyPin;
|
||||
int outPin;
|
||||
|
||||
bool isTX = false;
|
||||
|
||||
bool pttEnable = false;
|
||||
bool voxEnable = false;
|
||||
bool dvoxEnable = false;
|
||||
bool keyEnable = false;
|
||||
bool catEnable = false;
|
||||
|
||||
bool voxActive = false;
|
||||
bool L_voxActive = false;
|
||||
bool dvoxActive = false;
|
||||
bool L_dvoxActive = false;
|
||||
bool keyDown = false;
|
||||
bool L_keyDown = false;
|
||||
bool catActive = false;
|
||||
bool L_catActive = false;
|
||||
|
||||
elapsedMillis dvoxElapsed = 0;
|
||||
unsigned dvoxDelay = 250; // TODO: make dynamic
|
||||
};
|
||||
|
||||
extern UBitxTR TR;
|
||||
|
@@ -34,12 +34,9 @@ void ts590SendCommand(const char* format, ...) {
|
||||
* than two are supplied, then the command will be
|
||||
* initialized with a null prefix.
|
||||
*/
|
||||
TS590Command::TS590Command(const char* pre) {
|
||||
if (strlen(pre) >= 2) {
|
||||
myPrefix[0] = pre[0];
|
||||
myPrefix[1] = pre[1];
|
||||
}
|
||||
}
|
||||
TS590Command::TS590Command(const char* pre)
|
||||
: myPrefix(pre), prefixLength(strlen(pre))
|
||||
{}
|
||||
|
||||
TS590Command::~TS590Command() {}
|
||||
|
||||
@@ -49,7 +46,7 @@ TS590Command::~TS590Command() {}
|
||||
* @return True if a Read command; false otherwise.
|
||||
*/
|
||||
bool TS590Command::isReadCommand(const char* cmd) const {
|
||||
if (strlen(cmd) == 2) {
|
||||
if (strlen(cmd) == prefixLength) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -77,7 +74,7 @@ void TS590Command::process(const char* cmd) {
|
||||
DBGCMD( handleCommand(cmd) );
|
||||
switch(theError) {
|
||||
case NoError:
|
||||
if (theRig->getAI()) {
|
||||
if (theRig->isAI()) {
|
||||
DBGCMD( sendResponse(cmd) );
|
||||
}
|
||||
break;
|
||||
@@ -130,22 +127,65 @@ void TS590Command::setRig(UBitxRig* 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;
|
||||
UBitxDSP* TS590Command::theDSP = &DSP;
|
||||
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) {
|
||||
if (strlen(cmd) == 3) {
|
||||
switch (cmd[2]) {
|
||||
case '0':
|
||||
rig()->selectVFOA(true);
|
||||
rig()->splitOff(true);
|
||||
rig()->setVFOA();
|
||||
rig()->setSplitOff();
|
||||
break;
|
||||
|
||||
case '1':
|
||||
rig()->selectVFOB(true);
|
||||
rig()->splitOff(true);
|
||||
rig()->setVFOB();
|
||||
rig()->setSplitOff();
|
||||
break;
|
||||
|
||||
case '2':
|
||||
@@ -177,9 +217,9 @@ void TS590_FT::handleCommand(const char* cmd) {
|
||||
switch (cmd[2]) {
|
||||
case '0':
|
||||
if (rig()->isVFOA()) {
|
||||
rig()->splitOff(true);
|
||||
rig()->setSplitOff();
|
||||
} else if (rig()->isVFOB()) {
|
||||
rig()->splitOn(true);
|
||||
rig()->setSplitOn();
|
||||
} else {
|
||||
setSyntaxError();
|
||||
}
|
||||
@@ -187,9 +227,9 @@ void TS590_FT::handleCommand(const char* cmd) {
|
||||
|
||||
case '1':
|
||||
if (rig()->isVFOA()) {
|
||||
rig()->splitOn(true);
|
||||
rig()->setSplitOn();
|
||||
} else if (rig()->isVFOB()) {
|
||||
rig()->splitOff(true);
|
||||
rig()->setSplitOff();
|
||||
} else {
|
||||
setSyntaxError();
|
||||
}
|
||||
@@ -228,23 +268,19 @@ void TS590_MD::handleCommand(const char* cmd) {
|
||||
break;
|
||||
|
||||
case '1': // LSB
|
||||
rig()->selectLSB(true);
|
||||
rig()->cwOff(true);
|
||||
rig()->setLSB();
|
||||
break;
|
||||
|
||||
case '2': // USB
|
||||
rig()->selectUSB(true);
|
||||
rig()->cwOff(true);
|
||||
rig()->setUSB();
|
||||
break;
|
||||
|
||||
case '3': // CW
|
||||
rig()->selectUSB(true);
|
||||
rig()->cwOn(true);
|
||||
rig()->setCW();
|
||||
break;
|
||||
|
||||
case '7': // CW-R
|
||||
rig()->selectLSB(true);
|
||||
rig()->cwOn(true);
|
||||
rig()->setCWR();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -256,41 +292,157 @@ void TS590_MD::handleCommand(const char* cmd) {
|
||||
}
|
||||
|
||||
void TS590_MD::sendResponse(const char* cmd) {
|
||||
if (rig()->isCW()) {
|
||||
if (rig()->isUSB()) {
|
||||
ts590SendCommand("MD3");
|
||||
} else {
|
||||
ts590SendCommand("MD7");
|
||||
}
|
||||
if (rig()->isModeCW()) {
|
||||
ts590SendCommand("MD3");
|
||||
} else if (rig()->isModeCWR()) {
|
||||
ts590SendCommand("MD7");
|
||||
} else if (rig()->isModeUSB()) {
|
||||
ts590SendCommand("MD2");
|
||||
} else if (rig()->isModeLSB()) {
|
||||
ts590SendCommand("MD1");
|
||||
} else {
|
||||
if (rig()->isUSB()) {
|
||||
ts590SendCommand("MD2");
|
||||
} else {
|
||||
ts590SendCommand("MD1");
|
||||
}
|
||||
ts590SendCommand("MD0");
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
int ssbHiCut[14] = {1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3400, 4000, 5000};
|
||||
int ssbLoCut[12] = {0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
|
||||
|
||||
int ssbWidth[14] = {50, 80, 100, 150, 200, 250, 300, 400, 500, 600, 1000, 1500, 2000, 2500};
|
||||
int ssbCenter[14] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1750, 1800, 1900, 2000, 2100, 2210};
|
||||
|
||||
void TS590_SH::handleCommand(const char* cmd) {
|
||||
if (strlen(cmd) == 4) {
|
||||
index = strtoul(&cmd[2], NULL, 10);
|
||||
if (index < sizeof(ssbHiCut) / sizeof(ssbHiCut[0])) {
|
||||
dsp()->setRxFilterHi(ssbHiCut[index]);
|
||||
} else {
|
||||
setSyntaxError();
|
||||
}
|
||||
} else {
|
||||
setSyntaxError();
|
||||
}
|
||||
}
|
||||
|
||||
void TS590_SH::sendResponse(const char* cmd) {
|
||||
ts590SendCommand("SH%02u", index);
|
||||
}
|
||||
|
||||
void TS590_SL::handleCommand(const char* cmd) {
|
||||
if (strlen(cmd) == 4) {
|
||||
index = strtoul(&cmd[2], NULL, 10);
|
||||
if (index < sizeof(ssbLoCut) / sizeof(ssbLoCut[0])) {
|
||||
dsp()->setRxFilterLo(ssbLoCut[index]);
|
||||
} else {
|
||||
setSyntaxError();
|
||||
}
|
||||
} else {
|
||||
setSyntaxError();
|
||||
}
|
||||
}
|
||||
|
||||
void TS590_SL::sendResponse(const char* cmd) {
|
||||
ts590SendCommand("SL%02u", index);
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
void TS590_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_FB cmdFB;
|
||||
TS590_FR cmdFR;
|
||||
TS590_FT cmdFT;
|
||||
TS590_MD cmdMD;
|
||||
TS590_SH cmdSH;
|
||||
TS590_SL cmdSL;
|
||||
|
||||
TS590Command* catCommands[] = {
|
||||
&cmdFA,
|
||||
&cmdFB,
|
||||
&cmdFR,
|
||||
&cmdFT,
|
||||
&cmdMD
|
||||
&cmdMD,
|
||||
&cmdSH,
|
||||
&cmdSL
|
||||
};
|
||||
int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]);
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
|
||||
void UBitxTS590::begin() {
|
||||
Serial.begin(9600); // USB is always 12 Mbit/sec
|
||||
#ifdef DEBUG
|
||||
@@ -331,7 +483,7 @@ typedef class TS590Command* PCmd;
|
||||
|
||||
int compareCATCommands(const void* a, const void* 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
|
||||
Serial.print("Comparison: ");
|
||||
Serial.print((char*)a);
|
||||
|
@@ -2,6 +2,9 @@
|
||||
#define __TS590_h__
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Embedded_Template_Library.h>
|
||||
#include <etl/delegate.h>
|
||||
#include "DSP.h"
|
||||
#include "Rig.h"
|
||||
|
||||
/**********************************************************************/
|
||||
@@ -34,6 +37,72 @@ enum TS590Error {
|
||||
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 {
|
||||
public:
|
||||
TS590Command(const char* pre);
|
||||
virtual ~TS590Command() = 0;
|
||||
virtual ~TS590Command();
|
||||
|
||||
/*!
|
||||
* @brief Return the 2-character prefix for the command.
|
||||
@@ -51,11 +120,18 @@ class TS590Command {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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
|
||||
* results in an error, then set the appropriate flag with
|
||||
@@ -81,15 +157,80 @@ class TS590Command {
|
||||
static void setCommError();
|
||||
static void setProcessError();
|
||||
static void setRig(UBitxRig* r);
|
||||
static void setDSP(UBitxDSP* d);
|
||||
|
||||
private:
|
||||
char myPrefix[3] = "\0\0";
|
||||
const char* myPrefix;
|
||||
size_t prefixLength;
|
||||
static TS590Error theError;
|
||||
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.
|
||||
*/
|
||||
@@ -102,9 +243,9 @@ class TS590_FAB : public TS590Command {
|
||||
if (strlen(cmd) == 13) {
|
||||
unsigned long freq = strtoul(&cmd[2], NULL, 10);
|
||||
if (VFOA) {
|
||||
rig()->setFreqA(freq, true);
|
||||
rig()->setFreqA(freq);
|
||||
} else {
|
||||
rig()->setFreqB(freq, true);
|
||||
rig()->setFreqB(freq);
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
public:
|
||||
UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {}
|
||||
|
@@ -68,8 +68,8 @@ extern int magnitudelimit_low;
|
||||
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter
|
||||
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
|
||||
|
||||
// Raduino provides updated data to TeensyDSP
|
||||
// Raduino<=>TeensyDSP data exchange
|
||||
#define I2CMETER_RIGINF 0x50
|
||||
|
||||
// Raduino requests any CAT updates from TeensyDSP
|
||||
#define I2CMETER_REQCAT 0x51
|
||||
//#define I2CMETER_REQCAT 0x51
|
||||
|
@@ -363,8 +363,6 @@ void setup()
|
||||
//Serial1.println("Start...");
|
||||
}
|
||||
|
||||
bool sentRigInfFlag = false;
|
||||
|
||||
/*!
|
||||
@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.
|
||||
@@ -375,20 +373,12 @@ void i2cReceiveEvent(size_t numBytes)
|
||||
{
|
||||
int readCommand = 0;
|
||||
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) {
|
||||
readCommand = Wire1.read();
|
||||
if (readCommand == I2CMETER_RIGINF) {
|
||||
size_t len = 0;
|
||||
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
|
||||
Rig.rad().receive_RIGINF(numBytes - 1);
|
||||
exitLoop = true;
|
||||
}
|
||||
}
|
||||
@@ -424,43 +414,35 @@ void i2cRequestEvent(void)
|
||||
case I2CMETER_CALCS:
|
||||
// Returns an already-calculated S-meter value.
|
||||
Wire1.write(scaledSMeter);
|
||||
#ifdef DEBUG
|
||||
i2cRespCounter[i2cCommand - 0x50]++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case I2CMETER_UNCALCS:
|
||||
// Returns a raw signal strength value.
|
||||
Wire1.write(Sensors.sMeterUnscaled() >> 2); // divided by 4... do we want this?
|
||||
#ifdef DEBUG
|
||||
i2cRespCounter[i2cCommand - 0x50]++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case I2CMETER_CALCP:
|
||||
// Returns a raw forward power value.
|
||||
Wire1.write(int(fwdPower * 100.0));
|
||||
#ifdef DEBUG
|
||||
i2cRespCounter[i2cCommand - 0x50]++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case I2CMETER_CALCR:
|
||||
// Returns a raw reverse power value.
|
||||
Wire1.write(int(revPower * 100.0));
|
||||
#ifdef DEBUG
|
||||
i2cRespCounter[i2cCommand - 0x50]++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case I2CMETER_RIGINF:
|
||||
// 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
|
||||
//break;
|
||||
// NEEDS TO GET UPDATED
|
||||
break;
|
||||
|
||||
/*
|
||||
case I2CMETER_REQCAT:
|
||||
// Provide latest CAT updates, if any.
|
||||
//Wire1.write(catState.header); // temporary - just writing a single, null byte
|
||||
// NEEDS TO GET UPDATED
|
||||
if (Rig.updatedByCAT()) {
|
||||
if (sentRigInfFlag) {
|
||||
DBGPRINTLN("I2CMETER_REQCAT -- updated by CAT");
|
||||
@@ -475,14 +457,18 @@ void i2cRequestEvent(void)
|
||||
//Wire1.write(Rig.stateAsBytes(), sizeof(uint8_t));
|
||||
Wire1.write(0);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
i2cRespCounter[i2cCommand - 0x50]++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (0x50 <= i2cCommand && i2cCommand <= 0x59)
|
||||
{
|
||||
i2cRespCounter[i2cCommand - 0x50]++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//extern void Decode_Morse(float magnitude);
|
||||
@@ -541,9 +527,9 @@ void loop()
|
||||
}
|
||||
|
||||
// If CW mode, we need to update keying a lot...
|
||||
if (Rig.isCW()) {
|
||||
if (Rig.isCW()) Keyer.doPaddles();
|
||||
TR.update(Rig.isCW(), Keyer.isDown());
|
||||
if (Rig.isModeCWAny()) {
|
||||
if (Rig.isModeCWAny()) Keyer.doPaddles();
|
||||
TR.update(Rig.isModeCWAny(), Keyer.isDown());
|
||||
//if (TR.transmitting()) return;
|
||||
}
|
||||
|
||||
@@ -558,11 +544,11 @@ void loop()
|
||||
|
||||
// Update each of the subsystems, beginning with CAT control.
|
||||
TS590.update();
|
||||
TR.update(Rig.isCW(), Keyer.isDown());
|
||||
TR.update(Rig.isModeCWAny(), Keyer.isDown());
|
||||
Rig.update();
|
||||
DSP.update();
|
||||
|
||||
//if (Rig.isCW()) return;
|
||||
//if (Rig.isModeCWAny()) return;
|
||||
|
||||
#ifdef DEBUG
|
||||
// For debugging, output some debug info every 1.0" (40 frames @ 40 Hz).
|
||||
@@ -607,11 +593,11 @@ void loop()
|
||||
Serial.println(AudioMemoryUsageMax());
|
||||
Serial.println("----------------------------------------------------------------------");
|
||||
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(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(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(TR.catEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.catActivated() ? "Y" : "N");
|
||||
Serial.println();
|
||||
@@ -685,7 +671,7 @@ void loop()
|
||||
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) {
|
||||
// Do stuff that we do once per ADC interval--ADC colllection.
|
||||
@@ -703,7 +689,7 @@ void loop()
|
||||
//forwardData();
|
||||
}
|
||||
|
||||
//if (Rig.isCW()) return;
|
||||
//if (Rig.isModeCWAny()) return;
|
||||
|
||||
// Check Response Command
|
||||
if (responseCommand > 0 && sinceForward > LAST_TIME_INTERVAL)
|
||||
|
Reference in New Issue
Block a user