/* CESSB1.ino * * This INO tests the Dave Hershberger Controlled Envelope Single Sideband * transmit generator radioCESSBtransmit_F32. This was developed By W9GR * with the aim to "allow your rig to output more average power while * keeping peak envelope power PEP the same". The increase in perceived * loudness can be up to 4dB without any audible increase in distortion * and without making you sound "processed." See radioCESSBTransmit_F32.h * for more information and references. * * Your microSD card must have the WAV file W9GR48.WAV loaded to it. Source: * https://github.com/chipaudette/OpenAudio_ArduinoLibrary/blob/master/utility/ * * The SD card may connect to different pins, depending on the * hardware you are using. Configure the SD card * pins to match your hardware. It is set for T4.x Rev D PJRC * Teensy Audio Adaptor card here. * * This example code is in the public domain. */ #include #include #include #include #include "OpenAudio_ArduinoLibrary.h" // Normal input is from the WAV file with W9GR voice. // An alternative is two tones. Compile that with the following define. // #define USE_TWO_TONE // T3.x supported sample rates: 2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 44117, 48000, // 88200, 88235 (44117*2), 95680, 96000, 176400, 176470, 192000 // T4.x supports any sample rate the codec will handle. const float sample_rate_Hz = 48000.0f; const int audio_block_samples = 128; // Always 128, which is AUDIO_BLOCK_SAMPLES from AudioStream.h AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples); #ifdef USE_TWO_TONE AudioSynthWaveformSine_F32 sine3(audio_settings); //xy=231,444 AudioSynthWaveformSine_F32 sine4(audio_settings); //xy=259,345 AudioMixer4_F32 mixer4_4; //xy=405,427 #endif AudioSDPlayer_F32 playWav1(audio_settings); //xy=111,51 radioCESSBtransmit_F32 cessb1(audio_settings); RadioIQMixer_F32 iqMixer2(audio_settings); //xy=100,300 AudioFilter90Deg_F32 filter90deg1(audio_settings); //xy=250,300 RadioIQMixer_F32 iqMixer1(audio_settings); //xy=250,150 AudioAnalyzeRMS_F32 rms1; //xy=300,50 AudioMixer4_F32 mixer4_1; //xy=400,310 AudioMixer4_F32 mixer4_2; //xy=400,150 AudioOutputI2S_F32 audioOutput(audio_settings); //xy=520,250 AudioAnalyzeFFT1024_F32 fft1; //xy=550,125 #ifdef USE_TWO_TONE AudioConnection_F32 patchCorda(sine3, 0, mixer4_4, 1); AudioConnection_F32 patchCordb(sine4, 0, mixer4_4, 0); AudioConnection_F32 patchCordc(mixer4_4, 0, cessb1, 0); #else AudioConnection_F32 patchCord5(playWav1, 0, cessb1, 0); #endif AudioConnection_F32 patchCord1(cessb1, 0, iqMixer1, 0); AudioConnection_F32 patchCord2(cessb1, 1, iqMixer1, 1); AudioConnection_F32 patchCord9(iqMixer1, 0, mixer4_2, 0); AudioConnection_F32 patchCord10(iqMixer1, 1, mixer4_2, 1); AudioConnection_F32 patchCord13(mixer4_2, fft1); // Transmitter output // mixer4_2 is transmitter SSB output, iqMixer2 is receiver input AudioConnection_F32 patchCord14(mixer4_2, 0, iqMixer2, 0); AudioConnection_F32 patchCord3(iqMixer2, 0, filter90deg1, 0); AudioConnection_F32 patchCord4(iqMixer2, 1, filter90deg1, 1); AudioConnection_F32 patchCord7(filter90deg1, 0, mixer4_1, 0); AudioConnection_F32 patchCord8(filter90deg1, 1, mixer4_1, 1); AudioConnection_F32 patchCord11(mixer4_1, 0, audioOutput, 0); AudioConnection_F32 patchCord12(mixer4_1, 0, audioOutput, 1); AudioControlSGTL5000 sgtl5000_1; //xy=582,327 // Use these with the Teensy 4.x Rev D Audio Shield (NOT for T3.x) #define SDCARD_CS_PIN 10 #define SDCARD_MOSI_PIN 11 #define SDCARD_SCK_PIN 13 // Filter for AudioFilter90Deg_F32 hilbert1, for the test receiver. #include "hilbert251A.h" // This provides information about the current WAV file to this .INO struct wavData* pCurrentWavData; struct levels* pLevelData; uint32_t writeOne = 0; uint32_t cntFFT = 0; void setup() { // ********** SETUP ********** Serial.begin(9600); delay(1000); Serial.println("*** F32 WAV from SD Card ***"); AudioMemory_F32(70, audio_settings); pCurrentWavData = playWav1.getCurrentWavData(); sgtl5000_1.enable(); SPI.setMOSI(SDCARD_MOSI_PIN); SPI.setSCK(SDCARD_SCK_PIN); Serial.print("SD.begin() returns "); Serial.println(SD.begin(SDCARD_CS_PIN)); #ifdef USE_TWO_TONE // Two-tone test, multiples of 48000/1024 Hz sine3.frequency(468.75f); sine3.amplitude(0.5f); sine4.frequency(703.125f); sine4.amplitude(0.5f); #endif // Build the CESSB SSB transmitter cessb1.setSampleRate_Hz(48000.0f); // Required // Set input, correction, and output gains float32_t Pre_CESSB_Gain = 1.5f; // Sets the amount of clipping, 1.0 to 2.0f, 3 is excessive cessb1.setGains(Pre_CESSB_Gain, 2.0f, 1.0f); cessb1.setSideband(false); // true reverses the sideband pLevelData = cessb1.getLevels(0); // Gets pointer to struct // Generate SSB at 15 kHz iqMixer1.useTwoChannel(true); iqMixer1.useSimple(false); // enables setGainOut() // Following if cessb1.setSideband(false), otherwise reverse // Compensate for window loss in FFT as well as x2 for mixers iqMixer1.setGainOut(2.76f); iqMixer1.frequency(13650); // 13650 for LSB, 16350 for USB mixer4_2.gain(1, 1.0f); // 1.0f for LSB, -1.0f for USB // A receiver for the SSB transmitter iqMixer2.frequency(15000.0f); iqMixer2.useTwoChannel(false); filter90deg1.begin(hilbert251A, 251); // Set the Hilbert transform FIR filter mixer4_1.gain(0, -1.0f); // LSB + for USB audioOutput.setGain(0.05); // <<< Output volume control fft1.setOutputType(FFT_DBFS); fft1.windowFunction(AudioWindowBlackmanHarris1024); fft1.setNAverage(16); } void playFile(const char *filename) { if(playWav1.isPlaying()) return; Serial.println(""); Serial.print("Playing file: "); Serial.println(filename); // Allow for running the WAV file at a sub-rate from the Audio rate // Two of these are defined above for 1 and 4 sub rates. // playWav1.setSubMult(&wavQuarter); // Start playing the file. This sketch continues to // run while the file plays. playWav1.play(filename); // A brief delay for the library read WAV info delay(25); Serial.print("WAV file format = "); Serial.println(pCurrentWavData->audio_format); Serial.print("WAV number channels = "); Serial.println(pCurrentWavData->num_channels); Serial.print("WAV File Sample Rate = "); Serial.println(pCurrentWavData->sample_rate); Serial.print("Number of bits per Sample = "); Serial.println(pCurrentWavData->bits); Serial.print("File length, seconds = "); Serial.println(0.001f*(float32_t)playWav1.lengthMillis(), 3); } void loop() { // Thanks to W9GR for the test files. These are intended for testing // the CESSB radio transmission system (see CESSB1.ino in the examples) // but work well to provide a voice bandwidth test of WAV file reading. #ifndef USE_TWO_TONE playFile("W9GR48.WAV"); // filenames are always uppercase 8.3 format #endif if(fft1.available() && ++cntFFT>100 && cntFFT<102) { for(int kk=0; kk<512; kk++) { Serial.print(46.875f*(float32_t)kk); Serial.print(","); Serial.println(fft1.read(kk)); } } if(cessb1.levelDataCount() > 300) // Typically 300 to 3000 { cessb1.getLevels(1); // Cause write of data to struct & reset /* // Detailed Report Uncomment to print Serial.print(10.0f*log10f(pLevelData->pwr0)); Serial.print(" In Ave Pwr Out "); Serial.println(10.0f*log10f(pLevelData->pwr1)); Serial.print(20.0f*log10f(pLevelData->peak0)); Serial.print(" In Peak Out "); Serial.println(20.0f*log10f(pLevelData->peak1)); Serial.print(pLevelData->peak0, 6); Serial.print(" In Peak Volts Out "); Serial.println(pLevelData->peak1, 6); Serial.print("Enhancement = "); Serial.print((10.0f*log10f(pLevelData->pwr1) - 20.0f*log10f(pLevelData->peak1)) - (10.0f*log10f(pLevelData->pwr0) - 20.0f*log10f(pLevelData->peak0)) ); Serial.println(" dB"); */ /* // CSV Report suitable for entering to spread sheet, Uncomment to print // InAve, InPk, OutAve, OutPk, EnhancementdB Serial.print(pLevelData->pwr0, 5); Serial.print(","); Serial.print(pLevelData->peak0, 5); Serial.print(","); Serial.print(pLevelData->pwr1, 5); Serial.print(","); Serial.print(pLevelData->peak1, 5); Serial.print(","); Serial.println((10.0f*log10f(pLevelData->pwr1) - 20.0f*log10f(pLevelData->peak1)) - (10.0f*log10f(pLevelData->pwr0) - 20.0f*log10f(pLevelData->peak0)) ); */ } } // End loop()