ArduinoFFT.h generat's weird results

Hi folks,
I have a question about ArduinoFFT.h. I use it on a perfect sine wave sampled with 32*Fb the base frequency of 60Hz with no DC component and no harmonics. The array vReal[SAMPLES] is loaded with the 32 values, one sine cycle period I have generated in excel. The FFT seems to see a DC component in the Bin0 and harmonic in Bin2. Neither are the amplitudes right. The signal has a peak of 165 and the FFT calculates a peak of 1374 at bin 1 =60Hz and second harmonic at 120Hz at Bin2 with and amplitude of 620?
The same FFT in excel with the same data produces correct results.
Is there a problem with ArduinoFFT.h or am I missing something here. Anyone that give me a helping hand?

#include <Arduino.h>
#include <arduinoFFT.h>
 
#define SAMPLES 32              //Must be a power of 2
#define SAMPLING_FREQUENCY 1920  //Hz, must be less than 10000 due to ADC
 
arduinoFFT FFT = arduinoFFT();
 
unsigned int sampling_period_us;
unsigned long microseconds;
 
double vReal[SAMPLES]={	
0.0000	,
32.1899	,
63.1428	,
91.6691	,
116.6726	,
137.1925	,
152.4401	,
161.8296	,
165.0000	,
161.8296	,
152.4401	,
137.1925	,
116.6726	,
91.6691	,
63.1428	,
32.1899	,
0.0000	,
-32.1899	,
-63.1428	,
-91.6691	,
-116.6726	,
-137.1925	,
-152.4401	,
-161.8296	,
-165.0000	,
-161.8296	,
-152.4401	,
-137.1925	,
-116.6726	,
-91.6691	,
-63.1428	,
-32.1899	
};	



double vImag[SAMPLES];
 
void setup() {
    Serial.begin(115200);
 
    sampling_period_us = round(1000000.0/SAMPLING_FREQUENCY);
}
 
void loop() {
   
    /*SAMPLING*/
    // for(int i=0; i<SAMPLES; i++)
    // {
    //     microseconds = micros();    //Overflows after around 70 minutes!
     
    //     vReal[i] = analogRead(A0);
    //     vReal[i] -= 511;
    //     vImag[i] = 0;
     
    //     while(micros() < (microseconds + sampling_period_us)){
    //     }
    // }
 
    for (int i=0; i<SAMPLES;i++) { Serial.print(vReal[i], 1); Serial.print("\n"); }  
    Serial.print("\n");

    /*FFT*/
    FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
    FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
    FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
    double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
 
    /*PRINT RESULTS*/
    Serial.println(peak);   Serial.println("\n");  //Print out what frequency is the most dominant.
 
    for(int i=0; i<(SAMPLES/2); i++)
    {
        /*View all these three lines in serial terminal to see which frequencies has which amplitudes*/
         
        //Serial.print((i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES, 1);
        //Serial.print(" ");
        Serial.println(vReal[i], 1);    //View only this line in serial plotter to visualize the bins
    }
 
    //delay(1000);  //Repeat the process every second OR:
    while(1);       //Run code once
}

Spectrum

What do you expect the effect of using the window function to be?

Thats a good point, let me test it....... I tested it and yes only one ground wave shows up. You are the man of the day :clap:
I do have another question. The amplitude of this single Bin1 60Hz signal is 2640. In the excel FFT it is the same 2640 however when divide it by 16 or half the Bin length it turns out to be 165. That can't be a coincidence. Do you have and idea how the spectrum amplitudes relate to the real peak voltages of the signals?

There are 3 different ways to do FFTs regarding scaling.

The transform mathematically known as the discrete Fourier transform and the
inverse discrete Fourier transforms are not strictly inverses of each other, since applying one then the inverse scales the input up by N, where N is the number of points.

This means that implementations divide by N, either in the forward direction, or the inverse direction, or sometimes divide by sqrt(N) in both directions. This variation also exists for definitions of the transform in various textbooks too, its a big gotcha for working with them really.

For spectral analysis dividing by N in the forward direction is correct, but the ArduinoFFT library divides for the inverse direction. So its spectra are spuriously scaled up by N.

The issue with the window is a bug in the library, it doesn't correctly scale the window to have unity gain, so applying the window incorrectly/arbitrarily scales the result anyway.

Also looking at the code for the library its got an error in the definition of the Hann window function anyway (the most commonly used one).

If you want spectral peak values to be accurate you need to use a flat-top window function, these spread the peak by many bins though. Non-flat-top windows can be several dB out for peaks depending on how closely the frequency matches a fraction of the sample rate.

Thanks mark,
I do not understand everything you say but I understand the following

  1. dividing by N is correct, it's matter of library implementation
  2. this lib has and error in the hann windowing and that is the most used one.
    Thanks for the help

Additional for others:
I tested the procedure with inputs from a sinewave of 60Hz Ampl=165V + 200V DC and a 300Hz 10V noise harmonic. The FFT calculated also these values.

The performance of the code at and Atmega328 was as follows:
At N-32 the FFT.Compute + FFT.ComplexToMagnitude took 11ms, at N-64 it took 25ms. With and ISR sample routine the FFT can be done in real-time on a N=32 samples per cycle of a 60Hz grid signal.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.