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.