parent
7f0bbb18e6
commit
d6f30882a7
@ -0,0 +1,121 @@ |
||||
/* FFTFrequencyMeter.ino
|
||||
* Bob Larkin 20 Feb 2021 Pblic Domain |
||||
*
|
||||
* This INO demonstrates the use of FFT measurement of frequency. |
||||
* It generates a known, random frequency between 300 and 3000 Hz, |
||||
* measures that frequency with FFT interpolation, and prints the |
||||
* two frequencies and the difference between them (the error). |
||||
* This is mathematically correct for the Hann window only. A run |
||||
* of 400 samples of this INO showed: |
||||
* Average error = -0.0003 Hz |
||||
* Standard Deviation = 0.0247 Hz |
||||
* Maximum +/- error 0.042 Hz |
||||
* The method was generously provided by DerekR
|
||||
* https://forum.pjrc.com/threads/36358-A-New-Accurate-FFT-Interpolator-for-Frequency-Estimation
|
||||
*
|
||||
* Homework: Create a known S/N by adding a SynthGaussianWhiteNoise |
||||
* object combined along with the sine wave by an AudioMixer_F32 object. |
||||
* Cut the Serial output and paste it |
||||
* into your favorite spread sheet. Find the power S/N where |
||||
* the standard deviation of the error rises to a tenth of the 44100/1024 |
||||
* = 43 Hz bin spacing. |
||||
*
|
||||
* This INO was originally put together to demonstrate the use of the |
||||
* myFFT.getData(); that returns a pointer to the FFT output data. See |
||||
* findFrequency() below. |
||||
*
|
||||
* The Knuth and Lewis random noise generator might be useful elsewhere. |
||||
*/ |
||||
|
||||
#include "OpenAudio_ArduinoLibrary.h" |
||||
#include "AudioStream_F32.h" |
||||
#include "Audio.h" |
||||
|
||||
AudioSynthWaveformSine_F32 wv; |
||||
AudioAnalyzeFFT1024_F32 myFFT; |
||||
AudioOutputI2S_F32 i2sOut; |
||||
AudioConnection_F32 patchCord1(wv, 0, myFFT, 0); |
||||
|
||||
void setup(){ |
||||
Serial.begin(9600); |
||||
delay(1000); |
||||
AudioMemory_F32(20); |
||||
Serial.println("FFT Frequency Meter"); |
||||
Serial.println("Actual Measured Difference"); |
||||
|
||||
wv.amplitude(0.5); // Initialize Waveform Generator
|
||||
wv.frequency(1000); |
||||
|
||||
myFFT.setOutputType(FFT_POWER); |
||||
myFFT.windowFunction(AudioWindowHanning1024); |
||||
} |
||||
|
||||
void loop() { |
||||
static float sineFrequency, fftFrequency; |
||||
|
||||
Serial.print(sineFrequency, 3); Serial.print(", "); |
||||
fftFrequency = findFrequency(); |
||||
Serial.print(fftFrequency, 3); Serial.print(", "); |
||||
Serial.println(fftFrequency - sineFrequency, 3); |
||||
|
||||
// Find a new frequency for the next time around the loop.
|
||||
sineFrequency = 300.0f + 2700.0f*uniformRandom(); |
||||
wv.frequency(sineFrequency); |
||||
delay(250); // Let things settle
|
||||
} |
||||
|
||||
// Return the estimated frequency, based on powers in FFT bins.
|
||||
// Following from DerekR
|
||||
// https://forum.pjrc.com/threads/36358-A-New-Accurate-FFT-Interpolator-for-Frequency-Estimation
|
||||
// " 1) A record of length 1024 samples is windowed with a Hanning window
|
||||
// 2) The magnitude spectrum is computed from the FFT, and the two (adjacent)
|
||||
// largest amplitude lines are found. Let the largest be line L, and the
|
||||
// other be either L+1, of L-1.
|
||||
// 3) Compute the ratio R of the amplitude of the two largest lines.
|
||||
// 4) If the amplitude of L+1 is greater than L-1 then
|
||||
// f = (L + (2-R)/(1+R))*f_sample/1024
|
||||
// otherwise
|
||||
// f = (L - (2-R)/(1+R))*f_sample/1024 "
|
||||
float findFrequency(void) { |
||||
float specMax = 0.0f; |
||||
uint16_t iiMax = 0; |
||||
|
||||
// Get pointer to data array of powers, float output[512];
|
||||
float* pPwr = myFFT.getData(); |
||||
// Find biggest bin
|
||||
for(int ii=2; ii<510; ii++) { |
||||
if (*(pPwr + ii) > specMax) { // Find highest peak of 512
|
||||
specMax = *(pPwr + ii); |
||||
iiMax = ii; |
||||
} |
||||
} |
||||
float vm = sqrtf( *(pPwr + iiMax - 1) ); |
||||
float vc = sqrtf( *(pPwr + iiMax) ); |
||||
float vp = sqrtf( *(pPwr + iiMax + 1) ); |
||||
if(vp > vm) { |
||||
float R = vc/vp; |
||||
return ( (float32_t)iiMax + (2-R)/(1+R) )*44100.0f/1024.0f; |
||||
} |
||||
else { |
||||
float R = vc/vm; |
||||
return ( (float32_t)iiMax - (2-R)/(1+R) )*44100.0f/1024.0f; |
||||
} |
||||
} |
||||
|
||||
#define FL_ONE 0X3F800000 |
||||
#define FL_MASK 0X007FFFFF |
||||
/* The "Even Quicker" uniform random sample generator from D. E. Knuth and
|
||||
* H. W. Lewis and described in Chapter 7 of "Numerical Receipes in C", |
||||
* 2nd ed, with the comment "this is about as good as any 32-bit linear |
||||
* congruential generator, entirely adequate for many uses." |
||||
*/ |
||||
float uniformRandom(void) { |
||||
static uint32_t idum = 54321; |
||||
union { |
||||
uint32_t i32; |
||||
float32_t f32; |
||||
} uinf; |
||||
idum = (uint32_t)1664525 * idum + (uint32_t)1013904223; |
||||
uinf.i32 = FL_ONE | (FL_MASK & idum); // Generate random number
|
||||
return uinf.f32 - 1.0f; // resulting uniform deviate on (0.0, 1.0)
|
||||
} |
Loading…
Reference in new issue