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.

This commit is contained in:
Rob French 2021-03-14 23:18:24 -05:00
parent 20b475dace
commit 869e47d430
3 changed files with 347 additions and 228 deletions

View File

@ -56,7 +56,8 @@ AudioControlSGTL5000 audioCtrl; //xy=435,448
//} audio;
UBitxDSP::UBitxDSP() {
UBitxDSP::UBitxDSP()
: txSrc(MIC_IN), txSrcLatched(MIC_IN) {
}
void UBitxDSP::begin() {
@ -67,47 +68,12 @@ void UBitxDSP::begin() {
audioCtrl.volume(0.0); // headphone volume...
audioCtrl.muteHeadphone(); // ...not used by UBitxDSP
for (int i = 0; i < 4; i++) {
if (i == RX_AUDIO_CH)
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)
txOutAmp.gain(1.0);
setupRxAudio();
setupTxAudio();
// Default to RX.
muteRxIn();
muteTxIn();
muteRxIn(); // redundant?
muteTxIn(); // redundant?
isTx = true; // so that rx() call works
rx();
@ -120,56 +86,123 @@ void UBitxDSP::begin() {
}
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) {
float vol = usbIn.volume();
setTxInLevel(TX_USB, vol);
if (vol != usbVol) {
setTxInLevel(TX_USB, vol);
usbVol = vol;
}
sinceLastUpdate = 0;
}
// Update the VOX switches.
// TODO: Move the enable logic in here, so we don't process unnecessarily.
if (txVoxLevel.available()) {
if (txVoxLevel.read() > state.vox.threshold) {
state.vox.timeout = millis() + state.vox.delay;
state.vox.active = true;
if (state.vox[txSrc].enable && txVoxLevel.available()) {
if (txVoxLevel.read() > state.vox[txSrc].threshold) {
voxTimeout = millis() + state.voxDelay;
state.voxActive = true;
}
}
if (millis() > state.vox.timeout) {
state.vox.active = false;
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) {
muteTxIn();
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
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.rx[ch].level = level;
rxAudio.gain(ch, state.rx[ch].mute ? 0.0 : state.rx[ch].level);
state.rxin[ch].level = level;
rxAudio.gain(ch, state.rxin[ch].mute ? 0.0 : state.rxin[ch].level);
}
}
void UBitxDSP::muteRxIn() {
for (RxAudioCh i = RX_AUDIO; i < NUM_RX_AUDIO_CH; i++) {
state.rx[ch].mute = true;
rxAudio.gain(ch, 0.0);
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.rx[ch].mute) {
state.rx[ch].mute = true;
if (!state.rxin[ch].mute) {
state.rxin[ch].mute = true;
rxAudio.gain(ch, 0.0);
}
}
@ -177,70 +210,58 @@ void UBitxDSP::muteRxIn(RxAudioCh ch) {
void UBitxDSP::unmuteRxIn(RxAudioCh ch) {
if (ch < NUM_RX_AUDIO_CH) {
if (state.rx[ch].mute) {
state.rx[ch].mute = false;
rxAudio.gain(ch, state.rx[ch].level);
if (state.rxin[ch].mute) {
state.rxin[ch].mute = false;
rxAudio.gain(ch, state.rxin[ch].level);
}
}
}
void UBitxDSP::tx(TxAudioIn src) {
if (!isTx) {
muteRxIn(RX_AUDIO);
switch (src) {
case MIC_IN:
audioCtrl.inputSelect(AUDIO_INPUT_MIC);
audioCtrl.micGain(12); // TODO: Make this dynamic
unmuteTxIn(TX_LINE);
//======================================================================
// Transmit (TX) Audio Chain
//======================================================================
case LINE_IN:
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
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;
}
isTx = true;
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.tx[ch].level = level;
txAudio.gain(ch, state.tx[ch].mute ? 0.0 : state.tx[ch].level);
state.txin[ch].level = level;
txAudio.gain(ch, state.txin[ch].mute ? 0.0 : state.txin[ch].level);
}
}
void UBitxDSP::muteTxIn() {
for (TxAudioCh i = TX_LINE; i < NUM_TX_AUDIO_CH; i++) {
state.tx[ch].mute = true;
txAudio.gain(ch, 0.0);
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.tx[ch].mute) {
state.tx[ch].mute = true;
if (!state.txin[ch].mute) {
state.txin[ch].mute = true;
txAudio.gain(ch, 0.0);
}
}
@ -248,15 +269,82 @@ void UBitxDSP::muteTxIn(TxAudioCh ch) {
void UBitxDSP::unmuteTxIn(TxAudioCh ch) {
if (ch < NUM_TX_AUDIO_CH) {
if (state.tx[ch].mute) {
state.tx[ch].mute = false;
rxAudio.gain(ch, state.tx[ch].level);
if (state.txin[ch].mute) {
state.txin[ch].mute = false;
rxAudio.gain(ch, state.txin[ch].level);
}
}
}
/**********************************************************************/
// RX filter settings
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;
@ -266,8 +354,8 @@ 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.
*/
void UBitxDSP::bypassRxFilter() {
rxFilter.begin(FIR_PASSTHRU, NUM_COEFFICIENTS);
}
@ -346,50 +434,6 @@ void UBitxDSP::setRxFilterCenter(int center) {
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);
}
}
/*
NOTES

View File

@ -25,53 +25,19 @@
#define TX_USB_IN_CH 1
#define TX_NUM_CHANNELS 2
#define TX_VOX_USB_THRESH 0.25
#define TX_VOX_MIC_THRESH 0.0
#define TX_VOX_LINE_THRESH 0.25
#define TX_VOX_USB_THRESH 0.25
#define TX_VOX_TUNE_THRESH 0.0
#define TX_VOX_TT_THRESH 0.0
#define TX_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};
struct {
bool mute = true;
float level = 0.1;
} tx[NUM_TX_AUDIO_CH];
struct {
bool mute = true;
float level = 1.0;
} rx[NUM_RX_AUDIO_CH];
// VOX settings
/* struct {
bool isActive = false;
bool isUSB = false;
float usbThresh = TX_VOX_USB_THRESH;
float lineLevel = TX_VOX_LINE_THRESH;
unsigned delay = TX_VOX_DELAY;*/
unsigned voxTimeout[3];
};
enum TRState {
TRANSMIT,
RECEIVE
};
enum RxAudioIn {
RIG_IN = 0,
NUM_RX_AUDIO_IN
};
/**********************************************************************/
/*!
* @brief Defines the four separate RX audio input channels available.
* Not all are currently used by the implementation.
*/
enum RxAudioCh {
RX_AUDIO = 0,
RX_SPARE1,
@ -80,6 +46,36 @@ enum RxAudioCh {
NUM_RX_AUDIO_CH
};
/*!
* @brief Defines the different RX audio inputs. This is not really
* different than the channels, but in theory could be. See
* the corresponding TX enums for an example of this.
*/
enum RxAudioIn {
RIG_IN = 0,
NUM_RX_AUDIO_IN
};
/*!
* @brief Defines the four separate TX audio input channels available.
* They do not directly correlate to TX audio input sources.
* Specifically. both mic and line inputs use TX_LINE, and
* the two-tone input uses both TX_TONE1 and TX_TONE2 at the
* same time.
*/
enum TxAudioCh {
TX_LINE = 0,
TX_USB,
TX_TONE1,
TX_TONE2,
NUM_TX_AUDIO_CH
};
/*!
* @brief Defines the different TX audio input sources (not channels!)
* As noted above, MIC_IN and LINE_IN share channels, and
* TWO_TONE_IN uses multiple channels.
*/
enum TxAudioIn {
MIC_IN = 0,
LINE_IN,
@ -89,30 +85,122 @@ enum TxAudioIn {
NUM_TX_AUDIO_IN
};
enum TxAudioCh {
TX_LINE = 0,
TX_USB,
TX_TONE1,
TX_TONE2,
NUM_TX_AUDIO_CH
/*!
* @brief Describes a simple audio channel that can be muted.
*/
struct AudioChannel {
bool mute = false;
float level = 0.0;
};
/**********************************************************************/
struct DSPState {
// RX audio I/O settings - 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
};
AudioChannel rxout = {true, 1.0};
// TX audio I/O settings - 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
};
AudioChannel txout = {true, 1.0};
// TX VOX settings, per audio input (not channel!)
struct {
bool enable = false;
float threshold = 1.0;
} vox[NUM_TX_AUDIO_IN] = {
{false, TX_VOX_MIC_THRESH},
{true, TX_VOX_LINE_THRESH},
{true, TX_VOX_USB_THRESH},
{false, TX_VOX_TUNE_THRESH},
{false, TX_VOX_TT_THRESH}
};
unsigned voxDelay = TX_VOX_DELAY;
bool voxActive = false;
// RX filter settings
int rxFilterLo = 300;
int rxFilterHi = 3000;
};
class UBitxDSP {
public:
// Object construction.
public:
UBitxDSP();
// Basic administrative functions.
public:
void begin();
void update();
void end();
void end();
//====================================================================
// Transmit/Receive (T/R) Switching
//====================================================================
public:
void rx();
inline void tx() { tx(txSrc); }
void tx(TxAudioIn src);
inline void setTxAudioIn(TxAudioIn src) { txSrc = src; }
TxAudionIn getTxAudioIn() const { return txSrc; }
//====================================================================
// Receive (RX) Audio Chain
//====================================================================
// RX audio output settings
// General administrative function to setup the initial RX audio
// chain. This should only be called once, via begin().
private:
void setupRxAudio();
// RX filter settings
// 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.
//====================================================================
// Transmit (TX) Audio Chain
//====================================================================
// General administrative function to setup the initial TX audio
// chain. This should only be called once, via begin().
private:
void setupTxAudio();
// 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.
// 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.
//====================================================================
// RX Audio Filter Settings
//====================================================================
public:
void bypassRxFilter();
void setRxFilter(int lo, int hi);
void setRxFilterLo(int lo);
@ -151,20 +239,6 @@ class UBitxDSP {
void startTxInput(int ch);
void stopTxInput(int ch);
// VOX settings
inline void setDataVoxOn() { state.vox.isActive = true; }
inline void setDataVoxOff() { state.vox.isActive = false; }
inline void setDataVoxThreshold(float val) {
}
inline void setDataVoxThreshold(float val, bool isUSB) {
if (isUSB) {
state.vox.usbThresh = val;
} else {
state.vox.lineThresh = val;
}
}
private:
bool isTx = false;
@ -175,7 +249,8 @@ class UBitxDSP {
short coefficients[NUM_COEFFICIENTS];
elapsedMillis sinceLastUpdate;
TxAudioIn txSrc;
TxAudioIn txSrc, txSrcLatched;
float usbVol = 0.0;
unsigned voxTimeout;
};

View File

@ -36,15 +36,15 @@ void UBitxTR::update(bool cw, bool extKey) {
} else {
if ((pttEnable && pttPressed()) || (voxEnable && voxActivated())) {
// first, setup TX audio; then, start transmitting (from Mic)
DBGCMD( dsp.txMicIn() );
DBGCMD( dsp.tx(MIC_IN) );
DBGCMD( setTX() );
} else if (keyEnable && keyPressed()) {
// first, setup TX audio; then, start transmitting (from Line In)
DBGCMD( dsp.txLineIn() );
DBGCMD( dsp.tx(LINE_IN) );
DBGCMD( setTX() );
} else if (catEnable && catActivated()) {
// first, setup TX audio; then, start transmitting (USB)
DBGCMD( dsp.txUSBIn() );
DBGCMD( dsp.tx(USB_IN) );
DBGCMD( setTX() );
}
}