Go Down

Topic: Please help with arduino fft library (Read 18620 times) previous topic - next topic

hongp


The wikipedia article shows note frequencies with six significant digits.  In the low notes that's a resolution of 0.0001 Hz.  To get that resolution at 9,259 samples per second you would need an FFT size of 92.5 million samples (185 megabytes). That is not going to happen on an Arduino in any reasonable amount of time.

Perhaps you can use zero crossings to measure the frequency.  Have you looked at any frequency counter sketches?

http://interface.khm.de/index.php/lab/experiments/arduino-frequency-counter-library/


Thank you for the suggestion. I've tried a code made by Amanda from instructable.com. It works really well up until the frequency gets up to 1khz. I will definitely look into the one you suggested here.

I also want to know how FFT works. I've read many article about how it works, but it seems like I didn't really understand everything.
Can you explain how you get 92.5 million samples is necessary to have six digits of resolution at 9259hz sampling rate? and how you figure out the size if 185 mB?

Can you also please teach me how to set the simulated frequency? I think that's what I was missing when I tested out the FFT library. I thought I was filling up the array with sine wave data generated by  k = 127*sin(2*3.14 * 100 * t ), where t was incremented 256 times, which was the same size of the FFT real bin, imaginary bins were filled with zeros. I thought with this method, the only bin that will contain numbers greater than zero is bin 100, but it is not the case. I have a lot numbers in other bins.

Thank you

Magician

Quote
I I think arduino FFT library only provide max # of bin of 256 to filled in the real data. It doesn't go up to 512. I was definitely confused by ADC resolution and FFT resolution. Thank you for clear that out. Amanda from instructable.com was able to turn down ADC resolution to 8 bit in order to speed up the sampling rate, but you're saying to lower it down to get better resolution, and I can clearly see your point. If I were to lower it down, how would I be able to cover all the frequency range? from 27hz to 4186hz.
No, you wouldn't. My project covers 4 octaves only
I read your blog about pushing arduino to the limit. That was a very impressive project you made! A few questions after reading your blog:
1. How could you have 1024 bins for the FFT? The one comes with arduino IDE is limited to 512 bins as the max. Half of the bins are filled with the data taken from analog input, the other half is set to zero manually. Do you use different library?
Code is linked, not a library, written by myself.
2.The shadow masking technique you used, is it setting a range for the detected frequencies to fall in, and then light up corresponding LED even though the resolution limited to 7.8hz?
Requirements to distinguish two notes and for tunning are different
3. If you already pushed arduino mega to the limit in that project, does it mean I have to either get a Due board or call off my project, or do something similar to yours just to understand how micro-controllers works? What if I use multiple mega boards? having each of them detecting different frequency ranges?
You may try Due, I used UNO btw, not Mega.
4.From arduino FFT documentation, it suggests to set imaginary part to zero if the samples are taken from analog input. Is that what you did?
Yes

Using fft may be appropriate if there are a few notes play simultaneously, which is not the case for you project. Freq. measuremets library linked in reply #18 and #28 is better approach for tunning.

johnwasser


Can you explain how you get 92.5 million samples is necessary to have six digits of resolution at 9259hz sampling rate? and how you figure out the size if 185 mB?


It's the formula I showed earlier.  The resolution of the FFT is sampleRate/sampleCount.  To get resolution of 0.0001 Hz (1/10,000th Hz) you need sampleCount to be 10,000 times the sampleRate.

If each sample is a 2-byte integer you need 2*sampleCount bytes to store them.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

hongp


It's the formula I showed earlier.  The resolution of the FFT is sampleRate/sampleCount.  To get resolution of 0.0001 Hz (1/10,000th Hz) you need sampleCount to be 10,000 times the sampleRate.

If each sample is a 2-byte integer you need 2*sampleCount bytes to store them.

Got it! 2*10000*9259/(2^20) = 176 mB. 
Thank you so much!

johnwasser

To have "bin 100" represent 100 Hz you have to use a sample rate equal to the FFT size:

512 sample FFT at 512 samples per second.

I think the code would be something like this:
Code: [Select]

    for (int i = 0 ; i < 512 ; i += 2) {
      float time = i/512.0;       // Time in seconds from start of sample
      float cycle = time * 100;  // Cycles since start of samples
      fft_input[i] = 127 * sin(2 * Pi * cycle);; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

hongp

Yo, I got a few messages asking about the same problem I had with fft library.
If you still have problems getting the correct output from that library, look at my code.
This is the working code that I demo to my professor.
Code: [Select]

#define LOG_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft
#include <FFT.h> // include the library

//UNO clock Frequency = 16 MHz
//It takes 13 clock cycles to finish analog reading
int refFrequency = 0;
int currentFrequency = 0;
int previousFrequency = 0;
int errorFrequency = 0;
int refState = 0;
int currentState = 0;
int tunningState = 0;
boolean inRange = false;
float P = 0;
float I = 0.0;
float D = 0.0;
float drive = 0;
int pwmDrive = 0;
const int refInput = 8;  //reference pin
const int currentInput = 9;  //final pin
const int ForMotorControl = 10;  //pump control pin PWM
const int BacMotorControl = 11;  //pump control pin PWM
const int tunning = 12;  //tuning control pin
int bin = 0;
int frequency = 0;
int resolution = 9615 / 256;
int maxLogOut = 0;


void setup() {
  Serial.begin(9600); // use the serial port
  pinMode(refInput, INPUT);
  pinMode(currentInput, INPUT);
  pinMode(tunning, INPUT);
  pinMode(ForMotorControl, OUTPUT);
  pinMode(BacMotorControl, OUTPUT);
  ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); //set adc prescaler=128
  ADMUX = 0x40; // use adc0
  ADCSRA |= (1<<ADATE);  //enable auto trigger
  ADCSRA |= (1<<ADEN);  //enable ADC
  ADCSRA |= (1<<ADSC);  //start ADC measurements
}

void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    //sampling
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA |=(1<<ADSC); //start ADC measurements
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }//end of for
   
    //FFT
    fft_window(); // window the data for better frequency response
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    fft_mag_log(); // take the output of the fft
    sei();
    maxLogOut = 0;
    for(int i = 1; i < 128; i++){
      if(fft_log_out[i] > maxLogOut && fft_log_out[i] > 150){
          maxLogOut = fft_log_out[i];
          bin = i;
      }//end of if
    }//end of for
    frequency = bin * resolution;
    if(frequency > 1500 && frequency < 2700){
      inRange = true;
    } else {
      inRange = false;
    }
   
    //if digital pin 8 is high, set the reference frequency
    //if digital pin 9 is high, set the current frequency
    //if digital pin 12 is high, set tunning state = high
    refState = digitalRead(refInput);
    currentState = digitalRead(currentInput);
    tunningState = digitalRead(tunning);
    if(refState == HIGH && inRange == true){
      refFrequency = frequency;
    } else if(currentState == HIGH && inRange == true){
      currentFrequency = frequency;
    } //end of else if
   
    //error tracking
    //highest frequency = (2405)2479 hz  lowest frequency = (1517) 1628 hz
    errorFrequency = refFrequency - currentFrequency;
    P = errorFrequency;  //proportional term
   // I = Integral * 0;  //intergral term
   // D = (previousFrequency - currentFrequency) * 0;  //derivative term
   // previousFrequency = currentFrequency; 
    drive = P * (255.0/888.0); //scaleFactor = 255 * 888;

    //start tuning if tunning state = high
    if(tunningState == HIGH){
    //motor control
      if(errorFrequency > 30){      //pump liquid out
        //digitalWrite();  //pump in one direction
        digitalWrite(BacMotorControl, HIGH);
        analogWrite(ForMotorControl, 0);
      } else if (errorFrequency < -30){  //pump liquid in
        //digitalWrite();  //pump in another direction
        digitalWrite(BacMotorControl, LOW);
        pwmDrive = (int)abs(drive);
        if(pwmDrive < 80){ pwmDrive = pwmDrive + 80;}
        analogWrite(ForMotorControl, pwmDrive);
      } else {  //stop pumping
        analogWrite(ForMotorControl, 0);  //stop the motor
        digitalWrite(BacMotorControl, LOW);
      }
    } else {
      analogWrite(ForMotorControl, 0);  //stop the motor
      digitalWrite(BacMotorControl, LOW);
    }
   
    //debug
    Serial.print("frequency: ");
    Serial.print(frequency);
    Serial.print(" | ");
    Serial.print("reference frequency: ");
    Serial.print(refFrequency);
    Serial.print(" | ");
    Serial.print("Current frequency: ");
    Serial.print(currentFrequency);
    Serial.print(" | ");
    Serial.print("error frequency: ");
    Serial.println(errorFrequency);
  }//end of while
}//end of loop

rexhex


funfrancis

Can I do the same to fft library in arduino? I tried it. but a lot of the outputs aren't zero. Not sure if it's correct. Please point out any possible error.
Here is the code I used to test fft library:
Code: [Select]
#define LOG_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft
#include <FFT.h> // include the library

void setup() {
  Serial.begin(9600); // use the serial port
}

void loop() {
  // put your main code here, to run repeatedly:
   int k = 0;
   int t = 0;
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
      k = 127*sin(2*3.14 * 100 * t );
      t++;
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    fft_mag_log(); // take the output of the fft
    for(int i = 0; i < 128; i++){
          Serial.println(fft_log_out[i]);
    }
    delay(1000);
}




I used your codes based on fft library but where is it mentioned that the input is from A0.

I tried them with my hydrophone, pre-amp setup and I get the same answer:
Code: [Select]

32 43 39 43 35 39 39 39 30 30 30 35 59 82 58 35 19 19 24 27 27 27 30 19 27 24 30 27 24 24 27 27
32 43 39 43 35 39 39 39 30 30 30 35 59 82 58 35 19 19 24 27 27 27 30 19 27 24 30 27 24 24 27 27
32 43 39 43 35 39 39 39 30 30 30 35 59 82 58 35 19 19 24 27 27 27 30 19 27 24 30 27 24 24 27 27

where am I going wrong



rexhex

#38
Mar 02, 2016, 07:18 am Last Edit: Mar 02, 2016, 07:19 am by rexhex
in setup ( ADMUX = 0x40; // use adc0 ) this is using assembly language, a very basic level code. I took a class on it and its not easy or fun.
https://en.wikipedia.org/wiki/Assembly_language

https://en.wikipedia.org/wiki/ADC

whats a hydrophone?

funfrancis

in setup ( ADMUX = 0x40; // use adc0 ) this is using assembly language, a very basic level code. I took a class on it and its not easy or fun.
https://en.wikipedia.org/wiki/Assembly_language

https://en.wikipedia.org/wiki/ADC

whats a hydrophone?
Hydrophone is an underwater mic

AWOL

in setup ( ADMUX = 0x40; // use adc0 ) this is using assembly language, a very basic level code. I took a class on it and its not easy or fun.
https://en.wikipedia.org/wiki/Assembly_language

https://en.wikipedia.org/wiki/ADC

whats a hydrophone?
https://en.wikipedia.org/wiki/Hydrophone
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

rexhex


funfrancis

#42
Mar 05, 2016, 12:17 am Last Edit: Mar 05, 2016, 12:22 am by funfrancis
Hi

I managed to get a sine curve using the codes.

Code: [Select]
void setup() {
 Serial.begin(9600);
}

void loop() {
  int a[250];  //250 refers sample number
  for (int i=0; i<250; i++) {
  a[i] = analogRead(0);
  int k = 250*sin(2*3.14*0.006*i);
  Serial.println(k);
  }
Serial.println();
}


Now, if the sine curve varies with output from A0, I will obtain a FFT curve.
How do i accomplish this ?

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy