You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
OpenAudio_ArduinoLibrary/examples/ReceiverFM/ReceiverFM.ino

198 lines
6.3 KiB

/* ReceiverFM.ino Bob Larkin 26 April 2020
* No copyright.
*
* This INO runs the FM detector on input data from the Codec. It
* requires an FM modulated signal source at 15 kHz. The Serial out is
* the squelch level generated by the squelch noise detector.
*
* Commented out is a test of introducing a sine wave to the
* FM Detector and taking 512 samples of the output. It is
* a static test with a fixed frequency for test and so
* the output DC value and noise can be tested. Note that the 512
* samples include the startup transient, so the first 300,
* or so, points should be ignored in seeing the DC value.
*
* An option is to change the squelch noise filter (commented out)
* and this serves as an example of changing the 10 coefficients.
*
* Another commented out loop code will print the observed spectrum using
* the 1024 point FFT. This shows the NBFM de-emphasis curve.
*
* Change the value of sine1.frequency to see the DC output change.
* See FMReceiver2.ino for testing with real AC modulation.
*
* As an alternative the input can come from the ADC for "SINE_ADC 0"
*
* Output is sent to left channel SGTL5000 DAC.
*/
#include "Audio.h"
#include <OpenAudio_ArduinoLibrary.h>
// SINE_ADC 1 for internally generated sine wave.
// SINE_ADC 0 to use the SGTL5000 Teensy audio adaptor ADC/DAC
#define SINE_ADC 0
#if SINE_ADC
AudioSynthGaussian_F32 gwn1;
AudioSynthWaveformSine_F32 sine1;
AudioMixer4_F32 mix1;
RadioFMDetector_F32 fmDet1;
AudioOutputI2S_F32 i2sOut;
AudioAnalyzeFFT1024_F32 fft1;
AudioRecordQueue_F32 queue1;
AudioControlSGTL5000 sgtl5000_1;
AudioConnection_F32 connectA(gwn1, 0, mix1, 0);
AudioConnection_F32 connectB(sine1, 0, mix1, 1);
AudioConnection_F32 connectC(mix1, 0, fmDet1, 0);
#else // Input from Teensy Audio Adaptor SGTL5000
// Note - With no input, the FM detector output is all noise. This
// can be loud, so lower the gain below (like 0.05 gain).
AudioInputI2S_F32 i2sIn;
RadioFMDetector_F32 fmDet1;
AudioAnalyzeFFT1024_F32 fft1;
AudioRecordQueue_F32 queue1;
AudioOutputI2S_F32 i2sOut;
AudioControlSGTL5000 sgtl5000_1;
AudioConnection_F32 connectD(i2sIn, 0, fmDet1, 0); // left
#endif
// Common for both input sources
AudioConnection_F32 connect1(fmDet1, 1, i2sOut, 0); // Squelched
AudioConnection_F32 connect2(fmDet1, 0, fft1, 0);
AudioConnection_F32 connect3(fmDet1, 0, queue1, 0);
float dt1[512]; // Place to save output
float *pq1, *pd1;
uint16_t k;
int i;
void setup(void) {
AudioMemory(5);
AudioMemory_F32(100);
Serial.begin(300); delay(500); // Any rate is OK
Serial.println("Serial Started");
sgtl5000_1.enable();
sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
#if SINE_ADC
sine1.frequency(15000.0);
sine1.amplitude(0.0001f);
gwn1.amplitude(0.1f);
#endif
fft1.setOutputType(FFT_DBFS);
fft1.setNAverage(10);
// The FM detector has error checking during object construction
// when Serial.print is not available. See RadioFMDetector_F32.h:
Serial.print("FM Initialization errors: ");
Serial.println( fmDet1.returnInitializeFMError() );
// The following enables error checking inside of the "ubdate()"
// Output goes to the Serial (USB) Monitor. Normally, this is quiet.
fmDet1.showError(1);
fmDet1.setSquelchThreshold(0.7f);
// Here is an example of designing and using a non-default squelch
// noise filter. The INO provides storage for the new coefficients.
// static float newFilter[10];
// freq Q ptr to Coeff
// setBandpassBiQuad(2000.0f, 3.0f, &newFilter[0]);
// setBandpassBiQuad(4000.0f, 3.0f, &newFilter[5]);
// fmDet1.setSquelchFilter(newFilter);
// Set the volume control (0.0 to 1.0)
i2sOut.setGain(0.05f);
queue1.begin();
i = 0; k=0;
}
void loop(void) {
float lData[512];
Serial.print("sqLevel "); Serial.println(fmDet1.getSquelchLevel(), 6);
delay(500);
/*
if( fft1.available() ) {
float* pd = fft1.getData();
for(int k=0; k<512; k++)
lData[k] = pd[k];
for(int k=0; k<512; k++)
Serial.println(lData[k],3);
Serial.println(" -------");
}
*/
/*
// Collect 512 samples and output to Serial
// This "if" will be active for i = 0,1,2,3
if (queue1.available() >= 1) {
if( i>=0 && i<4) {
pq1 = queue1.readBuffer();
pd1 = &dt1[i*128];
for(k=0; k<128; k++)
*pd1++ = *pq1++;
queue1.freeBuffer();
if(i++==3) {
i=4; // Only collect 4 blocks
queue1.end(); // No more data to queue1
}
}
else {
queue1.freeBuffer();
}
}
// We have 512 data samples. Serial.print them
if(i == 4) {
#if SINE_ADC
Serial.println("For 14,000 Hz sine wave input:");
#endif
Serial.println("512 samples of FM Det output, starting t=0");
Serial.println("Time in sec, FM Output, Dev from 15,000 Hz:");
for (k=0; k<512; k++) {
Serial.print (0.000022667*(float32_t)k, 6); Serial.print (", ");
Serial.print (dt1[k],7); Serial.print (", ");
Serial.println (dt1[k]/0.000142421, 2); // Convert to Hz
}
i = 5;
}
if(i==5) {
i = 6;
Serial.print("CPU: Percent Usage, Max: ");
Serial.print(AudioProcessorUsage());
Serial.print(", ");
Serial.println(AudioProcessorUsageMax());
Serial.print("Int16 Memory Usage, Max: ");
Serial.print(AudioMemoryUsage());
Serial.print(", ");
Serial.println(AudioMemoryUsageMax());
Serial.print("Float Memory Usage, Max: ");
Serial.print(AudioMemoryUsage_F32());
Serial.print(", ");
Serial.println(AudioMemoryUsageMax_F32());
Serial.println();
}
*/
}
// Find BiQuad Coefficients for Squelch Noise filter.
// Only used if the default -6 dB points of 2680 and 4420 Hz
// are not suitable
void setBandpassBiQuad(float frequency, float q, float* cf) {
double w0 = frequency * (2 * 3.141592654 / 44100); // sampleRate_Hz);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
double scale = 1.0 / (1.0+alpha);
/* b0 */ cf[0] = alpha * scale;
/* b1 */ cf[1] = 0;
/* b2 */ cf[2] = (-alpha) * scale;
/* a1 */ cf[3] = -(-2.0 * cosW0) * scale;
/* a2 */ cf[4] = -(1.0 - alpha) * scale;
}