Fast Fourier Transform (FFT) (Part 10)

Part 1234567891011

Now that we know how to get nice, clean frequency spectra comes the time for extracting valuable information. For this pupose, I added a couple of DSP functions to the (now very popular) PlainFFT library.

As we will deal with peaks, I created a dedicated variable which will be update by these functions:

typedef struct strPeakProperties {
	double position;
	double height;
	uint16_t bin;
};

The first function searches for the strongest frequency peak within a frequency range. The principle of operation is pretty straightforward but quite effective. Firstly, the function converts the lower and upper  frequency range into a lower and an upper bin. Then it prevents searching out the data vector bounds. For each data point within the sought range, the function looks for data points which are surrounded by lowest signals. Once a peak is identified, its height is compared to the previously highest recorded signal.

Finally, the function updates the peak variable. Note that it is the pointer to structure which contains the result which passed to the function as an argument.

Here is a copy of the MajorPeak function:

void PlainFFT::MajorPeak(double *vData, uint16_t samples, double samplingFrequency, double loFrequency, double upFrequency, struct strPeakProperties *result) 
/* 
Find the major peak within a frequency spectrum 
A peak is determined by three consecutive points where the central one is above the others
The major peak is the highest peak within a range
*/
{
	int16_t loBin = ((loFrequency * samples) / samplingFrequency);
	int16_t upBin = ((upFrequency * samples) / samplingFrequency);
	/* Check parameters */
	if (loBin < 1) {
		loBin = 1;
	}
	if (upBin > ((samples >> 1) - 1)) {
		upBin = ((samples >> 1) - 1);
	}	
	/* Compute results */
	double yMax = 0;
	uint16_t binOfYMax = 0;
	for (uint16_t i = loBin; i < upBin; i++) {
		double y_m1 = *((vData + i) - 1); /* y - 1 value */
		double y = *(vData + i); /* actual y value */
		double y_p1 = *((vData + i) + 1); /* y + 1 value */
		/* Is the actual point above its right and left neighbors? */
		if ((y_m1 < y) && (y > y_p1)) {
			/* Is the actual point higher than any other previuously analysed point? */
			if (y > yMax) {
				/* Record values */
				yMax = y;
				binOfYMax = i;
			}
		}
	}
	/* Update results */
	result->bin = binOfYMax;
	result->position = ((binOfYMax * samplingFrequency) / samples); 
	result->height = yMax;
};

In many cases, we will look for the strongest peak within the whole frequency spectra, thus the overloaded function:

void PlainFFT::MajorPeak(double *vData, uint16_t samples, double samplingFrequency, struct strPeakProperties *result) 
{
	MajorPeak(vData, samples, samplingFrequency, 0.0, (samplingFrequency / 2.0), result);
};


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

 

Leave a Reply

You must be logged in to post a comment.