FHT/FFT frequency bins not low enough

I want to process an EEG signal with my Arduino, so I've been trying to use the FHT/FFT libraries. I need the frequency bins to be quite low (0 - 50Hz) so I can determine the strength of the ~10Hz component. The lowest possible frequency range I'm able to get is 0 - 4800Hz which gives a bin resolution of 37.5Hz. This is way too wide to isolate the 10Hz component.

Is there a way to perform a Fourier analysis at low frequencies (0 - 50Hz) using an Arduino?

FHT: http://wiki.openmusiclabs.com/wiki/ArduinoFHT FFT: http://wiki.openmusiclabs.com/wiki/ArduinoFFT

Post your current code, using code tags, as described in "How to use this forum".

The lowest possible frequency range I'm able to get is 0 - 4800Hz

There is no lower limit to the input frequency range.

Bin size = (sample rate/2)/(data buffer length/2)

To reduce the bin size use a larger data buffer, or sample at a lower rate (at least twice the maximum input frequency, limited by the low pass filter). Use a high order low pass input filter to reduce or eliminate input signal amplitude with frequencies higher than sample rate/2.

For input data with 50 Hz maximum input frequency, you need to sample at 100 Hz or faster and make sure that the low pass filter severely reduces signals higher than 50 Hz. With a data array of 256 elements, bin resolution would be 50 Hz/128 = 0.39 Hz.

I’m using the sample script provided by the FHT website linked in the initial post. Here is what I have

#define LOG_OUT 1 // use the log output function
#define FHT_N 256 // set to 256 point fht
 
#include <FHT.h> // include the library
void setup() {
  Serial.begin(115200); // use the serial port
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xEF; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0
  delay(2000);
}
 
void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < FHT_N ; i++) { // save 256 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready (interrupt flag)
      ADCSRA = 0xFF; // restart adc
      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
      fht_input[i] = k; // put real data into bins
    }
    fht_window(); // window the data for better frequency response
    fht_reorder(); // reorder the data before doing the fht
    fht_run(); // process the data in the fht
    fht_mag_log(); // take the output of the fht
    sei();
    Serial.write(255); // send a start byte
    Serial.write(fht_log_out, FHT_N/2); // send out the data
  }
}

The i^th frequency bin is given by: f(i) = i * (sample_rate)/FHT_N.

The ADCSRA registers are used to set the sample rate and the lowest possible value is 9.6kHz.

The variable FHT_N can only be set to a maximum of 256.

Seeing how I can’t lower the sampling rate any further, what code changes would I need to make to increase the buffer size?

You can't change the buffer size without rewriting the OpenMusicLabs FFT library. However, there are other libraries, such as ArduinoFFT, that do not have that limitation.

The ADC sample rate is not limited in any way, except that accuracy is reduced at very high sample rates.

Without modifying any ADC registers, you could, for example, average 96 samples taken at the default 9600 samples/second, for an effective sample rate of 100 Hz. You can also trigger the ADC with a separate timer, or use it in single conversion mode, to sample at ANY desired rate (up to about 200 kHz).

I tried averaging every 96 samples and it worked. Was able to get a 100Hz sample rate. It's hella slow though.

I'll look into using a timer to trigger the ADC instead. Thanks for your help.

It's hella slow though

What did you expect? Wasn't that the entire point?

timer to trigger the ADC

See https://www.gammon.com.au/adc and scroll down to "automatic mode" for an example (50 kHz, but can be changed, of course).