Compare commits
9 Commits
integratio
...
mode-based
Author | SHA1 | Date | |
---|---|---|---|
|
5e280b33dd | ||
|
cc94f89cfe | ||
|
119902b1e0 | ||
|
1bca18c3e1 | ||
|
f3887e7950 | ||
|
2cbc9abae8 | ||
|
8a416608a1 | ||
|
e5de516633 | ||
|
5b395cd922 |
1
Raduino/Debug.h
Symbolic link
1
Raduino/Debug.h
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../TeensyDSP/Debug.h
|
@@ -337,7 +337,7 @@ byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWK
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
//Check PTT while auto Sending
|
//Check PTT while auto Sending
|
||||||
autoSendPTTCheck();
|
//autoSendPTTCheck();
|
||||||
|
|
||||||
//Check_Cat(3);
|
//Check_Cat(3);
|
||||||
}
|
}
|
||||||
@@ -1449,6 +1449,7 @@ void setup()
|
|||||||
factory_alignment();
|
factory_alignment();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
rigState.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Auto save Frequency and Mode with Protected eeprom life by KD8CEC
|
//Auto save Frequency and Mode with Protected eeprom life by KD8CEC
|
||||||
@@ -1473,16 +1474,6 @@ void checkAutoSaveFreqMode()
|
|||||||
saveCheckTime = 0; //for reduce cpu use rate
|
saveCheckTime = 0; //for reduce cpu use rate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rigState.vfo[0] = vfoA;
|
|
||||||
rigState.vfo[1] = vfoB;
|
|
||||||
rigState.rit = ritRxFrequency - frequency;
|
|
||||||
rigState.flags = 0;
|
|
||||||
rigState.flags |= (vfoActive == VFO_B ? UBITX_VFOB_FLAG : 0);
|
|
||||||
rigState.flags |= (cwMode != 0 ? UBITX_CW_FLAG : 0);
|
|
||||||
rigState.flags |= (isUSB != 0 ? UBITX_USB_FLAG : 0);
|
|
||||||
rigState.flags |= (splitOn != 0 ? UBITX_SPLIT_FLAG : 0);
|
|
||||||
rigState.flags |= (ritOn != 0 ? UBITX_RIT_FLAG : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop(){
|
void loop(){
|
||||||
|
1
Raduino/RigState.cpp
Symbolic link
1
Raduino/RigState.cpp
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../TeensyDSP/RigState.cpp
|
@@ -255,11 +255,11 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode
|
|||||||
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter
|
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter
|
||||||
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
|
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
|
||||||
|
|
||||||
// Raduino provides updated data to TeensyDSP
|
// Raduino<=>TeensyDSP data exchange
|
||||||
#define I2CMETER_RIGINF 0x50
|
#define I2CMETER_RIGINF 0x50
|
||||||
|
|
||||||
// Raduino requests any CAT updates from TeensyDSP
|
// Raduino requests any CAT updates from TeensyDSP
|
||||||
#define I2CMETER_REQCAT 0x51
|
//#define I2CMETER_REQCAT 0x51
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// for public, Variable, functions
|
// for public, Variable, functions
|
||||||
@@ -340,10 +340,4 @@ extern void DisplayVersionInfo(const char* fwVersionInfo);
|
|||||||
//I2C Signal Meter, Version 1.097
|
//I2C Signal Meter, Version 1.097
|
||||||
extern int GetI2CSmeterValue(int valueType); //ubitx_ui.ino
|
extern int GetI2CSmeterValue(int valueType); //ubitx_ui.ino
|
||||||
|
|
||||||
extern void doRaduinoToTeensy(UBitxRigState* r);
|
|
||||||
extern void updateStateFromRaduino(UBitxRigState& r);
|
|
||||||
extern void updateRaduinoFromState(UBitxRigState& r);
|
|
||||||
|
|
||||||
extern UBitxRigState rigState;
|
|
||||||
|
|
||||||
#endif //end of if header define
|
#endif //end of if header define
|
||||||
|
@@ -296,7 +296,7 @@ void cwKeyer(void){
|
|||||||
return; //Tx stop control by Main Loop
|
return; //Tx stop control by Main Loop
|
||||||
}
|
}
|
||||||
|
|
||||||
Check_Cat(2);
|
//Check_Cat(2);
|
||||||
} //end of while
|
} //end of while
|
||||||
// } //end of elese
|
// } //end of elese
|
||||||
}
|
}
|
||||||
|
@@ -990,16 +990,16 @@ void SWS_Process(void)
|
|||||||
char checkCount = 0;
|
char checkCount = 0;
|
||||||
char checkCountSMeter = 0;
|
char checkCountSMeter = 0;
|
||||||
|
|
||||||
UBitxRigState rigState;
|
|
||||||
UBitxRigState catState;
|
|
||||||
|
|
||||||
//execute interval : 0.25sec
|
//execute interval : 0.25sec
|
||||||
void idle_process()
|
void idle_process()
|
||||||
{
|
{
|
||||||
// KC4UPR 2021-02-05 added update process for Raduino-TeensyDSP coordination
|
// KC4UPR 2021-02-05 added update process for Raduino-TeensyDSP coordination
|
||||||
updateStateFromRaduino(rigState);
|
rigState.send_RIGINF();
|
||||||
doRaduinoToTeensy(&rigState);
|
delay(1);
|
||||||
updateRaduinoFromState(rigState);
|
rigState.receive_RIGINF();
|
||||||
|
//updateStateFromRaduino(rigState);
|
||||||
|
//doRaduinoToTeensy(&rigState);
|
||||||
|
//updateRaduinoFromState(rigState);
|
||||||
|
|
||||||
//S-Meter Display
|
//S-Meter Display
|
||||||
if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency))
|
if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency))
|
||||||
|
@@ -263,7 +263,7 @@ void menuCHMemory(int btn, byte isMemoryToVfo){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Check_Cat(0); //To prevent disconnections
|
//Check_Cat(0); //To prevent disconnections
|
||||||
} //end of while (knob)
|
} //end of while (knob)
|
||||||
|
|
||||||
if (selectChannel < 20 && selectChannel >= 0)
|
if (selectChannel < 20 && selectChannel >= 0)
|
||||||
@@ -697,7 +697,7 @@ int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnob
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Check_Cat(0); //To prevent disconnections
|
//Check_Cat(0); //To prevent disconnections
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetValue;
|
return targetValue;
|
||||||
@@ -1290,7 +1290,7 @@ void doMenu(){
|
|||||||
default :
|
default :
|
||||||
menuExit(btnState); break;
|
menuExit(btnState); break;
|
||||||
} //end of switch
|
} //end of switch
|
||||||
Check_Cat(0); //To prevent disconnections
|
//Check_Cat(0); //To prevent disconnections
|
||||||
} //end of while
|
} //end of while
|
||||||
|
|
||||||
//****************************************************************************
|
//****************************************************************************
|
||||||
@@ -1690,7 +1690,7 @@ void menuSetupCarrier(int btn){
|
|||||||
si5351bx_setfreq(0, usbCarrier);
|
si5351bx_setfreq(0, usbCarrier);
|
||||||
printCarrierFreq(usbCarrier);
|
printCarrierFreq(usbCarrier);
|
||||||
|
|
||||||
Check_Cat(0); //To prevent disconnections
|
//Check_Cat(0); //To prevent disconnections
|
||||||
delay(100);
|
delay(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,7 +18,7 @@ const PROGMEM uint8_t meters_bitmap[] = {
|
|||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "RigState.h"
|
//#include "RigState.h"
|
||||||
|
|
||||||
//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA
|
//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA
|
||||||
|
|
||||||
@@ -299,94 +299,3 @@ int GetI2CSmeterValue(int valueType)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
|
|
||||||
void doRaduinoToTeensy(UBitxRigState* r) {
|
|
||||||
uint8_t* ptr = (uint8_t*)r;
|
|
||||||
|
|
||||||
Wire.beginTransmission(I2CMETER_ADDR);
|
|
||||||
Wire.write(I2CMETER_RIGINF);
|
|
||||||
//for (size_t i = 0; i < sizeof(UBitxRigState); i++) {
|
|
||||||
// Wire.write(ptr[i]);
|
|
||||||
//}
|
|
||||||
//Note, I can switch this back...
|
|
||||||
Wire.write(ptr, sizeof(UBitxRigState));
|
|
||||||
Wire.endTransmission();
|
|
||||||
|
|
||||||
Serial.println("BEFORE:");
|
|
||||||
Serial.print("VFO A: ");
|
|
||||||
Serial.print(r->vfo[0]);
|
|
||||||
Serial.print(", VFO B: ");
|
|
||||||
Serial.print(r->vfo[1]);
|
|
||||||
Serial.print(", Data Size: ");
|
|
||||||
Serial.print(sizeof(UBitxRigState));
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
// First we need to see if there's any updated state.
|
|
||||||
Wire.requestFrom(I2CMETER_ADDR, sizeof(uint8_t));
|
|
||||||
int len = 0;
|
|
||||||
int readflag = Wire.read();
|
|
||||||
if (readflag != 0) {
|
|
||||||
Wire.requestFrom(I2CMETER_ADDR, sizeof(UBitxRigState));
|
|
||||||
|
|
||||||
UBitxRigState tmp;
|
|
||||||
|
|
||||||
//int len = 0;
|
|
||||||
//ptr = (uint8_t*)&tmp;
|
|
||||||
|
|
||||||
while (Wire.available() > 0) {
|
|
||||||
uint8_t b = Wire.read();
|
|
||||||
if (len < sizeof(UBitxRigState)) {
|
|
||||||
ptr[len++] = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("AFTER:");
|
|
||||||
Serial.print("VFO A: ");
|
|
||||||
Serial.print(r->vfo[0]);
|
|
||||||
Serial.print(", VFO B: ");
|
|
||||||
Serial.print(r->vfo[1]);
|
|
||||||
Serial.print(", Data Size: ");
|
|
||||||
Serial.print(len);
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateStateFromRaduino(UBitxRigState& r) {
|
|
||||||
// Note, we really need to be checking a dirty flag for this. But, I don't have a dirty flag in this version of the data type...
|
|
||||||
if (vfoActive == VFO_A) {
|
|
||||||
rigState.vfo[0] = frequency;
|
|
||||||
rigState.flags &= ~UBITX_VFOB_FLAG;
|
|
||||||
} else if (vfoActive == VFO_B) {
|
|
||||||
rigState.vfo[1] = frequency;
|
|
||||||
rigState.flags |= UBITX_VFOB_FLAG;
|
|
||||||
}
|
|
||||||
rigState.rit = ritRxFrequency - frequency;
|
|
||||||
rigState.flags = 0;
|
|
||||||
rigState.flags |= (vfoActive == VFO_B ? UBITX_VFOB_FLAG : 0);
|
|
||||||
rigState.flags |= (cwMode != 0 ? UBITX_CW_FLAG : 0);
|
|
||||||
rigState.flags |= (isUSB != 0 ? UBITX_USB_FLAG : 0);
|
|
||||||
rigState.flags |= (splitOn != 0 ? UBITX_SPLIT_FLAG : 0);
|
|
||||||
rigState.flags |= (ritOn != 0 ? UBITX_RIT_FLAG : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateRaduinoFromState(UBitxRigState& r) {
|
|
||||||
vfoActive = rigState.flags & UBITX_VFOB_FLAG ? VFO_B : VFO_A;
|
|
||||||
if (vfoActive == VFO_A) {
|
|
||||||
if (rigState.vfo[0] != frequency) {
|
|
||||||
setFrequency(rigState.vfo[0]);
|
|
||||||
}
|
|
||||||
} else if (vfoActive == VFO_B) {
|
|
||||||
if (rigState.vfo[1] != frequency) {
|
|
||||||
setFrequency(rigState.vfo[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ritRxFrequency = frequency + rigState.rit;
|
|
||||||
splitOn = rigState.flags & UBITX_SPLIT_FLAG ? 1 : 0;
|
|
||||||
ritOn = rigState.flags & UBITX_RIT_FLAG ? 1 : 0;
|
|
||||||
isUSB = rigState.flags & UBITX_USB_FLAG ? 1 : 0;
|
|
||||||
if (rigState.flags & UBITX_CW_FLAG) {
|
|
||||||
cwMode = isUSB ? 2 : 1; // 2 = cwu / 1 = cwl
|
|
||||||
} else {
|
|
||||||
cwMode = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
BIN
Schematics/SW_Architecture.odg
Normal file
BIN
Schematics/SW_Architecture.odg
Normal file
Binary file not shown.
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include "DSP.h"
|
#include "DSP.h"
|
||||||
|
|
||||||
#include <Audio.h>
|
|
||||||
#include <i2c_t3.h>
|
#include <i2c_t3.h>
|
||||||
//#include <Wire.h>
|
//#include <Wire.h>
|
||||||
//#include <SPI.h>
|
//#include <SPI.h>
|
||||||
@@ -13,35 +12,60 @@
|
|||||||
|
|
||||||
#define RX_AUDIO_CH 0
|
#define RX_AUDIO_CH 0
|
||||||
|
|
||||||
#define TX_MIC_IN_CH 0
|
const int txMicInChannel = TX_MIC_IN_CH;
|
||||||
#define TX_LINE_IN_CH 0
|
const int txLineInChannel = TX_LINE_IN_CH;
|
||||||
#define TX_USB_IN_CH1 1
|
const int txUSBInChannel = TX_USB_IN_CH;
|
||||||
#define TX_USB_IN_CH2 2
|
const int txNumChannels = TX_NUM_CHANNELS;
|
||||||
|
|
||||||
|
const int txLineInVOX = TX_LINE_IN_VOX;
|
||||||
|
const int txUSBInLVOX = TX_USB_IN_L_VOX;
|
||||||
|
const int txUSBInRVOX = TX_USB_IN_R_VOX;
|
||||||
|
const int txNumVOX = TX_NUM_VOX;
|
||||||
|
|
||||||
|
|
||||||
UBitxDSP DSP;
|
UBitxDSP DSP;
|
||||||
|
|
||||||
//static struct {
|
//static struct {
|
||||||
|
|
||||||
// GUItool: begin automatically generated code
|
// GUItool: begin automatically generated code
|
||||||
AudioInputI2S lineIn; //xy=385.9371643066406,1001.9600830078125
|
AudioInputUSB usbIn; //xy=153,341
|
||||||
AudioInputUSB usbIn; //xy=390.9371643066406,1107.9600830078125
|
AudioInputI2S lineIn; //xy=161,233
|
||||||
AudioMixer4 rxAudio; //xy=642.9371643066406,915.9600830078125
|
AudioAnalyzeRMS usbInRMS_R; //xy=276,431
|
||||||
AudioMixer4 txAudio; //xy=646.9371643066406,1103.9600830078125
|
AudioAnalyzeRMS usbInRMS_L; //xy=335,392
|
||||||
AudioOutputUSB usbOut; //xy=1022.9371643066406,919.9600830078125
|
AudioAnalyzeRMS lineInRMS; //xy=387,273
|
||||||
AudioOutputI2S lineOut; //xy=1024.9371643066406,1017.9600830078125
|
AudioMixer4 rxAudio; //xy=418,147
|
||||||
AudioConnection patchCord1(lineIn, 0, rxAudio, 0);
|
AudioMixer4 txAudio; //xy=422,335
|
||||||
AudioConnection patchCord2(lineIn, 1, txAudio, 0);
|
AudioFilterFIR rxFilter; //xy=583,139
|
||||||
AudioConnection patchCord3(usbIn, 0, txAudio, 1);
|
AudioAmplifier usbOutAmp; //xy=748,135
|
||||||
AudioConnection patchCord4(usbIn, 1, txAudio, 2);
|
AudioAmplifier lineOutAmp; //xy=749,198
|
||||||
AudioConnection patchCord5(rxAudio, 0, lineOut, 0);
|
AudioAmplifier usbBypassAmp; //xy=756,261
|
||||||
AudioConnection patchCord6(rxAudio, 0, usbOut, 0);
|
AudioOutputI2S lineOut; //xy=966,330
|
||||||
AudioConnection patchCord7(rxAudio, 0, usbOut, 1);
|
AudioOutputUSB usbOut; //xy=968,291
|
||||||
AudioConnection patchCord8(txAudio, 0, lineOut, 1);
|
AudioConnection patchCord1(usbIn, 0, txAudio, 1);
|
||||||
AudioControlSGTL5000 audioCtrl; //xy=651.9371643066406,1244.9600830078125
|
AudioConnection patchCord2(usbIn, 0, usbInRMS_L, 0);
|
||||||
|
AudioConnection patchCord3(usbIn, 1, usbInRMS_R, 0);
|
||||||
|
AudioConnection patchCord4(lineIn, 0, rxAudio, 0);
|
||||||
|
AudioConnection patchCord5(lineIn, 1, txAudio, 0);
|
||||||
|
AudioConnection patchCord6(lineIn, 1, lineInRMS, 0);
|
||||||
|
AudioConnection patchCord7(rxAudio, rxFilter);
|
||||||
|
AudioConnection patchCord8(rxAudio, usbBypassAmp);
|
||||||
|
AudioConnection patchCord9(txAudio, 0, lineOut, 1);
|
||||||
|
AudioConnection patchCord10(rxFilter, usbOutAmp);
|
||||||
|
AudioConnection patchCord11(rxFilter, lineOutAmp);
|
||||||
|
AudioConnection patchCord12(usbOutAmp, 0, usbOut, 0);
|
||||||
|
AudioConnection patchCord13(lineOutAmp, 0, lineOut, 0);
|
||||||
|
AudioConnection patchCord14(usbBypassAmp, 0, usbOut, 1);
|
||||||
|
AudioControlSGTL5000 audioCtrl; //xy=427,476
|
||||||
// GUItool: end automatically generated code
|
// GUItool: end automatically generated code
|
||||||
|
|
||||||
//} audio;
|
//} audio;
|
||||||
|
|
||||||
|
UBitxDSP::UBitxDSP() {
|
||||||
|
voxRMS[txLineInVOX] = &lineInRMS;
|
||||||
|
voxRMS[txUSBInLVOX] = &usbInRMS_L;
|
||||||
|
voxRMS[txUSBInRVOX] = &usbInRMS_R;
|
||||||
|
}
|
||||||
|
|
||||||
void UBitxDSP::begin() {
|
void UBitxDSP::begin() {
|
||||||
AudioMemory(16);
|
AudioMemory(16);
|
||||||
audioCtrl.enable();
|
audioCtrl.enable();
|
||||||
@@ -77,17 +101,59 @@ void UBitxDSP::begin() {
|
|||||||
// SETUP THE AUDIO OUTPUTS
|
// SETUP THE AUDIO OUTPUTS
|
||||||
|
|
||||||
// Line Output (RX)
|
// Line Output (RX)
|
||||||
|
lineOutAmp.gain(1.0);
|
||||||
|
|
||||||
// USB Output (RX)
|
// USB Output (RX)
|
||||||
|
usbOutAmp.gain(1.0);
|
||||||
|
usbBypassAmp.gain(1.0);
|
||||||
|
|
||||||
// Rig (Line) Output (TX)
|
// Rig (Line) Output (TX)
|
||||||
|
|
||||||
// Default to RX.
|
// Default to RX.
|
||||||
rx();
|
rx();
|
||||||
|
|
||||||
|
// Setup the VOX - clean this up
|
||||||
|
state.voxActive[TX_LINE_IN_VOX] = false;
|
||||||
|
state.voxThresh[TX_LINE_IN_VOX] = TX_LINE_IN_VOX_THRESH;
|
||||||
|
state.voxDelay[TX_LINE_IN_VOX] = TX_LINE_IN_VOX_DELAY;
|
||||||
|
state.voxTimeout[TX_LINE_IN_VOX] = 0;
|
||||||
|
state.voxActive[TX_USB_IN_L_VOX] = false;
|
||||||
|
state.voxThresh[TX_USB_IN_L_VOX] = TX_USB_IN_L_VOX_THRESH;
|
||||||
|
state.voxDelay[TX_USB_IN_L_VOX] = TX_USB_IN_L_VOX_DELAY;
|
||||||
|
state.voxTimeout[TX_USB_IN_L_VOX] = 0;
|
||||||
|
state.voxActive[TX_USB_IN_R_VOX] = false;
|
||||||
|
state.voxThresh[TX_USB_IN_R_VOX] = TX_USB_IN_R_VOX_THRESH;
|
||||||
|
state.voxDelay[TX_USB_IN_R_VOX] = TX_USB_IN_R_VOX_DELAY;
|
||||||
|
state.voxTimeout[TX_USB_IN_R_VOX] = 0;
|
||||||
|
|
||||||
|
// Setup the RX Filter.
|
||||||
|
setRxFilter(300, 3000);
|
||||||
|
|
||||||
|
sinceLastUpdate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UBitxDSP::update() {
|
void UBitxDSP::update() {
|
||||||
|
// Only going to adjust the USB volume periodically.
|
||||||
|
if (sinceLastUpdate > DSP_MILLIS_PER_UPDATE) {
|
||||||
|
float vol = usbIn.volume();
|
||||||
|
setTxInputLevel(txUSBInChannel, vol);
|
||||||
|
sinceLastUpdate = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the VOX switches.
|
||||||
|
// TODO: Move the enable logic in here, so we don't process unnecessarily.
|
||||||
|
for (int i = 0; i < txNumVOX; i++) {
|
||||||
|
if (voxRMS[i]->available()) {
|
||||||
|
float lvl = voxRMS[i]->read();
|
||||||
|
if (lvl > state.voxThresh[i]) {
|
||||||
|
state.voxTimeout[i] = millis() + state.voxDelay[i];
|
||||||
|
state.voxActive[i] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (millis() > state.voxTimeout[i]) {
|
||||||
|
state.voxActive[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UBitxDSP::end() {
|
void UBitxDSP::end() {
|
||||||
@@ -138,13 +204,157 @@ void UBitxDSP::txUSBIn() {
|
|||||||
// restore the tx usb in audio
|
// restore the tx usb in audio
|
||||||
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
|
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (i == TX_USB_IN_CH1 || i == TX_USB_IN_CH2)
|
if (i == TX_USB_IN_CH)
|
||||||
txAudio.gain(i, 0.1);
|
txAudio.gain(i, 0.1);
|
||||||
else
|
else
|
||||||
txAudio.gain(i, 0.0);
|
txAudio.gain(i, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
// RX filter settings
|
||||||
|
|
||||||
|
const float minRxFilterLo = MIN_RX_FILTER_LO;
|
||||||
|
const float maxRxFilterHi = MAX_RX_FILTER_HI;
|
||||||
|
const float minRxFilterWidth = MIN_RX_FILTER_WIDTH;
|
||||||
|
const float maxRxFilterWidth = MAX_RX_FILTER_WIDTH;
|
||||||
|
const float minRxFilterCenter = MIN_RX_FILTER_CENTER;
|
||||||
|
const float maxRxFilterCenter = MAX_RX_FILTER_CENTER;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Bypass the RX audio filter.
|
||||||
|
*/
|
||||||
|
void UBitxDSP::bypassRxFilter() {
|
||||||
|
rxFilter.begin(FIR_PASSTHRU, NUM_COEFFICIENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Update the RX audio filter using the currently set low and
|
||||||
|
* high frequencies. This is called by each of the public
|
||||||
|
* filter methods to update the filter with new frequencies.
|
||||||
|
*/
|
||||||
|
void UBitxDSP::updateRxFilter() {
|
||||||
|
audioFilter(coefficients, NUM_COEFFICIENTS, ID_BANDPASS, W_HAMMING, double(state.rxFilterLo), double(state.rxFilterHi));
|
||||||
|
rxFilter.begin(coefficients, NUM_COEFFICIENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::setRxFilter(float lo, float hi) {
|
||||||
|
if (hi < lo + minRxFilterWidth) {
|
||||||
|
hi = lo + minRxFilterWidth;
|
||||||
|
}
|
||||||
|
if (hi > maxRxFilterHi) {
|
||||||
|
hi = maxRxFilterHi;
|
||||||
|
}
|
||||||
|
if (lo > hi - minRxFilterWidth) {
|
||||||
|
lo = hi - minRxFilterWidth;
|
||||||
|
}
|
||||||
|
if (lo < minRxFilterLo) {
|
||||||
|
lo = minRxFilterLo;
|
||||||
|
}
|
||||||
|
state.rxFilterHi = hi;
|
||||||
|
state.rxFilterLo = lo;
|
||||||
|
updateRxFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::setRxFilterLo(float lo) {
|
||||||
|
if (lo > state.rxFilterHi - minRxFilterWidth) {
|
||||||
|
lo = state.rxFilterHi - minRxFilterWidth;
|
||||||
|
}
|
||||||
|
if (lo < minRxFilterLo) {
|
||||||
|
lo = minRxFilterLo;
|
||||||
|
}
|
||||||
|
state.rxFilterLo = lo;
|
||||||
|
updateRxFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::setRxFilterHi(float hi) {
|
||||||
|
if (hi < state.rxFilterLo + minRxFilterWidth) {
|
||||||
|
hi = state.rxFilterLo + minRxFilterWidth;
|
||||||
|
}
|
||||||
|
if (hi > maxRxFilterHi) {
|
||||||
|
hi = maxRxFilterHi;
|
||||||
|
}
|
||||||
|
state.rxFilterHi = hi;
|
||||||
|
updateRxFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::setRxFilterWidth(float width) {
|
||||||
|
if (width < minRxFilterWidth) {
|
||||||
|
width = minRxFilterWidth;
|
||||||
|
} else if (width > maxRxFilterWidth) {
|
||||||
|
width = maxRxFilterWidth;
|
||||||
|
}
|
||||||
|
float center = (state.rxFilterHi + state.rxFilterLo) / 2;
|
||||||
|
float lo = center - (width / 2);
|
||||||
|
float hi = center + (width / 2);
|
||||||
|
setRxFilter(lo, hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::setRxFilterCenter(float center) {
|
||||||
|
if (center < minRxFilterCenter) {
|
||||||
|
center = minRxFilterCenter;
|
||||||
|
} else if (center > maxRxFilterCenter) {
|
||||||
|
center = maxRxFilterCenter;
|
||||||
|
}
|
||||||
|
float width = state.rxFilterHi - state.rxFilterLo;
|
||||||
|
float lo = center - (width / 2);
|
||||||
|
float hi = center + (width / 2);
|
||||||
|
setRxFilter(lo, hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
// TX audio input settings
|
||||||
|
|
||||||
|
void UBitxDSP::setTxInputLevel(int ch, float lvl) {
|
||||||
|
if ((ch > -1) && (ch < txNumChannels)) {
|
||||||
|
state.txInLvl[ch] = lvl;
|
||||||
|
float vol = lvl * float(state.txInEnable[ch] * state.txInTx[ch]);
|
||||||
|
txAudio.gain(ch, vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::enableTxInput(int ch) {
|
||||||
|
if ((ch > -1) && (ch < txNumChannels)) {
|
||||||
|
state.txInEnable[ch] = 1;
|
||||||
|
float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]);
|
||||||
|
txAudio.gain(ch, vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::disableTxInput(int ch) {
|
||||||
|
if ((ch > -1) && (ch < txNumChannels)) {
|
||||||
|
state.txInEnable[ch] = 0;
|
||||||
|
float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]);
|
||||||
|
txAudio.gain(ch, vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::startTxInput(int ch) {
|
||||||
|
if ((ch > -1) && (ch < txNumChannels)) {
|
||||||
|
state.txInTx[ch] = 1;
|
||||||
|
float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]);
|
||||||
|
txAudio.gain(ch, vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxDSP::stopTxInput(int ch) {
|
||||||
|
if ((ch > -1) && (ch < txNumChannels)) {
|
||||||
|
state.txInTx[ch] = 0;
|
||||||
|
float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]);
|
||||||
|
txAudio.gain(ch, vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VOX settings
|
||||||
|
bool UBitxDSP::isVoxActive(int vox) {
|
||||||
|
if ((vox > -1) && (vox < 3)) {
|
||||||
|
return state.voxActive[vox];
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// EOF
|
// EOF
|
||||||
//======================================================================
|
//======================================================================
|
||||||
|
104
TeensyDSP/DSP.h
104
TeensyDSP/DSP.h
@@ -5,8 +5,57 @@
|
|||||||
#ifndef __DSP_h__
|
#ifndef __DSP_h__
|
||||||
#define __DSP_h__
|
#define __DSP_h__
|
||||||
|
|
||||||
|
#include <Audio.h>
|
||||||
|
#include <dynamicFilters.h>
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
|
|
||||||
|
#define MIN_RX_FILTER_LO 0.0
|
||||||
|
#define MAX_RX_FILTER_HI 5000.0
|
||||||
|
#define MIN_RX_FILTER_WIDTH 0.0
|
||||||
|
#define MAX_RX_FILTER_WIDTH 5000.0
|
||||||
|
#define MIN_RX_FILTER_CENTER 0.0
|
||||||
|
#define MAX_RX_FILTER_CENTER 5000.0
|
||||||
|
|
||||||
|
#define DSP_MILLIS_PER_UPDATE 100
|
||||||
|
|
||||||
|
#define TX_MIC_IN_CH 0
|
||||||
|
#define TX_LINE_IN_CH 0
|
||||||
|
#define TX_USB_IN_CH 1
|
||||||
|
#define TX_NUM_CHANNELS 2
|
||||||
|
|
||||||
|
#define TX_LINE_IN_VOX 0
|
||||||
|
#define TX_USB_IN_L_VOX 1
|
||||||
|
#define TX_USB_IN_R_VOX 2
|
||||||
|
#define TX_NUM_VOX 3
|
||||||
|
|
||||||
|
#define TX_LINE_IN_VOX_THRESH 0.25
|
||||||
|
#define TX_USB_IN_L_VOX_THRESH 0.25
|
||||||
|
#define TX_USB_IN_R_VOX_THRESH 0.25
|
||||||
|
|
||||||
|
#define TX_LINE_IN_VOX_DELAY 500
|
||||||
|
#define TX_USB_IN_L_VOX_DELAY 500
|
||||||
|
#define TX_USB_IN_R_VOX_DELAY 500
|
||||||
|
|
||||||
|
struct DSPState {
|
||||||
|
|
||||||
|
// RX audio output settings
|
||||||
|
|
||||||
|
// RX filter settings
|
||||||
|
float rxFilterLo = 300.0;
|
||||||
|
float rxFilterHi = 3000.0;
|
||||||
|
|
||||||
|
// TX audio input settings
|
||||||
|
float txInLvl[4] = {0.5, 0.5, 0.0, 0.0};
|
||||||
|
int txInEnable[4] = {1, 1, 0, 0};
|
||||||
|
int txInTx[4] = {0, 0, 0, 0};
|
||||||
|
|
||||||
|
// VOX settings
|
||||||
|
bool voxActive[3];
|
||||||
|
float voxThresh[3];
|
||||||
|
unsigned voxDelay[3];
|
||||||
|
unsigned voxTimeout[3];
|
||||||
|
};
|
||||||
|
|
||||||
enum TRState {
|
enum TRState {
|
||||||
TRANSMIT,
|
TRANSMIT,
|
||||||
RECEIVE
|
RECEIVE
|
||||||
@@ -24,7 +73,7 @@ enum TxAudioIn {
|
|||||||
|
|
||||||
class UBitxDSP {
|
class UBitxDSP {
|
||||||
public:
|
public:
|
||||||
UBitxDSP() {};
|
UBitxDSP();
|
||||||
void begin();
|
void begin();
|
||||||
void update();
|
void update();
|
||||||
void end();
|
void end();
|
||||||
@@ -32,6 +81,59 @@ class UBitxDSP {
|
|||||||
void txMicIn();
|
void txMicIn();
|
||||||
void txLineIn();
|
void txLineIn();
|
||||||
void txUSBIn();
|
void txUSBIn();
|
||||||
|
|
||||||
|
// RX audio output settings
|
||||||
|
|
||||||
|
// RX filter settings
|
||||||
|
void bypassRxFilter();
|
||||||
|
void setRxFilter(float lo, float hi);
|
||||||
|
void setRxFilterLo(float lo);
|
||||||
|
void setRxFilterHi(float hi);
|
||||||
|
void setRxFilterWidth(float width);
|
||||||
|
void setRxFilterCenter(float center);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Get the current low frequency bound of the RX band pass filter.
|
||||||
|
* @return The low frequency bound.
|
||||||
|
*/
|
||||||
|
inline int getRxFilterLo() { return state.rxFilterLo; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Get the current high frequency bound of the RX band pass filter.
|
||||||
|
* @return The high frequency bound.
|
||||||
|
*/
|
||||||
|
inline int getRxFilterHi() { return state.rxFilterHi; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Get the current width of the RX band pass filter.
|
||||||
|
* @return The filter width.
|
||||||
|
*/
|
||||||
|
inline int getRxFilterWidth() { return state.rxFilterHi - state.rxFilterLo; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Get the current center frequency of the RX band pass filter.
|
||||||
|
* @return The center frequency.
|
||||||
|
*/
|
||||||
|
inline int getRxFilterCenter() { return (state.rxFilterHi + state.rxFilterLo) / 2; }
|
||||||
|
|
||||||
|
// TX audio input settings
|
||||||
|
void setTxInputLevel(int ch, float lvl);
|
||||||
|
void enableTxInput(int ch);
|
||||||
|
void disableTxInput(int ch);
|
||||||
|
void startTxInput(int ch);
|
||||||
|
void stopTxInput(int ch);
|
||||||
|
|
||||||
|
// VOX settings
|
||||||
|
bool isVoxActive(int vox);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateRxFilter();
|
||||||
|
|
||||||
|
DSPState state;
|
||||||
|
short coefficients[NUM_COEFFICIENTS];
|
||||||
|
elapsedMillis sinceLastUpdate;
|
||||||
|
|
||||||
|
AudioAnalyzeRMS* voxRMS[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern UBitxDSP DSP;
|
extern UBitxDSP DSP;
|
||||||
|
@@ -8,11 +8,13 @@
|
|||||||
#define DBGPRINTLN(MSG) do { Serial.print("DBG: "); Serial.println(MSG); } while (0)
|
#define DBGPRINTLN(MSG) do { Serial.print("DBG: "); Serial.println(MSG); } while (0)
|
||||||
#define DBGNEWLINE() do { Serial.println(); } while (0)
|
#define DBGNEWLINE() do { Serial.println(); } while (0)
|
||||||
#define DBGCMD(CMD) do { Serial.print("DBG: "); Serial.println(#CMD); CMD; } while (0)
|
#define DBGCMD(CMD) do { Serial.print("DBG: "); Serial.println(#CMD); CMD; } while (0)
|
||||||
|
#define IFDEBUG(CMD) do { CMD; } while (0)
|
||||||
#else
|
#else
|
||||||
#define DBGPRINT(MSG) do {} while (0)
|
#define DBGPRINT(MSG) do {} while (0)
|
||||||
#define DBGPRINTLN(MSG) do {} while (0)
|
#define DBGPRINTLN(MSG) do {} while (0)
|
||||||
#define DBGNEWLINE() do {} while (0)
|
#define DBGNEWLINE() do {} while (0)
|
||||||
#define DBGCMD(CMD) do { CMD; } while (0)
|
#define DBGCMD(CMD) do { CMD; } while (0)
|
||||||
|
#define IFDEBUG(CMD) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -61,7 +61,7 @@ speed(wpm), symWeight(weight)
|
|||||||
// Calculate the length of dot, dash and silence
|
// Calculate the length of dot, dash and silence
|
||||||
void UBitxKeyer::calcRatio()
|
void UBitxKeyer::calcRatio()
|
||||||
{
|
{
|
||||||
float w = (1 + symWeight) / (symWeight -1);
|
float w = (1.0 + symWeight) / (symWeight - 1.0);
|
||||||
spaceLen = (1200 / speed);
|
spaceLen = (1200 / speed);
|
||||||
dotLen = spaceLen * (w - 1);
|
dotLen = spaceLen * (w - 1);
|
||||||
dashLen = (1 + w) * spaceLen;
|
dashLen = (1 + w) * spaceLen;
|
||||||
@@ -73,6 +73,12 @@ void UBitxKeyer::setWPM(int wpm)
|
|||||||
calcRatio();
|
calcRatio();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UBitxKeyer::setWeight(float weight)
|
||||||
|
{
|
||||||
|
symWeight = weight;
|
||||||
|
calcRatio();
|
||||||
|
}
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// Latch paddle press
|
// Latch paddle press
|
||||||
//======================================================================
|
//======================================================================
|
||||||
@@ -107,11 +113,10 @@ bool UBitxKeyer::doPaddles()
|
|||||||
if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW) || (keyerControl & 0x03)) {
|
if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW) || (keyerControl & 0x03)) {
|
||||||
updatePaddleLatch();
|
updatePaddleLatch();
|
||||||
keyerState = CHK_DIT;
|
keyerState = CHK_DIT;
|
||||||
// letting this fall through // return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
// break;
|
return false;
|
||||||
|
//break;
|
||||||
|
|
||||||
case CHK_DIT: // See if the dit paddle was pressed
|
case CHK_DIT: // See if the dit paddle was pressed
|
||||||
if (keyerControl & DIT_L) {
|
if (keyerControl & DIT_L) {
|
||||||
@@ -119,59 +124,57 @@ bool UBitxKeyer::doPaddles()
|
|||||||
ktimer = dotLen;
|
ktimer = dotLen;
|
||||||
keyerState = KEYED_PREP;
|
keyerState = KEYED_PREP;
|
||||||
return true;
|
return true;
|
||||||
} else { // fall through
|
|
||||||
keyerState = CHK_DAH;
|
|
||||||
}
|
}
|
||||||
|
// fall through
|
||||||
|
keyerState = CHK_DAH;
|
||||||
|
|
||||||
case CHK_DAH: // See if dah paddle was pressed
|
case CHK_DAH: // See if dah paddle was pressed
|
||||||
if (keyerControl & DAH_L) {
|
if (keyerControl & DAH_L) {
|
||||||
ktimer = dashLen;
|
ktimer = dashLen;
|
||||||
keyerState = KEYED_PREP;
|
keyerState = KEYED_PREP;
|
||||||
// letting this fall through // return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
keyerState = IDLE;
|
keyerState = IDLE;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// break;
|
//break;
|
||||||
|
|
||||||
case KEYED_PREP: // Assert key down, start timing
|
case KEYED_PREP: // Assert key down, start timing
|
||||||
// state shared for dit or dah
|
// state shared for dit or dah
|
||||||
keyDown = true;
|
keyDown = true;
|
||||||
ktimer += millis(); // set ktimer to interval end time
|
ktimer += millis(); // set ktimer to interval end time
|
||||||
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
|
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
|
||||||
keyerState = KEYED; // next state
|
keyerState = KEYED; // next state
|
||||||
// letting this fall through // return true;
|
return true;
|
||||||
// break;
|
//break;
|
||||||
|
|
||||||
case KEYED: // Wait for timer to expire
|
case KEYED: // Wait for timer to expire
|
||||||
if (millis() > ktimer) { // are we at end of key down ?
|
if (millis() > ktimer) { // are we at end of key down ?
|
||||||
keyDown = false;
|
keyDown = false;
|
||||||
ktimer = millis() + spaceLen; // inter-element time
|
ktimer = millis() + spaceLen; // inter-element time
|
||||||
keyerState = INTER_ELEMENT; // next state
|
keyerState = INTER_ELEMENT; // next state
|
||||||
// letting this fall through // return true;
|
return true;
|
||||||
} else if (keyMode == IAMBICB) { // Iambic B Mode ?
|
} else if (keyMode == IAMBICB) { // Iambic B Mode ?
|
||||||
updatePaddleLatch(); // yes, early paddle latch in Iambic B mode
|
updatePaddleLatch(); // yes, early paddle latch in Iambic B mode
|
||||||
} else {
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
// break;
|
||||||
// break;
|
|
||||||
|
|
||||||
case INTER_ELEMENT: // Insert time between dits/dahs
|
case INTER_ELEMENT: // Insert time between dits/dahs
|
||||||
updatePaddleLatch(); // latch paddle state
|
updatePaddleLatch(); // latch paddle state
|
||||||
if (millis() > ktimer) { // are we at end of inter-space ?
|
if (millis() > ktimer) { // are we at end of inter-space ?
|
||||||
if (keyerControl & DIT_PROC) { // was it a dit or dah ?
|
if (keyerControl & DIT_PROC) { // was it a dit or dah ?
|
||||||
keyerControl &= ~(DIT_L + DIT_PROC); // clear two bits
|
keyerControl &= ~(DIT_L + DIT_PROC); // clear two bits
|
||||||
keyerState = CHK_DAH; // dit done, check for dah
|
keyerState = CHK_DAH; // dit done, check for dah
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
keyerControl &= ~(DAH_L); // clear dah latch
|
|
||||||
keyerState = IDLE; // go idle
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
keyerControl &= ~(DAH_L); // clear dah latch
|
||||||
|
keyerState = IDLE; // go idle
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// break;
|
}
|
||||||
|
return true;
|
||||||
|
//break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // resolve compiler warning; do we ever get here?
|
return false; // resolve compiler warning; do we ever get here?
|
||||||
|
@@ -44,10 +44,12 @@ public:
|
|||||||
//void cw_pin(int pin);
|
//void cw_pin(int pin);
|
||||||
//void ptt_pin(int pin);
|
//void ptt_pin(int pin);
|
||||||
void setWPM(int wpm);
|
void setWPM(int wpm);
|
||||||
|
inline int getWPM() { return speed; }
|
||||||
|
void setWeight(float weight);
|
||||||
|
inline float getWeight() { return symWeight; }
|
||||||
inline void setMode(int mode) { keyMode = mode; }
|
inline void setMode(int mode) { keyMode = mode; }
|
||||||
inline int getMode() { return keyMode; }
|
inline int getMode() { return keyMode; }
|
||||||
inline bool isDown() { return keyDown; }
|
inline bool isDown() { return keyDown; }
|
||||||
// void setWeight();
|
|
||||||
|
|
||||||
bool doPaddles();
|
bool doPaddles();
|
||||||
|
|
||||||
|
203
TeensyDSP/Rig.h
203
TeensyDSP/Rig.h
@@ -3,143 +3,122 @@
|
|||||||
|
|
||||||
#include "RigState.h"
|
#include "RigState.h"
|
||||||
|
|
||||||
enum UpdateSource {
|
#define DEFAULT_SSB_LO_CUT 300.0
|
||||||
NoSource,
|
#define DEFAULT_SSB_HI_CUT 3000.0
|
||||||
RaduinoSource,
|
#define DEFAULT_CW_WIDTH 500.0
|
||||||
CATSource
|
#define DEFAULT_LOW_USB false
|
||||||
|
#define DEFAULT_LOW_CWU true
|
||||||
|
#define DEFAULT_HIGH_USB true
|
||||||
|
#define DEFAULT_HIGH_CWU true
|
||||||
|
|
||||||
|
enum HamBand {
|
||||||
|
BAND_80M = 0,
|
||||||
|
BAND_60M,
|
||||||
|
BAND_40M,
|
||||||
|
BAND_30M,
|
||||||
|
BAND_20M,
|
||||||
|
BAND_17M,
|
||||||
|
BAND_15M,
|
||||||
|
BAND_12M,
|
||||||
|
BAND_10M,
|
||||||
|
NUM_BANDS
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModeConfig {
|
||||||
|
bool isUpper;
|
||||||
|
float dspLo;
|
||||||
|
float dspHi;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BandConfig {
|
||||||
|
ModeConfig cw;
|
||||||
|
ModeConfig ssb;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RigConfig {
|
||||||
|
//bool isData = false;
|
||||||
|
bool useUSBInput = true; // whether or not to use the USB input for data
|
||||||
};
|
};
|
||||||
|
|
||||||
class UBitxRig {
|
class UBitxRig {
|
||||||
public:
|
public:
|
||||||
|
UBitxRig();
|
||||||
|
|
||||||
inline void begin() {}
|
inline void begin() {}
|
||||||
inline void update() {}
|
inline void update() {}
|
||||||
inline unsigned long getFreqA() const { return state.vfo[0]; }
|
|
||||||
inline unsigned long getFreqB() const { return state.vfo[1]; }
|
|
||||||
inline long getRIT() const { return state.rit; }
|
|
||||||
inline long getXIT() const { return state.xit; }
|
|
||||||
inline bool isVFOA() const { return (state.flags & UBITX_VFOB_FLAG) != UBITX_VFOB_FLAG; }
|
|
||||||
inline bool isVFOB() const { return (state.flags & UBITX_VFOB_FLAG) == UBITX_VFOB_FLAG; }
|
|
||||||
inline bool isSplit() const { return (state.flags & UBITX_SPLIT_FLAG) == UBITX_SPLIT_FLAG; }
|
|
||||||
inline bool isRITOn() const { return (state.flags & UBITX_RIT_FLAG) == UBITX_RIT_FLAG; }
|
|
||||||
inline bool isXITOn() const { return (state.flags & UBITX_XIT_FLAG) == UBITX_XIT_FLAG; }
|
|
||||||
inline bool isCW() const { return (state.flags & UBITX_CW_FLAG) == UBITX_CW_FLAG; }
|
|
||||||
inline bool isLSB() const { return (state.flags & UBITX_USB_FLAG) != UBITX_USB_FLAG; }
|
|
||||||
inline bool isUSB() const { return (state.flags & UBITX_USB_FLAG) == UBITX_USB_FLAG; }
|
|
||||||
inline bool getAI() const { return autoInfo; }
|
|
||||||
inline bool updatedByCAT() const { return lastUpdatedBy == CATSource; }
|
|
||||||
inline bool updatedByRaduino() const { return lastUpdatedBy == RaduinoSource; }
|
|
||||||
|
|
||||||
inline void clearUpdate() { lastUpdatedBy = NoSource; }
|
inline unsigned getFreqA() const { return radState.getFreqA(); }
|
||||||
inline void setRaduinoUpdate() { lastUpdatedBy = RaduinoSource; }
|
inline unsigned getFreqB() const { return radState.getFreqB(); }
|
||||||
inline void setCATUpdate() { lastUpdatedBy = CATSource; }
|
|
||||||
|
|
||||||
inline void setFreqA(unsigned long freq, bool isCAT = false) {
|
inline int getRIT() const { return radState.getRIT(); }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
inline int getXIT() const { return radState.getXIT(); }
|
||||||
state.vfo[0] = freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void setFreqB(unsigned long freq, bool isCAT = false) {
|
inline bool isVFOA() const { return radState.isVFOA(); }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
inline bool isVFOB() const { return radState.isVFOB(); }
|
||||||
state.vfo[1] = freq;
|
inline bool isSplit() const { return radState.isSplit(); }
|
||||||
}
|
inline bool isRIT() const { return radState.isRIT(); }
|
||||||
|
inline bool isXIT() const { return radState.isXIT(); }
|
||||||
|
inline bool isModeCWAny() const { return radState.isModeCWAny(); }
|
||||||
|
inline bool isModeCW() const { return radState.isModeCW(); }
|
||||||
|
inline bool isModeCWR() const { return radState.isModeCWR(); }
|
||||||
|
inline bool isModeUSB() const { return radState.isModeUSB(); }
|
||||||
|
inline bool isModeLSB() const { return radState.isModeLSB(); }
|
||||||
|
|
||||||
inline void setRIT(short offset, bool isCAT = false) {
|
inline float getCWSidetone() const { return static_cast<float>(radState.getSidetone()); }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.rit = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void setXIT(short offset, bool isCAT = false) {
|
inline bool isUSBInput() const { return conf.useUSBInput; }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
inline bool isLineInput() const { return !conf.useUSBInput; }
|
||||||
state.xit = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void selectVFOA(bool isCAT = false) {
|
inline bool isAI() const { return autoInfo; }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags &= ~UBITX_VFOB_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void selectVFOB(bool isCAT = false) {
|
inline void setFreqA(unsigned freq) { catState.setFreqA(freq); }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
inline void setFreqB(unsigned freq) { catState.setFreqB(freq); }
|
||||||
state.flags |= UBITX_VFOB_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void toggleVFO(bool isCAT = false) {
|
inline void setRIT(int freq) { catState.setRIT(freq); }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
inline void setXIT(int freq) { catState.setXIT(freq); }
|
||||||
state.flags ^= UBITX_VFOB_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void splitOn(bool isCAT = false) {
|
inline void setVFOA() { catState.setVFOA(); }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
inline void setVFOB() { catState.setVFOB(); }
|
||||||
state.flags |= UBITX_SPLIT_FLAG;
|
inline void setSplitOn() { catState.setSplitOn(); }
|
||||||
}
|
inline void setSplitOff() { catState.setSplitOff(); }
|
||||||
|
inline void setRITOn() { catState.setRITOn(); }
|
||||||
|
inline void setRITOff() { catState.setRITOff(); }
|
||||||
|
inline void setXITOn() { catState.setXITOn(); }
|
||||||
|
inline void setXITOff() { catState.setXITOff(); }
|
||||||
|
inline void setModeCW() { catState.setModeCW(); }
|
||||||
|
inline void setModeCWR() { catState.setModeCWR(); }
|
||||||
|
inline void setModeUSB() { catState.setModeUSB(); }
|
||||||
|
inline void setModeLSB() { catState.setModeLSB(); }
|
||||||
|
|
||||||
inline void splitOff(bool isCAT = false) {
|
inline void setCWSidetone(float f) { catState.setSidetone(static_cast<uint16_t>(f)); }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags &= ~UBITX_SPLIT_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ritOn(bool isCAT = false) {
|
inline void setUSBInput() { conf.useUSBInput = true; }
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
inline void setLineInput() { conf.useUSBInput = false; }
|
||||||
state.flags |= UBITX_RIT_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ritOff(bool isCAT = false) {
|
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags &= ~UBITX_RIT_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void xitOn(bool isCAT = false) {
|
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags |= UBITX_XIT_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void xitOff(bool isCAT = false) {
|
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags &= ~UBITX_XIT_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void cwOn(bool isCAT = false) {
|
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags |= UBITX_CW_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void cwOff(bool isCAT = false) {
|
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags &= ~UBITX_CW_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void selectLSB(bool isCAT = false) {
|
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags &= ~UBITX_USB_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void selectUSB(bool isCAT = false) {
|
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
|
||||||
state.flags |= UBITX_USB_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void aiOn() { autoInfo = true; }
|
inline void aiOn() { autoInfo = true; }
|
||||||
inline void aiOff() { autoInfo = false; }
|
inline void aiOff() { autoInfo = false; }
|
||||||
|
|
||||||
uint8_t* const stateAsBytes() const { return (uint8_t* const)&state; }
|
inline UBitxRigState& cat() { return catState; }
|
||||||
|
inline UBitxRigState& rad() { return radState; }
|
||||||
|
|
||||||
inline void updateState(UBitxRigState& r, bool isCAT = false) {
|
/********************************************************************/
|
||||||
if ((r.vfo[0] == state.vfo[0]) &&
|
// New functional/mode-based Rig methods
|
||||||
(r.vfo[1] == state.vfo[1]) &&
|
|
||||||
(r.rit == state.rit) &&
|
// AG
|
||||||
(r.xit == state.xit) &&
|
//void setVolOut(uint8_t level);
|
||||||
(r.flags == state.flags)) {
|
//uint8_t getVolOut();
|
||||||
return;
|
|
||||||
} else {
|
// BD/BU
|
||||||
state = r;
|
//void setBand();
|
||||||
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
|
//void getBand();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
RigConfig conf;
|
||||||
|
UBitxRigState catState;
|
||||||
|
UBitxRigState radState;
|
||||||
bool autoInfo = false;
|
bool autoInfo = false;
|
||||||
UpdateSource lastUpdatedBy = NoSource;
|
|
||||||
UBitxRigState state;
|
BandConfig band[NUM_BANDS];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern UBitxRig Rig;
|
extern UBitxRig Rig;
|
||||||
|
447
TeensyDSP/RigState.cpp
Normal file
447
TeensyDSP/RigState.cpp
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
/*!
|
||||||
|
* @file RigState.cpp
|
||||||
|
*
|
||||||
|
* @mainpage uBITX V5X Software - RigState
|
||||||
|
*
|
||||||
|
* @section introsec Introduction
|
||||||
|
*
|
||||||
|
* TBD
|
||||||
|
*
|
||||||
|
* @section dependencies Dependencies
|
||||||
|
*
|
||||||
|
* TBD
|
||||||
|
*
|
||||||
|
* @section author Author
|
||||||
|
*
|
||||||
|
* Written by Rob "Scrape" French, KC4UPR
|
||||||
|
*
|
||||||
|
* @section license License
|
||||||
|
*
|
||||||
|
* TBD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "RigState.h"
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* COMMON FUNCTIONS
|
||||||
|
*
|
||||||
|
* The following are all common to RigState objects, whether on the
|
||||||
|
* Raduino or on the TeensyDSP.
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
static uint32_t zeroes[1] = {0}; // used to transmit zeroes
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Begin using the RigState object. In order to force an
|
||||||
|
* initial update (i.e. sending current state to the remote
|
||||||
|
* device), all fields are initially marked dirty.
|
||||||
|
*/
|
||||||
|
void UBitxRigState::begin() {
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* RADUINO FUNCTIONS
|
||||||
|
*
|
||||||
|
* The following are specific to the Raduino implementation. Note that
|
||||||
|
* this depends on the use of the TEENSYDUINO #define, which may result
|
||||||
|
* in a fragile implementation for other development environments (e.g.
|
||||||
|
* if the normal Arduino IDE is not being used).
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
#ifndef TEENSYDUINO
|
||||||
|
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "ubitx.h"
|
||||||
|
#include "ubitx_eemap.h"
|
||||||
|
|
||||||
|
extern unsigned long frequency, ritRxFrequency, ritTxFrequency, sideTone;
|
||||||
|
extern unsigned long vfoA;
|
||||||
|
extern unsigned long vfoB;
|
||||||
|
extern char cwMode;
|
||||||
|
extern char isUSB;
|
||||||
|
extern char vfoActive;
|
||||||
|
extern char ritOn;
|
||||||
|
extern char splitOn;
|
||||||
|
extern char inTx;
|
||||||
|
void setFrequency(unsigned long);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Send the RigState from the Raduino to the TeensyDSP. The
|
||||||
|
* basic process is: (1) read in any updated (dirty) data
|
||||||
|
* from the Raduino's state variables; (2) transmit the dirty
|
||||||
|
* data to the TeensyDSP; (2a) for clean data, zeroes are
|
||||||
|
* transmitted; (3) mark all data as clean.
|
||||||
|
*/
|
||||||
|
void UBitxRigState::send_RIGINF() {
|
||||||
|
readDirty();
|
||||||
|
Wire.beginTransmission(I2CMETER_ADDR);
|
||||||
|
Wire.write(I2CMETER_RIGINF);
|
||||||
|
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS; i++) {
|
||||||
|
if (i == DIRTY_WORD || isDirty(i)) {
|
||||||
|
// always send the current dirty bits
|
||||||
|
// or, bytes for updated (dirty) fields
|
||||||
|
Wire.write((byte*)&data[i], sizeof(uint32_t));
|
||||||
|
} else {
|
||||||
|
// otherwise, send out zeroes
|
||||||
|
Wire.write((byte*)&zeroes, sizeof(uint32_t));
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// NOTE: I am sending these zeroed out fields under a possibly
|
||||||
|
// mistaken assumption that in doing so, I will be sending a
|
||||||
|
// constant voltage on the SDA line most of the time, i.e. no
|
||||||
|
// bit changes, and so this will help reduce noise generated by
|
||||||
|
// I2C traffic (since most of the time there will be no updates.)
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Wire.endTransmission();
|
||||||
|
IFDEBUG( serialHexState("Sent") );
|
||||||
|
//IFDEBUG( serialPrettyState("Sent") );
|
||||||
|
setClean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// delay(1); // 1ms - some delay required between ending transmission and requesting?
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Receive the RigState from the TeensyDSP. This generally
|
||||||
|
* reflects changes due to CAT transmission to the TeensyDSP.
|
||||||
|
* @param numBytes
|
||||||
|
* Number of bytes received from the TeensyDSP.
|
||||||
|
*/
|
||||||
|
void UBitxRigState::receive_RIGINF(int numBytes) {
|
||||||
|
// Retrieve all of the deltas. Mark any received fields as dirty. It
|
||||||
|
// is assumed that send_RIGINF() was called immedaitely before this,
|
||||||
|
// so the fields are already clean.
|
||||||
|
byte* ptr = (byte*)&data;
|
||||||
|
Wire.requestFrom(I2CMETER_ADDR, sizeof(data));
|
||||||
|
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS && Wire.available(); i++) {
|
||||||
|
for (size_t j = 0; j < sizeof(uint32_t) && Wire.available(); j++) {
|
||||||
|
byte incomingByte = Wire.read();
|
||||||
|
if (i == DIRTY_WORD || isDirty(i)) {
|
||||||
|
// always overwrite the dirty bits
|
||||||
|
// and, update bytes for fields marked dirty
|
||||||
|
*ptr = incomingByte;
|
||||||
|
}
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeDirty();
|
||||||
|
IFDEBUG( serialHexState("Rcvd") );
|
||||||
|
//IFDEBUG( serialPrettyState("Rcvd") );
|
||||||
|
setClean(); // They get marked dirty as req'd during readDirty().
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Write dirty fields from the RigState out to the Raduino
|
||||||
|
* variables.
|
||||||
|
*/
|
||||||
|
void UBitxRigState::writeDirty() {
|
||||||
|
// VFO A frequency
|
||||||
|
if (isDirty(VFOA_WORD)) {
|
||||||
|
if (vfoActive == VFO_A) {
|
||||||
|
setFrequency(getFreqA());
|
||||||
|
} else {
|
||||||
|
vfoA = getFreqA();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VFO B frequency
|
||||||
|
if (isDirty(VFOB_WORD)) {
|
||||||
|
if (vfoActive == VFO_B) {
|
||||||
|
setFrequency(getFreqB());
|
||||||
|
} else {
|
||||||
|
vfoB = getFreqB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RIT and XIT frequencies
|
||||||
|
if (isDirty(OFFSETS_WORD)) {
|
||||||
|
// RIT
|
||||||
|
ritRxFrequency = getRIT() + ritTxFrequency;
|
||||||
|
if (ritOn == 1) {
|
||||||
|
if (inTx == 0) {
|
||||||
|
setFrequency(ritRxFrequency);
|
||||||
|
} else {
|
||||||
|
setFrequency(ritTxFrequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// XIT - TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Various flags
|
||||||
|
if (isDirty(FLAGS_WORD)) {
|
||||||
|
|
||||||
|
// VFO A/B selection
|
||||||
|
char prev = vfoActive;
|
||||||
|
vfoActive = isVFOA() ? VFO_A : VFO_B;
|
||||||
|
if (vfoActive != prev) {
|
||||||
|
if (vfoActive == VFO_A) {
|
||||||
|
if (vfoA != frequency) {
|
||||||
|
setFrequency(vfoA);
|
||||||
|
}
|
||||||
|
} else if (vfoActive == VFO_B) {
|
||||||
|
if (vfoB != frequency) {
|
||||||
|
setFrequency(vfoB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split on/off
|
||||||
|
splitOn = isSplit() ? 1 : 0;
|
||||||
|
|
||||||
|
// RIT on/off
|
||||||
|
prev = ritOn;
|
||||||
|
ritOn = isRIT() ? 1 : 0;
|
||||||
|
if (ritOn != prev) {
|
||||||
|
if ((ritOn == 1) && (inTx == 0)) {
|
||||||
|
setFrequency(ritRxFrequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XIT on/off
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
prev = (cwMode << 1) | isUSB;
|
||||||
|
isUSB = isModeUSB() ? 1 : 0;
|
||||||
|
if (isModeCW()) {
|
||||||
|
cwMode = 2; // 2 = cwu
|
||||||
|
} else if (isModeCWR()) {
|
||||||
|
cwMode = 1; // 1 = cwl
|
||||||
|
} else {
|
||||||
|
cwMode = 0; // 0 = no cw
|
||||||
|
}
|
||||||
|
if ((cwMode << 1) | isUSB != prev) {
|
||||||
|
setFrequency(frequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyer information
|
||||||
|
if (isDirty(KEYER_WORD)) {
|
||||||
|
|
||||||
|
// Sidetone frequency
|
||||||
|
sideTone = static_cast<unsigned long>(getSidetone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Read current Raduino variables into the RigState
|
||||||
|
* (if they are changed) and set the appropriate dirty flags.
|
||||||
|
* @param r
|
||||||
|
* RigState reference to put the values into.
|
||||||
|
*/
|
||||||
|
void UBitxRigState::readDirty() {
|
||||||
|
unsigned long freq;
|
||||||
|
short offset;
|
||||||
|
|
||||||
|
// VFO A frequency
|
||||||
|
freq = (vfoActive == VFO_A) ? frequency : vfoA;
|
||||||
|
if (getFreqA() != freq) {
|
||||||
|
setFreqA(freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VFO B frequency
|
||||||
|
freq = (vfoActive == VFO_B) ? frequency : vfoB;
|
||||||
|
if (getFreqB() != freq) {
|
||||||
|
setFreqB(freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RIT frequency
|
||||||
|
if (inTx) {
|
||||||
|
offset = ritRxFrequency - ritTxFrequency;
|
||||||
|
} else {
|
||||||
|
offset = frequency - ritTxFrequency;
|
||||||
|
}
|
||||||
|
if (getRIT() != offset) {
|
||||||
|
setRIT(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XIT frequency
|
||||||
|
offset = 0; // xitRxFrequency - frequency;
|
||||||
|
if (getXIT() != offset) {
|
||||||
|
setXIT(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VFO A/B selection
|
||||||
|
if (isVFOA() && vfoActive == VFO_B) {
|
||||||
|
setVFOB();
|
||||||
|
} else if (isVFOB() && vfoActive == VFO_A) {
|
||||||
|
setVFOA();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split selection
|
||||||
|
if (isSplit() && splitOn == 0) {
|
||||||
|
setSplitOff();
|
||||||
|
} else if (!isSplit() && splitOn != 0) {
|
||||||
|
setSplitOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// RIT selection
|
||||||
|
if (isRIT() && ritOn == 0) {
|
||||||
|
setRITOff();
|
||||||
|
} else if (!isRIT() && ritOn != 0) {
|
||||||
|
setRITOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// XIT selection
|
||||||
|
//setXITOff();
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
char prev = (isModeCW() ? 4 : 0) | (isModeCWR() ? 2 : 0) | (isModeUSB() ? 1 : 0);
|
||||||
|
char curr = (cwMode << 1) | isUSB;
|
||||||
|
if (curr != prev) {
|
||||||
|
if (cwMode == 2) {
|
||||||
|
setModeCW();
|
||||||
|
} else if (cwMode == 1) {
|
||||||
|
setModeCWR();
|
||||||
|
} else {
|
||||||
|
if (isUSB) {
|
||||||
|
setModeUSB();
|
||||||
|
} else {
|
||||||
|
setModeLSB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sidetone
|
||||||
|
if (getSidetone() != static_cast<uint16_t>(sideTone)) {
|
||||||
|
setSidetone(static_cast<uint16_t>(sideTone));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* TEENSYDSP FUNCTIONS
|
||||||
|
*
|
||||||
|
* The following are specific to the TeensyDSP implementation. Note
|
||||||
|
* that this depends on the use of the TEENSYDUINO #define, which may
|
||||||
|
* result in a fragile implementation for other development environments
|
||||||
|
* (e.g. if the normal Arduino IDE is not being used).
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <i2c_t3.h>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Receive RIGINF data from the Raduino. This method should
|
||||||
|
* be called on the TeensyDSP 'radState' (Raduino state)
|
||||||
|
* instance, when a RIGINF signal is received via I2C. It
|
||||||
|
* receives the incoming data from the Raduino and updates the
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
void UBitxRigState::receive_RIGINF(int numBytes) {
|
||||||
|
byte* ptr = (byte*)&data;
|
||||||
|
setClean(); // we'll get new dirty bits via the I2C message
|
||||||
|
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS && Wire1.available(); i++) {
|
||||||
|
for (size_t j = 0; j < sizeof(uint32_t) && Wire1.available(); j++) {
|
||||||
|
byte incomingByte = Wire1.read();
|
||||||
|
if (i == DIRTY_WORD || isDirty(i)) {
|
||||||
|
// always overwrite the dirty bits
|
||||||
|
// and, update bytes for fields marked dirty
|
||||||
|
*ptr = incomingByte;
|
||||||
|
}
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// Do anything that needs to happen when something is updated by the
|
||||||
|
// Raduino... this would be due to e.g. changes through the menu.
|
||||||
|
// Current (as of 2/19/2021) things that DON'T need to have any
|
||||||
|
// updates: frequency (those just get requested by CAT as needed).
|
||||||
|
// Current things that do need to get updated: sidetone (used to
|
||||||
|
// update CW filter values).
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
processDirty();
|
||||||
|
|
||||||
|
IFDEBUG( serialHexState("Rcvd") );
|
||||||
|
IFDEBUG( serialPrettyState("Rcvd") );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
* @brief Handle a RIGINF signal from the Raduino. This method should
|
||||||
|
* be called on the TeensyDSP 'catState' (CAT state)
|
||||||
|
* instance, when a RIGINF signal is received via I2C. It
|
||||||
|
* sends a response to the Raduino via I2C, using the Wire1
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
void UBitxRigState::send_RIGINF() {
|
||||||
|
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS; i++) {
|
||||||
|
if (i == DIRTY_WORD || isDirty(i)) {
|
||||||
|
// always send the current dirty bits
|
||||||
|
// or, bytes for updated (dirty) fields
|
||||||
|
Wire1.write((byte*)&data[i], sizeof(uint32_t));
|
||||||
|
} else {
|
||||||
|
// otherwise, send out zeroes
|
||||||
|
Wire1.write((byte*)&zeroes, sizeof(uint32_t));
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// NOTE: I am sending these zeroed out fields under a possibly
|
||||||
|
// mistaken assumption that in doing so, I will be sending a
|
||||||
|
// constant voltage on the SDA line most of the time, i.e. no
|
||||||
|
// bit changes, and so this will help reduce noise generated by
|
||||||
|
// I2C traffic (since most of the time there will be no updates.)
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IFDEBUG( serialHexState("Sent") );
|
||||||
|
IFDEBUG( serialPrettyState("Sent") );
|
||||||
|
setClean(); // now that we've sent them, they're clean
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// TODO: Need to look at possibly merging the two states together at
|
||||||
|
// this point. The purpose would be to minimize the turnaround time
|
||||||
|
// for getting the most recent data to a CAT response.
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Perform required actions based on any dirty bits set.
|
||||||
|
*/
|
||||||
|
void UBitxRigState::processDirty() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
char debugString[81] = {'\0'};
|
||||||
|
|
||||||
|
void UBitxRigState::serialHexState(const char* label = "RigState") {
|
||||||
|
Serial.print(label);
|
||||||
|
sprintf(debugString, ": %#010lx, %#010lx, %#010lx, %#010lx, %#010lx",
|
||||||
|
data[DIRTY_WORD], data[VFOA_WORD], data[VFOB_WORD], data[OFFSETS_WORD], data[FLAGS_WORD]);
|
||||||
|
Serial.println(debugString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBitxRigState::serialPrettyState(const char* label = "RigState") {
|
||||||
|
Serial.println(label);
|
||||||
|
sprintf(debugString, "VFO A : %011ld %1c / VFO B : %011ld %1c",
|
||||||
|
getFreqA(), isDirty(VFOA_WORD) ? 'D' : ' ', getFreqB(), isDirty(VFOB_WORD) ? 'D' : ' ');
|
||||||
|
Serial.println(debugString);
|
||||||
|
sprintf(debugString, "RIT : %011ld %1c / XIT : %011ld %1c",
|
||||||
|
getRIT(), isDirty(OFFSETS_WORD) ? 'D' : ' ', getXIT(), isDirty(OFFSETS_WORD) ? 'D' : ' ');
|
||||||
|
Serial.println(debugString);
|
||||||
|
sprintf(debugString, "Split? %1c / VFO? %1c / RIT? %1c / XIT? %1c / Mode? %3s",
|
||||||
|
isSplit() ? 'Y' : 'N', isVFOA() ? 'A' : 'B', isRIT() ? 'Y' : 'N', isXIT() ? 'Y' : 'N',
|
||||||
|
isModeUSB() ? "USB" : (isModeLSB() ? "LSB" : (isModeCW() ? "CW " : (isModeCWR() ? "CWR" : " "))));
|
||||||
|
Serial.println(debugString);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
#ifndef TEENSYDUINO
|
||||||
|
|
||||||
|
UBitxRigState _rigState;
|
||||||
|
UBitxRigState& rigState = _rigState;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* EOF
|
||||||
|
**********************************************************************/
|
@@ -1,14 +1,12 @@
|
|||||||
|
/*!
|
||||||
|
* @file RigState.h
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef __RigState_h__
|
#ifndef __RigState_h__
|
||||||
#define __RigState_h__
|
#define __RigState_h__
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#define UBITX_VFOA_UPDATE 0x00000001
|
|
||||||
#define UBITX_VFOB_UPDATE 0x00000002
|
|
||||||
#define UBITX_RIT_UPDATE 0x00000004
|
|
||||||
#define UBITX_XIT_UPDATE 0x00000008
|
|
||||||
#define UBITX_FLAGS_UPDATE 0x00000010
|
|
||||||
|
|
||||||
#define UBITX_VFOB_FLAG 0x00000001
|
#define UBITX_VFOB_FLAG 0x00000001
|
||||||
#define UBITX_SPLIT_FLAG 0x00000002
|
#define UBITX_SPLIT_FLAG 0x00000002
|
||||||
#define UBITX_RIT_FLAG 0x00000004
|
#define UBITX_RIT_FLAG 0x00000004
|
||||||
@@ -17,126 +15,341 @@
|
|||||||
#define UBITX_USB_FLAG 0x00000020
|
#define UBITX_USB_FLAG 0x00000020
|
||||||
#define UBITX_TX_FLAG 0x00000040
|
#define UBITX_TX_FLAG 0x00000040
|
||||||
|
|
||||||
|
#define UBITX_SIDETONE_MASK 0x000007FF
|
||||||
|
#define UBITX_KEYER_MODE_MASK 0x00003800
|
||||||
|
|
||||||
|
#ifdef TEENSYDUINO
|
||||||
|
#define DISABLEINTS(CMD) do { noInterrupts(); CMD; interrupts(); } while (0)
|
||||||
|
#else
|
||||||
|
#define DISABLEINTS(CMD) do { CMD; } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum RigStateWord {
|
||||||
|
DIRTY_WORD = 0,
|
||||||
|
VFOA_WORD,
|
||||||
|
VFOB_WORD,
|
||||||
|
OFFSETS_WORD,
|
||||||
|
FLAGS_WORD,
|
||||||
|
KEYER_WORD,
|
||||||
|
NUM_WORDS
|
||||||
|
};
|
||||||
|
|
||||||
|
inline RigStateWord& operator++(RigStateWord& orig) {
|
||||||
|
orig = static_cast<RigStateWord>(orig + 1);
|
||||||
|
// NOTE: Will overflow...
|
||||||
|
return orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RigStateWord operator++(RigStateWord& orig, int) {
|
||||||
|
RigStateWord rVal = orig;
|
||||||
|
++orig;
|
||||||
|
return rVal;
|
||||||
|
}
|
||||||
|
|
||||||
struct UBitxRigState {
|
struct UBitxRigState {
|
||||||
uint32_t header = 0;
|
volatile uint32_t data[NUM_WORDS] = {0};
|
||||||
uint32_t vfo[2];
|
|
||||||
int32_t rit;
|
void begin();
|
||||||
int32_t xit;
|
|
||||||
uint32_t flags = 0;
|
void send_RIGINF();
|
||||||
|
void receive_RIGINF(int numBytes = sizeof(data));
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Set the dirty bit for the specified word.
|
||||||
|
*
|
||||||
|
* @param w
|
||||||
|
* The word to mark as dirty.
|
||||||
|
*/
|
||||||
|
inline void setDirty(RigStateWord w) {
|
||||||
|
data[DIRTY_WORD] |= w < NUM_WORDS ? 1 << w : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Set the dirty bits for all words.
|
||||||
|
*/
|
||||||
|
inline void setDirty() { DISABLEINTS( data[DIRTY_WORD] = 0xFFFFFFFF ); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Clear the dirty bit for the specified word.
|
||||||
|
*
|
||||||
|
* @param w
|
||||||
|
* The word to mark as clean.
|
||||||
|
*/
|
||||||
|
inline void setClean(RigStateWord w) {
|
||||||
|
data[DIRTY_WORD] &= ~(w < NUM_WORDS ? 1 << w : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Clear the dirty bits for all words.
|
||||||
|
*/
|
||||||
|
inline void setClean() { DISABLEINTS( data[DIRTY_WORD] = 0 ); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Check whether the specified word is clean.
|
||||||
|
*
|
||||||
|
* @param w
|
||||||
|
* The word to check for clean status.
|
||||||
|
*
|
||||||
|
* @return True if the word is clean.
|
||||||
|
*/
|
||||||
|
inline bool isClean(RigStateWord w) {
|
||||||
|
bool clean;
|
||||||
|
DISABLEINTS( clean = ((1 << w) & data[DIRTY_WORD]) > 0 ? false : true );
|
||||||
|
return clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Check whether the data is clean (as a whole).
|
||||||
|
*
|
||||||
|
* @return True if the data is clean (no dirty fields).
|
||||||
|
*/
|
||||||
|
inline bool isClean() {
|
||||||
|
bool clean;
|
||||||
|
DISABLEINTS( clean = data[DIRTY_WORD] == 0 );
|
||||||
|
return clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Check whether the specified word is dirty.
|
||||||
|
*
|
||||||
|
* @param w
|
||||||
|
* The word to check for dirty status.
|
||||||
|
*
|
||||||
|
* @return True if the word is dirty.
|
||||||
|
*/
|
||||||
|
inline bool isDirty(RigStateWord w) {
|
||||||
|
bool dirty;
|
||||||
|
DISABLEINTS( dirty = ((1 << w) & data[DIRTY_WORD]) > 0 ? true : false );
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Check whether the data is dirty (as a whole).
|
||||||
|
*
|
||||||
|
* @return True if the data is dirty (at least one dirty field).
|
||||||
|
*/
|
||||||
|
inline bool isDirty() {
|
||||||
|
bool dirty;
|
||||||
|
DISABLEINTS( dirty = data[DIRTY_WORD] != 0 );
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Set the VFO A frequency.
|
||||||
|
*
|
||||||
|
* @param freq
|
||||||
|
* The new frequency in Hz.
|
||||||
|
*/
|
||||||
|
inline void setFreqA(uint32_t freq, bool mark = true) {
|
||||||
|
DISABLEINTS( data[VFOA_WORD] = freq;
|
||||||
|
if (mark) setDirty(VFOA_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t getFreqA() const {
|
||||||
|
uint32_t result;
|
||||||
|
DISABLEINTS( result = data[VFOA_WORD] );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Set the VFO B frequency.
|
||||||
|
*
|
||||||
|
* @param freq
|
||||||
|
* The new frequency in Hz.
|
||||||
|
*/
|
||||||
|
inline void setFreqB(uint32_t freq, bool mark = true) {
|
||||||
|
DISABLEINTS( data[VFOB_WORD] = freq );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t getFreqB() const {
|
||||||
|
uint32_t result;
|
||||||
|
DISABLEINTS( result = data[VFOB_WORD] );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setRIT(int16_t offset, bool mark = true) {
|
||||||
|
DISABLEINTS( data[OFFSETS_WORD] = (int32_t(offset) << 16) | (0x0000FFFF & data[OFFSETS_WORD]);
|
||||||
|
if (mark) setDirty(OFFSETS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int16_t getRIT() const {
|
||||||
|
int16_t result;
|
||||||
|
DISABLEINTS( result = data[OFFSETS_WORD] >> 16 );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setXIT(int16_t offset, bool mark = true) {
|
||||||
|
DISABLEINTS( data[OFFSETS_WORD] = (0xFFFF0000 & data[OFFSETS_WORD]) | offset;
|
||||||
|
if (mark) setDirty(OFFSETS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int16_t getXIT() const {
|
||||||
|
int16_t result;
|
||||||
|
DISABLEINTS( result = 0x0000FFFF & data[OFFSETS_WORD] );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVFOA(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_VFOB_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVFOB(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] |= UBITX_VFOB_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isVFOA() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_VFOB_FLAG ? false : true );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isVFOB() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_VFOB_FLAG ? true : false );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setSplitOn(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] |= UBITX_SPLIT_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setSplitOff(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_SPLIT_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isSplit() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_SPLIT_FLAG ? true : false );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setRITOn(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] |= UBITX_RIT_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setRITOff(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_RIT_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isRIT() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_RIT_FLAG ? true : false );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setXITOn(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] |= UBITX_XIT_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setXITOff(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_XIT_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isXIT() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = data[FLAGS_WORD] & UBITX_XIT_FLAG ? true : false );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setModeUSB(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
|
||||||
|
data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setModeLSB(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
|
||||||
|
data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setModeCW(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
|
||||||
|
data[FLAGS_WORD] |= UBITX_CW_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setModeCWR(bool mark = true) {
|
||||||
|
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
|
||||||
|
data[FLAGS_WORD] |= UBITX_CW_FLAG;
|
||||||
|
if (mark) setDirty(FLAGS_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isModeUSB() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) > 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) == 0) );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isModeLSB() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) == 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) == 0) );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isModeCWAny() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = (data[FLAGS_WORD] & UBITX_CW_FLAG) > 0 );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isModeCW() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) > 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) > 0) );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isModeCWR() const {
|
||||||
|
bool result;
|
||||||
|
DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) == 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) > 0) );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setSidetone(uint16_t f, bool mark = true) {
|
||||||
|
DISABLEINTS( data[KEYER_WORD] &= ~UBITX_SIDETONE_MASK;
|
||||||
|
data[KEYER_WORD] |= (uint32_t(f) & UBITX_SIDETONE_MASK);
|
||||||
|
if (mark) setDirty(KEYER_WORD) );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t getSidetone() {
|
||||||
|
uint32_t result;
|
||||||
|
DISABLEINTS( result = data[KEYER_WORD] & UBITX_SIDETONE_MASK );
|
||||||
|
return uint16_t(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
void serialHexState(const char* label);
|
||||||
|
void serialPrettyState(const char* label);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TEENSYDUINO
|
||||||
|
// These methods are only defined in the Raduino (Arduino) case of the
|
||||||
|
// RigState, not in the TeensyDSP (Teensy) case.
|
||||||
|
void writeDirty(); // write fields FROM RigState TO Raduino
|
||||||
|
void readDirty(); // read variables FROM Raduino TO RigState
|
||||||
|
#else
|
||||||
|
// These methods are only defined (currently) in the TeensyDSP case.
|
||||||
|
void processDirty();
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/**********************************************************************/
|
#ifndef TEENSYDUINO
|
||||||
|
|
||||||
template<typename T, int ID>
|
extern UBitxRigState& rigState;
|
||||||
struct Field {
|
|
||||||
byte id = ID;
|
|
||||||
bool dirty;
|
|
||||||
T data;
|
|
||||||
|
|
||||||
inline size_t sizeOfWrite() { return dirty ? sizeof(byte) + sizeof(T) : 0; }
|
#endif
|
||||||
|
|
||||||
template<typename STREAM> void writeChanges() {
|
|
||||||
if (dirty) {
|
|
||||||
STREAM().write(id);
|
|
||||||
STREAM().write((byte*)&data, sizeof(T));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename STREAM> int read() {
|
|
||||||
size_t len = 0;
|
|
||||||
byte* ptr = (byte*)&data;
|
|
||||||
while (STREAM().available() && len < sizeof(T)) {
|
|
||||||
ptr[len++] = STREAM().read();
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void merge(Field<T,ID>& f) {
|
|
||||||
if (dirty) {
|
|
||||||
f.data = data;
|
|
||||||
f.dirty = true;
|
|
||||||
} else if (f.dirty) {
|
|
||||||
data = f.data;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void markClean() { dirty = false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RigState {
|
|
||||||
Field<uint32_t, 0> vfoA;
|
|
||||||
Field<uint32_t, 1> vfoB;
|
|
||||||
Field<int32_t, 2> rit;
|
|
||||||
Field<int32_t, 3> xit;
|
|
||||||
Field<uint32_t, 4> flags;
|
|
||||||
|
|
||||||
inline size_t sizeOfWrite() {
|
|
||||||
size_t size = 0;
|
|
||||||
size += vfoA.sizeOfWrite();
|
|
||||||
size += vfoB.sizeOfWrite();
|
|
||||||
size += rit.sizeOfWrite();
|
|
||||||
size += xit.sizeOfWrite();
|
|
||||||
size += flags.sizeOfWrite();
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename STREAM> void writeChanges() {
|
|
||||||
vfoA.writeChanges<STREAM>();
|
|
||||||
vfoB.writeChanges<STREAM>();
|
|
||||||
rit.writeChanges<STREAM>();
|
|
||||||
xit.writeChanges<STREAM>();
|
|
||||||
flags.writeChanges<STREAM>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename STREAM> void readChanges(size_t size) {
|
|
||||||
size_t len = 0;
|
|
||||||
while (STREAM().available() && len < size) {
|
|
||||||
switch(STREAM().read()) {
|
|
||||||
case 0:
|
|
||||||
len += vfoA.read<STREAM>();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
len += vfoB.read<STREAM>();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
len += rit.read<STREAM>();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
len += xit.read<STREAM>();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
len += flags.read<STREAM>();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void merge(RigState& r) {
|
|
||||||
vfoA.merge(r.vfoA);
|
|
||||||
vfoB.merge(r.vfoB);
|
|
||||||
rit.merge(r.rit);
|
|
||||||
xit.merge(r.xit);
|
|
||||||
flags.merge(r.flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void markClean(RigState& r) {
|
|
||||||
vfoA.markClean();
|
|
||||||
vfoB.markClean();
|
|
||||||
rit.markClean();
|
|
||||||
xit.markClean();
|
|
||||||
flags.markClean();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
NOTE: This is all currently OBE, leaving it here for reference/future cleanup.
|
||||||
|
|
||||||
Protocol discussion:
|
Protocol discussion:
|
||||||
- I2C master: Raduino
|
- I2C master: Raduino
|
||||||
- I2C slave: TeensyDSP
|
- I2C slave: TeensyDSP
|
||||||
@@ -172,3 +385,7 @@ TeensyDSP state:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* EOF
|
||||||
|
**********************************************************************/
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "TR.h"
|
#include "TR.h"
|
||||||
|
|
||||||
UBitxTR TR(DSP);
|
UBitxTR _tr(DSP);
|
||||||
|
UBitxTR& TR = _tr;
|
||||||
|
|
||||||
void UBitxTR::update(bool cw, bool extKey) {
|
void UBitxTR::update(bool cw, bool extKey) {
|
||||||
updateKey();
|
updateKey();
|
||||||
|
@@ -19,6 +19,12 @@ const int uBitxTRPttPin = UBITX_TR_PTT_PIN;
|
|||||||
const int uBitxTRVoxPin = UBITX_TR_VOX_PIN;
|
const int uBitxTRVoxPin = UBITX_TR_VOX_PIN;
|
||||||
const int uBitxTRKeyPin = UBITX_TR_KEY_PIN;
|
const int uBitxTRKeyPin = UBITX_TR_KEY_PIN;
|
||||||
|
|
||||||
|
struct TxSource {
|
||||||
|
MIC_SOURCE = 0,
|
||||||
|
LINE_SOURCE,
|
||||||
|
USB_SOURCE,
|
||||||
|
};
|
||||||
|
|
||||||
class UBitxTR {
|
class UBitxTR {
|
||||||
public:
|
public:
|
||||||
UBitxTR(UBitxDSP& d, int out = uBitxTROutPin, int p = uBitxTRPttPin, int v = uBitxTRVoxPin, int k = uBitxTRKeyPin):
|
UBitxTR(UBitxDSP& d, int out = uBitxTROutPin, int p = uBitxTRPttPin, int v = uBitxTRVoxPin, int k = uBitxTRKeyPin):
|
||||||
@@ -63,9 +69,10 @@ class UBitxTR {
|
|||||||
inline bool catActivated() { return (L_catActive != catActive) && L_catActive; }
|
inline bool catActivated() { return (L_catActive != catActive) && L_catActive; }
|
||||||
inline bool catDeactivated() { return (L_catActive != catActive) && catActive; }
|
inline bool catDeactivated() { return (L_catActive != catActive) && catActive; }
|
||||||
|
|
||||||
inline void catTX() {
|
inline void catTX(TxSource src) {
|
||||||
L_catActive = catActive;
|
L_catActive = catActive;
|
||||||
catActive = true;
|
catActive = true;
|
||||||
|
txSource = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void catRX() {
|
inline void catRX() {
|
||||||
@@ -73,10 +80,11 @@ class UBitxTR {
|
|||||||
catActive = false;
|
catActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//======================================================================
|
//====================================================================
|
||||||
|
|
||||||
inline bool transmitting() { return isTX; }
|
inline bool transmitting() { return isTX; }
|
||||||
inline bool receiving() { return !isTX; }
|
inline bool receiving() { return !isTX; }
|
||||||
|
inline TxSource source() const { return txSource; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Check if any of the PTT's have been pressed or released
|
* @brief Check if any of the PTT's have been pressed or released
|
||||||
@@ -145,6 +153,7 @@ class UBitxTR {
|
|||||||
bool L_keyDown = false;
|
bool L_keyDown = false;
|
||||||
bool catActive = false;
|
bool catActive = false;
|
||||||
bool L_catActive = false;
|
bool L_catActive = false;
|
||||||
|
TxSource txSource = MIC_SOURCE;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern UBitxTR TR;
|
extern UBitxTR TR;
|
||||||
|
@@ -41,8 +41,6 @@ TS590Command::TS590Command(const char* pre) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TS590Command::~TS590Command() {}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Determine whether this is a Read command or not. by
|
* @brief Determine whether this is a Read command or not. by
|
||||||
* default, if it's a 2-letter command, it's a Read.
|
* default, if it's a 2-letter command, it's a Read.
|
||||||
@@ -77,7 +75,7 @@ void TS590Command::process(const char* cmd) {
|
|||||||
DBGCMD( handleCommand(cmd) );
|
DBGCMD( handleCommand(cmd) );
|
||||||
switch(theError) {
|
switch(theError) {
|
||||||
case NoError:
|
case NoError:
|
||||||
if (theRig->getAI()) {
|
if (theRig->isAI()) {
|
||||||
DBGCMD( sendResponse(cmd) );
|
DBGCMD( sendResponse(cmd) );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -130,7 +128,27 @@ void TS590Command::setRig(UBitxRig* r) {
|
|||||||
theRig = r;
|
theRig = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Set the DSP that will be used to process commands.
|
||||||
|
* @param d
|
||||||
|
* Pointer to the UBitxDSP object.
|
||||||
|
*/
|
||||||
|
void TS590Command::setDSP(UBitxDSP* d) {
|
||||||
|
theDSP = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Set the T/R that will be used to process commands.
|
||||||
|
* @param t
|
||||||
|
* Pointer to the UBitxTR object.
|
||||||
|
*/
|
||||||
|
void TS590Command::setDSP(UBitxTR* t) {
|
||||||
|
theTR = t;
|
||||||
|
}
|
||||||
|
|
||||||
UBitxRig* TS590Command::theRig = &Rig;
|
UBitxRig* TS590Command::theRig = &Rig;
|
||||||
|
UBitxDSP* TS590Command::theDSP = &DSP;
|
||||||
|
UBitxTR* TR590Command::theTR = &TR;
|
||||||
TS590Error TS590Command::theError = NoError;
|
TS590Error TS590Command::theError = NoError;
|
||||||
|
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
@@ -139,13 +157,13 @@ void TS590_FR::handleCommand(const char* cmd) {
|
|||||||
if (strlen(cmd) == 3) {
|
if (strlen(cmd) == 3) {
|
||||||
switch (cmd[2]) {
|
switch (cmd[2]) {
|
||||||
case '0':
|
case '0':
|
||||||
rig()->selectVFOA(true);
|
rig()->setVFOA();
|
||||||
rig()->splitOff(true);
|
rig()->setSplitOff();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '1':
|
case '1':
|
||||||
rig()->selectVFOB(true);
|
rig()->setVFOB();
|
||||||
rig()->splitOff(true);
|
rig()->setSplitOff();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '2':
|
case '2':
|
||||||
@@ -177,9 +195,9 @@ void TS590_FT::handleCommand(const char* cmd) {
|
|||||||
switch (cmd[2]) {
|
switch (cmd[2]) {
|
||||||
case '0':
|
case '0':
|
||||||
if (rig()->isVFOA()) {
|
if (rig()->isVFOA()) {
|
||||||
rig()->splitOff(true);
|
rig()->setSplitOff();
|
||||||
} else if (rig()->isVFOB()) {
|
} else if (rig()->isVFOB()) {
|
||||||
rig()->splitOn(true);
|
rig()->setSplitOn();
|
||||||
} else {
|
} else {
|
||||||
setSyntaxError();
|
setSyntaxError();
|
||||||
}
|
}
|
||||||
@@ -187,9 +205,9 @@ void TS590_FT::handleCommand(const char* cmd) {
|
|||||||
|
|
||||||
case '1':
|
case '1':
|
||||||
if (rig()->isVFOA()) {
|
if (rig()->isVFOA()) {
|
||||||
rig()->splitOn(true);
|
rig()->setSplitOn();
|
||||||
} else if (rig()->isVFOB()) {
|
} else if (rig()->isVFOB()) {
|
||||||
rig()->splitOff(true);
|
rig()->setSplitOff();
|
||||||
} else {
|
} else {
|
||||||
setSyntaxError();
|
setSyntaxError();
|
||||||
}
|
}
|
||||||
@@ -228,23 +246,19 @@ void TS590_MD::handleCommand(const char* cmd) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case '1': // LSB
|
case '1': // LSB
|
||||||
rig()->selectLSB(true);
|
rig()->setModeLSB();
|
||||||
rig()->cwOff(true);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '2': // USB
|
case '2': // USB
|
||||||
rig()->selectUSB(true);
|
rig()->setModeUSB();
|
||||||
rig()->cwOff(true);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '3': // CW
|
case '3': // CW
|
||||||
rig()->selectUSB(true);
|
rig()->setModeCW();
|
||||||
rig()->cwOn(true);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '7': // CW-R
|
case '7': // CW-R
|
||||||
rig()->selectLSB(true);
|
rig()->setModeCWR();
|
||||||
rig()->cwOn(true);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -256,21 +270,178 @@ void TS590_MD::handleCommand(const char* cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TS590_MD::sendResponse(const char* cmd) {
|
void TS590_MD::sendResponse(const char* cmd) {
|
||||||
if (rig()->isCW()) {
|
if (rig()->isModeCW()) {
|
||||||
if (rig()->isUSB()) {
|
ts590SendCommand("MD3");
|
||||||
ts590SendCommand("MD3");
|
} else if (rig()->isModeCWR()) {
|
||||||
|
ts590SendCommand("MD7");
|
||||||
|
} else if (rig()->isModeUSB()) {
|
||||||
|
ts590SendCommand("MD2");
|
||||||
|
} else if (rig()->isModeLSB()) {
|
||||||
|
ts590SendCommand("MD1");
|
||||||
|
} else {
|
||||||
|
ts590SendCommand("MD0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
int ssbHiCut[14] = {1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3400, 4000, 5000};
|
||||||
|
int ssbLoCut[12] = {0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
|
||||||
|
|
||||||
|
int ssbWidth[14] = {50, 80, 100, 150, 200, 250, 300, 400, 500, 600, 1000, 1500, 2000, 2500};
|
||||||
|
int ssbCenter[14] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1750, 1800, 1900, 2000, 2100, 2210};
|
||||||
|
|
||||||
|
void TS590_SH::handleCommand(const char* cmd) {
|
||||||
|
if (strlen(cmd) == 4) {
|
||||||
|
index = strtoul(&cmd[2], NULL, 10);
|
||||||
|
if (index < sizeof(ssbHiCut) / sizeof(ssbHiCut[0])) {
|
||||||
|
dsp()->setRxFilterHi(ssbHiCut[index]);
|
||||||
} else {
|
} else {
|
||||||
ts590SendCommand("MD7");
|
setSyntaxError();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (rig()->isUSB()) {
|
setSyntaxError();
|
||||||
ts590SendCommand("MD2");
|
|
||||||
} else {
|
|
||||||
ts590SendCommand("MD1");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TS590_SH::sendResponse(const char* cmd) {
|
||||||
|
ts590SendCommand("SH%02u", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TS590_SL::handleCommand(const char* cmd) {
|
||||||
|
if (strlen(cmd) == 4) {
|
||||||
|
index = strtoul(&cmd[2], NULL, 10);
|
||||||
|
if (index < sizeof(ssbLoCut) / sizeof(ssbLoCut[0])) {
|
||||||
|
dsp()->setRxFilterLo(ssbLoCut[index]);
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TS590_SL::sendResponse(const char* cmd) {
|
||||||
|
ts590SendCommand("SL%02u", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
void TS590_TX::handleCommand(const char* cmd) {
|
||||||
|
if (strlen(cmd) == 3) {
|
||||||
|
switch (cmd[2]) {
|
||||||
|
case '0':
|
||||||
|
tr.catTX(MIC_SOURCE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '1':
|
||||||
|
tr.catTX(rig.isUSBInput() ? USB_INPUT : LINE_INPUT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '2':
|
||||||
|
// TODO: Need to implement w/ Teensy Audio Tool.
|
||||||
|
//tr.catTX();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
} else if (strlen(cmd) == 2) {
|
||||||
|
tr.catTX(MIC_SOURCE);
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TS590_TX::sendResponse(const char* cmd) {
|
||||||
|
char src;
|
||||||
|
switch (tr.source()) {
|
||||||
|
case MIC_SOURCE:
|
||||||
|
src = '0';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LINE_SOURCE:
|
||||||
|
case USB_SOURCE:
|
||||||
|
src = '1';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ts590SeondCommand("TX%1c", src);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Create a new CAT EX command. It should be initialized with
|
||||||
|
* a 3-character P1 parameter (command number).
|
||||||
|
* @param pre
|
||||||
|
* A 3-character command prefix. If more than 3 characters
|
||||||
|
* are supplied, only the first two will be used. If less
|
||||||
|
* than three are supplied, then the command will be
|
||||||
|
* initialized with a null prefix.
|
||||||
|
*/
|
||||||
|
TS590EXCommand::TS590EXCommand(const char* P1):
|
||||||
|
TS590Command("EX") {
|
||||||
|
if (strlen(P1) >= 3) {
|
||||||
|
strncpy(myMenu, P1, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Determine whether this is a Read command or not. by
|
||||||
|
* default, if it's a 2-letter command, it's a Read.
|
||||||
|
* @return True if a Read command; false otherwise.
|
||||||
|
*/
|
||||||
|
bool TS590EXCommand::isReadCommand(const char* cmd) const {
|
||||||
|
if (strlen(cmd) == 9) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TS590EXCommand::sendResponse(const char* cmd) {
|
||||||
|
ts590sendCommand("%2c%3c0000%s", prefix(), menu(), getReturnValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
void TS590_EX034::handleCommand(const char* cmd) {
|
||||||
|
if (strlen(cmd) == 10 || strlen(cmd) == 11) {
|
||||||
|
index = (uint8_t)atol(&cmd[9]);
|
||||||
|
if (index < 15) {
|
||||||
|
rig().setCWSidetone(300.0 + (float)(index * 50));
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TS590_EX034::sendResponse(const char* cmd) {
|
||||||
|
ts590SendCommand("EX0340000%02d", index % 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
void TS590_EX063::handleCommand(const char* cmd) {
|
||||||
|
if (strlen(cmd) == 10) {
|
||||||
|
if (cmd[9] == '0') {
|
||||||
|
rig().setLineInput();
|
||||||
|
} else if (cmd[9] == '1') {
|
||||||
|
rig().setUSBInput();
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TS590_EX063::sendResponse(const char* cmd) {
|
||||||
|
ts590SendCommand("EX0630000%1c", rig().isUSBInput() ? '1' : '0');
|
||||||
|
}
|
||||||
|
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
|
||||||
TS590_FA cmdFA;
|
TS590_FA cmdFA;
|
||||||
@@ -278,13 +449,17 @@ TS590_FB cmdFB;
|
|||||||
TS590_FR cmdFR;
|
TS590_FR cmdFR;
|
||||||
TS590_FT cmdFT;
|
TS590_FT cmdFT;
|
||||||
TS590_MD cmdMD;
|
TS590_MD cmdMD;
|
||||||
|
TS590_SH cmdSH;
|
||||||
|
TS590_SL cmdSL;
|
||||||
|
|
||||||
TS590Command* catCommands[] = {
|
TS590Command* catCommands[] = {
|
||||||
&cmdFA,
|
&cmdFA,
|
||||||
&cmdFB,
|
&cmdFB,
|
||||||
&cmdFR,
|
&cmdFR,
|
||||||
&cmdFT,
|
&cmdFT,
|
||||||
&cmdMD
|
&cmdMD,
|
||||||
|
&cmdSH,
|
||||||
|
&cmdSL
|
||||||
};
|
};
|
||||||
int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]);
|
int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]);
|
||||||
|
|
||||||
@@ -343,8 +518,27 @@ int compareCATCommands(const void* a, const void* b) {
|
|||||||
return cmp;
|
return cmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int compareCATEXCommands(const void* a, const void* b) {
|
||||||
|
TS590Command const *B = *(TS590Command const **)b;
|
||||||
|
int cmp = strncmp((char*)a, (char*)B->prefix(), 5);
|
||||||
|
#ifdef DEBUG
|
||||||
|
Serial.print("Comparison: ");
|
||||||
|
Serial.print((char*)a);
|
||||||
|
Serial.print(" ? ");
|
||||||
|
Serial.print((char*)B->prefix());
|
||||||
|
Serial.print(" --> ");
|
||||||
|
Serial.println(cmp);
|
||||||
|
#endif
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
|
||||||
void UBitxTS590::processCommand() {
|
void UBitxTS590::processCommand() {
|
||||||
TS590Command** cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands);
|
TS590Command** cmd;
|
||||||
|
if (strncmp(buf, "EX", 2) == 0) {
|
||||||
|
cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATEXCommands);
|
||||||
|
} else {
|
||||||
|
cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands);
|
||||||
|
}
|
||||||
if (cmd == NULL) {
|
if (cmd == NULL) {
|
||||||
ts590SyntaxError();
|
ts590SyntaxError();
|
||||||
} else {
|
} else {
|
||||||
|
@@ -2,10 +2,15 @@
|
|||||||
#define __TS590_h__
|
#define __TS590_h__
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "DSP.h"
|
||||||
#include "Rig.h"
|
#include "Rig.h"
|
||||||
|
#include "TR.h"
|
||||||
|
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
|
||||||
|
// uncomment to use TS-590SG / comment to use TS-590S
|
||||||
|
#define USE_TS590SG
|
||||||
|
|
||||||
#define TS590_COMMAND_MAX_LENGTH 50 // including terminator (which will get converted to null)
|
#define TS590_COMMAND_MAX_LENGTH 50 // including terminator (which will get converted to null)
|
||||||
|
|
||||||
const int ts590CommandMaxLength = TS590_COMMAND_MAX_LENGTH;
|
const int ts590CommandMaxLength = TS590_COMMAND_MAX_LENGTH;
|
||||||
@@ -56,6 +61,16 @@ class TS590Command {
|
|||||||
*/
|
*/
|
||||||
inline UBitxRig* rig() const { return theRig; }
|
inline UBitxRig* rig() const { return theRig; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Return the DSP that this command will be used to control.
|
||||||
|
*/
|
||||||
|
inline UBitxDSP* dsp() const { return theDSP; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Return the T/R that this command will be used to control.
|
||||||
|
*/
|
||||||
|
inline UBitxDSP* tr() const { return theTR; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Handle the provided Set command. If the Set command
|
* @brief Handle the provided Set command. If the Set command
|
||||||
* results in an error, then set the appropriate flag with
|
* results in an error, then set the appropriate flag with
|
||||||
@@ -81,11 +96,14 @@ class TS590Command {
|
|||||||
static void setCommError();
|
static void setCommError();
|
||||||
static void setProcessError();
|
static void setProcessError();
|
||||||
static void setRig(UBitxRig* r);
|
static void setRig(UBitxRig* r);
|
||||||
|
static void setDSP(UBitxDSP* d);
|
||||||
|
static void setTR(UBitxTR* t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char myPrefix[3] = "\0\0";
|
char myPrefix[3] = "\0\0";
|
||||||
static TS590Error theError;
|
static TS590Error theError;
|
||||||
static UBitxRig* theRig;
|
static UBitxRig* theRig;
|
||||||
|
static UBitxDSP* theDSP;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
@@ -102,9 +120,9 @@ class TS590_FAB : public TS590Command {
|
|||||||
if (strlen(cmd) == 13) {
|
if (strlen(cmd) == 13) {
|
||||||
unsigned long freq = strtoul(&cmd[2], NULL, 10);
|
unsigned long freq = strtoul(&cmd[2], NULL, 10);
|
||||||
if (VFOA) {
|
if (VFOA) {
|
||||||
rig()->setFreqA(freq, true);
|
rig()->setFreqA(freq);
|
||||||
} else {
|
} else {
|
||||||
rig()->setFreqB(freq, true);
|
rig()->setFreqB(freq);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setSyntaxError();
|
setSyntaxError();
|
||||||
@@ -160,6 +178,159 @@ class TS590_MD : public TS590Command {
|
|||||||
|
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief CAT command for setting the receiver high-cut frequency.
|
||||||
|
*/
|
||||||
|
class TS590_SH : public TS590Command {
|
||||||
|
public:
|
||||||
|
TS590_SH(): TS590Command("SH") {}
|
||||||
|
virtual void handleCommand(const char* cmd);
|
||||||
|
virtual void sendResponse(const char* cmd);
|
||||||
|
private:
|
||||||
|
unsigned index;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief CAT command for setting the receiver low-cut frequency.
|
||||||
|
*/
|
||||||
|
class TS590_SL : public TS590Command {
|
||||||
|
public:
|
||||||
|
TS590_SL(): TS590Command("SL") {}
|
||||||
|
virtual void handleCommand(const char* cmd);
|
||||||
|
virtual void sendResponse(const char* cmd);
|
||||||
|
private:
|
||||||
|
unsigned index;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief CAT command to start transmitting.
|
||||||
|
*/
|
||||||
|
class TS590_TX : public TS590Command {
|
||||||
|
public:
|
||||||
|
TS590_TX(): TS590Command("TX") {}
|
||||||
|
virtual void handleCommand(const char* cmd);
|
||||||
|
virtual void sendResponse(const char* cmd);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
class TS590EXCommand : public TS590Command {
|
||||||
|
public:
|
||||||
|
TS590EXCommand(const char *P1);
|
||||||
|
virtual ~TS590EXCommand() = 0;
|
||||||
|
|
||||||
|
inline const char* menu() const { return &myMenu[0]; }
|
||||||
|
|
||||||
|
virtual const char* getReturnValue() const = 0;
|
||||||
|
|
||||||
|
virtual void sendResponse(const char* cmd);
|
||||||
|
|
||||||
|
private:
|
||||||
|
char myMenu[4] = "\0\0\0";
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief CAT command for setting the sidetone pitch/frequency.
|
||||||
|
*/
|
||||||
|
class TS590_EX034 : public TS590EXCommand {
|
||||||
|
public:
|
||||||
|
TS590_EX034(): TS590EXCommand("034") {}
|
||||||
|
virtual void handleCommand(const char* cmd);
|
||||||
|
virtual const char* getReturnValue();
|
||||||
|
private:
|
||||||
|
uint8_t index;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief CAT command for selecting the data input line.
|
||||||
|
*/
|
||||||
|
class TS590_EX063 : public TS590EXCommand {
|
||||||
|
public:
|
||||||
|
TS590_EX063(): TS590EXCommand("063") {}
|
||||||
|
virtual void handleCommand(const char* cmd);
|
||||||
|
virtual const char* getReturnValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief CAT command for setting USB/Line audio input levels.
|
||||||
|
*/
|
||||||
|
template<bool USB, bool SG>
|
||||||
|
class TS590_EXDataAudioInLevel : public TS590EXCommand {
|
||||||
|
public:
|
||||||
|
TS590_EXDataAudioInLevel(): TS590EXCommand(USB ? (SG ? "071" : "064") : (SG ? "073" : "066")) {}
|
||||||
|
|
||||||
|
virtual void handleCommand(const char* cmd) {
|
||||||
|
if (strlen(cmd) == 10) {
|
||||||
|
uint8_t val = (cmd[9] - 48) % 10;
|
||||||
|
if (USB) {
|
||||||
|
// set USB input level
|
||||||
|
} else {
|
||||||
|
// set Line input level
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const char* getReturnValue() {
|
||||||
|
static char str[2] = "\0";
|
||||||
|
// get input level - decimal 0 to 9 ... str[1] = ...
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef USE_TS590SG
|
||||||
|
typedef TS590_EXDataAudioInLevel<true, true> TS590_EX071;
|
||||||
|
typedef TS590_EXDataAudioInLevel<false, true> TS590_EX073;
|
||||||
|
#else
|
||||||
|
typedef TS590_EXDataAudioInLevel<true, false> TS590_EX064;
|
||||||
|
typedef TS590_EXDataAudioInLevel<true, false> TS590_EX066;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief CAT command for setting USB/Line audio output levels.
|
||||||
|
*/
|
||||||
|
template<bool USB, bool SG>
|
||||||
|
class TS590_EXDataAudioOutLevel : public TS590EXCommand {
|
||||||
|
public:
|
||||||
|
TS590_EXDataAudioOutLevel(): TS590EXCommand(USB ? (SG ? "072" : "065") : (SG ? "074" : "067")) {}
|
||||||
|
|
||||||
|
virtual void handleCommand(const char* cmd) {
|
||||||
|
if (strlen(cmd) == 10) {
|
||||||
|
uint8_t val = (cmd[9] - 48) % 10;
|
||||||
|
if (USB) {
|
||||||
|
// set USB output level
|
||||||
|
} else {
|
||||||
|
// set Line output level
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSyntaxError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const char* getReturnValue() {
|
||||||
|
static char str[2] = "\0";
|
||||||
|
// get output level - decimal 0 to 9 ... str[1] = ...
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef USE_TS590SG
|
||||||
|
typedef TS590_EXDataAudioOutLevel<true, true> TS590_EX072;
|
||||||
|
typedef TS590_EXDataAudioOutLevel<false, true> TS590_EX074;
|
||||||
|
#else
|
||||||
|
typedef TS590_EXDataAudioOutLevel<true, false> TS590_EX065;
|
||||||
|
typedef TS590_EXDataAudioOutLevel<true, false> TS590_EX067;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
class UBitxTS590 {
|
class UBitxTS590 {
|
||||||
public:
|
public:
|
||||||
UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {}
|
UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {}
|
||||||
|
@@ -68,8 +68,8 @@ extern int magnitudelimit_low;
|
|||||||
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter
|
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter
|
||||||
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
|
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
|
||||||
|
|
||||||
// Raduino provides updated data to TeensyDSP
|
// Raduino<=>TeensyDSP data exchange
|
||||||
#define I2CMETER_RIGINF 0x50
|
#define I2CMETER_RIGINF 0x50
|
||||||
|
|
||||||
// Raduino requests any CAT updates from TeensyDSP
|
// Raduino requests any CAT updates from TeensyDSP
|
||||||
#define I2CMETER_REQCAT 0x51
|
//#define I2CMETER_REQCAT 0x51
|
||||||
|
@@ -363,8 +363,6 @@ void setup()
|
|||||||
//Serial1.println("Start...");
|
//Serial1.println("Start...");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sentRigInfFlag = false;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Receive a command via I2C. The most recent command will be received, which will
|
@brief Receive a command via I2C. The most recent command will be received, which will
|
||||||
indicate which data the DSP should be preparing to return.
|
indicate which data the DSP should be preparing to return.
|
||||||
@@ -375,20 +373,12 @@ void i2cReceiveEvent(size_t numBytes)
|
|||||||
{
|
{
|
||||||
int readCommand = 0;
|
int readCommand = 0;
|
||||||
bool exitLoop = false;
|
bool exitLoop = false;
|
||||||
UBitxRigState tmpState;
|
|
||||||
|
|
||||||
|
// Does this really need to be a while loop? Don't we know the number of bytes?
|
||||||
while (Wire1.available() > 0 && !exitLoop) {
|
while (Wire1.available() > 0 && !exitLoop) {
|
||||||
readCommand = Wire1.read();
|
readCommand = Wire1.read();
|
||||||
if (readCommand == I2CMETER_RIGINF) {
|
if (readCommand == I2CMETER_RIGINF) {
|
||||||
size_t len = 0;
|
Rig.rad().receive_RIGINF(numBytes - 1);
|
||||||
uint8_t* const ptr = (uint8_t* const)&tmpState;
|
|
||||||
while ((Wire1.available() > 0) && (len < sizeof(UBitxRigState))) {
|
|
||||||
ptr[len++] = Wire1.read();
|
|
||||||
}
|
|
||||||
if (!Rig.updatedByCAT()) {
|
|
||||||
Rig.updateState(tmpState);
|
|
||||||
}
|
|
||||||
sentRigInfFlag = false; // so we know that we need to send the flag first
|
|
||||||
exitLoop = true;
|
exitLoop = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,43 +414,35 @@ void i2cRequestEvent(void)
|
|||||||
case I2CMETER_CALCS:
|
case I2CMETER_CALCS:
|
||||||
// Returns an already-calculated S-meter value.
|
// Returns an already-calculated S-meter value.
|
||||||
Wire1.write(scaledSMeter);
|
Wire1.write(scaledSMeter);
|
||||||
#ifdef DEBUG
|
|
||||||
i2cRespCounter[i2cCommand - 0x50]++;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case I2CMETER_UNCALCS:
|
case I2CMETER_UNCALCS:
|
||||||
// Returns a raw signal strength value.
|
// Returns a raw signal strength value.
|
||||||
Wire1.write(Sensors.sMeterUnscaled() >> 2); // divided by 4... do we want this?
|
Wire1.write(Sensors.sMeterUnscaled() >> 2); // divided by 4... do we want this?
|
||||||
#ifdef DEBUG
|
|
||||||
i2cRespCounter[i2cCommand - 0x50]++;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case I2CMETER_CALCP:
|
case I2CMETER_CALCP:
|
||||||
// Returns a raw forward power value.
|
// Returns a raw forward power value.
|
||||||
Wire1.write(int(fwdPower * 100.0));
|
Wire1.write(int(fwdPower * 100.0));
|
||||||
#ifdef DEBUG
|
|
||||||
i2cRespCounter[i2cCommand - 0x50]++;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case I2CMETER_CALCR:
|
case I2CMETER_CALCR:
|
||||||
// Returns a raw reverse power value.
|
// Returns a raw reverse power value.
|
||||||
Wire1.write(int(revPower * 100.0));
|
Wire1.write(int(revPower * 100.0));
|
||||||
#ifdef DEBUG
|
|
||||||
i2cRespCounter[i2cCommand - 0x50]++;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case I2CMETER_RIGINF:
|
case I2CMETER_RIGINF:
|
||||||
// Receive current rig state; transmit any CAT updates, if required.
|
// Receive current rig state; transmit any CAT updates, if required.
|
||||||
|
Rig.cat().send_RIGINF();
|
||||||
//Wire1.write(catState.header); // temporary - just writing a single, null byte
|
//Wire1.write(catState.header); // temporary - just writing a single, null byte
|
||||||
//break;
|
// NEEDS TO GET UPDATED
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
case I2CMETER_REQCAT:
|
case I2CMETER_REQCAT:
|
||||||
// Provide latest CAT updates, if any.
|
// Provide latest CAT updates, if any.
|
||||||
//Wire1.write(catState.header); // temporary - just writing a single, null byte
|
//Wire1.write(catState.header); // temporary - just writing a single, null byte
|
||||||
|
// NEEDS TO GET UPDATED
|
||||||
if (Rig.updatedByCAT()) {
|
if (Rig.updatedByCAT()) {
|
||||||
if (sentRigInfFlag) {
|
if (sentRigInfFlag) {
|
||||||
DBGPRINTLN("I2CMETER_REQCAT -- updated by CAT");
|
DBGPRINTLN("I2CMETER_REQCAT -- updated by CAT");
|
||||||
@@ -475,14 +457,18 @@ void i2cRequestEvent(void)
|
|||||||
//Wire1.write(Rig.stateAsBytes(), sizeof(uint8_t));
|
//Wire1.write(Rig.stateAsBytes(), sizeof(uint8_t));
|
||||||
Wire1.write(0);
|
Wire1.write(0);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
|
||||||
i2cRespCounter[i2cCommand - 0x50]++;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
*/
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (0x50 <= i2cCommand && i2cCommand <= 0x59)
|
||||||
|
{
|
||||||
|
i2cRespCounter[i2cCommand - 0x50]++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//extern void Decode_Morse(float magnitude);
|
//extern void Decode_Morse(float magnitude);
|
||||||
@@ -541,9 +527,9 @@ void loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If CW mode, we need to update keying a lot...
|
// If CW mode, we need to update keying a lot...
|
||||||
if (Rig.isCW()) {
|
if (Rig.isModeCWAny()) {
|
||||||
if (Rig.isCW()) Keyer.doPaddles();
|
if (Rig.isModeCWAny()) Keyer.doPaddles();
|
||||||
TR.update(Rig.isCW(), Keyer.isDown());
|
TR.update(Rig.isModeCWAny(), Keyer.isDown());
|
||||||
//if (TR.transmitting()) return;
|
//if (TR.transmitting()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,11 +544,11 @@ void loop()
|
|||||||
|
|
||||||
// Update each of the subsystems, beginning with CAT control.
|
// Update each of the subsystems, beginning with CAT control.
|
||||||
TS590.update();
|
TS590.update();
|
||||||
TR.update(Rig.isCW(), Keyer.isDown());
|
TR.update(Rig.isModeCWAny(), Keyer.isDown());
|
||||||
Rig.update();
|
Rig.update();
|
||||||
DSP.update();
|
DSP.update();
|
||||||
|
|
||||||
//if (Rig.isCW()) return;
|
//if (Rig.isModeCWAny()) return;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// For debugging, output some debug info every 1.0" (40 frames @ 40 Hz).
|
// For debugging, output some debug info every 1.0" (40 frames @ 40 Hz).
|
||||||
@@ -685,7 +671,7 @@ void loop()
|
|||||||
forwardData();
|
forwardData();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Rig.isCW()) return; // In CW, the ADC measurement messes with the timing. So need to use interrupts on the Keyer, and/or continuous ADC.
|
if (Rig.isModeCWAny()) return; // In CW, the ADC measurement messes with the timing. So need to use interrupts on the Keyer, and/or continuous ADC.
|
||||||
|
|
||||||
if (sinceADCMillis > adcIntervalMillis) {
|
if (sinceADCMillis > adcIntervalMillis) {
|
||||||
// Do stuff that we do once per ADC interval--ADC colllection.
|
// Do stuff that we do once per ADC interval--ADC colllection.
|
||||||
@@ -703,7 +689,7 @@ void loop()
|
|||||||
//forwardData();
|
//forwardData();
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (Rig.isCW()) return;
|
//if (Rig.isModeCWAny()) return;
|
||||||
|
|
||||||
// Check Response Command
|
// Check Response Command
|
||||||
if (responseCommand > 0 && sinceForward > LAST_TIME_INTERVAL)
|
if (responseCommand > 0 && sinceForward > LAST_TIME_INTERVAL)
|
||||||
|
386
TeensyDSP/temp.h
Normal file
386
TeensyDSP/temp.h
Normal file
@@ -0,0 +1,386 @@
|
|||||||
|
const int rxLoCutSSB[] = { 0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
|
||||||
|
const int rxHiCutSSB[] = {1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3400, 4000, 5000};
|
||||||
|
const int rxDataWidth[] = { 50, 80, 100, 150, 200, 250, 300, 400, 500, 600, 1000, 1500, 2000, 2500};
|
||||||
|
#ifdef USE_TS590SG
|
||||||
|
const int rxDataShift[] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1750, 1800, 1900, 2000, 2100, 2210};
|
||||||
|
#else
|
||||||
|
const int rxDataShift[] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2210};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int txLowCutFilter[] = { 10, 100, 200, 300, 400, 500};
|
||||||
|
const int txHighCutFilter[] = {2500, 2600, 2700, 2800, 2900, 3000};
|
||||||
|
const int timeOutMinutes[] = {3, 5, 10, 20, 30};
|
||||||
|
|
||||||
|
#define MAX_MENU_TITLE_LEN 13
|
||||||
|
#define MAX_MENU_OPTION_LEN 13
|
||||||
|
|
||||||
|
typedef void (*toggleFunc)(UBitxRig&);
|
||||||
|
typedef bool (*boolStatus)(UBitxRig&);
|
||||||
|
|
||||||
|
#define KEYER_MIN_SPEED 4
|
||||||
|
#define KEYER_MAX_SPEED 60
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Abstract base class for a switch/toggle (on/off, A/B, etc.)
|
||||||
|
* option. Derived classes must provide set() and get()
|
||||||
|
* methods for controlling the state of the switch.
|
||||||
|
*/
|
||||||
|
struct ConfigSwitch {
|
||||||
|
virtual ~ConfigSwitch() = 0;
|
||||||
|
virtual void set(bool on) = 0;
|
||||||
|
virtual bool get() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Abstract base class for an option that supports a linear
|
||||||
|
* sequence of integer values. There can be a minimum and a
|
||||||
|
* maximum value, but otherwise, all integers between those
|
||||||
|
* should be acceptable. The base class takes care of checking
|
||||||
|
* min/max bounds, so the derived class must provide an get()
|
||||||
|
* and onSet() methods. In addition, derived classes can
|
||||||
|
* optionally override the onTooHigh() and onTooLow(), which
|
||||||
|
* by default clamp any out-of-band inputs to the min/max.
|
||||||
|
*/
|
||||||
|
struct ConfigInteger {
|
||||||
|
ConfigInteger(int min, int max)
|
||||||
|
: myMin(min), myMax(max) {}
|
||||||
|
|
||||||
|
virtual ~ConfigInteger() = 0;
|
||||||
|
|
||||||
|
inline void set(int val) {
|
||||||
|
if (val > myMax) val = onTooHigh(val);
|
||||||
|
if (val < myMin) val = onTooLow(val);
|
||||||
|
onSet(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int get() const = 0;
|
||||||
|
virtual void onSet() = 0;
|
||||||
|
virtual int onTooHigh(int val) const { return myMax; }
|
||||||
|
virtual int onTooLow(int val) const { return myMin; }
|
||||||
|
|
||||||
|
int myMin, myMax;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Abstract base class for an option that supports a set
|
||||||
|
* of integer values stored in an array. The derived
|
||||||
|
* class must provide get() and onSet() methods. It can
|
||||||
|
* optionally provide an onTooHigh() method, which by
|
||||||
|
* default will clamp indices to the highest allowable.
|
||||||
|
*/
|
||||||
|
struct ConfigArray {
|
||||||
|
ConfigArray(int len, int* data)
|
||||||
|
: myLen(len), myData(data) {}
|
||||||
|
|
||||||
|
virtual ~ConfigArray() = 0;
|
||||||
|
|
||||||
|
inline void set(int i) {
|
||||||
|
if (i < 0) i = 0;
|
||||||
|
if (i > myLen - 1) i = onTooHigh(i);
|
||||||
|
onSet(myData[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int get() const = 0;
|
||||||
|
virtual void onSet(int val) = 0;
|
||||||
|
virtual int onTooHigh(int i) const { return myLen - 1; }
|
||||||
|
inline int getData(int i) { return myData[i]; }
|
||||||
|
|
||||||
|
int myLen;
|
||||||
|
int *myData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Option class to set the configuration of the RX DSP filter.
|
||||||
|
*/
|
||||||
|
template<bool isHIGH>
|
||||||
|
struct ConfigRXFilter : public ConfigArray {
|
||||||
|
ConfigFilter(UBitxDSP& d, int default)
|
||||||
|
: ConfigArray(0, NULL), dsp(d), useData(false) {
|
||||||
|
if (isHIGH) {
|
||||||
|
current[0] = 10; // SSB - hi cut
|
||||||
|
current[1] = 05; // Data - center (shift)
|
||||||
|
data[0] = rxHiCutSSB;
|
||||||
|
data[1] = rxDataShift;
|
||||||
|
length[0] = sizeof(rxHiCutSSB)/sizeof(rxHiCutSSB[0]);
|
||||||
|
length[1] = sizeof(rxDataShift)/sizeof(rxDataShift[0]);
|
||||||
|
} else {
|
||||||
|
current[0] = 04; // SSB - lo cut
|
||||||
|
current[1] = 12; // Data - width
|
||||||
|
data[0] = rxLoCutSSB;
|
||||||
|
data[1] = rxDataWidth;
|
||||||
|
length[0] = sizeof(rxLoCutSSB)/sizeof(rxLoCutSSB[0]);
|
||||||
|
length[1] = sizeof(rxDataWidth)/sizeof(rxDataWidth[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO - A TON MORE TO DO HERE TO MAKE IT CONSISTENT WITH CONSTRUCTION
|
||||||
|
inline void setSSB() {
|
||||||
|
useData = false;
|
||||||
|
}
|
||||||
|
inline void setData() { useData = true; }
|
||||||
|
|
||||||
|
virtual void get() { return current; }
|
||||||
|
virtual void onSet(int i) {
|
||||||
|
current = i;
|
||||||
|
float value = static_cast<float>(getData(i));
|
||||||
|
if (isHIGH) {
|
||||||
|
if (useCENTER) {
|
||||||
|
dsp.setRxFilterCenter(value);
|
||||||
|
} else {
|
||||||
|
dsp.setRxFilterHi(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (useCENTER) {
|
||||||
|
dsp.setRxFilterWidth(value);
|
||||||
|
} else {
|
||||||
|
dsp.setRxFilterLo(static_cast<float>(getData(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
UBitxDSP& dsp;
|
||||||
|
int* data[2];
|
||||||
|
int length[2];
|
||||||
|
int current[2];
|
||||||
|
int mode; // 0 = SSB, 1 = Data
|
||||||
|
};
|
||||||
|
typedef ConfigRXFilter<false, false> ConfigRxLoCutSSB;
|
||||||
|
typedef ConfigRXFilter<true, false> ConfigRxHiCutSSB;
|
||||||
|
typedef ConfigRXFilter<false, true > ConfigRxLoCutData;
|
||||||
|
typedef ConfigRXFilter<true, true > ConfigRxHiCutData;
|
||||||
|
|
||||||
|
struct DSPConfigurator {
|
||||||
|
DSPConfigurator(UBitxDSP& d)
|
||||||
|
: dsp(d) {}
|
||||||
|
|
||||||
|
ConfigRxLoCutSSB ssbRxLoCut;
|
||||||
|
ConfigRxHiCutSSB ssbRxHiCut;
|
||||||
|
ConfigRxLoCutData dataRxLoCut;
|
||||||
|
ConfigRxHiCutData dataRxHiCut;
|
||||||
|
|
||||||
|
private:
|
||||||
|
UBitxDSP& dsp;
|
||||||
|
} dspConfigurator(DSP);
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief A configuration class for the Keyer. Since the Keyer has
|
||||||
|
* its own state that is not 100% compatible with the way way
|
||||||
|
* the Rig will interact with it (primarily in terms of CAT
|
||||||
|
* commands), this class provides the glue. (This is mostly
|
||||||
|
* used because, while the configuration objects are generally
|
||||||
|
* all separate, there is some state that needs to be
|
||||||
|
* communicated between e.g. the iambic mode configuration and
|
||||||
|
* the bug mode configuration.)
|
||||||
|
*/
|
||||||
|
struct KeyerConfigurator {
|
||||||
|
KeyerConfigurator(UBitxKeyer& k): keyer(k) { iambicAB = keyer.getMode(); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief An inner class that supports switching between Iambic A & B.
|
||||||
|
*/
|
||||||
|
struct IambicMode : public ConfigSwitch {
|
||||||
|
IambicMode(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
|
||||||
|
virtual void set(bool on) {
|
||||||
|
config.iambicAB = on;
|
||||||
|
if (config.keyer.getMode() != STRAIGHT) {
|
||||||
|
config.keyer.setMode(config.iambicAB ? IAMBICB : IAMBICA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual bool get() const { return config.iambicAB; }
|
||||||
|
private: KeyerConfigurator& config;
|
||||||
|
} iambicMode;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief An inner class that supports switching bug mode on/off.
|
||||||
|
*/
|
||||||
|
struct BugMode : public ConfigSwitch {
|
||||||
|
BugMode(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
|
||||||
|
virtual void set(bool on) {
|
||||||
|
config.isBug = on;
|
||||||
|
if (config.isBug && (config.keyer.getMode() != STRAIGHT)) {
|
||||||
|
config.keyer.setMode(STRAIGHT);
|
||||||
|
} else if (!config.isBug && (config.keyer.getMode() == STRAIGHT)) {
|
||||||
|
config.keyer.setMode(config.iambicAB ? IAMBICB : IAMBICA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual bool get() const { return config.isBug; }
|
||||||
|
private: KeyerConfigurator& config;
|
||||||
|
} bugMode;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief An inner class that supports switching the left and right
|
||||||
|
* paddles. TODO: This currently does nothing.
|
||||||
|
*/
|
||||||
|
struct PaddleSwap : public ConfigSwitch {
|
||||||
|
PaddleSwap(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
|
||||||
|
virtual void set(bool on) { ; }
|
||||||
|
virtual bool get() const { return false(); }
|
||||||
|
private: KeyerConfigurator& config;
|
||||||
|
} paddleSwap;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief An inner class that supports changing the keyer speed.
|
||||||
|
*/
|
||||||
|
struct KeyerSpeed : public ConfigInteger {
|
||||||
|
KeyerSpeed(KeyerConfigurator& c): ConfigInteger(KEYER_MIN_SPEED, KEYER_MAX_SPEED), config(c) {}
|
||||||
|
virtual void onSet(int val) { config.keyer.setWPM(val); }
|
||||||
|
virtual int get { return config.keyer.getWPM(); }
|
||||||
|
private: KeyerConfigurator& config;
|
||||||
|
} keyerSpeed;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Keyer& keyer;
|
||||||
|
bool iambicAB;
|
||||||
|
bool isBug;
|
||||||
|
} keyerConfigurator(Keyer);
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
class MenuItem {
|
||||||
|
public:
|
||||||
|
MenuItem(int idno, const char* title, int numOpt, int defOpt, ): myID(idno), myTitle(title), numOptions(numOpt), selected(defOpt) {}
|
||||||
|
virtual ~MenuItem() = 0;
|
||||||
|
|
||||||
|
inline void id() { return myID; }
|
||||||
|
|
||||||
|
inline void writeTitle(char* outbuf, int maxlen) { strncpy(outbuf, myTitle, maxlen); }
|
||||||
|
inline char* writeOption(char* outbuf, int o = -1, int maxlen = MAX_MENU_OPTION_LEN) {
|
||||||
|
if (o == -1) {
|
||||||
|
setOptionText(selected);
|
||||||
|
} else if ((o >= 0) && (o < numOptions)) {
|
||||||
|
setOptionTitle(o);
|
||||||
|
} else {
|
||||||
|
memset(myOption, '\0', MAX_MENU_OPTION_LEN);
|
||||||
|
}
|
||||||
|
return strncpy(outbuf, myOption, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getNumOptions() { return numOptions; }
|
||||||
|
inline int getSelected() { return selected; }
|
||||||
|
inline int nextOption(bool doUpdate = true) {
|
||||||
|
selected = (selected + 1) % numOptions;
|
||||||
|
if (doUpdate) update();
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
inline int prevOption(bool doUpdate = true) {
|
||||||
|
selected = (selected - 1) % numOptions;
|
||||||
|
if (doUpdate) update();
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
inline int gotoOption(int o, bool doUpdate = true) {
|
||||||
|
selected = o >= numOptions ? numOptions : (o < 0 ? 0 : o);
|
||||||
|
if (doUpdate) update();
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
virtual void update() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int myID;
|
||||||
|
char myTitle[MAX_MENU_TITLE_LEN] = {'\0'};
|
||||||
|
char myOption[MAX_MENU_OPTION_LEN] = {'\0'};
|
||||||
|
|
||||||
|
int numOptions;
|
||||||
|
int selected;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StepItem : public MenuItem {
|
||||||
|
public:
|
||||||
|
StepItem(int idno, const char* title, int numOpt, int defOpt): MenuItem(idno, title, numOpt, defOpt) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char off_str[] = "OFF";
|
||||||
|
static const char on_str[] = "ON";
|
||||||
|
static const char line_str[] = "LINE";
|
||||||
|
static const char usb_str[] = "USB";
|
||||||
|
static const char key_a_str[] = "A";
|
||||||
|
static const char key_b_str[] = "B";
|
||||||
|
|
||||||
|
class ToggleItem : public MenuItem {
|
||||||
|
public:
|
||||||
|
ToggleItem(int idno, const char* title, const char* off, const char* on, int defOpt): MenuItem(idno, title, 2, defOpt) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
struct MenuConfig {
|
||||||
|
byte sidetoneVolume = 9;
|
||||||
|
byte ssbLoCut = 0;
|
||||||
|
byte ssbHiCut = 5;
|
||||||
|
byte dataLoCut = 0;
|
||||||
|
byte dataHiCut = 0;
|
||||||
|
byte keyerAB = 0;
|
||||||
|
byte sidetonePitch = 6;
|
||||||
|
};
|
||||||
|
|
||||||
|
// CAT COMMANDS - BASIC
|
||||||
|
|
||||||
|
// KS - Sets and reads the Keying speed.
|
||||||
|
IntegerItem ("KS", "KEYER SPEED", 0, 2, keyerConfigurator.keyerSpeed),
|
||||||
|
ArrayItem ("SH", "RX HI CUT ", 0, 2, dspConfigurator.ssbRxHiCut),
|
||||||
|
ArrayItem ("SL", "RX LO CUT ", 0, 2, dspConfigurator.ssbRxLoCut),
|
||||||
|
|
||||||
|
// CAT COMMANDS - EX MENU
|
||||||
|
|
||||||
|
// Sidetone volume
|
||||||
|
Steps {004, "ST VOL ", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},
|
||||||
|
// SSB/AM Low Cut transmit filter (Hz)
|
||||||
|
Steps {025, "SSB TX LO ", 6, [](auto x) { return txLowCutFilter[x]; }},
|
||||||
|
// SSB/AM High Cut transmit filter (Hz)
|
||||||
|
Steps {026, "SSB TX HI ", 6, [](auto x) { return txHighCutFilter[x]; }},
|
||||||
|
// SSB-DATA Low Cut transmit filter (Hz)
|
||||||
|
Steps {027, "DATA TX LO ", 6, [](auto x) { return txLowCutFilter[x]; }},
|
||||||
|
// SSB-DATA High Cut transmit filter (Hz)
|
||||||
|
Steps {028, "DATA TX HI ", 6, [](auto x) { return txHighCutFilter[x]; }},
|
||||||
|
// Electronic keyer operation mode
|
||||||
|
ToggleItem (032, "KEYER A/B ", "A ", "B ", keyerConfigurator.iambicMode),
|
||||||
|
// Sidetone/ pitch frequency setting (Hz)
|
||||||
|
Steps {034, "ST PITCH ", 15, [](auto x) { return 300+(x*50); }},
|
||||||
|
// Keying weight ratio
|
||||||
|
StepsSpec {036, "KEYER WEIGHT", 17, ...},
|
||||||
|
// Bug key function
|
||||||
|
ToggleItem (038, "KEYER BUG ", "OFF ", "ON ", keyerConfigurator.bugMode),
|
||||||
|
// Paddle dot/dash replacement setting
|
||||||
|
ToggleItem (039, "KEYER SWAP ", "OFF ", "ON ", keyerConfigurator.paddleSwap),
|
||||||
|
// Auto CW TX in SSB mode
|
||||||
|
//ToggleItem (041, "AUTO CW TX ", "OFF ", "ON ", [&Rig](bool on) { Rig.setKeyerAutoTransmitCW(on); } ),
|
||||||
|
// Time-out Timer
|
||||||
|
//StepsSpec {049, "TIMEOUT ", 6, ... },
|
||||||
|
// Transmit inhibit
|
||||||
|
ToggleItem (060, "TX INHIBIT ", "OFF ", "ON ", [&Rig](bool on) { Rig.setTransmitInhibit(on); } ),
|
||||||
|
// DATA moduleation line
|
||||||
|
ToggleItem (063, "DATA LINE ", "LINE", "USB ", [&Rig](bool usb) { Rig.setDataInputLine(usb); } ),
|
||||||
|
// USB audio input level
|
||||||
|
Steps {064, "USB IN LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
|
||||||
|
// USB audio output level
|
||||||
|
Steps {065, "USB OUT LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
|
||||||
|
// ACC2 terminal AF input level
|
||||||
|
Steps {066, "LINE IN LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
|
||||||
|
// ACC2 terminal AF output level
|
||||||
|
Steps {067, "LINE OUT LVL", [](auto x) { return static_cast<double>(x-48)/9.0; }},
|
||||||
|
// DATA VOX
|
||||||
|
ToggleItem (069, "DATA VOX ", "OFF ", "ON ", [&Rig](bool on) { Rig.setDataVoxOn(on); } ),
|
||||||
|
// DATA VOX delay
|
||||||
|
Steps {070, "DATA VOX DEL", 20, ...},
|
||||||
|
// DATA VOX gain for USB audio input
|
||||||
|
Steps {071, "USB VOX LVL ", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},
|
||||||
|
// DATA VOX gain for ACC2 terminal input
|
||||||
|
Steps {072, "LINE VOX LVL", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user