|
|
|
@ -1,11 +1,23 @@ |
|
|
|
|
/* ReceiverFM.ino Bob Larkin 26 April 2020
|
|
|
|
|
* This is a simple test of introducing a sine wave to the |
|
|
|
|
* 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. |
|
|
|
|
*
|
|
|
|
@ -22,32 +34,32 @@ |
|
|
|
|
#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; |
|
|
|
|
AudioConvert_F32toI16 cnvrtOut; |
|
|
|
|
AudioOutputI2S i2sOut; |
|
|
|
|
AudioControlSGTL5000 sgtl5000_1; |
|
|
|
|
AudioConnection_F32 connect1(sine1, 0, fmDet1, 0); |
|
|
|
|
AudioConnection_F32 connect3(fmDet1, 0, cnvrtOut, 0); |
|
|
|
|
AudioConnection connect4(cnvrtOut, 0, i2sOut, 0); // left
|
|
|
|
|
AudioConnection_F32 connect5(fmDet1, 0, queue1, 0); |
|
|
|
|
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 one can add a gain block at the fmDet1 output (like 0.05 gain).
|
|
|
|
|
AudioInputI2S i2sIn; |
|
|
|
|
AudioConvert_I16toF32 cnvrtIn; |
|
|
|
|
// can be loud, so lower the gain below (like 0.05 gain).
|
|
|
|
|
AudioInputI2S_F32 i2sIn; |
|
|
|
|
RadioFMDetector_F32 fmDet1; |
|
|
|
|
AudioAnalyzeFFT1024_F32 fft1; |
|
|
|
|
AudioRecordQueue_F32 queue1;
|
|
|
|
|
AudioConvert_F32toI16 cnvrtOut; |
|
|
|
|
AudioOutputI2S i2sOut; |
|
|
|
|
AudioOutputI2S_F32 i2sOut; |
|
|
|
|
AudioControlSGTL5000 sgtl5000_1; |
|
|
|
|
AudioConnection connect1(i2sIn, 0, cnvrtIn, 0); // left
|
|
|
|
|
AudioConnection_F32 connect2(cnvrtIn, 0, fmDet1, 0); |
|
|
|
|
AudioConnection_F32 connect3(fmDet1, 0, cnvrtOut, 0); |
|
|
|
|
AudioConnection_F32 connect5(fmDet1, 0, queue1, 0); |
|
|
|
|
AudioConnection connect7(cnvrtOut, 0, i2sOut, 0); |
|
|
|
|
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; |
|
|
|
@ -56,17 +68,22 @@ int i; |
|
|
|
|
|
|
|
|
|
void setup(void) { |
|
|
|
|
AudioMemory(5); |
|
|
|
|
AudioMemory_F32(5); |
|
|
|
|
Serial.begin(300); delay(1000); // Any rate is OK
|
|
|
|
|
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(14000.0); |
|
|
|
|
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: "); |
|
|
|
@ -76,20 +93,49 @@ void setup(void) { |
|
|
|
|
// 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++) { |
|
|
|
|
for(k=0; k<128; k++) |
|
|
|
|
*pd1++ = *pq1++;
|
|
|
|
|
} |
|
|
|
|
queue1.freeBuffer(); |
|
|
|
|
if(i++==3) { |
|
|
|
|
i=4; // Only collect 4 blocks
|
|
|
|
@ -132,4 +178,21 @@ void loop(void) { |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|