diff --git a/T41EEE/AudioSignal.h b/T41EEE/AudioSignal.h index ef35e5d..560018b 100644 --- a/T41EEE/AudioSignal.h +++ b/T41EEE/AudioSignal.h @@ -1,22 +1,60 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // Teensy and Open Audio Signal Chains include file. // Common to Transmitter and Receiver. const float sample_rate_Hz = 48000.0; // The transmitter operates at 48ksps. const int audio_block_samples = 128; // Always 128 AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples); +#ifdef JMS_QUAD +AudioInputI2SQuad_F32 i2s_quadIn_f32(audio_settings); // 4 inputs available in experimental Open Audio library. Ussing Terrane's code +AudioSwitch4_OA_F32 switchMicMute(audio_settings); //Switch Mic between bitBucket and Normal +//AudioRecordQueue_F32 micBitBucketL(audio_settings); //DataSink when Mic not in use +#else AudioInputI2SQuad i2s_quadIn; // 4 inputs available only in Teensy audio and not Open Audio library. +#endif AudioOutputI2SQuad_F32 i2s_quadOut_f32(audio_settings); // Transmitter -AudioControlSGTL5000 sgtl5000_1; // Controller for the Teensy Audio Adapter. +AudioControlSGTL5000 sgtl5000_1; +#ifdef JMS_QUAD // Controller for the Teensy Audio Adapter. +//not neeeded float conversion done in AudioInputI2SQuad_F32 +#else AudioConvert_I16toF32 int2Float1_tx; // Converts Int16 to Float. +#endif AudioEffectGain_F32 micGain(audio_settings), compGainCompensate(audio_settings); // Microphone gain control. AudioFilterEqualizer_F32 txEqualizer(audio_settings); AudioEffectCompressor2_F32 compressor1; // Open Audio Compressor radioCESSB_Z_transmit_F32 cessb1; +#ifdef JMS_QUAD +//not neeeded float conversion done in AudioInputI2SQuad_F32 +#else AudioConvert_F32toI16 float2Int1_tx, float2Int2_tx; // Converts Float to Int16. See class in AudioStream_F32.h - +#endif AudioSwitch4_OA_F32 switch3_tx, switch4_tx; AudioMixer4_F32 mixer1_tx, mixer2_tx, mixer3_tx; // Used to switch in tone during calibration. AudioSynthWaveformSine_F32 toneSSBCal1, toneSSBCal2; // Tones for SSB calibration and IMD testing. @@ -29,10 +67,17 @@ AudioPlayQueue_F32 Q_out_R_Ex; // AudioPlayQueue for driving the Q channel (CW/ AudioPlayQueue_F32 cwToneData; // The tone from the CW Exciter. // Begin transmit signal chain. +#ifdef JMS_QUAD +//don't need the int2float object so just connect input via mute mixer +AudioConnection_F32 connect31(i2s_quadIn_f32, 0, switchMicMute, 0); +AudioConnection_F32 connect3(switchMicMute, 0, mixer1_tx, 0); +//AudioConnection_F32 connect32(switchMicMute, 1, micBitBucketL, 0); +#else AudioConnection connect0(i2s_quadIn, 0, int2Float1_tx, 0); // Microphone audio channel. Must use int2Float because Open Audio does not have quad input. // Need a mixer to switch between microphone audio and tones used for calibration, testing, and CW. AudioConnection_F32 connect3(int2Float1_tx, 0, mixer1_tx, 0); // Connect microphone mixer1 output 0 via gain control. +#endif AudioConnection_F32 connect4(toneSSBCal1, 0, mixer1_tx, 1); // Connect tone for SSB calibration and IM3 testing. AudioConnection_F32 connect22(toneSSBCal2, 0, mixer1_tx, 2); // Connect tone for IM3 testing. @@ -75,15 +120,23 @@ AudioMixer4_F32 mixer4, mixer5; AudioSwitch4_OA_F32 switch1_rx, switch2_rx; AudioConvert_F32toI16 float2Int3, float2Int4, float2Int5, float2Int6; - +#ifdef JMS_QUAD +AudioRecordQueue_F32 ADC_RX_I(audio_settings); // Receiver I channel from ADC PCM1808, 32 bit. +AudioRecordQueue_F32 ADC_RX_Q(audio_settings); // Receiver Q channel from ADC PCM1808, 32 bit. +#else AudioRecordQueue ADC_RX_I; // Receiver I channel from ADC PCM1808, 16 bit. AudioRecordQueue ADC_RX_Q; // Receiver Q channel from ADC PCM1808, 16 bit. +#endif AudioPlayQueue_F32 audioOutQueue; // Receiver audio out and CW sidetone. AudioPlayQueue_F32 sidetoneOutQueue; // Receiver audio out and CW sidetone. - +#ifdef JMS_QUAD +AudioConnection_F32 patchCord1(i2s_quadIn_f32, 3, ADC_RX_I, 0); // Receiver I and Q channel data stream. +AudioConnection_F32 patchCord2(i2s_quadIn_f32, 2, ADC_RX_Q, 0); // This data stream goes to sketch code for processing. +#else AudioConnection patchCord1(i2s_quadIn, 3, ADC_RX_I, 0); // Receiver I and Q channel data stream. AudioConnection patchCord2(i2s_quadIn, 2, ADC_RX_Q, 0); // This data stream goes to sketch code for processing. +#endif AudioConnection_F32 patchCord3(audioOutQueue, 0, mixer5, 0); // mixer5 selects receiver or CW sidetone audio. AudioConnection_F32 patchCord35(sidetoneOutQueue, 0, mixer5, 1); @@ -220,11 +273,19 @@ void SetAudioOperatingState(RadioState operatingState) { mixer3_tx.gain(1, 0.0); // Connect audio paths +#ifdef JMS_QUAD + // AudioConnection_F32 doesn't support connect/disconnect +#else patchCord1.connect(); patchCord2.connect(); +#endif mixer5.gain(0, 1.0); // Connect receiver audio from DSP. mixer5.gain(1, 0); // Disconnect CW sidetone. +#ifdef JMS_QUAD + switchMicMute.setChannel(1);// Bypass tx audio chain, mic straight to micBitBucket +#else connect0.disconnect(); // Disconnect microphone input data stream. +#endif // Configure audio compressor (AGC) if (ConfigData.AGCMode == true) { // Activate compressor2_1 path. @@ -264,17 +325,27 @@ void SetAudioOperatingState(RadioState operatingState) { // QSD disabled and disconnected controlAudioOut(ConfigData.audioOut, true); // Mute all receiver audio. sgtl5000_1.unmuteLineout(); +#ifdef JMS_QUAD + // AudioConnection_F32 doesn't support connect/disconnect +#else patchCord1.disconnect(); // Receiver I channel patchCord2.disconnect(); // Receiver Q channel +#endif mixer5.gain(0, 0); // Stop receiver audio. mixer5.gain(1, 0); // Stop sidetone audio. +#ifdef JMS_QUAD + // AudioConnection_F32 doesn't support connect/disconnect +#else connect0.connect(); // Connect microphone input data stream. - +#endif ADC_RX_I.end(); ADC_RX_I.clear(); ADC_RX_Q.end(); ADC_RX_Q.clear(); - +#ifdef JMS_QUAD + switchMicMute.setChannel(0); // Connect Mic to tx audio chain +#endif + mixer1_tx.gain(0, 1); // microphone audio on. mixer1_tx.gain(1, 0); // testTone off. mixer_rxtx_I.gain(0, 1.0); // Connect transmitter back-end to Audio Adapter. @@ -305,8 +376,7 @@ void SetAudioOperatingState(RadioState operatingState) { Q_in_L_Ex.begin(); // I channel Microphone audio Q_in_R_Ex.begin(); // Q channel Microphone audio - - // Update equalizer. Update first 14 only. Last two are constant. + // Update equalizer. Update first 14 only. Last two are constant. for (int i = 0; i < 14; i = i + 1) dbBand1[i] = static_cast(ConfigData.equalizerXmt[i]); txEqualizer.equalizerNew(16, &fBand1[0], &dbBand1[0], 249, &equalizeCoeffs[0], 65.0f); @@ -314,20 +384,30 @@ void SetAudioOperatingState(RadioState operatingState) { break; - case RadioState::SSB_CALIBRATE_STATE: + case RadioState::SSB_CALIBRATE_STATE:// TRANSMIT SampleRate = SAMPLE_RATE_48K; InitializeDataArrays(); // I2S sample rate set in this function. controlAudioOut(AudioState::MUTE_BOTH, true); // Mute all audio. sgtl5000_1.unmuteLineout(); +#ifdef JMS_QUAD + // AudioConnection_F32 doesn't support connect/disconnect +#else patchCord1.connect(); // Receiver I channel patchCord2.connect(); // Receiver Q channel +#endif mixer5.gain(0, 0); // Stop receiver audio. mixer5.gain(1, 0); // Stop sidetone audio. ADC_RX_I.end(); ADC_RX_I.clear(); ADC_RX_Q.end(); ADC_RX_Q.clear(); - +#ifdef JMS_QUAD + // Just stop everything This changes behaviour + Q_in_L_Ex.end(); // Transmit I channel path. + Q_in_R_Ex.end(); // Transmit Q channel path. + Q_in_L_Ex.clear(); + Q_in_R_Ex.clear(); +#endif cessb1.setSampleRate_Hz(48000); //// // Test tone enabled and connected @@ -340,7 +420,14 @@ void SetAudioOperatingState(RadioState operatingState) { mixer1_tx.gain(1, 1); // testTone on. mixer1_tx.gain(2, 0); // testTone 2 off. +#ifdef JMS_QUAD + // AudioConnection_F32 doesn't support connect/disconnect + //switchMicMute.setChannel(1); // Bypass tx audio chain straight to micBitBucket + switchMicMute.setChannel(0); // May need tx audio chain to be running so tones are generated??? + +#else connect0.disconnect(); // Disconnect microphone input data stream. +#endif mixer_rxtx_I.gain(0, 1.0); // Connect transmitter back-end to Audio Adapter. mixer_rxtx_Q.gain(0, 1.0); mixer_rxtx_I.gain(1, 0); // Disconnect headphone path to Audio Adapter. @@ -388,12 +475,20 @@ void SetAudioOperatingState(RadioState operatingState) { // QSD disabled and disconnected controlAudioOut(AudioState::MUTE_BOTH, true); // Mute all audio. sgtl5000_1.unmuteLineout(); +#ifdef JMS_QUAD + // AudioConnection_F32 doesn't support connect/disconnect +#else patchCord1.disconnect(); // Receiver I channel patchCord2.disconnect(); // Receiver Q channel +#endif mixer5.gain(0, 0); // Stop receiver audio. mixer5.gain(1, 0); // Stop sidetone audio. +#ifdef JMS_QUAD + // AudioConnection_F32 doesn't support connect/disconnect + switchMicMute.setChannel(1); // Bypass tx audio chain straight to micBitBucket +#else connect0.disconnect(); // Disconnect microphone input data stream. - +#endif ADC_RX_I.end(); ADC_RX_I.clear(); ADC_RX_Q.end(); @@ -458,11 +553,19 @@ void SetAudioOperatingState(RadioState operatingState) { controlAudioOut(ConfigData.audioOut, true); // Mute all audio. Unmuted at end of configuration. sgtl5000_1.unmuteLineout(); // QSD disabled and disconnected +#ifdef JMS_QUAD + // AudioConnection_F32 doesn't support connect/disconnect +#else patchCord1.disconnect(); // Receiver I channel patchCord2.disconnect(); // Receiver Q channel +#endif mixer5.gain(0, 0); // Disconnect receiver audio from DSP. mixer5.gain(1, 1.0); // Connect CW sidetone. +#ifdef JMS_QUAD + switchMicMute.setChannel(1); // Bypass tx audio chain straight to micBitBucket +#else connect0.disconnect(); // Disconnect microphone input data stream. +#endif // Speaker and headphones should be unmuted according to current audio out state for sidetone. controlAudioOut(ConfigData.audioOut, false); @@ -522,7 +625,11 @@ void SetAudioOperatingState(RadioState operatingState) { InitializeDataArrays(); // I2S sample rate set in this function. controlAudioOut(AudioState::MUTE_BOTH, true); // Mute all audio. sgtl5000_1.unmuteLineout(); +#ifdef JMS_QUAD + switchMicMute.setChannel(1); // Bypass tx audio chain straight to micBitBucket +#else connect0.disconnect(); // Disconnect microphone input data stream. +#endif mixer5.gain(0, 0); // Stop receiver audio. mixer5.gain(1, 0); // Stop sidetone audio. @@ -563,8 +670,12 @@ void SetAudioOperatingState(RadioState operatingState) { cessb1.setIQCorrections(true, CalData.IQCWAmpCorrectionFactorUSB[ConfigData.currentBand], CalData.IQCWPhaseCorrectionFactorUSB[ConfigData.currentBand], 0.0); } +#ifdef JMS_QUAD + switchMicMute.setChannel(1); // Bypass tx audio chain straight to micBitBucket +#else patchCord1.connect(); // Connect I and Q receiver data converters. patchCord2.connect(); +#endif ADC_RX_I.begin(); // Calibration is full duplex! ADC_RX_Q.begin(); @@ -603,8 +714,12 @@ void SetAudioOperatingState(RadioState operatingState) { mixer_rxtx_Q.gain(0, 1.0); mixer_rxtx_I.gain(1, 0.0); // Disconnect headphone path to Audio Adapter. mixer_rxtx_Q.gain(1, 0.0); +#ifdef JMS_QUAD + switchMicMute.setChannel(1); // Bypass tx audio chain straight to micBitBucket +#else patchCord1.connect(); patchCord2.connect(); +#endif ADC_RX_I.begin(); // Calibration is full duplex! ADC_RX_Q.begin(); diff --git a/T41EEE/Button.cpp b/T41EEE/Button.cpp index 1b45cf4..416be08 100644 --- a/T41EEE/Button.cpp +++ b/T41EEE/Button.cpp @@ -1,3 +1,28 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // Button class #include "SDT.h" @@ -191,7 +216,7 @@ void Button::ExecuteButtonPress(MenuSelect val) { switch (val) { case MenuSelect::MENU_OPTION_SELECT: // 1 - + menucontrol.top = false; ShowMenu(&topMenus[mainMenuIndex], PRIMARY_MENU); functionPtr[mainMenuIndex](); // These are processed in MenuProcessing.cpp break; @@ -199,7 +224,7 @@ void Button::ExecuteButtonPress(MenuSelect val) { case MenuSelect::MAIN_MENU_UP: // 2 ButtonMenuIncrease(); ShowMenu(&topMenus[mainMenuIndex], PRIMARY_MENU); - + menucontrol.top = true; break; case MenuSelect::BAND_UP: // 3 @@ -213,6 +238,7 @@ void Button::ExecuteButtonPress(MenuSelect val) { case MenuSelect::MAIN_MENU_DN: // 5 ButtonMenuDecrease(); ShowMenu(&topMenus[mainMenuIndex], PRIMARY_MENU); + menucontrol.top = true; break; case MenuSelect::BAND_DN: // 6 diff --git a/T41EEE/MyConfigurationFile.h b/T41EEE/MyConfigurationFile.h index d9b00bc..d543b3e 100644 --- a/T41EEE/MyConfigurationFile.h +++ b/T41EEE/MyConfigurationFile.h @@ -1,3 +1,28 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // User configuration file. #pragma once @@ -10,7 +35,7 @@ const int RIGNAME_X_OFFSET = 570; // Pixel count to rig name field. //#define DEBUG_SWITCH_CAL // Uncomment to run switch cal by pushing and holding a button at power-up. // Debug switch cal must be disabled for normal radio operation! //#define DEBUG_CESSB // Uncomment to get CESSB operating parameters printed to the serial monitor. -//#define LOOP_TIMER +#define LOOP_TIMER #define FAST_TUNE // Uncomment to activate variable speed fast tune by Harry GM3RVL. #define DEFAULT_KEYER_WPM 15 // Startup value for keyer wpm #define FREQ_SEP_CHARACTER '.' // Some may prefer period, space, or combo diff --git a/T41EEE/ReceiveDSP.cpp b/T41EEE/ReceiveDSP.cpp index 2742532..6f27967 100644 --- a/T41EEE/ReceiveDSP.cpp +++ b/T41EEE/ReceiveDSP.cpp @@ -1,3 +1,28 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // Receive DSP. ProcessIQData is the primary DSP function of the receiver. #include "SDT.h" @@ -28,7 +53,11 @@ bool ReceiveDSP::ProcessIQData() { uint32_t AudioMaxIndex; float rfGainValue; int rfGain; - +#ifdef JMS_QUAD + /*Serial.printf("ADC_RX_I.available=%d ADC_RX_Q.available=%d \n", + ADC_RX_I.available(), + ADC_RX_Q.available() );*/ +#endif // Are there at least N_BLOCKS buffers in each channel available ? N_BLOCKS should be 16. Fill float_buffer_L/R[2048]. if (static_cast(ADC_RX_I.available()) > 15 && static_cast(ADC_RX_Q.available()) > 15) { usec = 0; @@ -39,8 +68,13 @@ bool ReceiveDSP::ProcessIQData() { Using arm_Math library, convert to float one buffer_size. Float_buffer samples are now standardized from > -1.0 to < 1.0 **********************************************************************************/ +#ifdef JMS_QUAD + arm_copy_f32(ADC_RX_Q.readBuffer(), &float_buffer_L[BUFFER_SIZE * i], BUFFER_SIZE); // move input buffer as float 32bit. BUFFER_SIZE = 128. + arm_copy_f32(ADC_RX_I.readBuffer(), &float_buffer_R[BUFFER_SIZE * i], BUFFER_SIZE); // move input buffer as float 32bit. BUFFER_SIZE = 128. +#else arm_q15_to_float(ADC_RX_Q.readBuffer(), &float_buffer_L[BUFFER_SIZE * i], BUFFER_SIZE); // convert int_buffer to float 32bit. BUFFER_SIZE = 128. arm_q15_to_float(ADC_RX_I.readBuffer(), &float_buffer_R[BUFFER_SIZE * i], BUFFER_SIZE); // convert int_buffer to float 32bit +#endif ADC_RX_I.freeBuffer(); ADC_RX_Q.freeBuffer(); } // end for loop diff --git a/T41EEE/RxCalibrate.cpp b/T41EEE/RxCalibrate.cpp index 429dc9f..6d38133 100644 --- a/T41EEE/RxCalibrate.cpp +++ b/T41EEE/RxCalibrate.cpp @@ -1,3 +1,28 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // RxCalibrate class // Class extensively modified to perform receive calibration only. Greg KF5N August 2025 @@ -695,7 +720,6 @@ void RxCalibrate::DoReceiveCalibrate(int calMode, bool radio, bool refine, bool void RxCalibrate::MakeFFTData() { float rfGainValue, powerScale; // AFP 2-11-23. Greg KF5N February 13, 2023 // float recBandFactor[7] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; // AFP 2-11-23 KF5N uniform values - /********************************************************************************** AFP 12-31-20 Get samples from queue buffers Teensy Audio Library stores ADC data in two buffers size=128, Q_in_L and Q_in_R as initiated from the audio lib. @@ -770,14 +794,19 @@ void RxCalibrate::MakeFFTData() { // Get I16 audio blocks from the record queues and convert them to float. // Read in 16 blocks of 128 samples in I and Q if available. - if (static_cast(ADC_RX_I.available()) > 16 and static_cast(ADC_RX_Q.available()) > 16) { - for (unsigned i = 0; i < 16; i++) { + if (static_cast(ADC_RX_I.available()) > 15 && static_cast(ADC_RX_Q.available()) > 15) { + for (unsigned i = 0; i < N_BLOCKS; i++) { /********************************************************************************** AFP 12-31-20 Using arm_Math library, convert to float one buffer_size. Float_buffer samples are now standardized from > -1.0 to < 1.0 **********************************************************************************/ - arm_q15_to_float(ADC_RX_Q.readBuffer(), &float_buffer_L[BUFFER_SIZE * i], BUFFER_SIZE); // convert int_buffer to float 32bit +#ifdef JMS_QUAD + arm_copy_f32(ADC_RX_Q.readBuffer(), &float_buffer_L[BUFFER_SIZE * i], BUFFER_SIZE); // move input buffer as float 32bit. BUFFER_SIZE = 128. + arm_copy_f32(ADC_RX_I.readBuffer(), &float_buffer_R[BUFFER_SIZE * i], BUFFER_SIZE); // move input buffer as float 32bit. BUFFER_SIZE = 128. +#else + arm_q15_to_float(ADC_RX_Q.readBuffer(), &float_buffer_L[BUFFER_SIZE * i], BUFFER_SIZE); // convert int_buffer to float 32bit. BUFFER_SIZE = 128. arm_q15_to_float(ADC_RX_I.readBuffer(), &float_buffer_R[BUFFER_SIZE * i], BUFFER_SIZE); // convert int_buffer to float 32bit +#endif ADC_RX_I.freeBuffer(); ADC_RX_Q.freeBuffer(); } diff --git a/T41EEE/SDT.h b/T41EEE/SDT.h index 65257b8..8d36264 100644 --- a/T41EEE/SDT.h +++ b/T41EEE/SDT.h @@ -1,3 +1,28 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // Includes and global variables. #pragma once @@ -32,6 +57,7 @@ constexpr int NUMBER_OF_SWITCHES = 18; // Number of push button switches. constexpr int TEMPMON_ROOMTEMP = 25.0; #define SD_CS BUILTIN_SDCARD // Works on T_3.6 and T_4.1 ... +#define JMS_QUAD 1 // Enables Float32 Input Object Needs latest version of OpenAudio_ArduinoLibrary //======================================== Symbolic constants ========================================================== @@ -271,6 +297,18 @@ enum class MenuSelect { MENU_OPTION_SELECT, BEARING, BOGUS_PIN_READ, DEFAULT }; + +// Menu functions struct. This sets the current menu level. +// This was adapted from T41EVE. +struct menuControl +{ + bool top{false}; + bool CWOptions{false}; + bool subMenuSelect{false}; + bool runOptionFunction{false}; + bool runButtonFunction{false}; +}; +extern menuControl menucontrol; /* struct maps { char mapNames[50]; @@ -306,8 +344,11 @@ extern Bands bands; // Configuration data structure. struct config_t { +#ifdef JMS_QUAD + char versionSettings[10] = "T41EQE.93"; // This is required to be the first! +#else char versionSettings[10] = "T41EEE.93"; // This is required to be the first! - +#endif bool AGCMode = true; float32_t AGCThreshold = -40.0; int speakerVolume = 30; @@ -549,8 +590,14 @@ extern int32_t NCOFreq; // AFP 04-16-22 // Teensy and OpenAudio objects which need to be global. Revised by KF5N July 24, 2024 extern AudioAmplifier volumeAdjust; +#ifdef JMS_QUAD +#warning JMS_QUAD enabled +extern AudioRecordQueue_F32 ADC_RX_I; // Receiver I channel from ADC PCM1808, 32 bit. +extern AudioRecordQueue_F32 ADC_RX_Q; // Receiver Q channel from ADC PCM1808, 32 bit. +#else extern AudioRecordQueue ADC_RX_I; extern AudioRecordQueue ADC_RX_Q; +#endif extern AudioRecordQueue_F32 Q_in_L_Ex; extern AudioRecordQueue_F32 Q_in_R_Ex; diff --git a/T41EEE/T41EEE.ino b/T41EEE/T41EEE.ino index e2bab15..2ee963e 100644 --- a/T41EEE/T41EEE.ino +++ b/T41EEE/T41EEE.ino @@ -1,3 +1,28 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // T41 Transceiver Arduino Sketch: T41EEE // "T41 Extreme Experimenters Edition" // @@ -131,6 +156,7 @@ float32_t DMAMEM float_buffer_RTemp[2048]; //======================================== Global structure declarations =============================================== config_t ConfigData; calibration_t CalData; +menuControl menucontrol; Bands bands = { { // Revised band struct with mode and sideband. Greg KF5N February 14, 2025 // band low band hi name mode sideband FHiCut FLoCut FAMCut Gain type gain AGC @@ -1086,7 +1112,17 @@ void loop() { long ditTimerOff; //AFP 09-22-22 bool cwKeyDown; unsigned long cwBlockIndex; - +#ifdef JMS_QUAD +/* + Serial.printf("lastState=%d radioState=%d memory_used=%d memory_used_max=%d f32_memory_used=%d f32_memory_used_max=%d\n", + lastState, + radioState, + (int)AudioStream::memory_used, + (int)AudioStream::memory_used_max, + (int)AudioStream_F32::f32_memory_used, + (int)AudioStream_F32::f32_memory_used_max); +*/ +#endif // Radio state detection before entering the primary radio loop. if (bands.bands[ConfigData.currentBand].mode == RadioMode::SSB_MODE and digitalRead(PTT) == HIGH) radioState = RadioState::SSB_RECEIVE_STATE; if (bands.bands[ConfigData.currentBand].mode == RadioMode::SSB_MODE and digitalRead(PTT) == LOW) radioState = RadioState::SSB_TRANSMIT_STATE; @@ -1108,7 +1144,17 @@ void loop() { // SSB and FT8 transmit operate via the main loop(). CW modes operate within independent while loops. Don't stop in SSB and FT8 modes to read the buttons. if ((radioState != RadioState::SSB_TRANSMIT_STATE) and (radioState != RadioState::FT8_TRANSMIT_STATE) and (calibrateFlag == false) and (morseDecodeAdjustFlag == false)) { menu = readButton(); - if (menu != MenuSelect::BOGUS_PIN_READ) button.ExecuteButtonPress(menu); + // Restrict allowed button selections if in top menu. + if (menucontrol.top == true) + { + if ((menu != MenuSelect::BOGUS_PIN_READ) and (menu == MenuSelect::MAIN_MENU_UP or menu == MenuSelect::MAIN_MENU_DN or menu == MenuSelect::MENU_OPTION_SELECT)) + button.ExecuteButtonPress(menu); + } + else + { + if (menu != MenuSelect::BOGUS_PIN_READ) + button.ExecuteButtonPress(menu); + } } // Transition to new state if required and only if the radio state has changed. diff --git a/T41EEE/TxCalibrate.cpp b/T41EEE/TxCalibrate.cpp index b8db1fb..e60c745 100644 --- a/T41EEE/TxCalibrate.cpp +++ b/T41EEE/TxCalibrate.cpp @@ -1,3 +1,28 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // Class TxCalibrate. Greg KF5N July 10, 2024 // Re-factored August 2025. @@ -76,7 +101,11 @@ void TxCalibrate::warmUpCal() { arm_max_q15(pixelnew, 512, &rawSpectrumPeak, &index_of_max); if (index_of_max > 251 and index_of_max < 260) { // The peak is in the correct bin? count = count + 1; - } else count = 0; // Reset count in case of failure. + } //else count = 0; // Reset count in case of failure. + else { + count = 0; // Reset count in case of failure. +// Serial.printf("index_of_max:%d\n",index_of_max); + } if (count == 5) break; // If five in a row, exit the loop. Warm-up is complete. } updateDisplayFlag = true; @@ -87,7 +116,7 @@ void TxCalibrate::warmUpCal() { arm_max_q15(pixelnew, 512, &rawSpectrumPeak, &index_of_max); // Serial.printf("TX rawSpectrumPeak = %d count = %d i = %d\n", rawSpectrumPeak, count, i); // Serial.printf("TX index_of_max = %d\n", index_of_max); - if (index_of_max < 251 or index_of_max > 260) Serial.printf("Problem with TX warmUpCal\n"); + if (index_of_max < 251 or index_of_max > 260) Serial.printf("Problem with TX warmUpCal index_of_max=%d\n",index_of_max); } @@ -1148,10 +1177,22 @@ void TxCalibrate::MakeFFTData() { float32_t* iBuffer = nullptr; // I and Q pointers needed for one-time read of record queues. float32_t* qBuffer = nullptr; - +#ifdef JMS_QUAD + // Make code a bit more readable and allow debug + int32_t lAvailable ; + int32_t rAvailable; + int32_t iAvailable; + int32_t qAvailable; + int exit=0; + do {// Loop until we have a full RX buffer Send TX if available + lAvailable = Q_in_L_Ex.available(); + rAvailable = Q_in_R_Ex.available(); // Read incoming I and Q audio blocks from the SSB exciter. // Are there at least N_BLOCKS buffers in each channel available ? + if ((lAvailable > 15) and (rAvailable > 15)) { +#else if (static_cast(Q_in_L_Ex.available()) > 15 and static_cast(Q_in_R_Ex.available()) > 15) { +#endif for (unsigned i = 0; i < 16; i++) { iBuffer = Q_in_L_Ex.readBuffer(); @@ -1206,29 +1247,48 @@ void TxCalibrate::MakeFFTData() { Q_out_L_Ex.play(float_buffer_L_EX, dataWidth); // play it! This is the I channel from the Audio Adapter line out to QSE I input. Q_out_R_Ex.play(float_buffer_R_EX, dataWidth); // play it! This is the Q channel from the Audio Adapter line out to QSE Q input. +// Serial.printf("T"); } else { +#ifdef JMS_QUAD +// fftSuccess = false; // Not enough transmit data. +// Serial.printf("Failed to get enough I and Q transmitter data!\n"); +// Serial.printf("TX:%d\n",lAvailable); +#else fftSuccess = false; // Not enough transmit data. Serial.printf("Failed to get enough I and Q transmitter data!\n"); +#endif } // End of transmit code. Begin receive code. // Get audio samples from the audio buffers and convert them to float. // Read in 16 blocks of 128 samples in I and Q if available. +#ifdef JMS_QUAD + iAvailable = ADC_RX_I.available(); + qAvailable = ADC_RX_Q.available(); + + if ((iAvailable> 15) and (qAvailable > 15)) { +#else if (static_cast(ADC_RX_I.available()) > 15 and static_cast(ADC_RX_Q.available()) > 15) { +#endif for (unsigned i = 0; i < N_BLOCKS; i++) { /********************************************************************************** AFP 12-31-20 Using arm_Math library, convert to float one buffer_size. Float_buffer samples are now standardized from > -1.0 to < 1.0 **********************************************************************************/ - arm_q15_to_float(ADC_RX_Q.readBuffer(), &float_buffer_L[BUFFER_SIZE * i], BUFFER_SIZE); // convert int_buffer to float 32bit +#ifdef JMS_QUAD + arm_copy_f32(ADC_RX_Q.readBuffer(), &float_buffer_L[BUFFER_SIZE * i], BUFFER_SIZE); // move input buffer as float 32bit. BUFFER_SIZE = 128. + arm_copy_f32(ADC_RX_I.readBuffer(), &float_buffer_R[BUFFER_SIZE * i], BUFFER_SIZE); // move input buffer as float 32bit. BUFFER_SIZE = 128. +#else + arm_q15_to_float(ADC_RX_Q.readBuffer(), &float_buffer_L[BUFFER_SIZE * i], BUFFER_SIZE); // convert int_buffer to float 32bit. BUFFER_SIZE = 128. arm_q15_to_float(ADC_RX_I.readBuffer(), &float_buffer_R[BUFFER_SIZE * i], BUFFER_SIZE); // convert int_buffer to float 32bit +#endif ADC_RX_I.freeBuffer(); ADC_RX_Q.freeBuffer(); } rfGainValue = pow(10, static_cast(ConfigData.rfGain[ConfigData.currentBand]) / 20); //AFP 2-11-23 - arm_scale_f32(float_buffer_L, rfGainValue, float_buffer_L, 2048); //AFP 2-11-23 - arm_scale_f32(float_buffer_R, rfGainValue, float_buffer_R, 2048); //AFP 2-11-23 + arm_scale_f32(float_buffer_L, rfGainValue, float_buffer_L, BUFFER_SIZE * N_BLOCKS); //AFP 2-11-23 + arm_scale_f32(float_buffer_R, rfGainValue, float_buffer_R, BUFFER_SIZE * N_BLOCKS); //AFP 2-11-23 // Manual IQ amplitude and phase correction (receive only). if (mode == 0) { @@ -1259,13 +1319,26 @@ void TxCalibrate::MakeFFTData() { // This process started because there are 2048 samples available. Perform FFT. updateDisplayFlag = true; - if (fftActive) ZoomFFTExe(2048); + if (fftActive) ZoomFFTExe(BUFFER_SIZE * N_BLOCKS); fftSuccess = true; +#ifdef JMS_QUAD + exit=1; // we have ebough RX data escape from do..while loop +// Serial.printf("R"); +#endif } // End of receive code else { +#ifdef JMS_QUAD +// fftSuccess = false; // Insufficient receive buffers to make FFT. Do not plot FFT data! +// Serial.printf("FFT failed due to insufficient I and Q receive data!\n"); +// Serial.printf("RX:%d\n",iAvailable); +#else fftSuccess = false; // Insufficient receive buffers to make FFT. Do not plot FFT data! Serial.printf("FFT failed due to insufficient I and Q receive data!\n"); +#endif } +#ifdef JMS_QUAD + } while(exit==0);// keep looping (FOREVER!!!!) until enough RX data received +#endif } diff --git a/T41EEE/TxCalibrate.h b/T41EEE/TxCalibrate.h index 6c7f258..a4540fd 100644 --- a/T41EEE/TxCalibrate.h +++ b/T41EEE/TxCalibrate.h @@ -1,3 +1,28 @@ +/* +T41EVE Copyright 2026 Gregory Raven + +This file is part of T41EVE. + +T41EVE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +T41EVE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with T41EVE. If not, see . + + This comment block must appear in the load page (e.g., main() or setup()) in any source code + that uses code presented as whole or part of the T41-EP source code. + + (c) Frank Dziock, DD4WH, 2020_05_8 + "TEENSY CONVOLUTION SDR" substantially modified by Jack Purdum, W8TEE, and Al Peter, AC8GY + + This software is made available under the GNU GPLv3 license agreement. If commercial use of this + software is planned, we would appreciate it if the interested parties contact Jack Purdum, W8TEE, + and Al Peter, AC8GY. + + Any and all other uses, written or implied, by the GPLv3 license are forbidden without written + permission from from Jack Purdum, W8TEE, and Al Peter, AC8GY. +*/ + // Transmit calibrate class. This calibrates both CW and SSB exciters.