#include #include #include #ifdef _MSC_VER #pragma warning(disable: 4244) #endif /************************************************************************ fft(int n, double xRe[], double xIm[], double yRe[], double yIm[]) ------------------------------------------------------------------------ NOTE : This is copyrighted material, Not public domain. See below. ------------------------------------------------------------------------ Input/output: int n transformation length. double xRe[] real part of input sequence. double xIm[] imaginary part of input sequence. double yRe[] real part of output sequence. double yIm[] imaginary part of output sequence. ------------------------------------------------------------------------ Function: The procedure performs a fast discrete Fourier transform (FFT) of a complex sequence, x, of an arbitrary length, n. The output, y, is also a complex sequence of length n. y[k] = sum(x[m]*exp(-i*2*pi*k*m/n), m=0..(n-1)), k=0,...,(n-1) The largest prime factor of n must be less than or equal to the constant maxPrimeFactor defined below. ------------------------------------------------------------------------ Author: Jens Joergen Nielsen For non-commercial use only. Bakkehusene 54 A $100 fee must be paid if used DK-2970 Hoersholm commercially. Please contact. DENMARK E-mail : jjn@get2net.dk All rights reserved. October 2000. Homepage : http://home.get2net.dk/jjn ------------------------------------------------------------------------ Implementation notes: The general idea is to factor the length of the DFT, n, into factors that are efficiently handled by the routines. A number of short DFT's are implemented with a minimum of arithmetical operations and using (almost) straight line code resulting in very fast execution when the factors of n belong to this set. Especially radix-10 is optimized. Prime factors, that are not in the set of short DFT's are handled with direct evaluation of the DFP expression. Please report any problems to the author. Suggestions and improvements are welcomed. ------------------------------------------------------------------------ Benchmarks: The Microsoft Visual C++ compiler was used with the following compile options: /nologo /Gs /G2 /W4 /AH /Ox /D "NDEBUG" /D "_DOS" /FR and the FFTBENCH test executed on a 50MHz 486DX : Length Time [s] Accuracy [dB] 128 0.0054 -314.8 256 0.0116 -309.8 512 0.0251 -290.8 1024 0.0567 -313.6 2048 0.1203 -306.4 4096 0.2600 -291.8 8192 0.5800 -305.1 100 0.0040 -278.5 200 0.0099 -280.3 500 0.0256 -278.5 1000 0.0540 -278.5 2000 0.1294 -280.6 5000 0.3300 -278.4 10000 0.7133 -278.5 ------------------------------------------------------------------------ The following procedures are used : factorize : factor the transformation length. transTableSetup : setup table with sofar-, actual-, and remainRadix. permute : permutation allows in-place calculations. twiddleTransf : twiddle multiplications and DFT's for one stage. initTrig : initialise sine/cosine table. fft_4 : length 4 DFT, a la Nussbaumer. fft_5 : length 5 DFT, a la Nussbaumer. fft_10 : length 10 DFT using prime factor FFT. fft_odd : length n DFT, n odd. *************************************************************************/ /************************************************************************ changes by Thomas Grill: - introduced REAL type for numbers - made functions static - threw fft_n functions out of twiddleTransf if feasible, these will be inlined by the compiler - changed log prints (to post) ************************************************************************/ #define REAL float extern "C" void post(const char *c,...); /************************************************************************/ #define maxPrimeFactor 8000 // all static data should fit into 256kB of cache #define maxPrimeFactorDiv2 (maxPrimeFactor+1)/2 #define maxFactorCount 100 static double c3_1 = -1.5000000000000E+00; /* c3_1 = cos(2*pi/3)-1; */ static double c3_2 = 8.6602540378444E-01; /* c3_2 = sin(2*pi/3); */ static double u5 = 1.2566370614359E+00; /* u5 = 2*pi/5; */ static double c5_1 = -1.2500000000000E+00; /* c5_1 = (cos(u5)+cos(2*u5))/2-1;*/ static double c5_2 = 5.5901699437495E-01; /* c5_2 = (cos(u5)-cos(2*u5))/2; */ static double c5_3 = -9.5105651629515E-01; /* c5_3 = -sin(u5); */ static double c5_4 = -1.5388417685876E+00; /* c5_4 = -(sin(u5)+sin(2*u5)); */ static double c5_5 = 3.6327126400268E-01; /* c5_5 = (sin(u5)-sin(2*u5)); */ static double c8 = 7.0710678118655E-01; /* c8 = 1/sqrt(2); */ static double pi; static int groupOffset,dataOffset,blockOffset,adr; static int groupNo,dataNo,blockNo,twNo; static double omega; static REAL tw_re,tw_im; static REAL twiddleRe[maxPrimeFactor], twiddleIm[maxPrimeFactor]; static REAL trigRe[maxPrimeFactor], trigIm[maxPrimeFactor]; static REAL zRe[maxPrimeFactor], zIm[maxPrimeFactor]; static REAL vRe[maxPrimeFactorDiv2], vIm[maxPrimeFactorDiv2]; static REAL wRe[maxPrimeFactorDiv2], wIm[maxPrimeFactorDiv2]; static void factorize(int n, int *nFact, int fact[]) { int i,j,k; int nRadix; int radices[7]; int factors[maxFactorCount]; nRadix = 6; radices[1]= 2; radices[2]= 3; radices[3]= 4; radices[4]= 5; radices[5]= 8; radices[6]= 10; if (n==1) { j=1; factors[1]=1; } else j=0; i=nRadix; while ((n>1) && (i>0)) { if ((n % radices[i]) == 0) { n=n / radices[i]; j=j+1; factors[j]=radices[i]; } else i=i-1; } if (factors[j] == 2) /*substitute factors 2*8 with 4*4 */ { i = j-1; while ((i>0) && (factors[i] != 8)) i--; if (i>0) { factors[j] = 4; factors[i] = 4; } } if (n>1) { for (k=2; k1) { j=j+1; factors[j]=n; } } for (i=1; i<=j; i++) { fact[i] = factors[j-i+1]; } *nFact=j; } /* factorize */ /**************************************************************************** After N is factored the parameters that control the stages are generated. For each stage we have: sofar : the product of the radices so far. actual : the radix handled in this stage. remain : the product of the remaining radices. ****************************************************************************/ static bool transTableSetup(int sofar[], int actual[], int remain[], int *nFact, int *nPoints) { int i; factorize(*nPoints, nFact, actual); if (actual[1] > maxPrimeFactor) { // T.Grill - replaced the printfs by a post post("FFT: Prime factor of FFT length is too large (%d) - aborted",actual[1]); return false; } remain[0]=*nPoints; sofar[1]=1; remain[1]=*nPoints / actual[1]; for (i=2; i<=*nFact; i++) { sofar[i]=sofar[i-1]*actual[i-1]; remain[i]=remain[i-1] / actual[i]; } return true; } /* transTableSetup */ /**************************************************************************** The sequence y is the permuted input sequence x so that the following transformations can be performed in-place, and the final result is the normal order. ****************************************************************************/ static void permute(int nPoint, int nFact, int fact[], int remain[], REAL xRe[], REAL xIm[], REAL yRe[], REAL yIm[]) { int i,j,k; int count[maxFactorCount]; for (i=1; i<=nFact; i++) count[i]=0; k=0; for (i=0; i<=nPoint-2; i++) { yRe[i] = xRe[k]; yIm[i] = xIm[k]; j=1; k=k+remain[j]; count[1] = count[1]+1; while (count[j] >= fact[j]) { count[j]=0; k=k-remain[j-1]+remain[j+1]; j=j+1; count[j]=count[j]+1; } } yRe[nPoint-1]=xRe[nPoint-1]; yIm[nPoint-1]=xIm[nPoint-1]; } /* permute */ /**************************************************************************** Twiddle factor multiplications and transformations are performed on a group of data. The number of multiplications with 1 are reduced by skipping the twiddle multiplication of the first stage and of the first group of the following stages. ***************************************************************************/ static void initTrig(int radix) { int i; double w,xre,xim,xre1,xim1; w=2*pi/radix; trigRe[0]=1; trigIm[0]=0; xre1=xre=cos(w); xim1=xim=-sin(w); trigRe[1]=xre; trigIm[1]=xim; for (i=2; i= n) k = k - n; } } for (j=1; j < max; j++) { zRe[0]=zRe[0] + vRe[j]; zIm[0]=zIm[0] + wIm[j]; } } /* fft_odd */ static void twiddleTransf(int sofarRadix, int radix, int remainRadix, REAL yRe[], REAL yIm[]) { /* twiddleTransf */ double cosw, sinw, gem; initTrig(radix); omega = 2*pi/(double)(sofarRadix*radix); cosw = cos(omega); sinw = -sin(omega); tw_re = 1.0; tw_im = 0; dataOffset=0; groupOffset=dataOffset; adr=groupOffset; for (dataNo=0; dataNo1) { twiddleRe[0] = 1.0; twiddleIm[0] = 0.0; twiddleRe[1] = tw_re; twiddleIm[1] = tw_im; for (twNo=2; twNo1) && (dataNo > 0)) { zRe[0]=yRe[adr]; zIm[0]=yIm[adr]; blockNo=1; do { adr = adr + sofarRadix; zRe[blockNo]= twiddleRe[blockNo] * yRe[adr] - twiddleIm[blockNo] * yIm[adr]; zIm[blockNo]= twiddleRe[blockNo] * yIm[adr] + twiddleIm[blockNo] * yRe[adr]; blockNo++; } while (blockNo < radix); } else for (blockNo=0; blockNo