Fast Fourier Transform (FFT) (Part 8)

Part 1234567891011

I received some questions related to the use of the FFT library. This example illustrate how to interface the FFT function to an acquisition engine, such as the optimized one from the PlainADC library.

/*

	Example of use of the ADC and FFT libraries
	Copyright (C) 2010 Didier Longueville

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/* Printing function options */
#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02

#include 
#include 
PlainADC ADC = PlainADC(); /* Create ADC object */
PlainFFT FFT = PlainFFT(); /* Create FFT object */

/* User defined variables */
const uint16_t samples = 128;
uint16_t frequency = 20000;
uint8_t channel = 0;
/* Data vectors */
uint8_t vData[samples]; 
double vReal[samples]; 
double vImag[samples];

void setup(){  
	/* Initialize serial comm port */
	Serial.begin(115200); // 
	/* Set acquisition parameters */
	ADC.setAcquisitionParameters(channel, samples, frequency);
}

void loop() {
  /* Acquire data and store them in a vector of bytes */
	ADC.acquireData(vData);
	/* Convert 8 bits unsigned data in 32 bits floats */
	for (uint16_t i = 0; i < samples; i++) {
		vReal[i] = double(vData[i]);
	}
	/* Weigh data */
	FFT.windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD);	
	/* Compute FFT: vReal and vImag vectors contain the source data 
	and will contain the result data on completion of executing the function*/
	FFT.compute(vReal, vImag, samples, FFT_FORWARD);
	/* Compute magnitudes: the resulting data can be read from the vReal vector */
	FFT.complexToMagnitude(vReal, vImag, samples); 
	/* Upload frequency spectrum */
	printVector(vReal, (samples >> 1), SCL_FREQUENCY);
	/* Pause */
	delay(5000);
}

void printVector(double *vD, uint8_t n, uint8_t scaleType) {
/* Mulitpurpose printing function */
	double timeInterval = (1.0 / frequency);
	for (uint16_t i = 0; i < n; i++) {
		/* Print abscissa value */
		switch (scaleType) {
		case SCL_INDEX:	Serial.print(i, DEC);	break;
		case SCL_TIME: Serial.print((i * timeInterval), 6); break;
		case SCL_FREQUENCY:	Serial.print((i / (timeInterval * (samples-1))), 6); break;
		}
		Serial.print(" ");
		/* Print ordinate value */
		Serial.print(vD[i], 6);
		Serial.println();
	}
	Serial.println();
}

Note: The huge popularity of PlainDSP (merge of PlainFFT and PlainADC libraries) and the numerous requests for help drove me to think about a convenient solution for all designers, artists, students, professors, R&D people, prototypists who need to understand, experiment, create applications featuring advanced Digital Signal Processing on Arduino. The result of intense thoughts, design and writing is the collection PlainDSP kits, starting with the audio kit.

Next post on same subject

19 Comments

  1. Simon says:

    Thanks a lot Didier, for those who will read all of your posts, this is the final answer i expected. By chance i found it on my own yesterday =)

  2. jay says:

    Hello, im new to the arduino and having some trouble. I am putting a sin wave varying between 100-500 Hz into my analog input channel 0 and trying to run a FFT on it. I have uploaded the PlainDac and PlainFFT libraries and need to find the fundamental frequency of my original sin wave. I want to take the highest magnitude of the FFT and assume that is my fundamental. When I run this example code and view the serial monitor, im getting the highest magnitude at zero. Am i doing something wrong? Whats the easiest way to determine the fundamental from this code? Any help greatly appreciated. -Thanks

  3. Didier says:

    There are a few precautions to take prior to running both libs. In their current state, the PlainDAC lib generates unsigned 8 bits data, while the PlainFFT lib requires doubles. I am currently working on both libs in order to ease their integration.
    Would you post your code in order to get a better idea of your problem? You should also check the process step by step, printing intermediate results (vectors actually)

  4. jay says:

    I realized that I wasn’t grounding the arduino. After doing it, everything started to work good. After getting everything running perfectly, I tried to get the maximum value of the values that I was getting from the FFT. For this case, I tried to run the max() function and then serial.print my result but I am getting either errors or results that do not make sense. I guess that my question is: What’s the best way to find the maximum frequency, utilizing your code? Any help will be greatly appreciated.

    Thanks,

  5. Siliconsoul says:

    This is indeed a very interesting library for the nice little Arduino. I preffered using a software based FFT processing algorithm as long as I am not doing any process intensive work on the Arduino. For process intensive applications I was considering using a MSGEQ7, a 7 frequency audio spectrum analyser. It’s a one channel chip so you would need 2 of these to be able to process stereo audio.

    Siliconsoul

  6. admin says:

    Indeed, the MSGEQ7 is a very clever chip. Or let’s put it right: the chip benefits from a clever design! However, its principle of operation (www.mix-sig.com/datasheets/MSGEQ7.pdf) is based on analog filtering and cuts the full bandwith in seven frequencies. Far enough for visual effects! You may save 5$ using FFT and some coding in order to select the 63 Hz, 160 Hz, 400 Hz, 1 kHz, 2.5 kHz, 6.25 kHz and 16 kHz bands.

  7. Siliconsoul says:

    I’m planing on using the Arduino with this chip and a chain of LEDs controlled by LED drivers with PWM using shift registry.
    As an addition I might be adding an LCD display and a rotary encoder to it. My basic idea was to have it work as a standalone machine or have it communicate via DMX.

  8. admin says:

    Good luck, and keep us posted!

  9. jay says:

    i seem to have worked out all my problems i was having, was making some beginner mistakes and such…anyways, thanks a bunch for posting this code, it has helped a ton!

  10. Hunfry says:

    Thanks from Spain.
    Very usefull

  11. Didier says:

    😉

    FFT pages are far ahead amongst the top 10 of Arduinoos. Most of visitors are intrested in coupling audio capture to FFT so that I am working on a fancy combination of PlainADC and PlainFFT.

  12. Hunfry says:

    I want to use PlainFFT library to calculate the harmonics of a intensity wave (50Hz in Spain), the idea is to calculate the Irms of the captured wave.

    I have a question: My wave hasn´t got negative values because i´ve added an offset before the capture (remember that arduino´s ADCs can´t capture negative values). It may be a problem? Sould I take away this offset by sofware before the FFT? (i asked this because, if i understand well, library works wtih symmetries).

    Thanks again

  13. Didier says:

    PlainSCAN has been replaced by PlainADC which allows single shot or scanned measurements. As for PlainSCAN, PlainADC outputs unsigned 16 bits data because in order to save memory. On the other hand, PlainFFT inputs doubles in order to perform any type of transform. So that, in between, you need to convert data in such a way (Pseudo code):

    READ DATA IN SCAN VECTOR
    CONVERT TO ANY UNIT
    SUBBTRACT OFFSET
    WRITE IN FFT VECTOR

    You may unfortunately and quickly face the question of memory size and have to tweat the code. For advanced performances, I have been forced to use an external memory.

  14. pablo says:

    Hello,
    thanks for your great work. I have a little problem with the code you posted.
    The problem is that, when I verify the code, the error “PlainADC has no member named ‘setAcquisitionParameters’” appears. I read into the library PlainADC to search AcquisitionParameters and AcquireData but I don’ t find it.
    Can you help me to solve the problem?
    Thank you very much.

    Pablo

    • Didier says:

      Hi Pablo,

      Please place a request for the latest versions which are (should be… ;-)) fully compatible! Sorry for the trouble that you faced.
      Again, one reason why I do not place the code for download is that I care a lot for compatibility of versions (I developed something like 30 libraries among which a dozen are available on request)

      • bslater216 says:

        Didier,

        I would first like to say that i greatly appreciate your efforts in providing us with this know how. I am an electrical engineering student and would like to download your libraries for use in a digital guitar tuner that I am making for a final project.

        If you can get back to me that would be awesome !

  15. pablo says:

    Hello,
    I would like to know the meaning of this scale type: FFT_SCL_TYP_MAGNITUDE,FFT_SCL_TYP_AMPLITUDE, FFT_SCL_TYP_RMS and if the results are in decibel or other units of measurement. Thank you very much.

  16. Didier says:

    If you are looking for dB scale, you must compute individual un-normalized dB values:
    result = 10 * (Math.Log((ImagValue * ImagValue) + (RealValue * RealValue )) / Math.Log(10.0))
    record the maximum value from each result, and normalize each value on completion of the previous stage doing something like
    For each value result -= MaxValue

    You may check this NI publication http://www.ni.com/white-paper/4278/en

Leave a Reply

You must be logged in to post a comment.