From 5b395cd922f551f429b3a4367b7afab916173d71 Mon Sep 17 00:00:00 2001 From: Rob French Date: Fri, 12 Feb 2021 22:19:14 -0600 Subject: [PATCH] Added multiple new audio (DSP) functions. Minor updates to the rig. Added additional CAT command (filter hi- and lo-cut frequencies, SH/SL). --- TeensyDSP/DSP.cpp | 248 ++++++++++++++++++++++++++++++++++++++++---- TeensyDSP/DSP.h | 104 ++++++++++++++++++- TeensyDSP/Rig.h | 11 ++ TeensyDSP/TS590.cpp | 58 ++++++++++- TeensyDSP/TS590.h | 34 ++++++ 5 files changed, 431 insertions(+), 24 deletions(-) diff --git a/TeensyDSP/DSP.cpp b/TeensyDSP/DSP.cpp index 2dc57ef..eecb5fa 100644 --- a/TeensyDSP/DSP.cpp +++ b/TeensyDSP/DSP.cpp @@ -4,7 +4,6 @@ #include "DSP.h" -#include #include //#include //#include @@ -13,35 +12,58 @@ #define RX_AUDIO_CH 0 -#define TX_MIC_IN_CH 0 -#define TX_LINE_IN_CH 0 -#define TX_USB_IN_CH1 1 -#define TX_USB_IN_CH2 2 +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 -AudioInputI2S lineIn; //xy=385.9371643066406,1001.9600830078125 -AudioInputUSB usbIn; //xy=390.9371643066406,1107.9600830078125 -AudioMixer4 rxAudio; //xy=642.9371643066406,915.9600830078125 -AudioMixer4 txAudio; //xy=646.9371643066406,1103.9600830078125 -AudioOutputUSB usbOut; //xy=1022.9371643066406,919.9600830078125 -AudioOutputI2S lineOut; //xy=1024.9371643066406,1017.9600830078125 -AudioConnection patchCord1(lineIn, 0, rxAudio, 0); -AudioConnection patchCord2(lineIn, 1, txAudio, 0); -AudioConnection patchCord3(usbIn, 0, txAudio, 1); -AudioConnection patchCord4(usbIn, 1, txAudio, 2); -AudioConnection patchCord5(rxAudio, 0, lineOut, 0); -AudioConnection patchCord6(rxAudio, 0, usbOut, 0); -AudioConnection patchCord7(rxAudio, 0, usbOut, 1); -AudioConnection patchCord8(txAudio, 0, lineOut, 1); -AudioControlSGTL5000 audioCtrl; //xy=651.9371643066406,1244.9600830078125 +AudioInputUSB usbIn; //xy=227,290 +AudioInputI2S lineIn; //xy=235,182 +AudioAnalyzeRMS usbInRMS_R; //xy=350,380 +AudioAnalyzeRMS usbInRMS_L; //xy=409,341 +AudioAnalyzeRMS lineInRMS; //xy=461,222 +AudioMixer4 rxAudio; //xy=492,96 +AudioMixer4 txAudio; //xy=496,284 +AudioFilterFIR rxFilter; //xy=657,88 +AudioAmplifier usbOutAmp; //xy=822,84 +AudioAmplifier lineOutAmp; //xy=823,147 +AudioOutputI2S lineOut; //xy=1040,279 +AudioOutputUSB usbOut; //xy=1042,240 +AudioConnection patchCord1(usbIn, 0, txAudio, 1); +AudioConnection patchCord2(usbIn, 0, usbInRMS_L, 0); +AudioConnection patchCord3(usbIn, 1, usbInRMS_R, 0); +AudioConnection patchCord4(lineIn, 0, rxAudio, 0); +AudioConnection patchCord5(lineIn, 1, txAudio, 0); +AudioConnection patchCord6(lineIn, 1, lineInRMS, 0); +AudioConnection patchCord7(rxAudio, rxFilter); +AudioConnection patchCord8(rxAudio, 0, usbOut, 1); +AudioConnection patchCord9(txAudio, 0, lineOut, 1); +AudioConnection patchCord10(rxFilter, usbOutAmp); +AudioConnection patchCord11(rxFilter, lineOutAmp); +AudioConnection patchCord12(usbOutAmp, 0, usbOut, 0); +AudioConnection patchCord13(lineOutAmp, 0, lineOut, 0); +AudioControlSGTL5000 audioCtrl; //xy=501,425 // GUItool: end automatically generated code //} audio; +UBitxDSP::UBitxDSP() { + voxRMS[txLineInVOX] = &lineInRMS; + voxRMS[txUSBInLVOX] = &usbInRMS_L; + voxRMS[txUSBInRVOX] = &usbInRMS_R; +} + void UBitxDSP::begin() { AudioMemory(16); audioCtrl.enable(); @@ -77,17 +99,55 @@ void UBitxDSP::begin() { // SETUP THE AUDIO OUTPUTS // Line Output (RX) + lineOutAmp.gain(0.0); // USB Output (RX) + usbOutAmp.gain(0.0); // Rig (Line) Output (TX) // Default to RX. rx(); + + // Setup the VOX - clean this up + state.voxActive[TX_LINE_IN_VOX] = false; + state.voxThresh[TX_LINE_IN_VOX] = TX_LINE_IN_VOX_THRESH; + state.voxDelay[TX_LINE_IN_VOX] = TX_LINE_IN_VOX_DELAY; + state.voxTimeout[TX_LINE_IN_VOX] = 0; + state.voxActive[TX_USB_IN_L_VOX] = false; + state.voxThresh[TX_USB_IN_L_VOX] = TX_USB_IN_L_VOX_THRESH; + state.voxDelay[TX_USB_IN_L_VOX] = TX_USB_IN_L_VOX_DELAY; + state.voxTimeout[TX_USB_IN_L_VOX] = 0; + state.voxActive[TX_USB_IN_R_VOX] = false; + state.voxThresh[TX_USB_IN_R_VOX] = TX_USB_IN_R_VOX_THRESH; + state.voxDelay[TX_USB_IN_R_VOX] = TX_USB_IN_R_VOX_DELAY; + state.voxTimeout[TX_USB_IN_R_VOX] = 0; + + sinceLastUpdate = 0; } void UBitxDSP::update() { - + // Only going to adjust the USB volume periodically. + if (sinceLastUpdate > DSP_MILLIS_PER_UPDATE) { + float vol = usbIn.volume(); + setTxInputLevel(txUSBInChannel, vol); + sinceLastUpdate = 0; + } + + // Update the VOX switches. + // TODO: Move the enable logic in here, so we don't process unnecessarily. + for (int i = 0; i < txNumVOX; i++) { + if (voxRMS[i]->available()) { + float lvl = voxRMS[i]->read(); + if (lvl > state.voxThresh[i]) { + state.voxTimeout[i] = millis() + state.voxDelay[i]; + state.voxActive[i] = true; + } + } + if (millis() > state.voxTimeout[i]) { + state.voxActive[i] = false; + } + } } void UBitxDSP::end() { @@ -138,13 +198,157 @@ void UBitxDSP::txUSBIn() { // restore the tx usb in audio audioCtrl.inputSelect(AUDIO_INPUT_LINEIN); for (int i = 0; i < 4; i++) { - if (i == TX_USB_IN_CH1 || i == TX_USB_IN_CH2) + if (i == TX_USB_IN_CH) txAudio.gain(i, 0.1); else txAudio.gain(i, 0.0); } } +/**********************************************************************/ +// RX filter settings + +const int minRxFilterLo = MIN_RX_FILTER_LO; +const int maxRxFilterHi = MAX_RX_FILTER_HI; +const int minRxFilterWidth = MIN_RX_FILTER_WIDTH; +const int maxRxFilterWidth = MAX_RX_FILTER_WIDTH; +const int minRxFilterCenter = MIN_RX_FILTER_CENTER; +const int maxRxFilterCenter = MAX_RX_FILTER_CENTER; + +/*! + * @brief Bypass the RX audio filter. + */ +void UBitxDSP::bypassRxFilter() { + rxFilter.begin(FIR_PASSTHRU, NUM_COEFFICIENTS); +} + +/*! + * @brief Update the RX audio filter using the currently set low and + * high frequencies. This is called by each of the public + * filter methods to update the filter with new frequencies. + */ +void UBitxDSP::updateRxFilter() { + audioFilter(coefficients, NUM_COEFFICIENTS, ID_BANDPASS, W_HAMMING, double(state.rxFilterLo), double(state.rxFilterHi)); + rxFilter.begin(coefficients, NUM_COEFFICIENTS); +} + +void UBitxDSP::setRxFilter(int lo, int hi) { + if (hi < lo + minRxFilterWidth) { + hi = lo + minRxFilterWidth; + } + if (hi > maxRxFilterHi) { + hi = maxRxFilterHi; + } + if (lo > hi - minRxFilterWidth) { + lo = hi - minRxFilterWidth; + } + if (lo < minRxFilterLo) { + lo = minRxFilterLo; + } + state.rxFilterHi = hi; + state.rxFilterLo = lo; + updateRxFilter(); +} + +void UBitxDSP::setRxFilterLo(int lo) { + if (lo > state.rxFilterHi - minRxFilterWidth) { + lo = state.rxFilterHi - minRxFilterWidth; + } + if (lo < minRxFilterLo) { + lo = minRxFilterLo; + } + state.rxFilterLo = lo; + updateRxFilter(); +} + +void UBitxDSP::setRxFilterHi(int hi) { + if (hi < state.rxFilterLo + minRxFilterWidth) { + hi = state.rxFilterLo + minRxFilterWidth; + } + if (hi > maxRxFilterHi) { + hi = maxRxFilterHi; + } + state.rxFilterHi = hi; + updateRxFilter(); +} + +void UBitxDSP::setRxFilterWidth(int width) { + if (width < minRxFilterWidth) { + width = minRxFilterWidth; + } else if (width > maxRxFilterWidth) { + width = maxRxFilterWidth; + } + int center = (state.rxFilterHi + state.rxFilterLo) / 2; + int lo = center - (width / 2); + int hi = center + (width / 2); + setRxFilter(lo, hi); +} + +void UBitxDSP::setRxFilterCenter(int center) { + if (center < minRxFilterCenter) { + center = minRxFilterCenter; + } else if (center > maxRxFilterCenter) { + center = maxRxFilterCenter; + } + int width = state.rxFilterHi - state.rxFilterLo; + int lo = center - (width / 2); + int hi = center + (width / 2); + setRxFilter(lo, hi); +} + + +/**********************************************************************/ +// TX audio input settings + +void UBitxDSP::setTxInputLevel(int ch, float lvl) { + if ((ch > -1) && (ch < txNumChannels)) { + state.txInLvl[ch] = lvl; + float vol = lvl * float(state.txInEnable[ch] * state.txInTx[ch]); + txAudio.gain(ch, vol); + } +} + +void UBitxDSP::enableTxInput(int ch) { + if ((ch > -1) && (ch < txNumChannels)) { + state.txInEnable[ch] = 1; + float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]); + txAudio.gain(ch, vol); + } +} + +void UBitxDSP::disableTxInput(int ch) { + if ((ch > -1) && (ch < txNumChannels)) { + state.txInEnable[ch] = 0; + float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]); + txAudio.gain(ch, vol); + } +} + +void UBitxDSP::startTxInput(int ch) { + if ((ch > -1) && (ch < txNumChannels)) { + state.txInTx[ch] = 1; + float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]); + txAudio.gain(ch, vol); + } +} + +void UBitxDSP::stopTxInput(int ch) { + if ((ch > -1) && (ch < txNumChannels)) { + state.txInTx[ch] = 0; + float vol = state.txInLvl[ch] * float(state.txInEnable[ch] * state.txInTx[ch]); + txAudio.gain(ch, vol); + } +} + +// VOX settings +bool UBitxDSP::isVoxActive(int vox) { + if ((vox > -1) && (vox < 3)) { + return state.voxActive[vox]; + } else { + return false; + } +} + //====================================================================== // EOF //====================================================================== diff --git a/TeensyDSP/DSP.h b/TeensyDSP/DSP.h index e64e790..fe293a8 100644 --- a/TeensyDSP/DSP.h +++ b/TeensyDSP/DSP.h @@ -5,8 +5,57 @@ #ifndef __DSP_h__ #define __DSP_h__ +#include +#include #include "Debug.h" +#define MIN_RX_FILTER_LO 0 +#define MAX_RX_FILTER_HI 5000 +#define MIN_RX_FILTER_WIDTH 0 +#define MAX_RX_FILTER_WIDTH 5000 +#define MIN_RX_FILTER_CENTER 0 +#define MAX_RX_FILTER_CENTER 5000 + +#define DSP_MILLIS_PER_UPDATE 100 + +#define TX_MIC_IN_CH 0 +#define TX_LINE_IN_CH 0 +#define TX_USB_IN_CH 1 +#define TX_NUM_CHANNELS 2 + +#define TX_LINE_IN_VOX 0 +#define TX_USB_IN_L_VOX 1 +#define TX_USB_IN_R_VOX 2 +#define TX_NUM_VOX 3 + +#define TX_LINE_IN_VOX_THRESH 0.25 +#define TX_USB_IN_L_VOX_THRESH 0.25 +#define TX_USB_IN_R_VOX_THRESH 0.25 + +#define TX_LINE_IN_VOX_DELAY 500 +#define TX_USB_IN_L_VOX_DELAY 500 +#define TX_USB_IN_R_VOX_DELAY 500 + +struct DSPState { + + // RX audio output settings + + // RX filter settings + int rxFilterLo = 300; + int rxFilterHi = 3000; + + // 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 @@ -24,7 +73,7 @@ enum TxAudioIn { class UBitxDSP { public: - UBitxDSP() {}; + UBitxDSP(); void begin(); void update(); void end(); @@ -32,6 +81,59 @@ class UBitxDSP { void txMicIn(); void txLineIn(); void txUSBIn(); + + // RX audio output settings + + // RX filter settings + void bypassRxFilter(); + void setRxFilter(int lo, int hi); + void setRxFilterLo(int lo); + void setRxFilterHi(int hi); + void setRxFilterWidth(int width); + void setRxFilterCenter(int center); + + /*! + * @brief Get the current low frequency bound of the RX band pass filter. + * @return The low frequency bound. + */ + inline int getRxFilterLo() { return state.rxFilterLo; } + + /*! + * @brief Get the current high frequency bound of the RX band pass filter. + * @return The high frequency bound. + */ + inline int getRxFilterHi() { return state.rxFilterHi; } + + /*! + * @brief Get the current width of the RX band pass filter. + * @return The filter width. + */ + inline int getRxFilterWidth() { return state.rxFilterHi - state.rxFilterLo; } + + /*! + * @brief Get the current center frequency of the RX band pass filter. + * @return The center frequency. + */ + inline int getRxFilterCenter() { return (state.rxFilterHi + state.rxFilterLo) / 2; } + + // TX audio input settings + void setTxInputLevel(int ch, float lvl); + void enableTxInput(int ch); + void disableTxInput(int ch); + void startTxInput(int ch); + void stopTxInput(int ch); + + // VOX settings + bool isVoxActive(int vox); + + private: + void updateRxFilter(); + + DSPState state; + short coefficients[NUM_COEFFICIENTS]; + elapsedMillis sinceLastUpdate; + + AudioAnalyzeRMS* voxRMS[3]; }; extern UBitxDSP DSP; diff --git a/TeensyDSP/Rig.h b/TeensyDSP/Rig.h index fa45b0a..ff3a911 100644 --- a/TeensyDSP/Rig.h +++ b/TeensyDSP/Rig.h @@ -136,6 +136,17 @@ class UBitxRig { } } + /********************************************************************/ + // New functional/mode-based Rig methods + + // AG + //void setVolOut(uint8_t level); + //uint8_t getVolOut(); + + // BD/BU + //void setBand(); + //void getBand(); + private: bool autoInfo = false; UpdateSource lastUpdatedBy = NoSource; diff --git a/TeensyDSP/TS590.cpp b/TeensyDSP/TS590.cpp index 069550a..a4fac4b 100644 --- a/TeensyDSP/TS590.cpp +++ b/TeensyDSP/TS590.cpp @@ -130,7 +130,17 @@ void TS590Command::setRig(UBitxRig* r) { theRig = r; } +/*! + * @brief Set the DSP that will be used to process commands. + * @param d + * Pointer to the UBitxDSP object. + */ +void TS590Command::setDSP(UBitxDSP* d) { + theDSP = d; +} + UBitxRig* TS590Command::theRig = &Rig; +UBitxDSP* TS590Command::theDSP = &DSP; TS590Error TS590Command::theError = NoError; /**********************************************************************/ @@ -273,18 +283,64 @@ void TS590_MD::sendResponse(const char* cmd) { /**********************************************************************/ +int ssbHiCut[14] = {1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3400, 4000, 5000}; +int ssbLoCut[12] = {0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}; + +int ssbWidth[14] = {50, 80, 100, 150, 200, 250, 300, 400, 500, 600, 1000, 1500, 2000, 2500}; +int ssbCenter[14] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1750, 1800, 1900, 2000, 2100, 2210}; + +void TS590_SH::handleCommand(const char* cmd) { + if (strlen(cmd) == 4) { + index = strtoul(&cmd[2], NULL, 10); + if (index < sizeof(ssbHiCut) / sizeof(ssbHiCut[0])) { + dsp()->setRxFilterHi(ssbHiCut[index]); + } else { + setSyntaxError(); + } + } else { + setSyntaxError(); + } +} + +void TS590_SH::sendResponse(const char* cmd) { + ts590SendCommand("SH%02u", index); +} + +void TS590_SL::handleCommand(const char* cmd) { + if (strlen(cmd) == 4) { + index = strtoul(&cmd[2], NULL, 10); + if (index < sizeof(ssbLoCut) / sizeof(ssbLoCut[0])) { + dsp()->setRxFilterLo(ssbLoCut[index]); + } else { + setSyntaxError(); + } + } else { + setSyntaxError(); + } +} + +void TS590_SL::sendResponse(const char* cmd) { + ts590SendCommand("SL%02u", index); +} + +/**********************************************************************/ + TS590_FA cmdFA; TS590_FB cmdFB; TS590_FR cmdFR; TS590_FT cmdFT; TS590_MD cmdMD; +TS590_SH cmdSH; +TS590_SL cmdSL; TS590Command* catCommands[] = { &cmdFA, &cmdFB, &cmdFR, &cmdFT, - &cmdMD + &cmdMD, + &cmdSH, + &cmdSL }; int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]); diff --git a/TeensyDSP/TS590.h b/TeensyDSP/TS590.h index 64c30ac..e941c8d 100644 --- a/TeensyDSP/TS590.h +++ b/TeensyDSP/TS590.h @@ -2,6 +2,7 @@ #define __TS590_h__ #include +#include "DSP.h" #include "Rig.h" /**********************************************************************/ @@ -56,6 +57,11 @@ class TS590Command { */ inline UBitxRig* rig() const { return theRig; } + /*! + * @brief Return the DSP that this command will be used to control. + */ + inline UBitxDSP* dsp() const { return theDSP; } + /*! * @brief Handle the provided Set command. If the Set command * results in an error, then set the appropriate flag with @@ -81,11 +87,13 @@ class TS590Command { static void setCommError(); static void setProcessError(); static void setRig(UBitxRig* r); + static void setDSP(UBitxDSP* d); private: char myPrefix[3] = "\0\0"; static TS590Error theError; static UBitxRig* theRig; + static UBitxDSP* theDSP; }; /**********************************************************************/ @@ -160,6 +168,32 @@ class TS590_MD : public TS590Command { /**********************************************************************/ +/*! + * @brief CAT command for setting the receiver high-cut frequency. + */ +class TS590_SH : public TS590Command { + public: + TS590_SH(): TS590Command("SH") {} + virtual void handleCommand(const char* cmd); + virtual void sendResponse(const char* cmd); + private: + unsigned index; +}; + +/*! + * @brief CAT command for setting the receiver low-cut frequency. + */ +class TS590_SL : public TS590Command { + public: + TS590_SL(): TS590Command("SL") {} + virtual void handleCommand(const char* cmd); + virtual void sendResponse(const char* cmd); + private: + unsigned index; +}; + +/**********************************************************************/ + class UBitxTS590 { public: UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {}