524 lines
15 KiB
C++
524 lines
15 KiB
C++
//======================================================================
|
|
// DSP.cpp
|
|
//======================================================================
|
|
|
|
#include "DSP.h"
|
|
|
|
#include <i2c_t3.h>
|
|
//#include <Wire.h>
|
|
//#include <SPI.h>
|
|
//#include <SD.h>
|
|
//#include <SerialFlash.h>
|
|
|
|
// 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;
|
|
|
|
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) {
|
|
isVoxActive = true;
|
|
sinceVoxActive = 0;
|
|
}
|
|
}
|
|
if (isVoxActive && (sinceVoxActive > state.voxDelay)) {
|
|
isVoxActive = false;
|
|
}
|
|
}
|
|
|
|
void UBitxDSP::end() {
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Transmit/Receive switching
|
|
**********************************************************************/
|
|
|
|
/*!
|
|
* Return to receive (RX) mode from transmit (TX) mode.
|
|
* First the transmit audio output is muted, to ensure that no more
|
|
* audio goes to the rig. Then we check to see if the latched TX audio
|
|
* source was different than the selected TX audio source; this happens
|
|
* if the radio is currently set for a particular input (which
|
|
* determines what is monitored by the VOX), but then is commanded to
|
|
* transmit a different source (e.g. based on a CAT command). The
|
|
* actual transmit audio source is latched during transmit, but upon
|
|
* returning to receive, we restore the selected transmit audio.
|
|
*/
|
|
void UBitxDSP::rx() {
|
|
if (isTx) {
|
|
muteTxOut();
|
|
if (txSrcLatched != txSrc) {
|
|
setTxAudioIn(txSrc);
|
|
}
|
|
if (txSrcLatched == MIC_IN) {
|
|
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
|
|
}
|
|
unmuteRxIn(RX_AUDIO);
|
|
isTx = false;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Enter transmit (TX) mode from receive (RX) mode.
|
|
*/
|
|
void UBitxDSP::tx(TxAudioIn src) {
|
|
if (!isTx) {
|
|
muteRxIn(RX_AUDIO);
|
|
|
|
txSrcLatched = src;
|
|
if (txSrcLatched != txSrc) {
|
|
setTxAudioIn(txSrcLatched, true);
|
|
}
|
|
|
|
if (txSrcLatched == MIC_IN) {
|
|
audioCtrl.inputSelect(AUDIO_INPUT_MIC);
|
|
audioCtrl.micGain(12); // TODO: Make this dynamic
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Receive audio chain
|
|
**********************************************************************/
|
|
|
|
void UBitxDSP::setRxInLevel(RxAudioCh ch, float level) {
|
|
if (ch < NUM_RX_AUDIO_CH) {
|
|
state.rxIn[ch].level = level;
|
|
rxAudio.gain(ch, state.rxIn[ch].mute ? 0.0 : state.rxIn[ch].level);
|
|
}
|
|
}
|
|
|
|
void UBitxDSP::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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UBitxDSP::setLineOutLevel(float level) {
|
|
state.rxOut[LINE_OUT].level = level;
|
|
lineOutAmp.gain(state.rxOut[LINE_OUT].mute ? 0.0 : state.rxOut[LINE_OUT].level);
|
|
}
|
|
|
|
void UBitxDSP::setUSBOutLevel(float level) {
|
|
state.rxOut[USB_OUT].level = level;
|
|
usbOutAmp.gain(state.rxOut[USB_OUT].mute ? 0.0 : state.rxOut[USB_OUT].level);
|
|
usbBypassAmp.gain(state.rxOut[USB_OUT].mute ? 0.0 : state.rxOut[USB_OUT].level);
|
|
}
|
|
|
|
void UBitxDSP::setLineOut255(unsigned level) { setLineOutLevel<255>(level); }
|
|
void UBitxDSP::setLineOut9(unsigned level) { setLineOutLevel<9>(level); }
|
|
void UBitxDSP::setUSBOut255(unsigned level) { setUSBOutLevel<255>(level); }
|
|
void UBitxDSP::setUSBOut9(unsigned level) { setUSBOutLevel<9>(level); }
|
|
|
|
unsigned UBitxDSP::getLineOut255() const { return getLineOutLevel<255>(); }
|
|
unsigned UBitxDSP::getLineOut9() const { return getLineOutLevel<9>(); }
|
|
unsigned UBitxDSP::getUSBOut255() const { return getUSBOutLevel<255>(); }
|
|
unsigned UBitxDSP::getUSBOut9() const { return getUSBOutLevel<9>(); }
|
|
|
|
/**********************************************************************
|
|
* 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::setLineIn255(unsigned level) { setLineInLevel<255>(level); }
|
|
void UBitxDSP::setLineIn9(unsigned level) { setLineInLevel<9>(level); }
|
|
void UBitxDSP::setUSBIn255(unsigned level) { setUSBInLevel<255>(level); }
|
|
void UBitxDSP::setUSBIn9(unsigned level) { setUSBInLevel<9>(level); }
|
|
|
|
unsigned UBitxDSP::getLineIn255() const { return getLineInLevel<255>(); }
|
|
unsigned UBitxDSP::getLineIn9() const { return getLineInLevel<9>(); }
|
|
unsigned UBitxDSP::getUSBIn255() const { return getUSBInLevel<255>(); }
|
|
unsigned UBitxDSP::getUSBIn9() const { return getUSBInLevel<9>(); }
|
|
|
|
void UBitxDSP::setTxAudioIn(TxAudioIn src, bool isTemp) {
|
|
if (!isTemp) {
|
|
txSrc = src;
|
|
}
|
|
|
|
if (!isTx) {
|
|
muteTxIn(); // Mute all channels, then unmute the desired ones.
|
|
switch (src) { // Don't switch inputs while transmitting.
|
|
case MIC_IN:
|
|
// Note that we can't actually use the VOX code on the mic input,
|
|
// because we can't make the actual mic input active without
|
|
// losing our receive audio. So, mic input is never actually
|
|
// selected until it is time for it to transmit, which makes the
|
|
// VOX moot. The caller must make use of an external, analog VOX
|
|
// circuit driving a GPIO pin, or something similar (or the PTT of
|
|
// course) to begin actually using the mic input. So this case
|
|
// just falls through to the line input.
|
|
|
|
case LINE_IN:
|
|
unmuteTxIn(TX_LINE);
|
|
break;
|
|
|
|
case USB_IN:
|
|
unmuteTxIn(TX_USB);
|
|
break;
|
|
|
|
case TUNE_IN:
|
|
tone1.amplitude(1.0); // TODO - just do this once.
|
|
tone1.frequency(1500); // TODO: Make this dynamic based on CW (sidetone freq) versus data (1500 Hz)
|
|
unmuteTxIn(TX_TONE1);
|
|
break;
|
|
|
|
case TWO_TONE_IN:
|
|
tone1.amplitude(1.0); // TODO - just do this once.
|
|
tone1.amplitude(1.0); // TODO - just do this once.
|
|
tone1.frequency(700);
|
|
tone2.frequency(1900);
|
|
unmuteTxIn(TX_TONE1);
|
|
unmuteTxIn(TX_TONE2);
|
|
break;
|
|
|
|
default:
|
|
// should never happen
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Receive audio filter (band pass)
|
|
**********************************************************************/
|
|
|
|
const int minRxFilterLo = MIN_RX_FILTER_LO;
|
|
const int maxRxFilterHi = MAX_RX_FILTER_HI;
|
|
const int minRxFilterWidth = MIN_RX_FILTER_WIDTH;
|
|
const int maxRxFilterWidth = MAX_RX_FILTER_WIDTH;
|
|
const int minRxFilterCenter = MIN_RX_FILTER_CENTER;
|
|
const int maxRxFilterCenter = MAX_RX_FILTER_CENTER;
|
|
|
|
/*!
|
|
* @brief Bypass the RX audio filter.
|
|
*/
|
|
void UBitxDSP::bypassRxFilter() {
|
|
rxFilter.begin(FIR_PASSTHRU, NUM_COEFFICIENTS);
|
|
}
|
|
|
|
/*!
|
|
@brief Update the RX audio filter using the currently set low and
|
|
high frequencies. This is called by each of the public
|
|
filter methods to update the filter with new frequencies.
|
|
*/
|
|
void UBitxDSP::updateRxFilter() {
|
|
audioFilter(coefficients, NUM_COEFFICIENTS, ID_BANDPASS, W_HAMMING, double(state.rxFilterLo), double(state.rxFilterHi));
|
|
rxFilter.begin(coefficients, NUM_COEFFICIENTS);
|
|
}
|
|
|
|
void UBitxDSP::setRxFilter(int lo, int hi) {
|
|
if (hi < lo + minRxFilterWidth) {
|
|
hi = lo + minRxFilterWidth;
|
|
}
|
|
if (hi > maxRxFilterHi) {
|
|
hi = maxRxFilterHi;
|
|
}
|
|
if (lo > hi - minRxFilterWidth) {
|
|
lo = hi - minRxFilterWidth;
|
|
}
|
|
if (lo < minRxFilterLo) {
|
|
lo = minRxFilterLo;
|
|
}
|
|
state.rxFilterHi = hi;
|
|
state.rxFilterLo = lo;
|
|
updateRxFilter();
|
|
}
|
|
|
|
void UBitxDSP::setRxFilterLo(int lo) {
|
|
if (lo > state.rxFilterHi - minRxFilterWidth) {
|
|
lo = state.rxFilterHi - minRxFilterWidth;
|
|
}
|
|
if (lo < minRxFilterLo) {
|
|
lo = minRxFilterLo;
|
|
}
|
|
state.rxFilterLo = lo;
|
|
updateRxFilter();
|
|
}
|
|
|
|
void UBitxDSP::setRxFilterHi(int hi) {
|
|
if (hi < state.rxFilterLo + minRxFilterWidth) {
|
|
hi = state.rxFilterLo + minRxFilterWidth;
|
|
}
|
|
if (hi > maxRxFilterHi) {
|
|
hi = maxRxFilterHi;
|
|
}
|
|
state.rxFilterHi = hi;
|
|
updateRxFilter();
|
|
}
|
|
|
|
void UBitxDSP::setRxFilterWidth(int width) {
|
|
if (width < minRxFilterWidth) {
|
|
width = minRxFilterWidth;
|
|
} else if (width > maxRxFilterWidth) {
|
|
width = maxRxFilterWidth;
|
|
}
|
|
int center = (state.rxFilterHi + state.rxFilterLo) / 2;
|
|
int lo = center - (width / 2);
|
|
int hi = center + (width / 2);
|
|
setRxFilter(lo, hi);
|
|
}
|
|
|
|
void UBitxDSP::setRxFilterCenter(int center) {
|
|
if (center < minRxFilterCenter) {
|
|
center = minRxFilterCenter;
|
|
} else if (center > maxRxFilterCenter) {
|
|
center = maxRxFilterCenter;
|
|
}
|
|
int width = state.rxFilterHi - state.rxFilterLo;
|
|
int lo = center - (width / 2);
|
|
int hi = center + (width / 2);
|
|
setRxFilter(lo, hi);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Transmit Voice-Operated-Switch (VOX)
|
|
**********************************************************************/
|
|
|
|
void UBitxDSP::setLineVOXThresh9(unsigned level) {
|
|
state.vox[TX_LINE].threshold = static_cast<float>(level) / 9.0;
|
|
}
|
|
|
|
unsigned UBitxDSP::getLineVOXThresh9() const {
|
|
return static_cast<unsigned>(state.vox[TX_LINE].threshold * 9.0);
|
|
}
|
|
|
|
void UBitxDSP::setUSBVOXThresh9(unsigned level) {
|
|
state.vox[TX_USB].threshold = static_cast<float>(level) / 9.0;
|
|
}
|
|
|
|
unsigned UBitxDSP::getUSBVOXThresh9() const {
|
|
return static_cast<unsigned>(state.vox[TX_USB].threshold * 9.0);
|
|
}
|
|
|
|
void UBitxDSP::setDataVoxDelay(unsigned msec) {
|
|
state.voxDelay = msec;
|
|
}
|
|
|
|
unsigned UBitxDSP::getDataVoxDelay() const {
|
|
return state.voxDelay;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Singleton - the DSP instance
|
|
**********************************************************************/
|
|
|
|
#ifndef UBITXDSP_CLASS
|
|
#define UBITXDSP_CLASS UBitxDSP
|
|
#endif
|
|
|
|
UBITXDSP_CLASS theDSP;
|
|
UBitxDSP& DSP = theDSP;
|
|
|
|
/*
|
|
NOTES
|
|
|
|
Major functions:
|
|
- tx() - start transmitting / pause receiving
|
|
- rx() - stop transmitting / resume receiving
|
|
- setTxSource() - set the TX audio source to MIC_IN, LINE_IN, or USB_IN
|
|
- also sets the relevant VOX source/parameters (as applicable)
|
|
|
|
Receive audio chain:
|
|
-
|
|
*/
|
|
|
|
//======================================================================
|
|
// EOF
|
|
//======================================================================
|