////////////////////////////////////////////////////////////////////// // Call to setup filter parameters // SampleRate in Hz // FLowcut is low cutoff frequency of filter in Hz // FHicut is high cutoff frequency of filter in Hz // Offset is the CW tone offset frequency // cutoff frequencies range from -SampleRate/2 to +SampleRate/2 // HiCut must be greater than LowCut // example to make 2700Hz USB filter: // SetupParameters( 100, 2800, 0, 48000); ////////////////////////////////////////////////////////////////////// void CFastFIR::SetupParameters( TYPEREAL FLoCut, TYPEREAL FHiCut, TYPEREAL Offset, TYPEREAL SampleRate) { int i; if( (FLoCut==m_FLoCut) && (FHiCut==m_FHiCut) && (Offset==m_Offset) && (SampleRate==m_SampleRate) ) { return; //return if no changes } m_FLoCut = FLoCut; m_FHiCut = FHiCut; m_Offset = Offset; m_SampleRate = SampleRate; FLoCut += Offset; FHiCut += Offset; //sanity check on filter parameters if( (FLoCut >= FHiCut) || (FLoCut >= SampleRate/2.0) || (FLoCut <= -SampleRate/2.0) || (FHiCut >= SampleRate/2.0) || (FHiCut <= -SampleRate/2.0) ) { fprintf(stderr, "Filter Parameter error:\n"); return; } //calculate some normalized filter parameters TYPEREAL nFL = FLoCut/SampleRate; TYPEREAL nFH = FHiCut/SampleRate; TYPEREAL nFc = (nFH-nFL)/2.0; //prototype LP filter cutoff TYPEREAL nFs = K_2PI*(nFH+nFL)/2.0; //2 PI times required frequency shift (FHiCut+FLoCut)/2 TYPEREAL fCenter = 0.5*(TYPEREAL)(CONV_FIR_SIZE-1); //floating point center index of FIR filter for(i=0; i<CONV_FFT_SIZE; i++) //zero pad entire coefficient buffer to FFT size { m_pFilterCoef[i].re = 0.0; m_pFilterCoef[i].im = 0.0; } //create LP FIR windowed sinc, sin(x)/x complex LP filter coefficients for(i=0; i<CONV_FIR_SIZE; i++) { TYPEREAL x = (TYPEREAL)i - fCenter; TYPEREAL z; if( (TYPEREAL)i == fCenter ) //deal with odd size filter singularity where sin(0)/0==1 z = 2.0 * nFc; else z = (TYPEREAL)MSIN(K_2PI*x*nFc)/(K_PI*x) * m_pWindowTbl[i]; //shift lowpass filter coefficients in frequency by (hicut+lowcut)/2 to form bandpass filter anywhere in range // (also scales by 1/FFTsize since inverse FFT routine scales by FFTsize) m_pFilterCoef[i].re = z * MCOS(nFs * x)/(TYPEREAL)CONV_FFT_SIZE; m_pFilterCoef[i].im = z * MSIN(nFs * x)/(TYPEREAL)CONV_FFT_SIZE; } //convert FIR coefficients to frequency domain by taking forward FFT m_Fft.FwdFFT(m_pFilterCoef); }
/////////////////////////////////////////////////////////////////////////// // function to convert LP filter coefficients into complex hilbert bandpass // filter coefficients. // Hbpreal(n)= 2*Hlp(n)*MCOS( 2PI*FreqOffset*(n-(N-1)/2)/samplerate ); // Hbpimaj(n)= 2*Hlp(n)*MSIN( 2PI*FreqOffset*(n-(N-1)/2)/samplerate ); void CFir::GenerateHBFilter( TYPEREAL FreqOffset) { int n; for(n=0; n<m_NumTaps; n++) { // apply complex frequency shift transform to low pass filter coefficients m_ICoef[n] = 2.0 * m_Coef[n] * MCOS( (K_2PI*FreqOffset/m_SampleRate)*((TYPEREAL)n - ( (TYPEREAL)(m_NumTaps-1)/2.0 ) ) ); m_QCoef[n] = 2.0 * m_Coef[n] * MSIN( (K_2PI*FreqOffset/m_SampleRate)*((TYPEREAL)n - ( (TYPEREAL)(m_NumTaps-1)/2.0 ) ) ); } //make a 2x length array for FIR flat calculation efficiency for (n = 0; n < m_NumTaps; n++) { m_ICoef[n+m_NumTaps] = m_ICoef[n]; m_QCoef[n+m_NumTaps] = m_QCoef[n]; } #if 0 //debug hack to write m_Coef's to a file for analysis QDir::setCurrent("d:/"); QFile File; File.setFileName("hbpcoef.txt"); if(File.open(QIODevice::WriteOnly)) { qDebug()<<"file Opened OK"; char Buf[256]; for( n=0; n<m_NumTaps; n++) { sprintf( Buf, "%19.12g %19.12g\r\n", m_ICoef[n], m_QCoef[n]); File.write(Buf); } } else qDebug()<<"file Failed to Open"; qDebug()<<"HBP taps="<<m_NumTaps << m_SampleRate; #endif }
CFastFIR::CFastFIR() { int i; m_pWindowTbl = NULL; m_pFFTBuf = NULL; m_pFFTOverlapBuf = NULL; m_pFilterCoef = NULL; //allocate internal buffer space on Heap m_pWindowTbl = new TYPEREAL[CONV_FIR_SIZE]; m_pFilterCoef = new TYPECPX[CONV_FFT_SIZE]; m_pFFTBuf = new TYPECPX[CONV_FFT_SIZE]; m_pFFTOverlapBuf = new TYPECPX[CONV_FIR_SIZE]; if(!m_pWindowTbl || !m_pFilterCoef || !m_pFFTBuf || !m_pFFTOverlapBuf) { //major poblems if memory fails here return; } m_InBufInPos = (CONV_FIR_SIZE - 1); for( i=0; i<CONV_FFT_SIZE; i++) { m_pFFTBuf[i].re = 0.0; m_pFFTBuf[i].im = 0.0; } #if 1 //create Blackman-Nuttall window function for windowed sinc low pass filter design for( i=0; i<CONV_FIR_SIZE; i++) { m_pWindowTbl[i] = (0.3635819 - 0.4891775*MCOS( (K_2PI*i)/(CONV_FIR_SIZE-1) ) + 0.1365995*MCOS( (2.0*K_2PI*i)/(CONV_FIR_SIZE-1) ) - 0.0106411*MCOS( (3.0*K_2PI*i)/(CONV_FIR_SIZE-1) ) ); m_pFFTOverlapBuf[i].re = 0.0; m_pFFTOverlapBuf[i].im = 0.0; } #endif #if 0 //create Blackman-Harris window function for windowed sinc low pass filter design for( i=0; i<CONV_FIR_SIZE; i++) { m_pWindowTbl[i] = (0.35875 - 0.48829*MCOS( (K_2PI*i)/(CONV_FIR_SIZE-1) ) + 0.14128*MCOS( (2.0*K_2PI*i)/(CONV_FIR_SIZE-1) ) - 0.01168*MCOS( (3.0*K_2PI*i)/(CONV_FIR_SIZE-1) ) ); m_pFFTOverlapBuf[i].re = 0.0; m_pFFTOverlapBuf[i].im = 0.0; } #endif #if 0 //create Nuttall window function for windowed sinc low pass filter design for( i=0; i<CONV_FIR_SIZE; i++) { m_pWindowTbl[i] = (0.355768 - 0.487396*MCOS( (K_2PI*i)/(CONV_FIR_SIZE-1) ) + 0.144232*MCOS( (2.0*K_2PI*i)/(CONV_FIR_SIZE-1) ) - 0.012604*MCOS( (3.0*K_2PI*i)/(CONV_FIR_SIZE-1) ) ); m_pFFTOverlapBuf[i].re = 0.0; m_pFFTOverlapBuf[i].im = 0.0; } #endif m_Fft.SetFFTParams(CONV_FFT_SIZE, false, 0.0, 1.0); m_FLoCut = -1.0; m_FHiCut = 1.0; m_Offset = 1.0; m_SampleRate = 1.0; }