1
votes

I am attempting to implement a Fast Fourier Transform with associated complex magnitude function on the STM32F411RE Nucleo developer board. My goal is to separate a combined signal with multiple sinusoidal elements into their separate frequency components, with correct amplitude.

My issues is that I cannot correctly line up the frequency bins outcomes from the Complex magnitude function with the frequencies. I am also starting to question the validity of these outcomes as such.

I have tried to use a number of different implementations posted by people for the FFT algorithm with the magnitude fix, most notably the examples listed on StackoverFlow by SleuthEye and Blog by LB9MG.

AFAIK I have a similar approach, but somehow their approaches yield the desired results and mine do not. Below is my code that I have altered to work via the implementation that SleuthEye has created.

int main(void)
{
    fftLen = 32;    // can be 32, 64, 128, 256, 512, 1024, 2048, 4096
    half_fftLen = fftLen/2;
    volatile float32_t sampleFreq = 50 * fftLen;    // Fs = binsize * fft length, desired binsize = 50 hz

    arm_rfft_fast_instance_f32 inst;
    arm_status status;
    status = arm_rfft_fast_init_f32(&inst, fftLen);

    float32_t signalCombined[fftLen] = {0};
    float32_t fftCombined[fftLen] = {0};
    float32_t fftMagnitude[fftLen] = {0};
    volatile float32_t fftFreq[fftLen] = {0};

    float32_t maxAmp;
    uint32_t maxAmpInd;

    while (1)
    {
        for (int i = 0; i< fftLen; i++)
        {
            signalCombined[i] = 40 * arm_sin_f32(450 * i); // 450 frequency at 40 amplitude
        }

        arm_rfft_fast_f32(&inst, signalCombined, fftCombined, 0); // perhaps switch to complex transform to allow for negative frequencies?
        arm_cmplx_mag_f32(fftCombined, fftMagnitude, half_fftLen);
        fftMagnitude[0] = fftCombined[0];
        fftMagnitude[half_fftLen] = fftCombined[1];

        arm_max_f32(fftMagnitude, half_fftLen, &maxAmp, &maxAmpInd); // We need the 3 max values

        for (int k = 0; k < fftLen ; k++)
        {
            fftFreq[k] = ((k*sampleFreq)/fftLen);
        }
}

Shown below are the results that I get out of the code listed above: whilst I do get a magnitude out of the algorithms (at the correct index 12), it does not correspond to the frequency or the amplitude of the input array signalCombined[].

results

Does anyone have an idea of why this is happening? Like so many of my errors it is probably a really trivial and stupid thing, but I cannot figure out for the life of me why this is happening.


EDIT: thanks to SleuthEye's help finding the frequencies is now possible, as the initial approach for generating the sin() signal was done incorrectly.

Some new issues popped up as the FFT only appears to yield the correct frequencies for the 32 samples, despite the bin size scaling accordingly to accommodate the adjusted sample size.

I am also unable to implement the amplitude fixing algorith: as per SleuthEye's Link with the example code 2*(1/N)*abs(X(k))^2 I have made my own implementation 2 * powf(fabs(fftMagnitude[j]), 2) / fftLen as shown in the code below, but this does not yield results that are even close to correct.

while (1)
{

    for (int i = 0; i < fftLen; i++)
    {
        signalCombined[i] =     400 * arm_sin_f32(2 * PI * 450 * i / sampleFreq);       // Sin Alpha, 400 amp at 10 kHz
//                      700 * arm_sin_f32(2 * PI * 33000 * i / sampleFreq) +        // Sin Bravo, 700 amp at 33 kHz
//                          300 * arm_sin_f32(2 * PI * 50000 * i / sampleFreq);             // Sin Charlie, 300 amp at 50 kHz
    }

    arm_rfft_fast_f32(&inst, signalCombined, fftCombined, 0); // calculate the fourier transform of the time domain signal
    arm_cmplx_mag_f32(fftCombined, fftMagnitude, half_fftLen);  // calculate the magnitude of the fourier transform
    fftMagnitude[0] = fftCombined[0];
    fftMagnitude[half_fftLen] = fftCombined[1];

    for (int j = 0; j < sizeof(fftMagnitude); j++)
    {
        fftMagnitude[j] = 2 * powf(fabs(fftMagnitude[j]), 2) / fftLen; // Algorithm to fix the amplitude of each unique frequency
    }

    arm_max_f32(fftMagnitude, half_fftLen, &maxAmp, &maxAmpInd); // We need the 3 max values

    for (int k = 0; k < fftLen ; k++)
    {
        fftFreq[k] = ((k*sampleFreq)/fftLen);
    }
}
1

1 Answers

2
votes

Your tone generation does not take into account the sampling frequency of 1600Hz, so you are effectively generating a tone at a frequency of 450*1600/(2*PI) ~ 114591Hz which gets aliased to ~608Hz. That 608Hz frequency roughly corresponds to a frequency index around 12 when using an FFT size of 32.

The generation of a 450Hz tone at a 1600Hz sampling frequency should be done as follows:

for (int i = 0; i< fftLen; i++)
{
    signalCombined[i] = 40 * arm_sin_f32(2 * PI * 450 * i / sampleFreq);
}

As far as matching the amplitude, keep in kind that there is a scaling factor between the time-domain and frequency-domain of approximately 0.5*fftLen (see this other post of mine).