//====================================================================== // DSP.cpp //====================================================================== #include "DSP.h" #include //#include //#include //#include //#include const int rxRigInChannel = RX_RIG_IN_CH; 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; UBitxDSP DSP; //static struct { // GUItool: begin automatically generated code AudioInputUSB usbIn; //xy=161,313 AudioInputI2S lineIn; //xy=169,205 AudioSynthWaveformSine tone1; //xy=234,386 AudioSynthWaveformSine tone2; //xy=234,422 AudioMixer4 rxAudio; //xy=426,119 AudioMixer4 txAudio; //xy=430,307 AudioAnalyzeRMS txVoxLevel; //xy=588,348 AudioFilterFIR rxFilter; //xy=591,111 AudioAmplifier usbOutAmp; //xy=756,107 AudioAmplifier lineOutAmp; //xy=757,170 AudioAmplifier usbBypassAmp; //xy=764,233 AudioAmplifier txOutAmp; //xy=811,309 AudioOutputI2S lineOut; //xy=974,302 AudioOutputUSB usbOut; //xy=976,263 AudioConnection patchCord1(usbIn, 0, txAudio, 1); AudioConnection patchCord2(lineIn, 0, rxAudio, 0); AudioConnection patchCord3(lineIn, 1, txAudio, 0); AudioConnection patchCord4(tone1, 0, txAudio, 2); AudioConnection patchCord5(tone2, 0, txAudio, 3); AudioConnection patchCord6(rxAudio, rxFilter); AudioConnection patchCord7(rxAudio, usbBypassAmp); AudioConnection patchCord8(txAudio, txVoxLevel); AudioConnection patchCord9(txAudio, txOutAmp); AudioConnection patchCord10(rxFilter, usbOutAmp); AudioConnection patchCord11(rxFilter, lineOutAmp); AudioConnection patchCord12(usbOutAmp, 0, usbOut, 0); AudioConnection patchCord13(lineOutAmp, 0, lineOut, 0); AudioConnection patchCord14(usbBypassAmp, 0, usbOut, 1); AudioConnection patchCord15(txOutAmp, 0, lineOut, 1); AudioControlSGTL5000 audioCtrl; //xy=435,448 // GUItool: end automatically generated code //} audio; UBitxDSP::UBitxDSP() : txSrc(MIC_IN), txSrcLatched(MIC_IN) { } void UBitxDSP::begin() { // Basic audio setup AudioMemory(16); // TODO: optimize this audioCtrl.enable(); audioCtrl.volume(0.0); // headphone volume... audioCtrl.muteHeadphone(); // ...not used by UBitxDSP setupRxAudio(); setupTxAudio(); // Default to RX. muteRxIn(); // redundant? muteTxIn(); // redundant? isTx = true; // so that rx() call works rx(); // Setup the VOX - TBD // Setup the RX Filter. setRxFilter(300, 3000); sinceLastUpdate = 0; } void UBitxDSP::update() { // Update the USB volume (level of TX USB output) periodically. if (sinceLastUpdate > DSP_MILLIS_PER_UPDATE) { float vol = usbIn.volume(); if (vol != usbVol) { setTxInLevel(TX_USB, vol); usbVol = vol; } sinceLastUpdate = 0; } if (state.vox[txSrc].enable && txVoxLevel.available()) { if (txVoxLevel.read() > state.vox[txSrc].threshold) { voxTimeout = millis() + state.voxDelay; state.voxActive = true; } } if (millis() > voxTimeout) { state.voxActive = false; } } void UBitxDSP::end() { } //====================================================================== // Transmit/Receive (T/R) Switching //====================================================================== /*! * @brief Revert to receive (RX) mode from transmit (TX) mode. First * the transmit audio output is muted, to ensure that no more * audio goes to the rig. Then we check to see if the latched * TX audio source was different than the selected TX audio * source; this happens if the radio is currently set for a * particular input (which determines what is monitored by the * VOX), but then is commanded to transmit a different source * (e.g. based on a CAT command). The actual transmit audio * source is latched during transmit, but upon returning to * receive, we restore the selected transmit audio. */ void UBitxDSP::rx() { if (isTx) { muteTxOut(); if (txSrcLatched != txSrc) { setTxAudioIn(txSrc); } if (txSrcLatched == MIC_IN) { audioCtrl.inputSelect(AUDIO_INPUT_LINEIN); } unmuteRxIn(RX_AUDIO); isTx = false; } } 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(12); // TODO: Make this dynamic } unmuteTxOut(); isTx = true; } } //====================================================================== // Receive (RX) Audio Chain //====================================================================== 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) lineOutAmp.gain(1.0); // USB Output (RX) usbOutAmp.gain(1.0); usbBypassAmp.gain(1.0); } void UBitxDSP::setRxInLevel(RxAudioCh ch, float level) { if (ch < NUM_RX_AUDIO_CH) { state.rxin[ch].level = level; rxAudio.gain(ch, state.rxin[ch].mute ? 0.0 : state.rxin[ch].level); } } void UBitxDSP::muteRxIn() { for (int i = 0; i < NUM_RX_AUDIO_CH; i++) { state.rxin[RxAudioCh(i)].mute = true; rxAudio.gain(i, 0.0); } } void UBitxDSP::muteRxIn(RxAudioCh ch) { if (ch < NUM_RX_AUDIO_CH) { if (!state.rxin[ch].mute) { state.rxin[ch].mute = true; rxAudio.gain(ch, 0.0); } } } void UBitxDSP::unmuteRxIn(RxAudioCh ch) { if (ch < NUM_RX_AUDIO_CH) { if (state.rxin[ch].mute) { state.rxin[ch].mute = false; rxAudio.gain(ch, state.rxin[ch].level); } } } //====================================================================== // Transmit (TX) Audio Chain //====================================================================== void UBitxDSP::setupTxAudio() { for (int i = 0; i < NUM_TX_AUDIO_CH; i++) { txAudio.gain(i, 0.0); } // Mic Input (TX) audioCtrl.micGain(0); // TODO: set value // Line Input (TX) // USB Input (TX) // 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::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::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 } } } //====================================================================== // RX Audio 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); } /* NOTES Major functions: - tx() - start transmitting / pause receiving - rx() - stop transmitting / resume receiving - setTxSource() - set the TX audio source to MIC_IN, LINE_IN, or USB_IN - also sets the relevant VOX source/parameters (as applicable) Receive audio chain: - */ //====================================================================== // EOF //======================================================================