Fast Fourier Transform (FFT) (Part 11)

Part 12345678910, 11

Here is an other peak related DSP function. The purpose of this one is to get the peak information of a targeted frequency peak. It uses the same early steps as in the MajorPeak function. The filtering process includes a peak interpolation (quadratic type), and the calculated peak apex position is compared to the target value. The function updates the structure (passed by its address) containing information related to best fitting peak.

void PlainFFT::TargetPeak(double *vData, uint16_t samples, double samplingFrequency, double targetPosition, double tolerance, struct strPeakProperties *result) 
/* Finding quadratically interpolated peaks */
{
	int16_t loBin = (((targetPosition - tolerance) * samples) / samplingFrequency);
	int16_t upBin = (((targetPosition + tolerance) * samples) / samplingFrequency);
	/* Check parameters */
	if (loBin < 1) {
		loBin = 1;
	}
	if (upBin > ((samples >> 1) - 1)) {
		upBin = ((samples >> 1) - 1);
	}
	double freqInterval = (samplingFrequency / samples);
	double least_x_err = DBL_MAX;
	double best_x_calc = 0.0;
	double best_y_calc = 0.0;
	uint16_t bestBin = 0;
	for (uint16_t i = loBin; i < upBin; i++) {
		double x = (i * freqInterval); /* actual x value */
		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 */
		if ((y_m1 < y) && (y > y_p1)) {
			double x_diff = (((y_m1 - y_p1) / (y_m1 - (2.0 * y) + y_p1)) / 2.0);
			double x_calc = (x + (x_diff * freqInterval));
			double y_calc = (y - (((y_m1 - y_p1) * x_diff) / 4.0));
			double x_err = fabs(x - targetPosition);
			if (x_err < least_x_err){
				/* Retain best peak properties */
				least_x_err = x_err;
				best_x_calc = x_calc;
				best_y_calc = y_calc;
				bestBin = i;
			}
		}
	}
	/* Returned results*/
	result->bin = bestBin;
	result->position = best_x_calc; /* equ to (*result).position = best_x_calc; */
	result->height = best_y_calc;
}

Other interpolation methods can be used . Their performances depend very much on the peak shapes – which are themselves dependent on the windowing type – and the number of data points describing the peaks. In our particular case, we are often short in data points (restricted number of samples due to limited amount of memory) and we do not want to spend too long at modelling peak shapes thus the choice for the quadratic model.

 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.

Leave a Reply

You must be logged in to post a comment.