// ---------------------------------------------------------------------------- // fftfilt.cxx -- Fast convolution Overlap-Add filter // // Filter implemented using overlap-add FFT convolution method // h(t) characterized by Windowed-Sinc impulse response // // Reference: // "The Scientist and Engineer's Guide to Digital Signal Processing" // by Dr. Steven W. Smith, http://www.dspguide.com // Chapters 16, 18 and 21 // // Copyright (C) 2006-2008 Dave Freese, W1HKJ // // This file is part of fldigi. // // Fldigi is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Fldigi is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with fldigi. If not, see . // ---------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include //------------------------------------------------------------------------------ // initialize the filter // create forward and reverse FFTs //------------------------------------------------------------------------------ // Only need a single instance of g_fft, used for both forward and reverse void fftfilt::init_filter() { flen2 = flen >> 1; fft = new g_fft(flen); filter = new cmplx[flen]; data = new cmplx[flen]; output = new cmplx[flen2]; ovlbuf = new cmplx[flen2]; memset(filter, 0, flen * sizeof(cmplx)); memset(data, 0, flen * sizeof(cmplx)); memset(output, 0, flen2 * sizeof(cmplx)); memset(ovlbuf, 0, flen2 * sizeof(cmplx)); inptr = 0; } //------------------------------------------------------------------------------ // fft filter // f1 < f2 ==> band pass filter // f1 > f2 ==> band reject filter // f1 == 0 ==> low pass filter // f2 == 0 ==> high pass filter //------------------------------------------------------------------------------ fftfilt::fftfilt(float f1, float f2, int len) { flen = len; init_filter(); create_filter(f1, f2); } fftfilt::~fftfilt() { if (fft) delete fft; if (filter) delete [] filter; if (data) delete [] data; if (output) delete [] output; if (ovlbuf) delete [] ovlbuf; } void fftfilt::create_filter(float f1, float f2) { // initialize the filter to zero memset(filter, 0, flen * sizeof(cmplx)); // create the filter shape coefficients by fft bool b_lowpass, b_highpass; b_lowpass = (f2 != 0); b_highpass = (f1 != 0); for (int i = 0; i < flen2; i++) { filter[i] = 0; // lowpass @ f2 if (b_lowpass) filter[i] += fsinc(f2, i, flen2); // highighpass @ f1 if (b_highpass) filter[i] -= fsinc(f1, i, flen2); } // highpass is delta[flen2/2] - h(t) if (b_highpass && f2 < f1) filter[flen2 / 2] += 1; for (int i = 0; i < flen2; i++) filter[i] *= _blackman(i, flen2); fft->ComplexFFT(filter); // normalize the output filter for unity gain float scale = 0, mag; for (int i = 0; i < flen2; i++) { mag = abs(filter[i]); if (mag > scale) scale = mag; } if (scale != 0) { for (int i = 0; i < flen; i++) filter[i] /= scale; } } // Filter with fast convolution (overlap-add algorithm). int fftfilt::runFilt(const cmplx & in, cmplx **out) { data[inptr++] = in; if (inptr < flen2) return 0; inptr = 0; fft->ComplexFFT(data); for (int i = 0; i < flen; i++) data[i] *= filter[i]; fft->InverseComplexFFT(data); for (int i = 0; i < flen2; i++) { output[i] = ovlbuf[i] + data[i]; ovlbuf[i] = data[flen2 + i]; } memset (data, 0, flen * sizeof(cmplx)); *out = output; return flen2; } // Second version for single sideband int fftfilt::runSSB(const cmplx & in, cmplx **out, bool usb) { data[inptr++] = in; if (inptr < flen2) return 0; inptr = 0; fft->ComplexFFT(data); // Discard frequencies for ssb if ( usb ) for (int i = 0; i < flen2; i++) { data[i] *= filter[i]; data[flen2 + i] = 0; } else for (int i = 0; i < flen2; i++) { data[i] = 0; data[flen2 + i] *= filter[flen2 + i]; } // in-place FFT: freqdata overwritten with filtered timedata fft->InverseComplexFFT(data); // overlap and add for (int i = 0; i < flen2; i++) { output[i] = ovlbuf[i] + data[i]; ovlbuf[i] = data[i+flen2]; } memset (data, 0, flen * sizeof(cmplx)); *out = output; return flen2; }