fourier arduino
TRANSCRIPT
Conceptualmente hablando FFT es bastante simple. En la prctica, la FFT es difcil! As que no voy a entrar en todos los detalles de este algoritmo y voy a permanecer centrado en su uso y aplicaciones. Esta serie de mensajes va a terminar con una biblioteca con todas las funciones que usted ser capaz de utilizar para todo tipo de aplicaciones, sin preocuparse de las matemticas.
La trama en un osciloscopio muestra una onda que est hecha de muestras que se caracterizan por sus intensidades (ordenadas) y su tiempo de muestreo (abscisa). Estamos en el dominio del tiempo.
Cuanto ms simple sea la seal (por ejemplo, una onda sinusoidal simple), el ms simple es la caracterizacin. La transformada de Fourier propone para descomponer cualquier seal en una suma de seno y coseno. Para cada punto de datos del espectro de potencia de FFT corresponde una magnitud (ordenadas) y una frecuencia (abscisa). Ahora estamos en el dominio de la frecuencia.
Cuanto ms compleja es la seal (por ejemplo, la seal + ruido + armnicos + +) ...
..... ms compleja es la caracterizacin.
Y adivinen qu? El algoritmo de la FFT se puede ejecutar en modo inverso, de modo que a partir de una FFT puede reconstruir una seal de la vida real. Podemos estudiar ms adelante cmo esta propiedad puede ser utilizado para la de eliminacin de ruido de las seales.
FFT se aplica a vectores que contienen las muestras n, donde n debe ser una potencia de 2. Ejecutar el algoritmo de estos datos puede conducir a resultados inesperados. La razn es que el vector trunca la informacin de la seal y pueden contener ondas incompletamente descritas (por ejemplo, las ondas de baja frecuencia). Este efecto secundario puede ser corregida por medio de pesaje de la seal, dando menos importancia a los datos principales y colas. Esta es la funcin de ventanas. La funcin de pesaje en la ventanas depende del tipo de seal a analizar:
Transients whose duration is shorter than the length of the window : Rectangular (Box car) Transients whose duration is longer than the length of the window : Exponential, Hann
General-purpose applications : Hann
Spectral analysis (frequency-response measurements) : Hann (for random excitation), Rectangular (for pseudorandom excitation)
Separation of two tones with frequencies very close to each other but with widely differing amplitudes : Kaiser-Bessel
Separation of two tones with frequencies very close to each other but with almost equal amplitudes : Rectangular
Accurate single-tone amplitude measurements : Flat top
Sine wave or combination of sine waves : Hann
Sine wave and amplitude accuracy is important : Flat top
Narrowband random signal (vibration data) : Hann
Broadband random (white noise) : Uniform
Closely spaced sine waves : Uniform, Hamming
Excitation signals (hammer blow) : Force
Response signals : Exponential
Unknown content : Hann
Once the windowing is executed, you can run the FFT. The result of this algorithm lies in to vectors containing the real and imaginary computed values. You need then to apply some math to convert them into a vector of intensities.Window type Rectangle (box car)
Window type Hamming:
Window type Flat top
All plots exported from my DSP tool box Panorama
Here is a recap of the pieces of code that we need in order to convert a wave into a frequency spectrum:Store data in a vector (Double data type)
Weigh this data according to one function (default is Rectangle, also known as box car, in other words, no weighing!)
Execute the FFT algorithm
Convert complex values in usable data
Without knowing too much of the details from the various algorithms, you will very quickly face a dilemma: would you prefer speed or precision? Precision will require large vectors of data, while speed will require multiple vectors: e.g. instead of computing weighed values for each new set of data, we could compute weighing factors once and apply them repeatedly. Same thoughts for the bit reversal at the top of the FFT algorithm.The proposed example do not use pre-processing of data, to the benefit of the number of samples, and to the clarity of the code.Firstly, some constants shall be declared in the header (.h) file
01// Custom constants
02#define FORWARD 0x01
03#define REVERSE 0x00
04// Windowing type
05#define WIN_TYP_RECTANGLE 0x00// rectangle (Box car)
06#define WIN_TYP_HAMMING 0x01// hamming
07#define WIN_TYP_HANN 0x02// hann
08#define WIN_TYP_TRIANGLE 0x03// triangle (Bartlett)
09#define WIN_TYP_BLACKMAN 0x04// blackmann
10#define WIN_TYP_FLT_TOP 0x05// flat top
11#define WIN_TYP_WELCH 0x06// welch
Then data shall be stored in one of the two fixed size vectors
1const uint8_t samples = 64;
2double vReal[samples];
3double vImag[samples];
And this is the weighing routine
01void PlainFFT::windowing(double *vData, uint8_t samples, uint8_t windowType, uint8_t dir) {
02// The weighing function is symetric; half the weighs are recorded
03double samplesMinusOne = (double(samples) - 1.0);
04for (uint8_t i = 0; i < (samples >> 1); i++) {
05double indexMinusOne = double(i);
06double ratio = (indexMinusOne / samplesMinusOne);
07double weighingFactor = 1.0;
08// compute and record weighting factor
09switch (windowType) {
10case WIN_TYP_RECTANGLE: // rectangle (box car)
11weighingFactor = 1.0;
12break;
13case WIN_TYP_HAMMING: // hamming
14weighingFactor = 0.54 - (0.46 * cos(2.0 * pi * ratio));
15break;
16case WIN_TYP_HANN: // hann
17weighingFactor = 0.54 * (1.0 - cos(2.0 * pi * ratio));
18break;
19case WIN_TYP_TRIANGLE: // triangle (Bartlett)
20weighingFactor = 1.0 - ((2.0 * abs(indexMinusOne - (samplesMinusOne / 2.0))) / samplesMinusOne);
21break;
22case WIN_TYP_BLACKMAN: // blackmann
23weighingFactor = 0.42323 - (0.49755 * (cos(2.0 * pi * ratio))) + (0.07922 * (cos(4.0 * pi * ratio)));
24break;
25case WIN_TYP_FLT_TOP: // flat top
26weighingFactor = 0.2810639 - (0.5208972 * cos(2.0 * pi * ratio)) + (0.1980399 * cos(4.0 * pi * ratio));
27break;
28case WIN_TYP_WELCH: // welch
29weighingFactor = 1.0 - sq((indexMinusOne - samplesMinusOne / 2.0) / (samplesMinusOne / 2.0));
30break;
31}
32if (dir == FORWARD) {
33vData[i] *= weighingFactor;
34vData[samples - (i + 1)] *= weighingFactor;
35}
36else {
37vData[i] /= weighingFactor;
38vData[samples - (i + 1)] /= weighingFactor;
39}
40}
41}
Notes:There is a little trick here. As the weighing function is symetrical, why bother computing them all? Half of them are computed and applied symetrically to the vector of data
The dir parameter stands for direction: remember, FFT is reversible, so that we can apply it in FORWARD or REVERSE mode
FFT stands for fast Fourier Transform. The DFT (Direct Fourier Transform) applies to vectors containing any number of signal samples. But it is sssssssssllllllllllllllllllllllloooooooooooooowwwwwwww due to the repeated number of operation. Computing a DFT of n points takes n^2 arithmetical operations, while an FFT can compute the same result in only n log2(n) operations. The implemented algorithm is the CooleyTukey algorithm which the far most popular. The only limitation is that the number of samples in the signal must be a power of two. However, if the number of samples is less than that, it is possible to replace missing data with 0 values without affecting the final result: this is the zero padding.01void PlainFFT::compute(double *vR, double *vI, uint8_t samples, uint8_t dir) {
02// This computes an in-place complex-to-complex FFT
03// dir = 1 gives forward transform, dir = 0 gives reverse transform
04// Reverse bits
05uint8_t j = 0;
06for (uint8_t i = 0; i < (samples - 1); i++) {
07if (i < j) {
08swap(&vR[i], &vR[j]);
09swap(&vI[i], &vI[j]);
10}
11uint8_t k = (samples >> 1);
12while (k >= 1;
15}
16j += k;
17}
18// Compute the FFT
19double c1 = -1.0;
20double c2 = 0.0;
21uint8_t l2 = 1;
22for (uint8_t l = 0; l < exponent(samples); l++) {
23uint8_t l1 = l2;
24l2