From 75508503676cf3e02c98a005d46d9ea104ab28a8 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Wed, 4 Feb 2026 10:05:20 -0500 Subject: [PATCH 1/3] Restore and update Config.h for FT8 integration --- code/src/PhoenixSketch/Config.h | Bin 3218 -> 7162 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/code/src/PhoenixSketch/Config.h b/code/src/PhoenixSketch/Config.h index 380087a9822fc60619d77a358ecc5ad8c6d0e72a..2c2194b5ea4ad28467fd332684846bd80e5415a5 100644 GIT binary patch literal 7162 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 RCl04&h!@k-GmFqI^B;0uccB0P From 2020809d9e10f24ef1677a93f5f504d02b8e3de2 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Wed, 4 Feb 2026 10:56:53 -0500 Subject: [PATCH 2/3] FT8 integration: USB bridge, DSP routing, CAT control, audio fixes --- code/src/PhoenixSketch/CAT.cpp | 26 ++- code/src/PhoenixSketch/DSP.cpp | 98 ++++++-- code/src/PhoenixSketch/Ft8UsbBridge.cpp | 221 ++++++++++++++++++ code/src/PhoenixSketch/Ft8UsbBridge.h | 33 +++ code/src/PhoenixSketch/Globals.cpp | 2 +- code/src/PhoenixSketch/Loop.cpp | 25 +- code/src/PhoenixSketch/MainBoard_AudioIO.cpp | 158 +++++++++++-- code/src/PhoenixSketch/MainBoard_AudioIO.h | 4 + .../PhoenixSketch/MainBoard_DisplayHome.cpp | 29 ++- code/src/PhoenixSketch/SDT.h | 10 +- 10 files changed, 548 insertions(+), 58 deletions(-) create mode 100644 code/src/PhoenixSketch/Ft8UsbBridge.cpp create mode 100644 code/src/PhoenixSketch/Ft8UsbBridge.h diff --git a/code/src/PhoenixSketch/CAT.cpp b/code/src/PhoenixSketch/CAT.cpp index 376951a..1691df0 100644 --- a/code/src/PhoenixSketch/CAT.cpp +++ b/code/src/PhoenixSketch/CAT.cpp @@ -1,4 +1,5 @@ #include "CAT.h" +#include "MainBoard_AudioIO.h" // GetFt8Mode() // Kenwood TS-480 CAT Interface (partial) // @@ -693,10 +694,22 @@ char *RX_write( char* cmd ){ char *TX_write( char* cmd ){ switch (modeSM.state_id){ case (ModeSm_StateId_SSB_RECEIVE):{ +#ifdef T41_USB_AUDIO + // Only allow CAT PTT (WSJT-X TX) when FT8 mode is enabled + if (GetFt8Mode()) { + ModeSm_dispatch_event(&modeSM, ModeSm_EventId_PTT_PRESSED); + } else { + // Ignore TX request if not in FT8 mode + // (optional) Serial.println("CAT TX ignored: FT8 mode is OFF"); + } +#else + // No USB audio build: allow normal CAT PTT behavior ModeSm_dispatch_event(&modeSM, ModeSm_EventId_PTT_PRESSED); +#endif break; } case (ModeSm_StateId_CW_RECEIVE):{ + // CW keying via CAT still allowed ModeSm_dispatch_event(&modeSM, ModeSm_EventId_KEY_PRESSED); break; } @@ -706,6 +719,7 @@ char *TX_write( char* cmd ){ return empty_string_p; } + char *VX_write( char* cmd ){ Debug("Got VX write"); return empty_string_p; @@ -751,8 +765,8 @@ char *PR_read( char* cmd ){ void CheckForCATSerialEvents(void){ int i; char c; - while( ( i = SerialUSB1.available() ) > 0 ){ - c = ( char )SerialUSB1.read(); + while( ( i = Serial.available() ) > 0 ){ + c = ( char )Serial.read(); i--; catCommand[ catCommandIndex ] = c; #ifdef DEBUG_CAT @@ -777,17 +791,17 @@ void CheckForCATSerialEvents(void){ #endif // DEBUG_CAT int i = 0; while( parser_output[i] != '\0' ){ - if( SerialUSB1.availableForWrite() > 0 ){ - SerialUSB1.print( parser_output[i] ); + if( Serial.availableForWrite() > 0 ){ + Serial.print( parser_output[i] ); #ifdef DEBUG_CAT Serial.print( parser_output[i] ); #endif i++; }else{ - SerialUSB1.flush(); + Serial.flush(); } } - SerialUSB1.flush(); + Serial.flush(); #ifdef DEBUG_CAT Serial.println(); #endif // DEBUG_CAT diff --git a/code/src/PhoenixSketch/DSP.cpp b/code/src/PhoenixSketch/DSP.cpp index 47f7169..87a0867 100644 --- a/code/src/PhoenixSketch/DSP.cpp +++ b/code/src/PhoenixSketch/DSP.cpp @@ -1,4 +1,6 @@ #include "SDT.h" +#include "Ft8UsbBridge.h" +#include "MainBoard_AudioIO.h" // for GetFt8Mode() float32_t DMAMEM float_buffer_L[READ_BUFFER_SIZE]; float32_t DMAMEM float_buffer_R[READ_BUFFER_SIZE]; @@ -14,6 +16,11 @@ static char *filename = nullptr; void SaveData(DataBlock *data, uint32_t suffix); // used by the unit tests static uint32_t swrTimer_ms = 0; +#ifdef T41_USB_AUDIO +extern AudioPlayQueue Q_usbOut_L; +extern AudioPlayQueue Q_usbOut_R; +#endif + #define RXTXZoom 3 #define TXIQZOOM 3 @@ -718,6 +725,36 @@ void PlayBuffer(DataBlock *data){ } } +#ifdef T41_USB_AUDIO +static float32_t usbTmp[BUFFER_SIZE]; +static float32_t g_usbRxGain = 1.5f; // GAIN SET FOR WJST, DEFAULT IS 2 + +void PlayUsbBufferPreVol(DataBlock *data){ + for (unsigned i = 0; i < N_BLOCKS; i++) { + int16_t *pL = Q_usbOut_L.getBuffer(); + int16_t *pR = Q_usbOut_R.getBuffer(); + + // Copy one block then apply gain + arm_copy_f32(&data->I[BUFFER_SIZE * i], usbTmp, BUFFER_SIZE); + arm_scale_f32(usbTmp, g_usbRxGain, usbTmp, BUFFER_SIZE); + + // Optional: clip to [-1, +1] to avoid wrap distortion + for (size_t k = 0; k < BUFFER_SIZE; k++) { + if (usbTmp[k] > 1.0f) usbTmp[k] = 1.0f; + else if (usbTmp[k] < -1.0f) usbTmp[k] = -1.0f; + } + + arm_float_to_q15(usbTmp, pL, BUFFER_SIZE); + arm_float_to_q15(usbTmp, pR, BUFFER_SIZE); + + Q_usbOut_L.playBuffer(); + Q_usbOut_R.playBuffer(); + } +} +#endif + + + /** * Initialize the global variables to their default startup values * 1) Configure the RXfilters @@ -921,23 +958,21 @@ DataBlock * ReceiveProcessing(const char *fname){ CWAudioFilter(&data, &RXfilters); } - // Interpolate - InterpolateReceiveData(&data, &RXfilters); - - // Volume adjust for audio volume setting. I and Q contain duplicate data, don't - // need to scale both - AdjustVolume(&data, &RXfilters); - - SaveData(&data, 6); // used by the unit tests - - // Play sound on the speaker - PlayBuffer(&data); - - elapsed_micros_sum = elapsed_micros_sum + usec; - elapsed_micros_idx_t++; - //Flag(0); - - return &data; +// Interpolate +InterpolateReceiveData(&data, &RXfilters); +// Volume knob only affects the speaker path +PlayUsbBufferPreVol(&data); +// Volume knob only affects the speaker path +AdjustVolume(&data, &RXfilters); +SaveData(&data, 6); // used by the unit tests +// Play sound on the speaker (post-volume) +PlayBuffer(&data); + +elapsed_micros_sum = elapsed_micros_sum + usec; +elapsed_micros_idx_t++; +//Flag(0); + +return &data; } @@ -965,7 +1000,34 @@ float32_t GetMicRRMS(void){ * @param data The data block to put the samples in * @return ESUCCESS if samples were read, EFAIL if insufficient samples are available */ -errno_t ReadMicrophoneBuffer(DataBlock *data){ +errno_t ReadMicrophoneBuffer(DataBlock *data) +{ + if (!data) return EFAIL; + +#ifdef T41_USB_AUDIO +if (GetFt8Mode()) { + const uint32_t outCount = N_BLOCKS_EX * BUFFER_SIZE; + + bool ok = Ft8UsbBridge_GetSamples(data->I, outCount); + if (!ok) { + memset(data->I, 0, outCount * sizeof(float32_t)); + } + + // Dual-mono + attenuation (prevents harshness/clipping/pulsing) + for (uint32_t i = 0; i < outCount; i++) { + float s = data->I[i] * 0.20f; // try 0.10–0.30 + data->I[i] = s; + data->Q[i] = s; + } + + data->N = outCount; + data->sampleRate_Hz = SR[SampleRate].rate; + return ESUCCESS; +} +#endif + + // ----- existing microphone code continues below ----- + // are there at least N_BLOCKS buffers in each channel available ? if ((uint32_t)Q_in_L_Ex.available() > N_BLOCKS_EX+0 && (uint32_t)Q_in_R_Ex.available() > N_BLOCKS_EX+0) { //counter++; diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.cpp b/code/src/PhoenixSketch/Ft8UsbBridge.cpp new file mode 100644 index 0000000..ab4883e --- /dev/null +++ b/code/src/PhoenixSketch/Ft8UsbBridge.cpp @@ -0,0 +1,221 @@ +// Ft8UsbBridge.cpp +// +// USB (WSJT-X, ~44.1 kHz) -> Phoenix SDR TX path (sampleRate, e.g. 192 kHz) +// Linear resampler with a float FIFO. +// +// This version adds a simple "warm-up" phase: +// - The first few calls to Ft8UsbBridge_GetSamples() return pure silence. +// - After that, we only send fully interpolated data plus a small +// "hold last sample" tail if we ever run short. +// Goal: avoid the ugly "slow motor" pulsing on the very first TUNE. + +#include +#include +#include "Ft8UsbBridge.h" + +// ----------------------------------------------------------------------------- +// USB audio objects +// We use LEFT channel only from WSJT-X (WSJT usually sends same audio on L/R). +// ----------------------------------------------------------------------------- +AudioInputUSB g_usbIn; +AudioRecordQueue g_usbQueueL; +AudioRecordQueue g_usbQueueR; +AudioConnection g_pcUsbToQueueL(g_usbIn, 0, g_usbQueueL, 0); +AudioConnection g_pcUsbToQueueR(g_usbIn, 1, g_usbQueueR, 0); + +// ----------------------------------------------------------------------------- +// Internal SRC state +// ----------------------------------------------------------------------------- + +// SDR sample rate (e.g., 192000.0f). Set in Ft8UsbBridge_Init(). +static float g_sdrSampleRate = 192000.0f; + +// Teensy USB audio nominal sample rate. +// AUDIO_SAMPLE_RATE_EXACT ≈ 44117.64706 Hz. +static const float kUsbSampleRate = AUDIO_SAMPLE_RATE_EXACT; + +// FIFO for buffering USB-rate samples (float, mono). +// 4096 samples is ~90 ms at 44.1 kHz – plenty of cushion. +static float g_usbFifo[4096]; +static uint32_t g_fifoCount = 0; // number of valid samples in g_usbFifo[] +static float g_srcPhase = 0.0f; // fractional index in FIFO (in USB samples) + +// Track the last valid audio sample so we can avoid sudden zeros mid-stream. +static float g_lastSample = 0.0f; +static bool g_haveSample = false; + +// Simple "warm-up" counter: how many complete SDR blocks we've produced +// since Ft8UsbBridge_Init() was called. +static uint32_t g_blocksSinceInit = 0; + +// ----------------------------------------------------------------------------- +// FIFO helpers +// ----------------------------------------------------------------------------- + +// Compact FIFO after "consumed" samples have been used. +static void fifo_consume(uint32_t consumed) +{ + if (consumed == 0 || g_fifoCount == 0) return; + + if (consumed >= g_fifoCount) { + g_fifoCount = 0; + return; + } + + memmove(g_usbFifo, + g_usbFifo + consumed, + (g_fifoCount - consumed) * sizeof(float)); + + g_fifoCount -= consumed; +} + +// Pull as many 128-sample USB blocks as we can and append them to FIFO. +static void pump_usb_to_fifo() +{ + const uint32_t blockSamples = 128; + const uint32_t fifoCapacity = (uint32_t)(sizeof(g_usbFifo) / sizeof(g_usbFifo[0])); + + while (g_usbQueueL.available() > 0 && + g_usbQueueR.available() > 0 && + (g_fifoCount + blockSamples) <= fifoCapacity) + { + int16_t *pL = g_usbQueueL.readBuffer(); + int16_t *pR = g_usbQueueR.readBuffer(); + if (!pL || !pR) break; + + for (uint32_t i = 0; i < blockSamples; ++i) { + float l = (float)pL[i] / 32768.0f; + float r = (float)pR[i] / 32768.0f; + g_usbFifo[g_fifoCount + i] = 0.5f * (l + r); + } + + g_fifoCount += blockSamples; + g_usbQueueL.freeBuffer(); + g_usbQueueR.freeBuffer(); + } +} + + +// ----------------------------------------------------------------------------- +// Public API +// ----------------------------------------------------------------------------- + +void Ft8UsbBridge_Init(float sdrSampleRateHz) +{ + g_sdrSampleRate = (sdrSampleRateHz > 0.0f) ? sdrSampleRateHz : 192000.0f; + + g_fifoCount = 0; + g_srcPhase = 0.0f; + g_lastSample = 0.0f; + g_haveSample = false; + g_blocksSinceInit = 0; + + // Optional but helpful: reset queues so we start clean + g_usbQueueL.end(); + g_usbQueueR.end(); + g_usbQueueL.begin(); + g_usbQueueR.begin(); +} + + +// Get "outCount" SDR-rate samples for the TX path. +// Caller (DSP.cpp) will: +// - Put this into data->I, +// - Zero data->Q, +// - Run the TX chain (or direct-to-IQ, depending on your variant). +// +// We always fill the whole buffer. On startup we deliberately output +// a few blocks of silence so the USB FIFO can "stabilize" before the +// rig ever sees actual RF drive. +bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount) +{ + if (!out || outCount == 0) { + return false; + } + + // -------------------------------------------------------------- + // 0) WARM-UP PHASE + // + // On the very first TUNE, USB audio may only have just started to + // arrive from WSJT-X. To avoid any chance of block-by-block + // "motorboating", we deliberately send a few blocks of *pure + // silence* before we ever send any real tone. + // + // At 192 kHz with 2048-sample blocks, each block is ~10.7 ms. + // 4 blocks ≈ 43 ms of silence – inaudible in an FT8 TX but enough + // to get a stable FIFO. + // -------------------------------------------------------------- + const uint32_t WARMUP_BLOCKS = 4; + + if (g_blocksSinceInit < WARMUP_BLOCKS) + { + // Keep draining USB queues so they don’t overflow while we “warm up” + pump_usb_to_fifo(); + + memset(out, 0, outCount * sizeof(float)); + g_blocksSinceInit++; + return true; + } + + + // Bring in fresh USB data. + pump_usb_to_fifo(); + + // How many input (USB) samples to advance per output (SDR) sample. + // E.g. for 44.1k -> 192k: step ≈ 0.2299 + const float step = kUsbSampleRate / g_sdrSampleRate; + + float phase = g_srcPhase; + uint32_t produced = 0; + + // -------------------------------------------------------------- + // 1) Normal interpolation from FIFO + // -------------------------------------------------------------- + while (produced < outCount && g_fifoCount > 1) { + uint32_t i0 = (uint32_t)phase; + + // Need two samples for interpolation. + if (i0 + 1 >= g_fifoCount) { + break; + } + + float frac = phase - (float)i0; + float s0 = g_usbFifo[i0]; + float s1 = g_usbFifo[i0 + 1]; + + float s = s0 + frac * (s1 - s0); // linear interpolation + + out[produced++] = s; + + // Remember last valid sample so we can avoid sharp transitions + // if the FIFO ever runs slightly short mid-block. + g_lastSample = s; + g_haveSample = true; + + phase += step; + } + + // How many whole input samples did we consume? + uint32_t consumed = (uint32_t)phase; + g_srcPhase = phase - (float)consumed; + + if (consumed > 0) { + fifo_consume(consumed); + } + + // -------------------------------------------------------------- + // 2) Tail fill: if we ever underrun after warm-up, fill the rest + // of the block with the last valid sample. For a continuous + // WSJT tone this should be rare and effectively inaudible. + // -------------------------------------------------------------- + if (produced < outCount) { + float fill = (g_haveSample ? g_lastSample : 0.0f); + + for (uint32_t i = produced; i < outCount; ++i) { + out[i] = fill; + } + } + + g_blocksSinceInit++; + return true; +} diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.h b/code/src/PhoenixSketch/Ft8UsbBridge.h new file mode 100644 index 0000000..92a85a4 --- /dev/null +++ b/code/src/PhoenixSketch/Ft8UsbBridge.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +// Simple USB→SDR sample-rate converter for FT8 transmit. +// - Captures mono audio from WSJT via USB (AudioInputUSB) +// - Stores it in a FIFO at ~44.1 kHz +// - Resamples to the Phoenix SDR sample rate (sampleRate, e.g., 192 kHz) +// - Provides blocks of float samples for the FT8 TX path. + +/** + * Initialize the FT8 USB bridge. + * + * Must be called after AudioMemory() and after the audio system is + * initialized (e.g., from InitializeAudio()). + * + * @param sdrSampleRateHz Phoenix SDR's internal sample rate (e.g. 192000.0f) + */ +void Ft8UsbBridge_Init(float sdrSampleRateHz); + +/** + * Get a block of SDR-rate audio samples for FT8 transmit. + * + * @param out Pointer to output buffer (length = outCount floats) + * @param outCount Number of SDR-rate samples requested + * + * @return true if we produced a reasonably complete block, + * false if there was not enough USB audio yet. + * + * On failure, the caller should typically zero-fill the TX buffer + * (or just accept that this block will be silent). + */ +bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount); diff --git a/code/src/PhoenixSketch/Globals.cpp b/code/src/PhoenixSketch/Globals.cpp index 8a81a23..c7ea452 100644 --- a/code/src/PhoenixSketch/Globals.cpp +++ b/code/src/PhoenixSketch/Globals.cpp @@ -489,7 +489,7 @@ time_t getTeensy3Time() { void setup(void){ Serial.begin(115200); - SerialUSB1.begin(38400); // For CAT control + Serial.begin(38400); // For CAT control Serial.println("T41 SDT Setup"); // get TIME from real time clock with 3V backup battery diff --git a/code/src/PhoenixSketch/Loop.cpp b/code/src/PhoenixSketch/Loop.cpp index 6bd3b12..a263421 100644 --- a/code/src/PhoenixSketch/Loop.cpp +++ b/code/src/PhoenixSketch/Loop.cpp @@ -85,6 +85,9 @@ */ #include "SDT.h" +#include "MainBoard_AudioIO.h" +#include "FrontPanel.h" + // FIFO buffer for interrupt events #define INTERRUPT_BUFFER_SIZE 16 @@ -1275,11 +1278,23 @@ void ConsumeInterrupt(void){ // Handle all the other non-encoder interrupts switch (interrupt){ - case (iBUTTON_PRESSED):{ - int32_t button = GetButton(); - HandleButtonPress(button); - break; - } + +case (iBUTTON_PRESSED):{ + int32_t button = GetButton(); + SetButton(-1); // clear latched button + + if (button == 16) { // SET FT8 MODE + bool newState = !GetFt8Mode(); + SetFt8Mode(newState); + break; + } + + HandleButtonPress(button); + break; +} + + + case (iVFO_CHANGE):{ // The VFO has been updated. We might have selected a different active VFO, // we might have changed frequency. diff --git a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp index cfbc434..6b75b0a 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp @@ -52,6 +52,7 @@ */ #include "MainBoard_AudioIO.h" +#include "Ft8UsbBridge.h" /** * The transition from analog to digital and digital to analog are handled using a fork @@ -98,6 +99,7 @@ * to see what it is what you're trying to transmit. Probably don't need this anymore. */ + // Generated using this tool: https://www.pjrc.com/teensy/gui/index.html // GUItool: begin automatically generated code AudioInputI2SQuad i2s_quadIn; //xy=576.75,225 @@ -120,6 +122,16 @@ AudioMixer4 modeSelectOutL; //xy=1725.75,184 AudioMixer4 modeSelectOutExR; //xy=1727.75,103 AudioMixer4 modeSelectOutR; //xy=1732.75,291 AudioOutputI2SQuad i2s_quadOut; //xy=1969.75,138 + +#ifdef T41_USB_AUDIO +AudioPlayQueue Q_usbOut_L; +AudioPlayQueue Q_usbOut_R; +AudioOutputUSB usbOut; +// stereo gain blocks for the PC feed +AudioAmplifier usbRxGainL; +AudioAmplifier usbRxGainR; +#endif + AudioConnection patchCord1(i2s_quadIn, 0, modeSelectInExL, 0); AudioConnection patchCord2(i2s_quadIn, 1, modeSelectInExR, 0); AudioConnection patchCord3(i2s_quadIn, 2, modeSelectInL, 0); @@ -141,7 +153,17 @@ AudioConnection patchCord18(Q_out_L, 0, modeSelectOutL, 0); AudioConnection patchCord19(modeSelectOutExL, 0, i2s_quadOut, 0); AudioConnection patchCord20(modeSelectOutL, 0, i2s_quadOut, 2); AudioConnection patchCord21(modeSelectOutExR, 0, i2s_quadOut, 1); -AudioConnection patchCord22(modeSelectOutR, 0, i2s_quadOut, 3); +AudioConnection patchCord22(modeSelectOutR, 0, i2s_quadOut, 3); + +#ifdef T41_USB_AUDIO +AudioConnection patchUsbGainL(Q_usbOut_L, 0, usbRxGainL, 0); +AudioConnection patchUsbGainR(Q_usbOut_R, 0, usbRxGainR, 0); + +AudioConnection patchUsbL(usbRxGainL, 0, usbOut, 0); +AudioConnection patchUsbR(usbRxGainR, 0, usbOut, 1); +#endif + + AudioControlSGTL5000 pcm5102_mainBoard; //xy=874.75,449 // GUItool: end automatically generated code @@ -151,18 +173,20 @@ AudioControlSGTL5000_Extended sgtl5000_teensy; static ModeSm_StateId previousAudioIOState = ModeSm_StateId_ROOT; -/** - * Get the previous audio I/O state. - * - * Returns the ModeSm state that the audio routing was last configured for. - * Used to detect state changes and avoid unnecessary reconfiguration of the - * audio graph when the mode hasn't changed. - * - * @return The previous ModeSm_StateId that audio routing was configured for - */ -ModeSm_StateId GetAudioPreviousState(void){ - return previousAudioIOState; -} +// Always exist so other modules can link +static bool g_ft8Mode = false; + +#ifdef T41_USB_AUDIO +enum class TxAudioSource : uint8_t { MIC = 0, USB = 2 }; +static void SelectTxInputSource(void); + +// remember the user’s modulation so FT8 can restore it +static ModulationType g_savedModulation = USB; +static bool g_haveSavedModulation = false; + +static TxAudioSource g_prevTxAudioSource = TxAudioSource::MIC; +static TxAudioSource g_txAudioSource = TxAudioSource::MIC; // <-- IMPORTANT: MIC at boot +#endif /** * Select a single active channel on a 4-channel audio mixer. @@ -177,6 +201,7 @@ ModeSm_StateId GetAudioPreviousState(void){ * @param mixer Pointer to the AudioMixer4 object to configure * @param channel Channel number to enable (0-3), all others will be muted */ + void SelectMixerChannel(AudioMixer4 *mixer, uint8_t channel){ for (uint8_t k = 0; k < 4; k++){ if (k == channel) mixer->gain(k,1); @@ -184,6 +209,58 @@ void SelectMixerChannel(AudioMixer4 *mixer, uint8_t channel){ } } +#ifdef T41_USB_AUDIO +static void SelectTxInputSource(void) +{ + const uint8_t ch = static_cast(g_txAudioSource); + SelectMixerChannel(&modeSelectInExL, ch); + SelectMixerChannel(&modeSelectInExR, ch); +} +#endif + +void SetFt8Mode(bool enabled) +{ + g_ft8Mode = enabled; + +#ifdef T41_USB_AUDIO + // Select TX audio source + g_txAudioSource = enabled ? TxAudioSource::USB : TxAudioSource::MIC; + + if (enabled) { + // Save current modulation once, then force USB + if (!g_haveSavedModulation) { + g_savedModulation = ED.modulation[ED.activeVFO]; + g_haveSavedModulation = true; + } + ED.modulation[ED.activeVFO] = USB; + UpdateRFHardwareState(); + } else { + // Restore modulation when leaving FT8 + if (g_haveSavedModulation) { + ED.modulation[ED.activeVFO] = g_savedModulation; + g_haveSavedModulation = false; + UpdateRFHardwareState(); + } + } + + // If we're already in TX, apply immediately + if (modeSM.state_id == ModeSm_StateId_SSB_TRANSMIT) { + SelectTxInputSource(); + g_prevTxAudioSource = g_txAudioSource; + } +#else + (void)enabled; +#endif +} + + + +bool GetFt8Mode(void) +{ + return g_ft8Mode; +} + + /** * Mute all channels on a 4-channel audio mixer. * @@ -259,11 +336,21 @@ void UpdateTransmitAudioGain(void){ * @see ModeSm state machine for state transition logic * @see UpdateRFHardwareState() in RFBoard.cpp */ + void UpdateAudioIOState(void){ - if (modeSM.state_id == previousAudioIOState){ - // Already in this state, no need to change - return; +if (modeSM.state_id == previousAudioIOState){ + +#ifdef T41_USB_AUDIO + // If still in TX and the selected TX source changed, re-apply it. + if (modeSM.state_id == ModeSm_StateId_SSB_TRANSMIT && g_txAudioSource != g_prevTxAudioSource) { + SelectTxInputSource(); + g_prevTxAudioSource = g_txAudioSource; } +#endif + + return; +} + switch (modeSM.state_id){ case (ModeSm_StateId_CALIBRATE_OFFSET_SPACE): case (ModeSm_StateId_CALIBRATE_TX_IQ_SPACE): @@ -297,13 +384,36 @@ void UpdateAudioIOState(void){ Q_in_L.begin(); Q_in_R.begin(); // Microphone input starts - Q_in_L_Ex.begin(); + #ifdef T41_USB_AUDIO + if (GetFt8Mode()) { + // FT8 TX uses Ft8UsbBridge (not the mic record queues) + Q_in_L_Ex.end(); + Q_in_R_Ex.end(); + } else { + // Normal voice SSB TX uses microphone queues + Q_in_L_Ex.begin(); + Q_in_R_Ex.begin(); + } + #else + Q_in_L_Ex.begin(); Q_in_R_Ex.begin(); + #endif + sgtl5000_teensy.micGain(ED.currentMicGain); // Input is microphone - SelectMixerChannel(&modeSelectInExL,0); + #ifdef T41_USB_AUDIO + SelectTxInputSource(); // chooses MIC(0) or USB(2) + #else + SelectMixerChannel(&modeSelectInExL,0); // mic SelectMixerChannel(&modeSelectInExR,0); + #endif + + #ifdef T41_USB_AUDIO + g_prevTxAudioSource = g_txAudioSource; + #endif + + // Output is samples to RF transmit SelectMixerChannel(&modeSelectOutExL,0); SelectMixerChannel(&modeSelectOutExR,0); @@ -425,7 +535,10 @@ void UpdateAudioIOState(void){ * @see SR[] array in Config.h for supported sample rates */ void InitializeAudio(void){ + SetI2SFreq(SR[SampleRate].rate); + + // The sgtl5000_teensy is the controller for the Teensy Audio board. We use it to get // the microphone input for SSB, and the I/Q output for the exciter board. In other // words, it is used for the transmit path. @@ -433,6 +546,15 @@ void InitializeAudio(void){ sgtl5000_teensy.enable(); AudioMemory(500); AudioMemory_F32(10); + + +#ifdef T41_USB_AUDIO +Ft8UsbBridge_Init((float)SR[SampleRate].rate); +usbRxGainL.gain(2.0f); // try 1.0 to 6.0 FOR FT8 GAIN TO WINDOWS +usbRxGainR.gain(2.0f); +SetFt8Mode(false); +#endif + sgtl5000_teensy.inputSelect(AUDIO_INPUT_MIC); // set mic pre-amp gain to 40dB & audio gain to 12dB sgtl5000_teensy.micGain(10); // sets pre-amp and input gain to achieve 10dB of total gain sgtl5000_teensy.lineInLevel(0); // set ADC right and left channel volumes to 0dB diff --git a/code/src/PhoenixSketch/MainBoard_AudioIO.h b/code/src/PhoenixSketch/MainBoard_AudioIO.h index 95088ae..750c904 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.h +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.h @@ -24,6 +24,10 @@ extern AudioSynthWaveformSine transmitIQcal_oscillator; */ int SetI2SFreq(int freq); +// FT8 / USB TX audio select +void SetFt8Mode(bool enabled); +bool GetFt8Mode(void); + /** * @brief Initialize dual-codec audio subsystem for Phoenix SDR * @note Configures SGTL5000 codecs for transmit path (Teensy Audio Board) and receive path (main board) diff --git a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp index 24994ab..8bbb06d 100644 --- a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp +++ b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp @@ -21,6 +21,8 @@ #include #include "FreeSansBold24pt7b.h" #include "FreeSansBold18pt7b.h" +#include "MainBoard_AudioIO.h" + // External references to objects and variables defined in MainBoard_Display.cpp extern RA8875 tft; @@ -285,23 +287,32 @@ static int64_t oldCenterFreq = 0; static int32_t oldBand = -1; static ModeSm_StateId oldState = ModeSm_StateId_ROOT; static ModulationType oldModulation = DCF77; +static bool oldFt8Mode = false; /** * Render the frequency, band name, and modulation mode pane. */ void DrawFreqBandModPane(void) { - if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || - (oldBand != ED.currentBand[ED.activeVFO]) || - (oldState != modeSM.state_id) || - (oldModulation != ED.modulation[ED.activeVFO])){ - PaneFreqBandMod.stale = true; - } + bool ft8 = false; + #ifdef T41_USB_AUDIO + ft8 = GetFt8Mode(); + #endif + +if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || + (oldBand != ED.currentBand[ED.activeVFO]) || + (oldState != modeSM.state_id) || + (oldModulation != ED.modulation[ED.activeVFO]) || + (oldFt8Mode != ft8)){ + PaneFreqBandMod.stale = true; +} + if (!PaneFreqBandMod.stale) return; oldCenterFreq = ED.centerFreq_Hz[ED.activeVFO]; oldBand = ED.currentBand[ED.activeVFO]; oldState = modeSM.state_id; oldModulation = ED.modulation[ED.activeVFO]; + oldFt8Mode = ft8; tft.setFontDefault(); tft.fillRect(PaneFreqBandMod.x0, PaneFreqBandMod.y0, PaneFreqBandMod.width, PaneFreqBandMod.height, RA8875_BLACK); @@ -336,8 +347,10 @@ void DrawFreqBandModPane(void) { tft.print("(LSB)"); break; case USB: - tft.print("(USB)"); - break; + // Only label FT8 when FT8 mode is enabled + if (ft8) tft.print("(FT8)"); + else tft.print("(USB)"); + break; case AM: tft.print("(AM)"); break; diff --git a/code/src/PhoenixSketch/SDT.h b/code/src/PhoenixSketch/SDT.h index 9d4198a..648240c 100644 --- a/code/src/PhoenixSketch/SDT.h +++ b/code/src/PhoenixSketch/SDT.h @@ -6,11 +6,17 @@ #include "Config.h" #define RIGNAME "T41-EP SDT" -#define VERSION "Phx V1.2" +#define VERSION "Phx V1.3" // WSJT VERSION #include "BuildInfo.h" -#define Debug(x) Serial.println(x) +// Debug logging: enable only when you are NOT using USB Serial for CAT (e.g., FT8) +#ifdef ENABLE_DEBUG_SERIAL + #define Debug(x) Serial.println(x) +#else + #define Debug(x) do {} while(0) +#endif + #include // Installed via Arduino library manager #include // https://github.com/chipaudette/OpenAudio_ArduinoLibrary From bf2f9533bbaebb1c8f1aa22908103e6858149f2f Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Fri, 6 Feb 2026 10:03:51 -0500 Subject: [PATCH 3/3] FT8 integration: USB bridge, FT8 mode toggle, CAT gating, RX audio tap pre-volume --- code/src/PhoenixSketch/CAT.cpp | 56 +++++++++--------- code/src/PhoenixSketch/Config.h | Bin 7162 -> 3537 bytes code/src/PhoenixSketch/DSP.cpp | 34 ++++++----- code/src/PhoenixSketch/Ft8UsbBridge.cpp | 5 ++ code/src/PhoenixSketch/Globals.cpp | 8 ++- code/src/PhoenixSketch/Loop.cpp | 24 ++++---- code/src/PhoenixSketch/MainBoard_AudioIO.cpp | 12 ++-- .../PhoenixSketch/MainBoard_DisplayHome.cpp | 22 +++---- 8 files changed, 87 insertions(+), 74 deletions(-) diff --git a/code/src/PhoenixSketch/CAT.cpp b/code/src/PhoenixSketch/CAT.cpp index 1691df0..d09f23a 100644 --- a/code/src/PhoenixSketch/CAT.cpp +++ b/code/src/PhoenixSketch/CAT.cpp @@ -754,7 +754,6 @@ char *PR_read( char* cmd ){ return obuf; } - /** * Poll SerialUSB1 for incoming CAT commands and process them * @@ -765,55 +764,56 @@ char *PR_read( char* cmd ){ void CheckForCATSerialEvents(void){ int i; char c; - while( ( i = Serial.available() ) > 0 ){ - c = ( char )Serial.read(); + +#ifdef T41_USB_AUDIO + while ( (i = Serial.available()) > 0 ) { + c = (char)Serial.read(); +#else + while ( (i = SerialUSB1.available()) > 0 ) { + c = (char)SerialUSB1.read(); +#endif i--; catCommand[ catCommandIndex ] = c; - #ifdef DEBUG_CAT + +#ifdef DEBUG_CAT Serial.print( catCommand[ catCommandIndex ] ); - #endif +#endif + if( c == ';' ){ - // Finished reading CAT command - #ifdef DEBUG_CAT +#ifdef DEBUG_CAT Serial.println(); - #endif // DEBUG_CAT - - // Check to see if the command is a good one BEFORE sending it - // to the command executor - //Serial.println( String("catCommand is ")+String(catCommand)+String(" catCommandIndex is ")+String(catCommandIndex)); +#endif char *parser_output = command_parser( catCommand ); catCommandIndex = 0; - // We executed it, now erase it memset( catCommand, 0, sizeof( catCommand )); + if( parser_output[0] != '\0' ){ - #ifdef DEBUG_CAT1 - Serial.println( parser_output ); - #endif // DEBUG_CAT int i = 0; while( parser_output[i] != '\0' ){ +#ifdef T41_USB_AUDIO if( Serial.availableForWrite() > 0 ){ Serial.print( parser_output[i] ); - #ifdef DEBUG_CAT - Serial.print( parser_output[i] ); - #endif +#else + if( SerialUSB1.availableForWrite() > 0 ){ + SerialUSB1.print( parser_output[i] ); +#endif i++; - }else{ - Serial.flush(); } } +#ifdef T41_USB_AUDIO Serial.flush(); - #ifdef DEBUG_CAT - Serial.println(); - #endif // DEBUG_CAT +#else + SerialUSB1.flush(); +#endif } - }else{ + } else { catCommandIndex++; if( catCommandIndex >= 128 ){ catCommandIndex = 0; - memset( catCommand, 0, sizeof( catCommand )); //clear out that overflowed buffer! - #ifdef DEBUG_CAT + memset( catCommand, 0, sizeof( catCommand )); +#ifdef DEBUG_CAT Serial.println( "CAT command buffer overflow" ); - #endif +#endif } } } diff --git a/code/src/PhoenixSketch/Config.h b/code/src/PhoenixSketch/Config.h index 2c2194b5ea4ad28467fd332684846bd80e5415a5..7d67707e145867052c5df1ce09b5647c61417b96 100644 GIT binary patch literal 3537 zcmb7HTXUOA41UkA(4;TPX(l%0*7Oa-k}$>K1513;mz_;m+swhhaH*UA`lDTJ)^3xu z$CEfV(n{J-(kJCZDi&EGVEV{x%lF}TuVZUsJLDKfBXGomcX$NCaXjh0^+2My0mZAm2gjLzq&}M6Kgw6+u zzwx{i4J^tUI4o+>tf1y;Ru0zfB#KBtnH!P6nFTlISiUfwXym~Snf1En1a8I`hnkZ? z(;=>v_*9zo0y`p8bbwhNkM|Pw8BUDx8C>BnO3uLCe(JxE$M-)@f40YF;haRwClADj zD@-DW7`MD`y9%muBWlRSH<1s=Tq~xGCIMmlQK)0P3S89X8u(NBO(=*|LAPC@Gapkc zqREU+tiS>J#xBRx-Zhmuz@K&{Kt6opMXIo;**Yth%3!k%uX(P8D0GF#GvA8unUh2o z#e!thc);hr+(2I9c}#7y!Y^@^b4*)rLOHO@qOHpuMDbjvcvGq_{HY`k@&(Tuogos~p@ZgK%cfxj<8$-Mm2qKlbd6Bo7qG#rAjt?`?Ld=x z<1MT>rv4IA(PCRQP$8O4E2^`jcdyo)mrYe-_G@hupXAw70~?{78|ULi-;OFn*uGtr zixjcrMYGP@J(_U5h}e|bVKVcH!{|TiTv*Ap^8B+h$MI9Sgm@kS=9jol#n2P1SRF@AJML=a|@HkuAHL%XAI3ZO<8vtRS9xl*Kd4#zICe4-+c;eeZ>wE6Y_j z`>yjhJZpQ_F8UJtSG+)l{N2Elz`_$CzGtnZ8GAmI6P7M-b!Tj8n0+CS*dOzm6lUNcT0olpPc}E9Ioc1+-Xd z_+7^RZbcLGDx*(7m6Bf?Y;E#!qw2#Fw7*_*P zsU$<)8iQ1=AX=ab*c_x^!|<0TzZj$uiHTZ>yx>k9#5bL1W!X90jdiKe@a_)ze~P1a zcNDG%%ML+7;;UU|C)UNGN4(y{G?+o?VN*oJNobnlU_W&bSw1=e9Ff?LF_|ebj4Gr0Sh|5%r%Q4ZA q8yd6iwlzs89sXZ2G-%tE>QJmvld!0rX-{Fl4RNpEzq1V8H2wn^c*OSr literal 7162 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 #include #include "Ft8UsbBridge.h" +#include "Config.h" + +#if defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL)) // ----------------------------------------------------------------------------- // USB audio objects @@ -219,3 +222,5 @@ bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount) g_blocksSinceInit++; return true; } + +#endif \ No newline at end of file diff --git a/code/src/PhoenixSketch/Globals.cpp b/code/src/PhoenixSketch/Globals.cpp index c7ea452..6a5f6be 100644 --- a/code/src/PhoenixSketch/Globals.cpp +++ b/code/src/PhoenixSketch/Globals.cpp @@ -489,7 +489,13 @@ time_t getTeensy3Time() { void setup(void){ Serial.begin(115200); - Serial.begin(38400); // For CAT control + +#ifndef T41_USB_AUDIO + // Non-USB-audio build: + // CAT on SerialUSB1 + SerialUSB1.begin(38400); +#endif + Serial.println("T41 SDT Setup"); // get TIME from real time clock with 3V backup battery diff --git a/code/src/PhoenixSketch/Loop.cpp b/code/src/PhoenixSketch/Loop.cpp index a263421..607e24d 100644 --- a/code/src/PhoenixSketch/Loop.cpp +++ b/code/src/PhoenixSketch/Loop.cpp @@ -1279,21 +1279,19 @@ void ConsumeInterrupt(void){ // Handle all the other non-encoder interrupts switch (interrupt){ -case (iBUTTON_PRESSED):{ - int32_t button = GetButton(); - SetButton(-1); // clear latched button - - if (button == 16) { // SET FT8 MODE - bool newState = !GetFt8Mode(); - SetFt8Mode(newState); - break; - } - - HandleButtonPress(button); - break; -} + case (iBUTTON_PRESSED):{ + int32_t button = GetButton(); + SetButton(-1); // clear latched button + if (button == 16) { // SET FT8 MODE + bool newState = !GetFt8Mode(); + SetFt8Mode(newState); + break; + } + HandleButtonPress(button); + break; + } case (iVFO_CHANGE):{ // The VFO has been updated. We might have selected a different active VFO, diff --git a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp index 6b75b0a..8b23df1 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp @@ -548,12 +548,12 @@ void InitializeAudio(void){ AudioMemory_F32(10); -#ifdef T41_USB_AUDIO -Ft8UsbBridge_Init((float)SR[SampleRate].rate); -usbRxGainL.gain(2.0f); // try 1.0 to 6.0 FOR FT8 GAIN TO WINDOWS -usbRxGainR.gain(2.0f); -SetFt8Mode(false); -#endif + #ifdef T41_USB_AUDIO + Ft8UsbBridge_Init((float)SR[SampleRate].rate); + usbRxGainL.gain(2.0f); // try 1.0 to 6.0 FOR FT8 GAIN TO WINDOWS + usbRxGainR.gain(2.0f); + SetFt8Mode(false); + #endif sgtl5000_teensy.inputSelect(AUDIO_INPUT_MIC); // set mic pre-amp gain to 40dB & audio gain to 12dB sgtl5000_teensy.micGain(10); // sets pre-amp and input gain to achieve 10dB of total gain diff --git a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp index 8bbb06d..5424d18 100644 --- a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp +++ b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp @@ -298,13 +298,13 @@ void DrawFreqBandModPane(void) { ft8 = GetFt8Mode(); #endif -if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || - (oldBand != ED.currentBand[ED.activeVFO]) || - (oldState != modeSM.state_id) || - (oldModulation != ED.modulation[ED.activeVFO]) || - (oldFt8Mode != ft8)){ - PaneFreqBandMod.stale = true; -} + if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || + (oldBand != ED.currentBand[ED.activeVFO]) || + (oldState != modeSM.state_id) || + (oldModulation != ED.modulation[ED.activeVFO]) || + (oldFt8Mode != ft8)){ + PaneFreqBandMod.stale = true; + } if (!PaneFreqBandMod.stale) return; @@ -347,10 +347,10 @@ if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || tft.print("(LSB)"); break; case USB: - // Only label FT8 when FT8 mode is enabled - if (ft8) tft.print("(FT8)"); - else tft.print("(USB)"); - break; + // Only label FT8 when FT8 mode is enabled + if (ft8) tft.print("(FT8)"); + else tft.print("(USB)"); + break; case AM: tft.print("(AM)"); break;