Elm-chan's audio spectrum FFT program returning unexpected values

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi freaks

First of all,belated Happy new year to all of you guys!!

Recently I was working on a audio spectrum analyzer using MSGEQ7 but the chinese crap IC seems to be faulty.So as an alternative,i choose elm-chans FFT for spectrum analysis.I am using 9.6ksps ADC speed and 64 samples FFT(as the hardware is already designed using 7 nos of LED arrays so 64pts is more than enough for me!!).

As per the chan's library descriptions inside the 'ffft.s',the program should return the valus ranging between 64 to 1024.But in my case,The values starting from Bin2 to Bin31 is varying upto 3000.I can understand the Bin0 is a DC compnent and the value is showing randomly(around 30k everytime).But Bin1 is also showing around 15k.

As per my understanding,64 samples FFT should represent 150Hz on every spectrum output ranging from Bin0 to Bin31 in which Bin0 should be noise.My mega328p is running at 16Mhz

My question is

1)why the output values (Bin2 to Bin31) are not in the range as per description(64 to 1024)?

2)Why Bin1 is also showing random values around 15k ?(In my understanding,it should represent 150Hz frequency values).

 My main code is as follows and also i am attaching pics with ADC input connected to nothing and 900Hz being applied to the circuit.I am also attaching chan's 'ffft.h' and as i am unable to attach .s extension files here,i am changing the ffft.s to 'ffft.txt'

 

/*------------------------------------------------*/
/* FFTEST : A test program for FFT module         */

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include "uart.h"        /* Defs for using Software UART module (Debugging via AVRSP-COM) */
#include "ffft.h"        /* Defs for using Fixed-point FFT module */

extern volatile uint8_t uart_data;

int16_t capture[FFT_N];            /* Wave captureing buffer */
complex_t bfly_buff[FFT_N];        /* FFT buffer */
uint16_t spektrum[FFT_N/2];        /* Spectrum output buffer */

 

/*------------------------------------------------*/
/* Capture waveform                               */

void capture_wave (int16_t *buffer, uint16_t count)
{
    ADMUX = _BV(REFS0)|_BV(ADLAR);    // channel

    do {
        ADCSRA = _BV(ADEN)|_BV(ADSC)|_BV(ADATE)|_BV(ADIF)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0);
        while(bit_is_clear(ADCSRA, ADIF));
        *buffer++ = ADC - 32768;
    } while(--count);

    ADCSRA = 0;
}

 

int main (void)
{
    uint16_t m, n, s;    
    char buf1[10];
    char buf2[10];
    uart_init();
    sei();
    DDRD = 0b00000010;
    PORTD = 0b11111101;
    TCCR1B = 3;    /* clk/64 */

    uart_sendstring("Welcome to FFT\r\n");

    for(;;) {
                
        switch (uart_data)
        {    
            case '1' :
                uart_data = 0;        /* Blank line */
                break;
                
                
            case 's' :        /* s: show spectrum */
                capture_wave(capture, FFT_N);
                TCNT1 = 0;    /* performance counter */
                fft_input(capture, bfly_buff);
                TCNT1 = 0;
                fft_execute(bfly_buff);
                TCNT1 = 0;
                fft_output(bfly_buff, spektrum);
                TCNT1;
                for (n = 0; n < FFT_N / 2; n++) {
                    s = spektrum[n];
                    sprintf(buf1,"%u",n);
                    uart_sendstring(buf1);
                    uart_send(':');
                    sprintf(buf2,"%u",s);
                    uart_sendstring(buf2);
                    uart_send(' ');
                    uart_data = 0;
                }
                uart_sendstring("\r\n");
                break;
        }
    }
}

 

ADC_floating_InADC_900Hz_in

Any help is highly appreciated.

Attachment(s): 

Last Edited: Tue. Jan 17, 2017 - 06:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A problem is in the time domain. :)

What "magic" happens, related to TCNT1?

It all starts with a mental vision.

Last Edited: Tue. Jan 17, 2017 - 11:09 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To get 48-4800Hz freq range, you sample at 9600 samps per sec for 20ms or 192 samples. Fill the buffer with a 2400 Hz sin wave to test.

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you mean this line??

 

TCNT1;

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Work with some known functions before trying to understand noisy data. Correct, channel 0 is the DC offset, you can just set it to zero or subtract the mean from each channel beforehand (in case a pending overflow during the transform causes automatic scaling and loss of precision).

What happens when there is a large difference between the first and last channels?  The FFT assumes a periodic signal so that appears to it to be an abrupt change. Search "fft windowing", for example https://en.wikipedia.org/wiki/Wi...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you thought of testing the fft ... with analytical functions (ex : a constant; sin(k*t); a square signal? -results are known)...

 

Have you got a frequencemeter/oscilloscope (if you want to know whether you sample at the "good" frequency in capture_wave, you just have to add two lines setting/clearing a extra , unused, pin).

 

Have you got a signal generator/ a NE 555/ an arduino ("tone" library can be used to generate square  signals very easily

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes,I have already tested the FFT with real audio signals of different frequencies and also getting the peak values of that band in the output buffer as posted in the previous pics(Please check first).

My main concern is the output values are not in the range as mentioned by chan(64-1024).Instead I am getting higher values.

 

For Ex - In the second pic,I have applied 900Hz to the circuit from Laptop sound output port using a online tone generator.Also as expected,I am getting the max value in the 6th position of the array which is 2843.

 

My question is why the value is greater than the max value mention in the library(1024)??Or is this the correct values which i am receiving??
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry guys.....i think there is a silly mistake made by me.....What was written in the ASM file is goes like this below

 

; The number of points FFT_N is defined in "ffft.h" and the value can be
; power of 2 in range of 64 - 1024.

 

I think what is he trying to say that the sample values should lie within this range (64 - 1024).

 

whereas I thought this was for the output value range in the buffer.

 

This was causing the confusion about the output values!!

 

sadsadsadsad

 

Thanks a lot for all your valuable comments!!

 

yesyes

 

Last Edited: Tue. Jan 17, 2017 - 05:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But still there is a question....why the Bin1(fundamental frequency x 1 i.e representation of 150 Hz peak value as per my understanding) always returning a value nearabout 15k or around.I have tried applying different frequencies and getting the same result??

 

Please follow the 2nd pic where i applied a 900Hz to the circuit and printed the output buffer values at Realterm.

 

Last Edited: Tue. Jan 17, 2017 - 06:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excuse me, and congratulations for solving your problem.

 

Thanks a lot for letting me know Chan's fft library works...

 

People looking for solutions to their problem (mine will be/would have been , within 2 monthe  "where can I find a fft library") can look for similar issues, and read them if they have been  solved -make reading less frustrating- ... I bet you can declare your thread as solved...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are sampling at 9600 samples per sec. Your top freq is 4800, and you need to take 128 samples in the buffer, The first line is 0 hz, then 4800/128=37hz, then 74hz, 97hz etc.

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok guys!!I have found a problem in the original program.The main capture program was written like this below

 

void capture_wave (int16_t *buffer, uint16_t count)
{
    ADMUX = _BV(REFS0)|_BV(ADLAR);    // channel

    do {
        ADCSRA = _BV(ADEN)|_BV(ADSC)|_BV(ADATE)|_BV(ADIF)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0);
        while(bit_is_clear(ADCSRA, ADIF));
        *buffer++ = ADC - 32768;
    } while(--count);

    ADCSRA = 0;
}

 

ADLAR bit was set so the data must be left adjusted.So in order to read the ADC value,one must read the ADCH register only to get a 8 bit data.But the author is reading the whole ADC register after ADLAR bit is set.

 

Now I rewrite the program using ADC interrupt like this below....

 

 

volatile  uint8_t  position = 0;
extern volatile uint8_t uart_data;

int16_t capture[FFT_N];            /* Wave captureing buffer */
complex_t bfly_buff[FFT_N];        /* FFT buffer */
uint16_t spektrum[FFT_N/2];        /* Spectrum output buffer */

void adc_init(void)
{
    ADMUX |=(1<<REFS0);//Internally connected to Vcc with external cap on the pin
    ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
    
}

ISR(ADC_vect)
{
    if (position >= FFT_N)
    return;
    
    capture[position] = ADC;
    position++;
}

int main(void)
{
    
        char buf1[10];
        uint16_t temp;
             
        uart_init();
        adc_init();
        sei();
        
        
        while(1)
        {
            if(uart_data == 's')
            {
                    fft_input(capture, bfly_buff);
                    fft_execute(bfly_buff);
                    fft_output(bfly_buff, spektrum);
                    
                    for (uint8_t i=0;i<FFT_N/2;i++)
                    {
                        temp = spektrum[i];
                        sprintf(buf1,"%u",i);
                        uart_sendstring(buf1);
                        uart_send(':');
                        sprintf(buf1,"%u",temp);
                        uart_sendstring(buf1);
                        uart_send(' ');
                     }
                    uart_sendstring("\r\n");
                    position = 0;
                    uart_data = 0;
                }
    }
}

 

Now I am attaching two screenshots applying 900Hz signal to the ADC and in the other nothing is applied and at last,I am getting 0s.

 

But still now i am confused about the output value range.....Please reply!!

 

900Hz is applied to the ADC

 

 

 

And No signal is applied to the ADC

Last Edited: Thu. Jan 19, 2017 - 10:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You really should :

a) test with a known function . what happens if the function i not periodic? if the period doas not match (i.e is not an integer multiple) of the sampling period (ex : sin(t*100) , t=1,2, ...)

 

b) remove the mean from the signal, before applying the fft(you are interested in DC signals, are not you; average is not interesting)

 

ex

int mean = signal[0]; // signal is what you name capture...

for (i =1 ; i< sizeof(signal); i++) mean += signal[i];

mean=mean/sizeof(signal);

for (i=0; i<sizeof(signal); i++) signal[i]=signal[i] - mean;

 

c) display the incoming values (if sizeof(signal == 256, it will be 16 lines of 16*(4+1 : comma) chars; teraterm/real term can live with it.

This would help to eliminate cases where the signal is not connected to the right pin of the adc (I know I am accustumed to this unpleasant habit) when trying with a real signal; if teraterm/real term can deal with cut and paste, you can check (R, octave/matlab and I bet many other free, system agnostic softwares can compute a fft) withe other ffts

and display your signal (excel, gnuplot and many other softs can).

 

d) if necessary (more beautiful, due to smoothing spectra and coping with non periodic signals), you can multiply (after removing the mean, to avoid overflows) by a given window :  dak664 gave a very nice link to a wide range of windows and deserves thanks; I saw in GNUlinux Magazine implementations of Hamming  and Hann windows for embedded -with fixed point arithmetic- : their coefficients were computed by a PC, and stored into an included file ("hann.h", resp "hamming.h", say) to be put into a stm32....

 

Edited :

Added "really"

Notice that windowing functions, if needed (real values, between 0.0 and 1.0) should be converted int integers by your PC (bash+bc, awk, ocltave, R, python, C, fortran can multiply and know trigo; excel too, maybe) by multiplyin by a constant, large enough to keep precision, small enough not to induce overflows.

Last Edited: Thu. Jan 19, 2017 - 12:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"But still now i am confused about the output value range."

 

The output is as expected. The peak value of 82 at index 0 is the DC offset, the peak value of 69 at index 6 is 900 Hz (= 6*(4800/32)).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
Ok,first of all,i want to say that now I am using an op amp as inverting amplifier with a dc offset of 2.5v at the input of the ADC.
So,after getting the adc reading,I am subtracting 512 from the current value and the values are getting inputted to the input buffer.
As i can see,that the various bin values of the output spektrum are ranging below 50(Max value received at full volume of my PC output).
Why the bin values for the mixed music is so much less???

 
Last Edited: Wed. Jan 25, 2017 - 08:29 AM