: $Id: myfft.mod,v 1.3 2009/02/16 15:56:24 samn Exp $ NEURON { SUFFIX myfft GLOBAL INSTALLED GLOBAL verbose } PARAMETER { INSTALLED=0 verbose=0 } VERBATIM /* Windowed Sinc FIR Generator Bob Maling (BobM.DSP@gmail.com) Contributed to musicdsp.org Source Code Archive Last Updated: April 12, 2005 Usage: Lowpass: wsfirLP(h, N, WINDOW, fc) Highpass: wsfirHP(h, N, WINDOW, fc) Bandpass: wsfirBP(h, N, WINDOW, fc1, fc2) Bandstop: wsfirBS(h, N, WINDOW, fc1, fc2) where: h (double[N]): filter coefficients will be written to this array N (int): number of taps WINDOW (int): Window (W_BLACKMAN, W_HANNING, or W_HAMMING) fc (double): cutoff (0 < fc < 0.5, fc = f/fs) --> for fs=48kHz and cutoff f=12kHz, fc = 12k/48k => 0.25 fc1 (double): low cutoff (0 < fc < 0.5, fc = f/fs) fc2 (double): high cutoff (0 < fc < 0.5, fc = f/fs) Windows included here are Blackman, Hanning, and Hamming. Other windows can be added by doing the following: 1. "Window type constants" section: Define a global constant for the new window. 2. wsfirLP() function: Add the new window as a case in the switch() statement. 3. Create the function for the window For windows with a design parameter, such as Kaiser, some modification will be needed for each function in order to pass the parameter. */ #include #include // Function prototypes void wsfirLP(double h[], int N, int WINDOW, double fc, int Vsz); void wsfirHP(double h[], int N, int WINDOW, double fc, int Vsz); void wsfirBS(double h[], int N, int WINDOW, double fc1, double fc2, int Vsz); void wsfirBP(double h[], int N, int WINDOW, double fc1, double fc2, int Vsz); void genSinc(double sinc[], int N, double fc, int Vsz); void wBlackman(double w[], int N, int Vsz); void wHanning(double w[], int N, int Vsz); void wHamming(double w[], int N, int Vsz); // Window type contstants #define W_BLACKMAN 1 #define W_HANNING 2 #define W_HAMMING 3 // Generate lowpass filter // // This is done by generating a sinc function and then windowing it void wsfirLP(double h[], // h[] will be written with the filter coefficients int N, // size of the filter (number of taps) int WINDOW, // window function (W_BLACKMAN, W_HANNING, etc.) double fc, // cutoff frequency int Vsz) { int i; double *w = (double*) calloc(N,sizeof(double)); // window function double *sinc = (double*) calloc(N,sizeof(double)); // sinc function // 1. Generate Sinc function genSinc(sinc, N, fc, N); // 2. Generate Window function switch (WINDOW) { case W_BLACKMAN: // W_BLACKMAN wBlackman(w, N, N); break; case W_HANNING: // W_HANNING wHanning(w, N, N); break; case W_HAMMING: // W_HAMMING wHamming(w, N, N); break; default: break; } // 3. Make lowpass filter for (i = 0; i < N; i++) { h[i] = sinc[i] * w[i]; } for ( ; i < Vsz; i++) { h[i] = 0.; } // Delete dynamic storage free(w); free(sinc); return; } void lpwin(void* vv) { double* x, fc; int fsz,win,vsz; vsz = vector_instance_px(vv,&x); fc = *getarg(1); fsz = ifarg(2) ? (int)*getarg(2) : vsz; win= ifarg(3) ? (int)*getarg(3) : W_BLACKMAN; wsfirLP(x,fsz,win,fc,vsz); } // Generate highpass filter // // This is done by generating a lowpass filter and then spectrally inverting it void wsfirHP(double h[], // h[] will be written with the filter coefficients int N, // size of the filter int WINDOW, // window function (W_BLACKMAN, W_HANNING, etc.) double fc, // cutoff frequency int Vsz) { int i; // 1. Generate lowpass filter wsfirLP(h, N, WINDOW, fc, Vsz); // 2. Spectrally invert (negate all samples and add 1 to center sample) lowpass filter // = delta[n-((N-1)/2)] - h[n], in the time domain for (i = 0; i < N; i++) { h[i] *= -1.0; // = 0 - h[i] } h[(N-1)/2] += 1.0; // = 1 - h[(N-1)/2] return; } void hpwin(void* vv) { double* x, fc; int fsz,win,vsz; vsz = vector_instance_px(vv,&x); fc = *getarg(1); fsz = ifarg(2) ? (int)*getarg(2) : vsz; win = ifarg(3) ? (int)*getarg(3) : W_BLACKMAN; wsfirHP(x,fsz,win,fc,vsz); } // Generate bandstop filter // // This is done by generating a lowpass and highpass filter and adding them void wsfirBS(double h[], // h[] will be written with the filter taps int N, // size of the filter int WINDOW, // window function (W_BLACKMAN, W_HANNING, etc.) double fc1, // low cutoff frequency double fc2, // high cutoff frequency int Vsz) { int i; double *h1 = (double*) calloc(N,sizeof(double)); double *h2 = (double*) calloc(N,sizeof(double)); // 1. Generate lowpass filter at first (low) cutoff frequency wsfirLP(h1, N, WINDOW, fc1, N); // 2. Generate highpass filter at second (high) cutoff frequency wsfirHP(h2, N, WINDOW, fc2, N); // 3. Add the 2 filters together for (i = 0; i < N; i++) { h[i] = h1[i] + h2[i]; } for (; i < Vsz; i++) { h[i] = 0.; } // Delete dynamic memory free(h1); free(h2); return; } void bswin(void* vv) { double* x, fc1, fc2; int fsz,win,vsz; vsz = vector_instance_px(vv,&x); fc1 = *getarg(1); fc2 = *getarg(2); fsz = ifarg(3) ? (int)*getarg(3) : vsz; win = ifarg(4) ? (int)*getarg(4) : W_BLACKMAN; wsfirBS(x,fsz,win,fc1,fc2,vsz); } // Generate bandpass filter // // This is done by generating a bandstop filter and spectrally inverting it void wsfirBP(double h[], // h[] will be written with the filter taps int N, // size of the filter int WINDOW, // window function (W_BLACKMAN, W_HANNING, etc.) double fc1, // low cutoff frequency double fc2, // high cutoff frequency int Vsz) { int i; // 1. Generate bandstop filter wsfirBS(h, N, WINDOW, fc1, fc2, Vsz); // 2. Spectrally invert bandstop (negate all, and add 1 to center sample) for (i = 0; i < N; i++) { h[i] *= -1.0; // = 0 - h[i] } h[(N-1)/2] += 1.0; // = 1 - h[(N-1)/2] return; } void bpwin(void* vv) { double* x, fc1, fc2; int fsz,win,vsz; vsz = vector_instance_px(vv,&x); fc1 = *getarg(1); fc2 = *getarg(2); fsz = ifarg(3) ? (int)*getarg(3) : vsz; win = ifarg(4) ? (int)*getarg(4) : W_BLACKMAN; wsfirBP(x,fsz,win,fc1,fc2,vsz); } // Generate sinc function - used for making lowpass filter void genSinc(double sinc[], // sinc[] will be written with the sinc function int N, // size (number of taps) double fc, // cutoff frequency int Vsz) { int i; double M = N-1; double n; // Constants double PI = 3.14159265358979323846; // Generate sinc delayed by (N-1)/2 for (i = 0; i < N; i++) { if (i == M/2.0) { sinc[i] = 2.0 * fc; } else { n = (double)i - M/2.0; sinc[i] = sin(2.0*PI*fc*n) / (PI*n); } } for (; i < Vsz; i++) { sinc[i] = 0.; } return; } void sincwin(void* vv) { double* x, fc; int fsz,vsz; vsz = vector_instance_px(vv,&x); fc = *getarg(1); fsz = ifarg(2) ? (int) *getarg(2) : vsz; genSinc(x,fsz,fc,vsz); } // Generate window function (Blackman) void wBlackman(double w[], // w[] will be written with the Blackman window int N, // size of the window int Vsz) { int i; double M = N-1; double PI = 3.14159265358979323846; for (i = 0; i < N; i++) { w[i] = 0.42 - (0.5 * cos(2.0*PI*(double)i/M)) + (0.08*cos(4.0*PI*(double)i/M)); } for (; i < Vsz; i++) { w[i] = 0.; } return; } void blackmanwin(void* vv) { double* x; int fsz,vsz; vsz = vector_instance_px(vv,&x); fsz = ifarg(1) ? (int) *getarg(1) : vsz; wBlackman(x,fsz,vsz); } // Generate window function (Hanning) void wHanning(double w[], // w[] will be written with the Hanning window int N, // size of the window int Vsz) //size of output buffer (extra space for zero padding) { int i; double M = N-1; double PI = 3.14159265358979323846; for (i = 0; i < N; i++) { w[i] = 0.5 * (1.0 - cos(2.0*PI*(double)i/M)); } for (; i < Vsz; i++) { w[i] = 0.; } return; } void hanningwin(void* vv) { double* x; int fsz,vsz; vsz = vector_instance_px(vv,&x); fsz = ifarg(1) ? (int) *getarg(1) : vsz; wHanning(x,fsz,vsz); } // Generate window function (Hamming) void wHamming(double w[], // w[] will be written with the Hamming window int N, // size of the window int VSz) //size of output buffer (extra space for zero padding) { int i; double M = N-1; double PI = 3.14159265358979323846; for (i = 0; i < N; i++) { w[i] = 0.54 - (0.46*cos(2.0*PI*(double)i/M)); } for (; i < VSz; i++) { w[i] = 0.; } return; } void hammingwin(void* vv) { double* x; int fsz,vsz; vsz = vector_instance_px(vv,&x); fsz = ifarg(1) ? (int) *getarg(1) : vsz; wHamming(x,fsz,vsz); } double* wrap(double* x,int n,int flen){ double* y = (double*) calloc(n,sizeof(double)); int i,j=0; for(i=flen/2+1;i vsz) { printf("wraparound ERRA: invalid filter len %d > vector len %d!\n",fsz,vsz); return; } y = wrap(x,vsz,fsz); for(i=0;i fft_pow) ( calling args long ll -> long winlength) (long m -> long log2length) */ #include #include typedef double real; extern void four1(double mdata[], unsigned long nn, int isign); extern void twofft(double data1[], double data2[], double fft1[], double fft2[], unsigned long n); extern void realft(double mdata[], unsigned long n, int isign); extern void convlv(double mdata[], unsigned long n, double respns[], unsigned long m, int isign, double ans[]); extern void correl(double data1[], double data2[], unsigned long n, double ans[]); extern void spctrm(double mdata[], double p[], int m, int k); /* convlv() -- convolution of a read data set with a response function the respns function must be stored in wrap-around order N.R.C p. 430 */ void nrerror(char error_text[]) /* Numerical Recipes standard error handler */ { hoc_execerror("Numerical Recipes run-time error...\n", error_text); } /* twofft() -- discrete FFT of two real functions simultaneously N.R.C p. 414 */ void twofft(double data1[], double data2[], double fft1[], double fft2[], unsigned long n) { void nrfour1(double mdata[], unsigned long nn, int isign); unsigned long nn3,nn2,jj,j; double rep,rem,aip,aim; nn3=1+(nn2=2+n+n); for (j=1,jj=2;j<=n;j++,jj+=2) { fft1[jj-1]=data1[j]; fft1[jj]=data2[j]; } nrfour1(fft1,n,1); fft2[1]=fft1[2]; fft1[2]=fft2[2]=0.0; for (j=3;j<=n+1;j+=2) { rep=0.5*(fft1[j]+fft1[nn2-j]); rem=0.5*(fft1[j]-fft1[nn2-j]); aip=0.5*(fft1[j+1]+fft1[nn3-j]); aim=0.5*(fft1[j+1]-fft1[nn3-j]); fft1[j]=rep; fft1[j+1]=aim; fft1[nn2-j]=rep; fft1[nn3-j] = -aim; fft2[j]=aip; fft2[j+1] = -rem; fft2[nn2-j]=aip; fft2[nn3-j]=rem; } } /* realft() -- discrete FFT of a real function with 2n data pts N.R.C p. 417 */ void realft(double mdata[], unsigned long n, int isign) { void nrfour1(double mdata[], unsigned long nn, int isign); unsigned long i,i1,i2,i3,i4,np3; double c1=0.5,c2,h1r,h1i,h2r,h2i; double wr,wi,wpr,wpi,wtemp,theta; theta=3.141592653589793/(double) (n>>1); if (isign == 1) { c2 = -0.5; nrfour1(mdata,n>>1,1); } else { c2=0.5; theta = -theta; } wtemp=sin(0.5*theta); wpr = -2.0*wtemp*wtemp; wpi=sin(theta); wr=1.0+wpr; wi=wpi; np3=n+3; for (i=2;i<=(n>>2);i++) { i4=1+(i3=np3-(i2=1+(i1=i+i-1))); h1r=c1*(mdata[i1]+mdata[i3]); h1i=c1*(mdata[i2]-mdata[i4]); h2r = -c2*(mdata[i2]+mdata[i4]); h2i=c2*(mdata[i1]-mdata[i3]); mdata[i1]=h1r+wr*h2r-wi*h2i; mdata[i2]=h1i+wr*h2i+wi*h2r; mdata[i3]=h1r-wr*h2r+wi*h2i; mdata[i4] = -h1i+wr*h2i+wi*h2r; wr=(wtemp=wr)*wpr-wi*wpi+wr; wi=wi*wpr+wtemp*wpi+wi; } if (isign == 1) { mdata[1] = (h1r=mdata[1])+mdata[2]; mdata[2] = h1r-mdata[2]; } else { mdata[1]=c1*((h1r=mdata[1])+mdata[2]); mdata[2]=c1*(h1r-mdata[2]); nrfour1(mdata,n>>1,-1); } } static double nrsqrarg; #define SQR(a) ((nrsqrarg=(a)) == 0.0 ? 0.0 : nrsqrarg*nrsqrarg) void convlv(double mdata[], unsigned long n, double respns[], unsigned long m, int isign, double ans[]) { void realft(double mdata[], unsigned long n, int isign); void twofft(double data1[], double data2[], double fft1[], double fft2[], unsigned long n); unsigned long i,no2; double dum,mag2,*fft; fft=nrvector(1,n<<1); for (i=1;i<=(m-1)/2;i++) respns[n+1-i]=respns[m+1-i]; for (i=(m+3)/2;i<=n-(m-1)/2;i++) respns[i]=0.0; twofft(mdata,respns,fft,ans,n); no2=n>>1; for (i=2;i<=n+2;i+=2) { if (isign == 1) { ans[i-1]=(fft[i-1]*(dum=ans[i-1])-fft[i]*ans[i])/no2; ans[i]=(fft[i]*dum+fft[i-1]*ans[i])/no2; } else if (isign == -1) { if ((mag2=SQR(ans[i-1])+SQR(ans[i])) == 0.0) nrerror("Deconvolving at response zero in convlv"); ans[i-1]=(fft[i-1]*(dum=ans[i-1])+fft[i]*ans[i])/mag2/no2; ans[i]=(fft[i]*dum-fft[i-1]*ans[i])/mag2/no2; } else nrerror("No meaning for isign in convlv"); } ans[2]=ans[n+1]; realft(ans,n,-1); nrfree_vector(fft,1,n<<1); } double rconvlv(double* mdata,int n,double* respns,int m,double* ans) { // Vect* v3 = (Vect*)v; // Vect* v1, *v2; // data set // v1 = vector_arg(1); // filter // v2 = vector_arg(2); // convolve unless isign is -1, then deconvolve! int isign = 1; // if (ifarg(3)) isign = (int)(*getarg(3)); else isign = 1; // make both data sets equal integer power of 2 // int v1n = v1->capacity(); // int v2n = v2->capacity(); // int m = (nIN > mIN) ? nIN : mIN; // int n = 1; // while(n < m) n*=2; // double *data = (double *)calloc(n,(unsigned)sizeof(double)); // int i; // for (i=0;ielem(i); // assume respns is given in "wrap-around" order, // with countup t=0..t=n/2 followed by countdown t=n..t=n/2 // v2n should be an odd <= n // double *respns = (double *)calloc(n,(unsigned)sizeof(double)); // for (i=0;ielem(i); // double *ans = (double *)calloc(2*n,(unsigned)sizeof(double)); // convlv(&data[0]-1,n,&respns[0]-1,v2n,isign,&ans[0]-1); convlv(&mdata[0]-1,n,&respns[0]-1,m,isign,&ans[0]-1); // if (v3->capacity() != n) v3->resize(n); // for (i=0;ielem(i)=ans[i]; // free((char *)data); // free((char *)respns); // free((char *)ans); // return v3->temp_objvar(); return 1; } #define PI 3.1415926535897932 #define PI8 0.392699081698724 /* PI / 8.0 */ #define RT2 1.4142135623731 /* sqrt(2.0) */ #define IRT2 0.707106781186548 /* 1.0/sqrt(2.0) */ #define signum(i) (i < 0 ? -1 : i == 0 ? 0 : 1) int FAST(real*, int); void FR2TR(int, real*, real*); void FR4TR(int, int, real*, real*, real*, real*); void FORD1(int, real*); void FORD2(int, real*); int fastlog2(int); double two_to_the(double N){ return (int)(pow(2.0,(N))+0.5); } int fft_real(real *orig, real *fftd, int npoint) { int i; for(i = 0; i< npoint; i++) fftd[i] = orig[i]; if(FAST(fftd, npoint) == 0 ){ fprintf(stderr, "Error calculating fft.\n"); return 0; } return 1; } /* double bandfilt(double* p,int sz,int lohz,int hihz){ double dret=0.0; int inv = 1; // make data set integer power of 2 int n = 1; while(n < sz) n*=2; double *mdata = (double *)calloc(n,(unsigned)sizeof(double)); int i; for (i=0;icapacity() != n) v3->resize(n); for (i=0;ielem(i)=mdata[i]; free((char *)mdata); return v3->temp_objvar(); dret=1.0; return dret; } */ double fftband(void* v){ double* x; //input vector int n = vector_instance_px(v,&x); double dret = 0.; double* pout = 0; int outlen = vector_arg_px(1,&pout); //size of filtered output int lowhz = (int)*getarg(2); //lowest Hz we want to keep int hihz = (int)*getarg(3); //highest Hz we want to keep int log2length = ceil(log((double)(n))/log(2.0)); int fftlen = two_to_the((double)log2length); double* fftd = (double*)calloc(fftlen,sizeof(double)); //stores fft results if(outlen < fftlen){ printf("fftband ERRA: outlen %d < %d",outlen,fftlen); goto FBCLEANUP; } if(!fft_real(x,fftd,n)){ printf("fftband ERRB: couldn't perform fft\n"); goto FBCLEANUP; } int hz = 0 , di = 0; int npoints = (int) (pow(2.0,(real) log2length) + 0.5); int npoints2 = npoints / 2; // fftd[0]=0.; fftd[npoints2]=0.; fftd[0]=fftd[1]=0.; int i,j,k; for(i=1;khihz){ di = i - hihz; if(di==1){ fftd[j] /= 2.; fftd[k] /= 2.; } else if(di==2){ fftd[j] /= 4.; fftd[k] /= 4.; //} else if(di==3){ // fftd[j] /= 8.; fftd[k] /= 8.; } else { fftd[j] = 0.; fftd[k] = 0.; } } } // if(!fft_real(fftd,pout,fftlen)){ if(!fft_real(fftd,pout,n)){ printf("fftbadn ERRC: couldn't perform fft\n"); goto FBCLEANUP; } dret=1.; FBCLEANUP: if(fftd) free(fftd); return dret; } int fft_pow(real *orig, real *power, long winlength, long log2length) { int i, j, k; real *temp = NULL; int npoints, npoints2; npoints = (int) (pow(2.0,(real) log2length) + 0.5); npoints2 = npoints / 2; temp = (real*) malloc(npoints * sizeof(real)); if(temp == (real*) NULL) { fprintf(stderr, "Error mallocing memory in fft_pow()\n"); return 0; } for(i=0;i0) { k0 = in*4 + 1; kl = k0 + in - 1; for (k=k0;k<=kl;k++) { kk = k-1; pr = IRT2 * (b1[kk]-b3[kk]); pi = IRT2 * (b1[kk]+b3[kk]); b3[kk] = b2[kk] + pi; b1[kk] = pi - b2[kk]; b2[kk] = b0[kk] - pr; b0[kk] = b0[kk] + pr; } } } else { arg = th2*piovn; c1 = cos(arg); s1 = sin(arg); c2 = c1*c1 - s1*s1; s2 = c1*s1 + c1*s1; c3 = c1*c2 - s1*s2; s3 = c2*s1 + s2*c1; int4 = in*4; j0=jr*int4 + 1; k0=ji*int4 + 1; jlast = j0+in-1; for(j=j0;j<=jlast;j++) { k = k0 + j - j0; kk = k-1; jj = j-1; r1 = b1[jj]*c1 - b5[kk]*s1; r5 = b1[jj]*s1 + b5[kk]*c1; t2 = b2[jj]*c2 - b6[kk]*s2; t6 = b2[jj]*s2 + b6[kk]*c2; t3 = b3[jj]*c3 - b7[kk]*s3; t7 = b3[jj]*s3 + b7[kk]*c3; t0 = b0[jj] + t2; t4 = b4[kk] + t6; t2 = b0[jj] - t2; t6 = b4[kk] - t6; t1 = r1 + t3; t5 = r5 + t7; t3 = r1 - t3; t7 = r5 - t7; b0[jj] = t0 + t1; b7[kk] = t4 + t5; b6[kk] = t0 - t1; b1[jj] = t5 - t4; b2[jj] = t2 - t7; b5[kk] = t6 + t3; b4[kk] = t2 + t7; b3[jj] = t3 - t6; } jr += 2; ji -= 2; if(ji-jl <= 0) { ji = 2*jr - 1; jl = jr; } } } } /* an inplace reordering subroutine */ void FORD1(int m, real *b) { int j, k = 4, kl = 2, n = 0x1 << m; real mt; for(j = 4; j <= n; j += 2) { if(k - j>0) { mt = b[j-1]; b[j - 1] = b[k - 1]; b[k - 1] = mt; } k -= 2; if(k - kl <= 0) { k = 2*j; kl = j; } } } /* the other inplace reordering subroutine */ void FORD2(int m, real *b) { real mt; int n = 0x1<>= 1; power += 1; if(n & 0x01) { if(n > 1) return(0); else return(power); } } return(0); } ENDVERBATIM FUNCTION hfastlog2(){ VERBATIM return fastlog2((int)*getarg(1)); ENDVERBATIM } FUNCTION log2(){ VERBATIM static double l2 = 0.693147181; return log(*getarg(1))/l2; ENDVERBATIM } PROCEDURE install () { if (INSTALLED==1) { printf("already installed myfft.mod") } else { INSTALLED=1 VERBATIM install_vector_method("fftpow", fftpow); install_vector_method("fftband",fftband); install_vector_method("lpwin",lpwin); install_vector_method("hpwin",hpwin); install_vector_method("bswin",bswin); install_vector_method("bpwin",bpwin); install_vector_method("sincwin",sincwin); install_vector_method("blackmanwin",blackmanwin); install_vector_method("hanningwin",hanningwin); install_vector_method("hammingwin",hammingwin); install_vector_method("wraparound",wraparound); ENDVERBATIM } }