Don't bother. I just realized you need the complex input/output for your application.
No, pure real data in this project, I'm not doing reverse IFFT, and adc is always real
Thanks again for posting a code, it has more value for me than a million words. I really appreciate, when conversation is going more specific, down to earth - digits in this particular case.
Running this piece of code up and down, I finally created the test procedure, that Magnifying the small prints in your insurance policy, I was talking about.
Excuse for delay to everyone, who is reading this, was busy doing testing with a code. Here is what I came up to.
Test condition:
//https://www.embedded.com/design/configurable-systems/4024443/The-Goertzel-Algorithm
Goertzel algorithm is initialized to detect Target Frequency = 500.
//FFT is my own, feel free to replace it with whatever you like, FFTW, CMSIS-DSP pack etc.
FFT algorithm doesn't need to be initialized, magnitude is extracted at:
Resolution = sampling freq. /256 = 31.25 Hz
Bin = target /resolution = 500 / 31.25 = 16.
No windowing function is applied in this test, not to obscure the purpose of the testing.
Input:
Single tone sine wave, no phase data, no noise added in this test.
Test tone is swapped from 0 to Nyquist = sampling /2.
Swapped tone is stepping at 31.25 Hz, since there was no windowing function, stepping goes at exact "bin's frequencies" in
order to prevent any side lobes that would be generated otherwise and ruin the objective of this test.
When test tone "hits" a target frequency, detection happened and magnitude is recorded.
Any other frequencies, except target, becomes in this case "interference", "noise" or "adjacent channel power ratio".
Obviously, dsp core (Goertzel or FFT) Must eliminate any interference getting into the target frequency (detection band).
The magnitude of the interference is preset to -40 dB, 20.47 riding over 2047 DC level - 12-bits ADC approximation.
To precisely evaluate this "interference withstanding" or "noise reduction" capability of the dsp-core SINAD is calculated :
sinad = (20.0 * log10(temp /tot_magnt));
where tot_magnt is RMS of all interference getting into detection band,
and variable "temp" is assigned to magnitude at Target frequency.
Results:
bin 2620.176270 tot_m 1.124978 sinad_grt 67.343732
bin 2620.160156 tot_m 0.020573 sinad_fft 102.100667
You see, there is 35 dB (!) difference in the SNR.
Goertzel is miserable failed on the objective.
NB1:
Not sure what is correct name for this test would be. I already used hardware analogy with pre-selector, so "selectivity" comes to mind.
NB2:
I agree, that using "bandwidth" was wrong on my side, indeed BW (defined in terms of sqrt(2)) is practically identical between
single bin (Goertzel) and FFT.
Can't explain any better.
Code:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#define SAMPLING_RATE 8000.0 // 8kHz
#define TARGET_FREQUENCY 500 // 1875//941.0 //941 Hz
#define N 256 // 205 //Block size
//bin 2559.962402 tot_m 1.118038 sinad_grt 67.195540
//bin 2559.995117 tot_m 0.020119 sinad_fft 102.092452
float coeff;
float Q1;
float Q2;
float sine;
float cosine;
double testData[N];
/*
//bin 2559.989502 tot_m 0.182476 sinad_grt 82.940644
//bin 2559.995117 tot_m 0.020119 sinad_fft 102.092452
double coeff;
double Q1;
double Q2;
double sine;
double cosine;
double testData[N];
*/
#define FFT_SIZE N
#define MIRROR (FFT_SIZE / 2)
void ResetGoertzel(void)
{
Q2 = 0;
Q1 = 0;
}
void InitGoertzel(void)
{
int k;
float floatN;
float omega;
floatN = (float) N;
k = (int) (0.5 + ((floatN * TARGET_FREQUENCY) / SAMPLING_RATE));
omega = (2.0 * M_PI * k) / floatN;
sine = sin(omega);
cosine = cos(omega);
coeff = 2.0 * cosine;
printf("For SAMPLING_RATE = %f", SAMPLING_RATE);
printf(" N = %d", N);
printf(" and FREQUENCY = %d,\n", TARGET_FREQUENCY);
printf("k = %d and coeff = %f, sine = %f, cosine = %f\n\n", k, coeff, sine, cosine);
ResetGoertzel();
}
void Processfloat(float sample)
{
float Q0;
Q0 = coeff * Q1 - Q2 + (float) sample;
Q2 = Q1;
Q1 = Q0;
}
void GetRealImag(float *realPart, float *imagPart)
{
*realPart = (Q1 - Q2 * cosine);
*imagPart = (Q2 * sine);
}
float GetMagnitudeSquared(void)
{
float result;
result = Q1 * Q1 + Q2 * Q2 - Q1 * Q2 * coeff;
return result;
}
void Generate(float frequency)
{
int index;
float step;
step = frequency * ((2.0 * M_PI) / SAMPLING_RATE);
for( index = 0; index < N; index++) {
testData[index] = 20.470 * sin(index * step) +2047.0;
}
}
void GenerateAndTest_GRT(float frequency, float *result)
{
int index;
float magnitudeSquared;
float magnitude;
float real;
float imag;
printf("F1=%7.2f, ", frequency);
Generate(frequency);
/*
// 2.Windowing
double base_angle = 2.0 *M_PI / (FFT_SIZE -1);
for( int16_t i = 0; i < FFT_SIZE; i++ ) {
double angle = i *base_angle;
float temp = 0.35875 -(0.48829*cos(angle)) +(0.14128*cos(2*angle)) -(0.01168*cos(3*angle));
// testData[i] *= temp;
testData[i] *= (0.5 - (0.5 * cos( 2 *M_PI *i /(FFT_SIZE -1))));
}
*/
for( index = 0; index < N; index++) {
//Processfloat(testData[index]);
float Q0;
Q0 = coeff * Q1 - Q2 + (float)testData[index];
Q2 = Q1;
Q1 = Q0;
}
GetRealImag(&real, &imag);
// printf("r= %15.5f i= %15.5f", real, imag);
magnitudeSquared = real*real + imag*imag;
// printf("rel mag^2=%16.5f ", magnitudeSquared);
magnitude = sqrt(magnitudeSquared);
// printf("mag=%15.5f\n", magnitude);
printf("mag=%15.5f", magnitude);
ResetGoertzel();
*result = magnitude;
}
// FFT
void rev_bin( float *fr, int16_t fft_n)
{
int m, mr, nn, l;
float tr;
mr = 0;
nn = fft_n - 1;
for (m=1; m<=nn; ++m) {
l = fft_n;
do {
l >>= 1;
} while (mr+l > nn);
mr = (mr & (l-1)) + l;
if (mr <= m)
continue;
tr = fr[m];
fr[m] = fr[mr];
fr[mr] = tr;
}
}
void get_MagnitF(float *fr)
{
for(int i = 1; i < MIRROR; i++) {
float real = fr[i];
float imag = fr[FFT_SIZE -i];
float Magnit = (float) (sqrt((real * real) + (imag * imag)));
fr[i] = Magnit;
}
}
void rfft33(float X[], int Nd)
{
/****************************************************************************
* rfft(float X[],int N) *
* A real-valued, in-place, split-radix FFT program *
* Decimation-in-time, cos/sin in second loop *
* Length is N=2**M (i.e. N must be power of 2--no error checking) *
* *
* Original Fortran code by Sorensen; published in H.V. Sorensen, D.L. Jones,*
* M.T. Heideman, C.S. Burrus (1987) Real-valued fast fourier transform *
* algorithms. IEEE Trans on Acoustics, Speech, & Signal Processing, 35, *
* 849-863. Adapted to C by Bill Simpson, 1995 wsimpson@uwinnipeg.ca *
* --------------------------------------------------------------------------*
* C/C++ language bugs fixing & correction: *
* 1. C-style array start adress [0]; *
* 2. SIN() and COS tweed factors change place; *
* 3. Rev Bin - replaced by new fastest version. *
* made by Anatoly Kuzmenko, 2017, anatolyk69@gmail.com *
****************************************************************************/
int I, I0, I1, I2, I3, I4, I5, I6, I7, I8, IS, ID;
int J, K, M, N2, N4, N8;
float A, A3, CC1, SS1, CC3, SS3, E, R1;
float T1, T2, T3, T4, T5, T6;
M = (int)(log(N) / log(2.0)); /* N=2^M */
/* ----Length two butterflies--------------------------------------------- */
IS = 0;
ID = 4;
do {
for (I0 = IS; I0 < Nd; I0 += ID) {
I1 = I0 + 1;
R1 = X[I0];
X[I0] = R1 + X[I1];
X[I1] = R1 - X[I1];
}
IS = 2 * ID - 2;
ID = 4 * ID;
} while (IS < Nd);
/* ----L shaped butterflies----------------------------------------------- */
N2 = 2;
for (K = 1; K < M; K++) {
N2 = N2 * 2;
N4 = N2 / 4;
N8 = N2 / 8;
E = (float) 6.2831853071719586f / N2;
IS = 0;
ID = N2 * 2;
do {
for ( I = IS; I < Nd; I += ID) {
I1 = I; // + 1;
I2 = I1 + N4;
I3 = I2 + N4;
I4 = I3 + N4;
T1 = X[I4] + X[I3];
X[I4] = X[I4] - X[I3];
X[I3] = X[I1] - T1;
X[I1] = X[I1] + T1;
if (N4 != 1) {
I1 += N8;
I2 += N8;
I3 += N8;
I4 += N8;
T1 = (X[I3] + X[I4]) * .7071067811865475244f;
T2 = (X[I3] - X[I4]) * .7071067811865475244f;
X[I4] = X[I2] - T1;
X[I3] = -X[I2] - T1;
X[I2] = X[I1] - T2;
X[I1] = X[I1] + T2;
}
}
IS = 2 * ID - N2;
ID = 4 * ID;
} while ( IS < Nd );
A = E;
for ( J = 1; J < N8; J++) {
A = (float)J * E;
A3 = 3.0 * A;
CC1 = cos( A);
SS1 = sin( A);
CC3 = cos(A3);
SS3 = sin(A3);
IS = 0;
ID = 2 * N2;
do {
for ( I = IS; I < Nd; I += ID) {
I1 = I + J;
I2 = I1 + N4;
I3 = I2 + N4;
I4 = I3 + N4;
I5 = I + N4 - J;// + 2;
I6 = I5 + N4;
I7 = I6 + N4;
I8 = I7 + N4;
T1 = X[I3] * CC1 + X[I7] * SS1;
T2 = X[I7] * CC1 - X[I3] * SS1;
T3 = X[I4] * CC3 + X[I8] * SS3;
T4 = X[I8] * CC3 - X[I4] * SS3;
T5 = T1 + T3;
T6 = T2 + T4;
T3 = T1 - T3;
T4 = T2 - T4;
T2 = X[I6] + T6;
X[I3] = T6 - X[I6];
X[I8] = T2;
T2 = X[I2] - T3;
X[I7] = -X[I2] - T3;
X[I4] = T2;
T1 = X[I1] + T5;
X[I6] = X[I1] - T5;
X[I1] = T1;
T1 = X[I5] + T4;
X[I5] = X[I5] - T4;
X[I2] = T1;
}
IS = 2 * ID - N2;
ID = 4 * ID;
} while ( IS < Nd );
}
}
}
void GenerateAndTest_FFT(float frequency, float *result)
{
float magnitude;
Generate(frequency);
float fr[FFT_SIZE];
for( int16_t i = 0; i < FFT_SIZE; i++ ) {
fr[i] = (float)testData[i];
}
/*
printf(" X[i] Input Data \n");
for ( int i = 0; i < FFT_SIZE; i++)
{ printf("%8.1f", fr[i]);
if ((i+1)%16 == 0) printf("\n");
}
printf("\n.........................................\n");
*/
/*
// 2.Windowing
double base_angle = 2.0 *M_PI / (FFT_SIZE -1);
for( int16_t i = 0; i < FFT_SIZE; i++ ) {
double angle = i *base_angle;
float temp = 0.35875 -(0.48829*cos(angle)) +(0.14128*cos(2*angle)) -(0.01168*cos(3*angle));
// fr[i] *= temp;
fr[i] *= (0.5 - (0.5 * cos( 2 *M_PI *i /(FFT_SIZE -1))));
}
*/
/*
printf(" X[i] Input Data \n");
for ( int i = 0; i < FFT_SIZE; i++)
{ printf("%8d", fr[i]);
if ((i+1)%16 == 0) printf("\n");
}
printf("\n.........................................\n");
*/
rev_bin( fr, (int16_t) FFT_SIZE);
/*
printf(" X[i] Input Data \n");
for ( int i = 0; i < FFT_SIZE; i++)
{ printf("%8.0f", fr[i]);
if ((i+1)%16 == 0) printf("\n");
}
printf("\n.........................................\n");
*/
rfft33( fr, FFT_SIZE);
/*
printf(" X[i] Output Data \n");
for ( int i = 0; i < FFT_SIZE; i++)
{ printf("%8.0f", fr[i]);
if ((i+1)%16 == 0) printf("\n");
}
printf("\n.........................................\n");
*/
get_MagnitF(fr);
/*
printf(" X[i] Output Data \n");
for ( int i = 0; i < MIRROR; i++)
{ printf("%8.0f", fr[i]);
if ((i+1)%16 == 0) printf("\n");
}
printf("\n.........................................\n");
*/
magnitude = fr[16];
printf(" mag=%15.5f\n", magnitude);
*result = magnitude;
}
int main(void)
{
float freq;
InitGoertzel();
float test;
float snr_grt[MIRROR] = { 0.0};
float snr_fft[MIRROR] = { 0.0};
int index = 0;
float step = SAMPLING_RATE / FFT_SIZE;
// for (freq = TARGET_FREQUENCY - 300; freq <= TARGET_FREQUENCY + 300; freq += 15)
// for( freq = 0.0; freq < SAMPLING_RATE /2.0; freq += 31.25) {
for( freq = 0.0; freq < SAMPLING_RATE /2.0; freq += step) {
GenerateAndTest_GRT(freq, &test);
snr_grt[index] = test;
GenerateAndTest_FFT(freq, &test);
snr_fft[index] = test;
if(index >= MIRROR) break;
index++;
}
/*
printf(" snr_grt \n");
for( int i = 0; i < MIRROR; i++) {
printf("%15.6f", snr_grt[i]);
if ((i+1)%8 == 0) printf("\n");
}
printf("\n.........................................\n");
printf(" snr_fft \n");
for( int i = 0; i < MIRROR; i++) {
printf("%15.6f", snr_fft[i]);
if ((i+1)%8 == 0) printf("\n");
}
printf("\n.........................................\n");
*/
int bin_numbr = 16;
double tot_magnt = 0;
double temp = 0.0;
double sinad = 0.0;
for( int i = 0; i < MIRROR; i++) {
if(i == bin_numbr) continue;
temp = snr_grt[i];
tot_magnt += (temp * temp);
}
tot_magnt = sqrt(tot_magnt);
if (tot_magnt == 0) tot_magnt = 0.000001;
temp = snr_grt[bin_numbr];
sinad = (20.0 * log10(temp /tot_magnt));
printf("\n bin %12.6f", temp);
printf(" tot_m %12.6f", tot_magnt);
printf(" sinad_grt %12.6f", sinad);
tot_magnt = 0;
temp = 0.0;
sinad = 0.0;
for( int i = 0; i < MIRROR; i++) {
if(i == bin_numbr) continue;
temp = snr_fft[i];
tot_magnt += (temp * temp);
}
tot_magnt = sqrt(tot_magnt);
if (tot_magnt == 0) tot_magnt = 0.000001;
temp = snr_fft[bin_numbr];
sinad = (20.0 * log10(temp /tot_magnt));
printf("\n bin %12.6f", temp);
printf(" tot_m %12.6f", tot_magnt);
printf(" sinad_fft %12.6f", sinad);
return 0;
}
Raw data:
For SAMPLING_RATE = 8000.000000 N = 256 and FREQUENCY = 500,
k = 16 and coeff = 1.847759, sine = 0.382683, cosine = 0.923880
F1= 0.00, mag= 0.10611 mag= 0.00000
F1= 31.25, mag= 0.09423 mag= 0.00000
F1= 62.50, mag= 0.10754 mag= 0.00000
F1= 93.75, mag= 0.08793 mag= 0.00000
F1= 125.00, mag= 0.11068 mag= 0.00000
F1= 156.25, mag= 0.07982 mag= 0.00000
F1= 187.50, mag= 0.11203 mag= 0.00000
F1= 218.75, mag= 0.07678 mag= 0.00000
F1= 250.00, mag= 0.09029 mag= 0.00000
F1= 281.25, mag= 0.10285 mag= 0.00000
F1= 312.50, mag= 0.08015 mag= 0.00000
F1= 343.75, mag= 0.10285 mag= 0.00000
F1= 375.00, mag= 0.09063 mag= 0.00000
F1= 406.25, mag= 0.09256 mag= 0.00000
F1= 437.50, mag= 0.08764 mag= 0.00000
F1= 468.75, mag= 0.09484 mag= 0.00000
F1= 500.00, mag= 2620.17627 mag= 2620.16016
F1= 531.25, mag= 0.10418 mag= 0.00000
F1= 562.50, mag= 0.10534 mag= 0.00000
F1= 593.75, mag= 0.10568 mag= 0.00000
F1= 625.00, mag= 0.09694 mag= 0.00000
F1= 656.25, mag= 0.08705 mag= 0.00000
F1= 687.50, mag= 0.09377 mag= 0.00000
F1= 718.75, mag= 0.09259 mag= 0.00000
F1= 750.00, mag= 0.10259 mag= 0.00195
F1= 781.25, mag= 0.08477 mag= 0.00000
F1= 812.50, mag= 0.10585 mag= 0.00000
F1= 843.75, mag= 0.10384 mag= 0.00000
F1= 875.00, mag= 0.10069 mag= 0.00000
F1= 906.25, mag= 0.10469 mag= 0.00000
F1= 937.50, mag= 0.11233 mag= 0.00000
F1= 968.75, mag= 0.09861 mag= 0.00000
F1=1000.00, mag= 0.11733 mag= 0.00391
F1=1031.25, mag= 0.10588 mag= 0.00000
F1=1062.50, mag= 0.09900 mag= 0.00000
F1=1093.75, mag= 0.09996 mag= 0.00000
F1=1125.00, mag= 0.09220 mag= 0.00000
F1=1156.25, mag= 0.11435 mag= 0.00000
F1=1187.50, mag= 0.09662 mag= 0.00000
F1=1218.75, mag= 0.10372 mag= 0.00000
F1=1250.00, mag= 0.09398 mag= 0.00276
F1=1281.25, mag= 0.09437 mag= 0.00000
F1=1312.50, mag= 0.09865 mag= 0.00000
F1=1343.75, mag= 0.11403 mag= 0.00000
F1=1375.00, mag= 0.10098 mag= 0.00000
F1=1406.25, mag= 0.08598 mag= 0.00000
F1=1437.50, mag= 0.10105 mag= 0.00000
F1=1468.75, mag= 0.09867 mag= 0.00000
F1=1500.00, mag= 0.10565 mag= 0.00485
F1=1531.25, mag= 0.09424 mag= 0.00000
F1=1562.50, mag= 0.09212 mag= 0.00000
F1=1593.75, mag= 0.10664 mag= 0.00000
F1=1625.00, mag= 0.11636 mag= 0.00195
F1=1656.25, mag= 0.08445 mag= 0.00000
F1=1687.50, mag= 0.10084 mag= 0.00000
F1=1718.75, mag= 0.09308 mag= 0.00000
F1=1750.00, mag= 0.10372 mag= 0.00249
F1=1781.25, mag= 0.09571 mag= 0.00000
F1=1812.50, mag= 0.09840 mag= 0.00000
F1=1843.75, mag= 0.10246 mag= 0.00000
F1=1875.00, mag= 0.11236 mag= 0.00195
F1=1906.25, mag= 0.11003 mag= 0.00000
F1=1937.50, mag= 0.10503 mag= 0.00000
F1=1968.75, mag= 0.11180 mag= 0.00000
F1=2000.00, mag= 0.09783 mag= 0.00254
F1=2031.25, mag= 0.09688 mag= 0.00000
F1=2062.50, mag= 0.09493 mag= 0.00000
F1=2093.75, mag= 0.09357 mag= 0.00000
F1=2125.00, mag= 0.09151 mag= 0.00000
F1=2156.25, mag= 0.10432 mag= 0.00000
F1=2187.50, mag= 0.09754 mag= 0.00000
F1=2218.75, mag= 0.10882 mag= 0.00195
F1=2250.00, mag= 0.10045 mag= 0.00202
F1=2281.25, mag= 0.11314 mag= 0.00000
F1=2312.50, mag= 0.09258 mag= 0.00000
F1=2343.75, mag= 0.09818 mag= 0.00000
F1=2375.00, mag= 0.11400 mag= 0.00195
F1=2406.25, mag= 0.09444 mag= 0.00000
F1=2437.50, mag= 0.10033 mag= 0.00000
F1=2468.75, mag= 0.07606 mag= 0.00000
F1=2500.00, mag= 0.10632 mag= 0.00465
F1=2531.25, mag= 0.11568 mag= 0.00000
F1=2562.50, mag= 0.11631 mag= 0.00000
F1=2593.75, mag= 0.10031 mag= 0.00000
F1=2625.00, mag= 0.10132 mag= 0.00000
F1=2656.25, mag= 0.10407 mag= 0.00000
F1=2687.50, mag= 0.10097 mag= 0.00000
F1=2718.75, mag= 0.07938 mag= 0.00195
F1=2750.00, mag= 0.09015 mag= 0.00383
F1=2781.25, mag= 0.11763 mag= 0.00195
F1=2812.50, mag= 0.09597 mag= 0.00000
F1=2843.75, mag= 0.10604 mag= 0.00000
F1=2875.00, mag= 0.11056 mag= 0.00195
F1=2906.25, mag= 0.09866 mag= 0.00000
F1=2937.50, mag= 0.09622 mag= 0.00000
F1=2968.75, mag= 0.09654 mag= 0.00361
F1=3000.00, mag= 0.08598 mag= 0.00149
F1=3031.25, mag= 0.09443 mag= 0.00289
F1=3062.50, mag= 0.10529 mag= 0.00383
F1=3093.75, mag= 0.10426 mag= 0.00195
F1=3125.00, mag= 0.07579 mag= 0.00361
F1=3156.25, mag= 0.08571 mag= 0.00000
F1=3187.50, mag= 0.11280 mag= 0.00000
F1=3218.75, mag= 0.07765 mag= 0.00000
F1=3250.00, mag= 0.09780 mag= 0.00195
F1=3281.25, mag= 0.08297 mag= 0.00000
F1=3312.50, mag= 0.09670 mag= 0.00195
F1=3343.75, mag= 0.11009 mag= 0.00000
F1=3375.00, mag= 0.11433 mag= 0.00345
F1=3406.25, mag= 0.10358 mag= 0.00000
F1=3437.50, mag= 0.09293 mag= 0.00195
F1=3468.75, mag= 0.11226 mag= 0.00868
F1=3500.00, mag= 0.12093 mag= 0.00379
F1=3531.25, mag= 0.10341 mag= 0.00000
F1=3562.50, mag= 0.10189 mag= 0.00076
F1=3593.75, mag= 0.11852 mag= 0.00195
F1=3625.00, mag= 0.08856 mag= 0.00217
F1=3656.25, mag= 0.11255 mag= 0.00000
F1=3687.50, mag= 0.10105 mag= 0.00195
F1=3718.75, mag= 0.11565 mag= 0.00518
F1=3750.00, mag= 0.08955 mag= 0.00293
F1=3781.25, mag= 0.09275 mag= 0.00195
F1=3812.50, mag= 0.10781 mag= 0.00000
F1=3843.75, mag= 0.10727 mag= 0.00000
F1=3875.00, mag= 0.09191 mag= 0.00546
F1=3906.25, mag= 0.09398 mag= 0.00656
F1=3937.50, mag= 0.09004 mag= 0.00000
F1=3968.75, mag= 0.10833 mag= 0.00000
bin 2620.176270 tot_m 1.124978 sinad_grt 67.343732
bin 2620.160156 tot_m 0.020573 sinad_fft 102.100667