9 Commits

Author SHA1 Message Date
295b99ef36 making sure any outstanding updates are committed 2025-02-15 09:48:00 -06:00
Rob French
e6009989db Reorganizing DSP and TR. Thoroughly broken ATM, just committing for backup. 2021-03-30 12:31:44 -05:00
Rob French
1f3585d8e4 Various changes. Compiles. Hasn't been tested. 2021-03-19 15:30:30 -05:00
Rob French
c8aecdfb0d Further updates to DSP, and significant changes to TS590. Implementing templated CAT commands using the delegate template from the ETL. Compiles, but not tested. 2021-03-16 23:11:17 -05:00
Rob French
f724142fca Updated DSP. Compiles, no warnings/errors. Going to do some work on CAT. 2021-03-16 18:37:14 -05:00
Rob French
869e47d430 Significant updates to DSP. Compiles. Likely doesn't work at the moment. Next step should be to get it running again on the rig, NOT to try any more fancy updates to CAT etc. 2021-03-14 23:18:24 -05:00
Rob French
20b475dace Updates to the DSP. One issue that needs to be resolved: in order to use the VOX the way it is currently setup, muting of the overall TX chain needs to be at the END, not the beginning; whatever is setup as the default TX audio source, needs to be unmuted even during RX. 2021-03-04 23:12:41 -06:00
Rob French
962a3ce80f Revert "Updated with some of the changes I made on travel. Will probably break everything..."
This reverts commit 86ae1ddb2f.
2021-03-04 07:16:14 -06:00
Rob French
86ae1ddb2f Updated with some of the changes I made on travel. Will probably break everything... 2021-03-03 10:44:25 -06:00
18 changed files with 941 additions and 1200 deletions

View File

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

0
Raduino/Debug.h Executable file
View File

View File

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

0
Raduino/RigState.cpp Executable file
View File

View File

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

0
Raduino/RigState.h Executable file
View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

@@ -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.0 + symWeight) / (symWeight - 1.0); float w = (1 + symWeight) / (symWeight -1);
spaceLen = (1200 / speed); spaceLen = (1200 / speed);
dotLen = spaceLen * (w - 1); dotLen = spaceLen * (w - 1);
dashLen = (1 + w) * spaceLen; dashLen = (1 + w) * spaceLen;
@@ -73,12 +73,6 @@ void UBitxKeyer::setWPM(int wpm)
calcRatio(); calcRatio();
} }
void UBitxKeyer::setWeight(float weight)
{
symWeight = weight;
calcRatio();
}
//====================================================================== //======================================================================
// Latch paddle press // Latch paddle press
//====================================================================== //======================================================================
@@ -113,10 +107,11 @@ 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;
return true; // letting this fall through // return true;
} else {
return false;
} }
return false; // break;
//break;
case CHK_DIT: // See if the dit paddle was pressed case CHK_DIT: // See if the dit paddle was pressed
if (keyerControl & DIT_L) { if (keyerControl & DIT_L) {
@@ -124,57 +119,59 @@ 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;
return true; // letting this fall through // 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
return true; // letting this fall through // 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
return true; // letting this fall through // 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; return true;
} else {
keyerControl &= ~(DAH_L); // clear dah latch
keyerState = IDLE; // go idle
return false;
}
} else { } else {
keyerControl &= ~(DAH_L); // clear dah latch return true;
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?

View File

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

View File

@@ -3,56 +3,18 @@
#include "RigState.h" #include "RigState.h"
#define DEFAULT_SSB_LO_CUT 300.0 struct RigState {
#define DEFAULT_SSB_HI_CUT 3000.0
#define DEFAULT_CW_WIDTH 500.0
#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 getFreqA() const { return radState.getFreqA(); } inline unsigned getFreqA() const { return radState.getFreqA(); }
inline unsigned getFreqB() const { return radState.getFreqB(); } inline unsigned getFreqB() const { return radState.getFreqB(); }
inline int getRIT() const { return radState.getRIT(); } inline int getRIT() const { return radState.getRIT(); }
inline int getXIT() const { return radState.getXIT(); } inline int getXIT() const { return radState.getXIT(); }
inline bool isVFOA() const { return radState.isVFOA(); } inline bool isVFOA() const { return radState.isVFOA(); }
inline bool isVFOB() const { return radState.isVFOB(); } inline bool isVFOB() const { return radState.isVFOB(); }
inline bool isSplit() const { return radState.isSplit(); } inline bool isSplit() const { return radState.isSplit(); }
@@ -63,20 +25,12 @@ class UBitxRig {
inline bool isModeCWR() const { return radState.isModeCWR(); } inline bool isModeCWR() const { return radState.isModeCWR(); }
inline bool isModeUSB() const { return radState.isModeUSB(); } inline bool isModeUSB() const { return radState.isModeUSB(); }
inline bool isModeLSB() const { return radState.isModeLSB(); } inline bool isModeLSB() const { return radState.isModeLSB(); }
inline float getCWSidetone() const { return static_cast<float>(radState.getSidetone()); }
inline bool isUSBInput() const { return conf.useUSBInput; }
inline bool isLineInput() const { return !conf.useUSBInput; }
inline bool isAI() const { return autoInfo; } inline bool isAI() const { return autoInfo; }
inline void setFreqA(unsigned freq) { catState.setFreqA(freq); } inline void setFreqA(unsigned freq) { catState.setFreqA(freq); }
inline void setFreqB(unsigned freq) { catState.setFreqB(freq); } inline void setFreqB(unsigned freq) { catState.setFreqB(freq); }
inline void setRIT(int freq) { catState.setRIT(freq); } inline void setRIT(int freq) { catState.setRIT(freq); }
inline void setXIT(int freq) { catState.setXIT(freq); } inline void setXIT(int freq) { catState.setXIT(freq); }
inline void setVFOA() { catState.setVFOA(); } inline void setVFOA() { catState.setVFOA(); }
inline void setVFOB() { catState.setVFOB(); } inline void setVFOB() { catState.setVFOB(); }
inline void setSplitOn() { catState.setSplitOn(); } inline void setSplitOn() { catState.setSplitOn(); }
@@ -85,16 +39,12 @@ class UBitxRig {
inline void setRITOff() { catState.setRITOff(); } inline void setRITOff() { catState.setRITOff(); }
inline void setXITOn() { catState.setXITOn(); } inline void setXITOn() { catState.setXITOn(); }
inline void setXITOff() { catState.setXITOff(); } inline void setXITOff() { catState.setXITOff(); }
inline void setModeCW() { catState.setModeCW(); } inline void setCW() { catState.setCW(); }
inline void setModeCWR() { catState.setModeCWR(); } inline void setCWR() { catState.setCWR(); }
inline void setModeUSB() { catState.setModeUSB(); } inline void setUSB() { catState.setUSB(); }
inline void setModeLSB() { catState.setModeLSB(); } inline void setLSB() { catState.setLSB(); }
inline void setCWSidetone(float f) { catState.setSidetone(static_cast<uint16_t>(f)); }
inline void setUSBInput() { conf.useUSBInput = true; }
inline void setLineInput() { conf.useUSBInput = false; }
inline void setAI(bool on) { autoInfo = on; }
inline void aiOn() { autoInfo = true; } inline void aiOn() { autoInfo = true; }
inline void aiOff() { autoInfo = false; } inline void aiOff() { autoInfo = false; }
@@ -112,13 +62,10 @@ class UBitxRig {
//void setBand(); //void setBand();
//void getBand(); //void getBand();
private: private:
RigConfig conf;
UBitxRigState catState; UBitxRigState catState;
UBitxRigState radState; UBitxRigState radState;
bool autoInfo = false; bool autoInfo = false; // TODO: Move this to rig state struct
BandConfig band[NUM_BANDS];
}; };
extern UBitxRig Rig; extern UBitxRig Rig;

View File

@@ -56,7 +56,7 @@ void UBitxRigState::begin() {
#include "ubitx.h" #include "ubitx.h"
#include "ubitx_eemap.h" #include "ubitx_eemap.h"
extern unsigned long frequency, ritRxFrequency, ritTxFrequency, sideTone; extern unsigned long frequency, ritRxFrequency, ritTxFrequency;
extern unsigned long vfoA; extern unsigned long vfoA;
extern unsigned long vfoB; extern unsigned long vfoB;
extern char cwMode; extern char cwMode;
@@ -169,10 +169,8 @@ void UBitxRigState::writeDirty() {
// XIT - TODO // XIT - TODO
} }
// Various flags // VFO A/B selection
if (isDirty(FLAGS_WORD)) { if (isDirty(FLAGS_WORD)) {
// VFO A/B selection
char prev = vfoActive; char prev = vfoActive;
vfoActive = isVFOA() ? VFO_A : VFO_B; vfoActive = isVFOA() ? VFO_A : VFO_B;
if (vfoActive != prev) { if (vfoActive != prev) {
@@ -216,13 +214,6 @@ void UBitxRigState::writeDirty() {
setFrequency(frequency); setFrequency(frequency);
} }
} }
// Keyer information
if (isDirty(KEYER_WORD)) {
// Sidetone frequency
sideTone = static_cast<unsigned long>(getSidetone());
}
} }
/*! /*!
@@ -293,22 +284,17 @@ void UBitxRigState::readDirty() {
char curr = (cwMode << 1) | isUSB; char curr = (cwMode << 1) | isUSB;
if (curr != prev) { if (curr != prev) {
if (cwMode == 2) { if (cwMode == 2) {
setModeCW(); setCW();
} else if (cwMode == 1) { } else if (cwMode == 1) {
setModeCWR(); setCWR();
} else { } else {
if (isUSB) { if (isUSB) {
setModeUSB(); setUSB();
} else { } else {
setModeLSB(); setLSB();
} }
} }
} }
// Sidetone
if (getSidetone() != static_cast<uint16_t>(sideTone)) {
setSidetone(static_cast<uint16_t>(sideTone));
}
} }
/*********************************************************************** /***********************************************************************
@@ -346,16 +332,6 @@ void UBitxRigState::receive_RIGINF(int numBytes) {
} }
} }
//--------------------------------------------------------------------
// 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( serialHexState("Rcvd") );
IFDEBUG( serialPrettyState("Rcvd") ); IFDEBUG( serialPrettyState("Rcvd") );
} }
@@ -396,13 +372,6 @@ void UBitxRigState::send_RIGINF() {
//-------------------------------------------------------------------- //--------------------------------------------------------------------
} }
/*!
* @brief Perform required actions based on any dirty bits set.
*/
void UBitxRigState::processDirty() {
}
#endif #endif
#ifdef DEBUG #ifdef DEBUG

View File

@@ -15,9 +15,6 @@
#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 #ifdef TEENSYDUINO
#define DISABLEINTS(CMD) do { noInterrupts(); CMD; interrupts(); } while (0) #define DISABLEINTS(CMD) do { noInterrupts(); CMD; interrupts(); } while (0)
#else #else
@@ -30,7 +27,6 @@ enum RigStateWord {
VFOB_WORD, VFOB_WORD,
OFFSETS_WORD, OFFSETS_WORD,
FLAGS_WORD, FLAGS_WORD,
KEYER_WORD,
NUM_WORDS NUM_WORDS
}; };
@@ -259,25 +255,25 @@ struct UBitxRigState {
return result; return result;
} }
inline void setModeUSB(bool mark = true) { inline void setUSB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG; DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
data[FLAGS_WORD] &= ~UBITX_CW_FLAG; data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) ); if (mark) setDirty(FLAGS_WORD) );
} }
inline void setModeLSB(bool mark = true) { inline void setLSB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG; DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
data[FLAGS_WORD] &= ~UBITX_CW_FLAG; data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) ); if (mark) setDirty(FLAGS_WORD) );
} }
inline void setModeCW(bool mark = true) { inline void setCW(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG; DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
data[FLAGS_WORD] |= UBITX_CW_FLAG; data[FLAGS_WORD] |= UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) ); if (mark) setDirty(FLAGS_WORD) );
} }
inline void setModeCWR(bool mark = true) { inline void setCWR(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG; DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
data[FLAGS_WORD] |= UBITX_CW_FLAG; data[FLAGS_WORD] |= UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) ); if (mark) setDirty(FLAGS_WORD) );
@@ -313,18 +309,6 @@ struct UBitxRigState {
return result; 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 #ifdef DEBUG
void serialHexState(const char* label); void serialHexState(const char* label);
void serialPrettyState(const char* label); void serialPrettyState(const char* label);
@@ -335,9 +319,6 @@ struct UBitxRigState {
// RigState, not in the TeensyDSP (Teensy) case. // RigState, not in the TeensyDSP (Teensy) case.
void writeDirty(); // write fields FROM RigState TO Raduino void writeDirty(); // write fields FROM RigState TO Raduino
void readDirty(); // read variables FROM Raduino TO RigState void readDirty(); // read variables FROM Raduino TO RigState
#else
// These methods are only defined (currently) in the TeensyDSP case.
void processDirty();
#endif #endif
}; };

View File

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

View File

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

View File

@@ -34,12 +34,11 @@ void ts590SendCommand(const char* format, ...) {
* than two are supplied, then the command will be * than two are supplied, then the command will be
* initialized with a null prefix. * initialized with a null prefix.
*/ */
TS590Command::TS590Command(const char* pre) { TS590Command::TS590Command(const char* pre)
if (strlen(pre) >= 2) { : myPrefix(pre), prefixLength(strlen(pre))
myPrefix[0] = pre[0]; {}
myPrefix[1] = pre[1];
} TS590Command::~TS590Command() {}
}
/*! /*!
* @brief Determine whether this is a Read command or not. by * @brief Determine whether this is a Read command or not. by
@@ -47,7 +46,7 @@ TS590Command::TS590Command(const char* pre) {
* @return True if a Read command; false otherwise. * @return True if a Read command; false otherwise.
*/ */
bool TS590Command::isReadCommand(const char* cmd) const { bool TS590Command::isReadCommand(const char* cmd) const {
if (strlen(cmd) == 2) { if (strlen(cmd) == prefixLength) {
return true; return true;
} else { } else {
return false; return false;
@@ -137,22 +136,45 @@ void TS590Command::setDSP(UBitxDSP* d) {
theDSP = 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; UBitxDSP* TS590Command::theDSP = &DSP;
UBitxTR* TR590Command::theTR = &TR;
TS590Error TS590Command::theError = NoError; TS590Error TS590Command::theError = NoError;
/**********************************************************************/ /**********************************************************************/
void TS590Command_Bool::handleCommand(const char* cmd) {
setter(cmd[length()] == '0' ? false : true);
}
void TS590Command_Bool::sendResponse(const char* cmd) {
ts590SendCommand("%s%s", prefix(), getter() ? "1" : "0");
}
/**********************************************************************/
void TS590Command_UL::handleCommand(const char* cmd) {
unsigned val = static_cast<unsigned>(strtoul(&cmd[length()], NULL, 10));
if (val < myMin) {
val = myMin;
} else if (val > myMax) {
val = myMax;
}
val = (val * mySlope) + myIntercept;
setter(val);
}
void TS590Command_UL::sendResponse(const char* cmd) {
unsigned val = getter();
val = (val - myIntercept) / mySlope;
if (val < myMin) {
val = myMin;
} else if (val > myMax) {
val = myMax;
}
ts590SendCommand("%s%0*u", prefix(), myWidth, getter());
}
/**********************************************************************/
void TS590_FR::handleCommand(const char* cmd) { void TS590_FR::handleCommand(const char* cmd) {
if (strlen(cmd) == 3) { if (strlen(cmd) == 3) {
switch (cmd[2]) { switch (cmd[2]) {
@@ -246,19 +268,19 @@ void TS590_MD::handleCommand(const char* cmd) {
break; break;
case '1': // LSB case '1': // LSB
rig()->setModeLSB(); rig()->setLSB();
break; break;
case '2': // USB case '2': // USB
rig()->setModeUSB(); rig()->setUSB();
break; break;
case '3': // CW case '3': // CW
rig()->setModeCW(); rig()->setCW();
break; break;
case '7': // CW-R case '7': // CW-R
rig()->setModeCWR(); rig()->setCWR();
break; break;
default: default:
@@ -327,122 +349,78 @@ void TS590_SL::sendResponse(const char* cmd) {
/**********************************************************************/ /**********************************************************************/
void TS590_TX::handleCommand(const char* cmd) { void TS590_VX::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) { void TS590_VX::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);
} }
/**********************************************************************/ /**********************************************************************/
/*! void nullSetFunc(unsigned x) { return; }
* @brief Create a new CAT EX command. It should be initialized with unsigned getIDFunc() {
* a 3-character P1 parameter (command number). #ifndef USE_TS590SG_CAT
* @param pre return 021;
* A 3-character command prefix. If more than 3 characters #else
* are supplied, only the first two will be used. If less return 023;
* than three are supplied, then the command will be #endif
* initialized with a null prefix.
*/
TS590EXCommand::TS590EXCommand(const char* P1):
TS590Command("EX") {
if (strlen(P1) >= 3) {
strncpy(myMenu, P1, 3);
}
} }
/*! SetUL setAG = SetUL::create<UBitxDSP, &UBitxDSP::setLineOut255>(DSP);
* @brief Determine whether this is a Read command or not. by GetUL getAG = GetUL::create<UBitxDSP, &UBitxDSP::getLineOut255>(DSP);
* default, if it's a 2-letter command, it's a Read. SetUL setAI = [](unsigned v) -> void { v == 0 ? Rig.aiOff() : Rig.aiOn(); };
* @return True if a Read command; false otherwise. GetUL getAI = []() -> unsigned { return Rig.isAI() ? 4 : 0; };
*/ //SetUL setSidetone = SetUL::create<UBitxDSP, ...>(...);
bool TS590EXCommand::isReadCommand(const char* cmd) const { //GetUL getSidetone = GetUL::create<UBitxDSP, ...>(...);
if (strlen(cmd) == 9) { SetUL setUSBin = SetUL::create<UBitxDSP, &UBitxDSP::setUSBIn9>(DSP);
return true; GetUL getUSBin = GetUL::create<UBitxDSP, &UBitxDSP::getUSBIn9>(DSP);
} else { SetUL setUSBout = SetUL::create<UBitxDSP, &UBitxDSP::setUSBOut9>(DSP);
return false; GetUL getUSBout = GetUL::create<UBitxDSP, &UBitxDSP::getUSBOut9>(DSP);
} SetUL setACC2in = SetUL::create<UBitxDSP, &UBitxDSP::setLineIn9>(DSP);
} GetUL getACC2in = GetUL::create<UBitxDSP, &UBitxDSP::getLineIn9>(DSP);
SetUL setACC2out = SetUL::create<UBitxDSP, &UBitxDSP::setLineOut9>(DSP);
GetUL getACC2out = GetUL::create<UBitxDSP, &UBitxDSP::getLineOut9>(DSP);
SetUL setVoxDelay = SetUL::create<UBitxDSP, &UBitxDSP::setDataVoxDelay>(DSP);
GetUL getVoxDelay = GetUL::create<UBitxDSP, &UBitxDSP::getDataVoxDelay>(DSP);
SetUL setUSBvox = SetUL::create<UBitxDSP, &UBitxDSP::setUSBVOXThresh9>(DSP);
GetUL getUSBvox = GetUL::create<UBitxDSP, &UBitxDSP::getUSBVOXThresh9>(DSP);
SetUL setACC2vox = SetUL::create<UBitxDSP, &UBitxDSP::setLineVOXThresh9>(DSP);
GetUL getACC2vox = GetUL::create<UBitxDSP, &UBitxDSP::getLineVOXThresh9>(DSP);
SetUL setID = SetUL::create<nullSetFunc>();
GetUL getID = GetUL::create<getIDFunc>();
void TS590EXCommand::sendResponse(const char* cmd) { TS590Command_UL TS590_AG("AG0", 3, 0, 255, setAG, getAG);
ts590sendCommand("%2c%3c0000%s", prefix(), menu(), getReturnValue()); TS590Command_UL TS590_AI("AI", 1, 0, 4, setAI, getAI);
} // TS590_AS
// TS590_BD
/**********************************************************************/ // TS590_BU
// TS590_CA
void TS590_EX034::handleCommand(const char* cmd) { // TS590_CD0
if (strlen(cmd) == 10 || strlen(cmd) == 11) { // TS590_CD1
index = (uint8_t)atol(&cmd[9]); // TS590_CD2
if (index < 15) { // TS590_CH
rig().setCWSidetone(300.0 + (float)(index * 50)); #ifndef USE_TS590SG_CAT
} else { //TS590Command_UL TS590_EX034("EX0340000", 2, 0, 14, 50, 300, setSideTone, getSideTone);
setSyntaxError(); TS590Command_UL TS590_EX064("EX0640000", 1, 0, 9, setUSBin, getUSBin);
} TS590Command_UL TS590_EX065("EX0650000", 1, 0, 9, setUSBout, getUSBout);
} else { TS590Command_UL TS590_EX066("EX0660000", 1, 0, 9, setACC2in, getACC2in);
setSyntaxError(); TS590Command_UL TS590_EX067("EX0670000", 1, 0, 9, setACC2out, getACC2out);
} TS590Command_UL TS590_EX070("EX0700000", 2, 0, 20, 5, 0, setVoxDelay, getVoxDelay);
} TS590Command_UL TS590_EX071("EX0710000", 1, 0, 9, setUSBvox, getUSBvox);
TS590Command_UL TS590_EX072("EX0720000", 1, 0, 9, setACC2vox, getACC2vox);
void TS590_EX034::sendResponse(const char* cmd) { #else
ts590SendCommand("EX0340000%02d", index % 15); //TS590Command_UL TS590_EX040("EX0400000", 2, 0, 14, 50, 300, setSideTone, getSideTone);
} TS590Command_UL TS590_EX071("EX0710000", 1, 0, 9, setUSBin, getUSBin);
TS590Command_UL TS590_EX072("EX0720000", 1, 0, 9, setUSBout, getUSBout);
/**********************************************************************/ TS590Command_UL TS590_EX073("EX0730000", 1, 0, 9, setACC2in, getACC2in);
TS590Command_UL TS590_EX074("EX0740000", 1, 0, 9, setACC2out, getACC2out);
void TS590_EX063::handleCommand(const char* cmd) { TS590Command_UL TS590_EX077("EX0770000", 2, 0, 20, 5, 0, setVoxDelay, getVoxDelay);
if (strlen(cmd) == 10) { TS590Command_UL TS590_EX078("EX0780000", 1, 0, 9, setUSBvox, getUSBvox);
if (cmd[9] == '0') { TS590Command_UL TS590_EX079("EX0790000", 1, 0, 9, setACC2vox, getACC2vox);
rig().setLineInput(); #endif
} else if (cmd[9] == '1') { TS590Command_UL TS590_ID("ID", 3, 21, 23, setID, getID);
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;
TS590_FB cmdFB; TS590_FB cmdFB;
@@ -465,7 +443,6 @@ int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]);
/**********************************************************************/ /**********************************************************************/
void UBitxTS590::begin() { void UBitxTS590::begin() {
Serial.begin(9600); // USB is always 12 Mbit/sec Serial.begin(9600); // USB is always 12 Mbit/sec
#ifdef DEBUG #ifdef DEBUG
@@ -506,21 +483,7 @@ typedef class TS590Command* PCmd;
int compareCATCommands(const void* a, const void* b) { int compareCATCommands(const void* a, const void* b) {
TS590Command const *B = *(TS590Command const **)b; TS590Command const *B = *(TS590Command const **)b;
int cmp = strncmp((char*)a, (char*)B->prefix(), 2); int cmp = strncmp((char*)a, (char*)B->prefix(), B->length());
#ifdef DEBUG
Serial.print("Comparison: ");
Serial.print((char*)a);
Serial.print(" ? ");
Serial.print((char*)B->prefix());
Serial.print(" --> ");
Serial.println(cmp);
#endif
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 #ifdef DEBUG
Serial.print("Comparison: "); Serial.print("Comparison: ");
Serial.print((char*)a); Serial.print((char*)a);
@@ -533,12 +496,7 @@ int compareCATEXCommands(const void* a, const void* b) {
} }
void UBitxTS590::processCommand() { void UBitxTS590::processCommand() {
TS590Command** cmd; TS590Command** cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands);
if (strncmp(buf, "EX", 2) == 0) {
cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATEXCommands);
} else {
cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands);
}
if (cmd == NULL) { if (cmd == NULL) {
ts590SyntaxError(); ts590SyntaxError();
} else { } else {

View File

@@ -2,15 +2,13 @@
#define __TS590_h__ #define __TS590_h__
#include <Arduino.h> #include <Arduino.h>
#include <Embedded_Template_Library.h>
#include <etl/delegate.h>
#include "DSP.h" #include "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;
@@ -39,6 +37,72 @@ enum TS590Error {
ProcessError ProcessError
}; };
/**********************************************************************
**********************************************************************/
/*
class TS590BaseCommand {
public:
TS590BaseCommand(const char* prefix)
: myPrefix(prefix), prefixLength(strlen(prefix))
{}
virtual ~TS590BaseCommand() = 0;
inline const char* prefix() { return &myPrefix[0]; }
inline size_t length() { return prefixLength; }
virtual void handleCommand(const char* cmd) = 0;
virtual void sendResponse(const char* cmd) = 0;
virtual bool isReadCommand(const char* cmd) const;
inline void process(const char* cmd) {
theError = NoError;
if (isReadCommand(cmd)) {
DBGCMD( sendResponse(cmd) );
} else {
DBGCMD( handleCommand(cmd) );
switch(theError) {
case NoError:
if (theRig->isAI()) {
DBGCMD( sendResponse(cmd) );
}
break;
case SyntaxError:
DBGCMD( ts590SyntaxError() );
break;
case CommError:
DBGCMD( ts590CommError() );
break;
case ProcessError:
DBGCMD( ts590ProcessError() );
break;
}
}
}
private:
const char* myPrefix;
size_t prefixLength;
};
*/
typedef etl::delegate<bool(const char*)> ValidateFunc;
typedef etl::delegate<bool(const char*)> IsReadFunc;
typedef etl::delegate<void(bool)> ToggleFunc;
/*
class TS590ToggleCommand : public TS590BaseCommand {
TS590ToggleCommand(const char* prefix, ToggleFunc& setter, ToggleFunc& getter)
: TS590BaseCommand(prefix)
{}
private:
ToggleFunc& mySetter;
ToggleFunc& myGetter;
};
*/
/**********************************************************************/ /**********************************************************************/
/*! /*!
@@ -48,7 +112,7 @@ enum TS590Error {
class TS590Command { class TS590Command {
public: public:
TS590Command(const char* pre); TS590Command(const char* pre);
virtual ~TS590Command() = 0; virtual ~TS590Command();
/*! /*!
* @brief Return the 2-character prefix for the command. * @brief Return the 2-character prefix for the command.
@@ -56,6 +120,8 @@ class TS590Command {
*/ */
inline const char* prefix() const { return &myPrefix[0]; } inline const char* prefix() const { return &myPrefix[0]; }
inline size_t length() const { return prefixLength; }
/*! /*!
* @brief Return the rig that this command will be used to control. * @brief Return the rig that this command will be used to control.
*/ */
@@ -66,11 +132,6 @@ class TS590Command {
*/ */
inline UBitxDSP* dsp() const { return theDSP; } 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
@@ -97,10 +158,10 @@ class TS590Command {
static void setProcessError(); static void setProcessError();
static void setRig(UBitxRig* r); static void setRig(UBitxRig* r);
static void setDSP(UBitxDSP* d); static void setDSP(UBitxDSP* d);
static void setTR(UBitxTR* t);
private: private:
char myPrefix[3] = "\0\0"; const char* myPrefix;
size_t prefixLength;
static TS590Error theError; static TS590Error theError;
static UBitxRig* theRig; static UBitxRig* theRig;
static UBitxDSP* theDSP; static UBitxDSP* theDSP;
@@ -108,6 +169,68 @@ class TS590Command {
/**********************************************************************/ /**********************************************************************/
typedef etl::delegate<void(bool)> SetBool;
typedef etl::delegate<bool(void)> GetBool;
class TS590Command_Bool : public TS590Command {
public:
TS590Command_Bool(const char* prefix, SetBool set, GetBool get)
: TS590Command(prefix), setter(set), getter(get) {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
SetBool setter;
GetBool getter;
};
/**********************************************************************/
typedef etl::delegate<void(unsigned)> SetUL;
typedef etl::delegate<unsigned(void)> GetUL;
class TS590Command_UL : public TS590Command {
public:
TS590Command_UL(const char* prefix, size_t width, unsigned min, unsigned max, SetUL set, GetUL get)
: TS590Command(prefix), myWidth(width), myMin(min), myMax(max), mySlope(1), myIntercept(0), setter(set), getter(get) {}
TS590Command_UL(const char* prefix, size_t width, unsigned min, unsigned max, unsigned slope, unsigned intercept, SetUL set, GetUL get)
: TS590Command(prefix), myWidth(width), myMin(min), myMax(max), mySlope(slope), myIntercept(intercept), setter(set), getter(get) {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
size_t myWidth;
unsigned myMin;
unsigned myMax;
unsigned mySlope;
unsigned myIntercept;
SetUL setter;
GetUL getter;
};
/**********************************************************************/
/*
typedef etl::delegate<void(bool)> SetULArray;
typedef etl::delegate<bool(void)> GetULArray;
class TS590Command_ULArray : public TS590Command {
public:
TS590Command_ULArray(const char* prefix, SetUL set, GetUL get)
: TS590Command(prefix), setter(set), getter(get) {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
private:
SetUL setter;
GetUL getter;
};
*/
/**********************************************************************/
/*! /*!
* @brief CAT command for setting or reading the VFO A/B frequency. * @brief CAT command for setting or reading the VFO A/B frequency.
*/ */
@@ -203,134 +326,19 @@ class TS590_SL : public TS590Command {
}; };
/*! /*!
* @brief CAT command to start transmitting. * CAT command for enabling or disabling the mic VOX.
*/ */
class TS590_TX : public TS590Command { class TS590_VX : public TS590Command {
public: public:
TS590_TX(): TS590Command("TX") {} TS590_VX(): TS590Command("VX") {}
virtual void handleCommand(const char* cmd); virtual void handleCommand(const char* cmd);
virtual void sendResponse(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: private:
char myMenu[4] = "\0\0\0"; unsigned index;
}; };
/**********************************************************************/ /**********************************************************************/
/*!
* @brief CAT command for setting the sidetone pitch/frequency.
*/
class TS590_EX034 : public TS590EXCommand {
public:
TS590_EX034(): TS590EXCommand("034") {}
virtual void handleCommand(const char* cmd);
virtual const char* getReturnValue();
private:
uint8_t index;
};
/*!
* @brief CAT command for selecting the data input line.
*/
class TS590_EX063 : public TS590EXCommand {
public:
TS590_EX063(): TS590EXCommand("063") {}
virtual void handleCommand(const char* cmd);
virtual const char* getReturnValue();
};
/**********************************************************************/
/*!
* @brief CAT command for setting USB/Line audio input levels.
*/
template<bool USB, bool SG>
class TS590_EXDataAudioInLevel : public TS590EXCommand {
public:
TS590_EXDataAudioInLevel(): TS590EXCommand(USB ? (SG ? "071" : "064") : (SG ? "073" : "066")) {}
virtual void handleCommand(const char* cmd) {
if (strlen(cmd) == 10) {
uint8_t val = (cmd[9] - 48) % 10;
if (USB) {
// set USB input level
} else {
// set Line input level
}
} else {
setSyntaxError();
}
}
virtual const char* getReturnValue() {
static char str[2] = "\0";
// get input level - decimal 0 to 9 ... str[1] = ...
return str;
}
};
#ifdef USE_TS590SG
typedef TS590_EXDataAudioInLevel<true, true> TS590_EX071;
typedef TS590_EXDataAudioInLevel<false, true> TS590_EX073;
#else
typedef TS590_EXDataAudioInLevel<true, false> TS590_EX064;
typedef TS590_EXDataAudioInLevel<true, false> TS590_EX066;
#endif
/**********************************************************************/
/*!
* @brief CAT command for setting USB/Line audio output levels.
*/
template<bool USB, bool SG>
class TS590_EXDataAudioOutLevel : public TS590EXCommand {
public:
TS590_EXDataAudioOutLevel(): TS590EXCommand(USB ? (SG ? "072" : "065") : (SG ? "074" : "067")) {}
virtual void handleCommand(const char* cmd) {
if (strlen(cmd) == 10) {
uint8_t val = (cmd[9] - 48) % 10;
if (USB) {
// set USB output level
} else {
// set Line output level
}
} else {
setSyntaxError();
}
}
virtual const char* getReturnValue() {
static char str[2] = "\0";
// get output level - decimal 0 to 9 ... str[1] = ...
return str;
}
};
#ifdef USE_TS590SG
typedef TS590_EXDataAudioOutLevel<true, true> TS590_EX072;
typedef TS590_EXDataAudioOutLevel<false, true> TS590_EX074;
#else
typedef TS590_EXDataAudioOutLevel<true, false> TS590_EX065;
typedef TS590_EXDataAudioOutLevel<true, false> TS590_EX067;
#endif
/**********************************************************************/
class UBitxTS590 { class UBitxTS590 {
public: public:
UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {} UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {}

View File

@@ -593,11 +593,11 @@ void loop()
Serial.println(AudioMemoryUsageMax()); Serial.println(AudioMemoryUsageMax());
Serial.println("----------------------------------------------------------------------"); Serial.println("----------------------------------------------------------------------");
Serial.print("Enabled/Active: PTT: "); Serial.print("Enabled/Active: PTT: ");
Serial.print(TR.pttEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.pttPressed() ? "Y" : "N"); Serial.print(TR.micPTTEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.micPTTPressed() ? "Y" : "N");
Serial.print(", VOX: "); Serial.print(", VOX: ");
Serial.print(TR.voxEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.voxActivated() ? "Y" : "N"); Serial.print(TR.micVOXEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.micVOXActivated() ? "Y" : "N");
Serial.print(", Key: "); Serial.print(", Key: ");
Serial.print(TR.keyEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.keyPressed() ? "Y" : "N"); Serial.print(TR.linePTTEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.linePTTPressed() ? "Y" : "N");
Serial.print(", CAT: "); Serial.print(", CAT: ");
Serial.print(TR.catEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.catActivated() ? "Y" : "N"); Serial.print(TR.catEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.catActivated() ? "Y" : "N");
Serial.println(); Serial.println();

View File

@@ -1,386 +0,0 @@
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; }},