From 26531c2da601a1c21c50e3644350c604c27f8658 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 18:59:23 -0400 Subject: filter: fixed FIR filter taps and added complex FFT filter. --- gr-filter/examples/fft_filter_ccc.py | 107 +++++++++ gr-filter/examples/fir_filter_ccc.py | 101 ++++++++ gr-filter/examples/fir_filter_fff.py | 7 + gr-filter/include/filter/CMakeLists.txt | 2 + gr-filter/include/filter/fft_filter.h | 105 +++++++++ gr-filter/include/filter/fft_filter_ccc.h | 67 ++++++ gr-filter/include/filter/fft_filter_fff.h | 88 +++++++ gr-filter/include/filter/fir_filter.h | 7 +- gr-filter/lib/CMakeLists.txt | 2 + gr-filter/lib/fft_filter.cc | 174 ++++++++++++++ gr-filter/lib/fft_filter_ccc_impl.cc | 119 ++++++++++ gr-filter/lib/fft_filter_ccc_impl.h | 61 +++++ gr-filter/lib/fft_filter_fff_impl.cc | 123 ++++++++++ gr-filter/lib/fft_filter_fff_impl.h | 88 +++++++ gr-filter/lib/fir_filter.cc | 14 +- gr-filter/lib/fir_filter_XXX_impl.cc.t | 8 +- gr-filter/lib/fir_filter_XXX_impl.h.t | 2 +- gr-filter/python/qa_fft_filter.py | 380 ++++++++++++++++++++++++++++++ gr-filter/python/qa_fir_filter.py | 22 +- gr-filter/swig/filter_swig.i | 3 + 20 files changed, 1463 insertions(+), 17 deletions(-) create mode 100755 gr-filter/examples/fft_filter_ccc.py create mode 100755 gr-filter/examples/fir_filter_ccc.py create mode 100644 gr-filter/include/filter/fft_filter.h create mode 100644 gr-filter/include/filter/fft_filter_ccc.h create mode 100644 gr-filter/include/filter/fft_filter_fff.h create mode 100644 gr-filter/lib/fft_filter.cc create mode 100644 gr-filter/lib/fft_filter_ccc_impl.cc create mode 100644 gr-filter/lib/fft_filter_ccc_impl.h create mode 100644 gr-filter/lib/fft_filter_fff_impl.cc create mode 100644 gr-filter/lib/fft_filter_fff_impl.h create mode 100755 gr-filter/python/qa_fft_filter.py (limited to 'gr-filter') diff --git a/gr-filter/examples/fft_filter_ccc.py b/gr-filter/examples/fft_filter_ccc.py new file mode 100755 index 000000000..0844c88ef --- /dev/null +++ b/gr-filter/examples/fft_filter_ccc.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + +from gnuradio import gr, filter +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +try: + import scipy +except ImportError: + print "Error: could not import scipy (http://www.scipy.org/)" + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: could not import pylab (http://matplotlib.sourceforge.net/)" + sys.exit(1) + +class example_fft_filter_ccc(gr.top_block): + def __init__(self, N, fs, bw0, bw1, tw, atten, D): + gr.top_block.__init__(self) + + self._nsamps = N + self._fs = fs + self._bw0 = bw0 + self._bw1 = bw1 + self._tw = tw + self._at = atten + self._decim = D + taps = gr.firdes.complex_band_pass_2(1, self._fs, + self._bw0, self._bw1, + self._tw, self._at) + print "Num. Taps: ", len(taps) + + self.src = gr.noise_source_c(gr.GR_GAUSSIAN, 1) + self.head = gr.head(gr.sizeof_gr_complex, self._nsamps) + + self.filt0 = filter.fft_filter_ccc(self._decim, taps) + self.filt1 = gr.fft_filter_ccc(self._decim, taps) + #self.filt1 = filter.fft_filter_ccc(self._decim, taps) + + self.vsnk_src = gr.vector_sink_c() + self.vsnk_out = gr.vector_sink_c() + self.vsnk_gr = gr.vector_sink_c() + + self.connect(self.src, self.head, self.vsnk_src) + self.connect(self.head, self.filt0, self.vsnk_out) + self.connect(self.head, self.filt1, self.vsnk_gr) + +def main(): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=10000, + help="Number of samples to process [default=%default]") + parser.add_option("-s", "--samplerate", type="eng_float", default=8000, + help="System sample rate [default=%default]") + parser.add_option("-S", "--start-pass", type="eng_float", default=1000, + help="Start of Passband [default=%default]") + parser.add_option("-E", "--end-pass", type="eng_float", default=2000, + help="End of Passband [default=%default]") + parser.add_option("-T", "--transition", type="eng_float", default=100, + help="Transition band [default=%default]") + parser.add_option("-A", "--attenuation", type="eng_float", default=80, + help="Stopband attenuation [default=%default]") + parser.add_option("-D", "--decimation", type="int", default=1, + help="Decmation factor [default=%default]") + (options, args) = parser.parse_args () + + put = example_fft_filter_ccc(options.nsamples, + options.samplerate, + options.start_pass, + options.end_pass, + options.transition, + options.attenuation, + options.decimation) + put.run() + + data_src = scipy.array(put.vsnk_src.data()) + data_snk = scipy.array(put.vsnk_out.data()) + data_gr = scipy.array(put.vsnk_gr.data()) + + # Plot the signals PSDs + nfft = 1024 + f1 = pylab.figure(1, figsize=(12,10)) + s1 = f1.add_subplot(1,1,1) + s1.psd(data_src, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_snk, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + + + f2 = pylab.figure(2, figsize=(12,10)) + s2 = f2.add_subplot(1,1,1) + s2.plot(data_src) + s2.plot(data_snk.real, 'g') + s2.plot(data_gr.real, 'r--') + + pylab.show() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/examples/fir_filter_ccc.py b/gr-filter/examples/fir_filter_ccc.py new file mode 100755 index 000000000..57df868f7 --- /dev/null +++ b/gr-filter/examples/fir_filter_ccc.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +from gnuradio import gr, filter +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +try: + import scipy +except ImportError: + print "Error: could not import scipy (http://www.scipy.org/)" + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: could not import pylab (http://matplotlib.sourceforge.net/)" + sys.exit(1) + +class example_fir_filter_ccc(gr.top_block): + def __init__(self, N, fs, bw, tw, atten, D): + gr.top_block.__init__(self) + + self._nsamps = N + self._fs = fs + self._bw = bw + self._tw = tw + self._at = atten + self._decim = D + taps = gr.firdes.low_pass_2(1, self._fs, self._bw, self._tw, self._at) + print "Num. Taps: ", len(taps) + + self.src = gr.noise_source_c(gr.GR_GAUSSIAN, 1) + self.head = gr.head(gr.sizeof_gr_complex, self._nsamps) + + self.filt0 = filter.fir_filter_ccc(self._decim, taps) + self.filt1 = gr.fir_filter_ccc(self._decim, taps) + #self.filt1 = filter.fft_filter_ccc(self._decim, taps) + + self.vsnk_src = gr.vector_sink_c() + self.vsnk_out = gr.vector_sink_c() + self.vsnk_gr = gr.vector_sink_c() + + self.connect(self.src, self.head, self.vsnk_src) + self.connect(self.head, self.filt0, self.vsnk_out) + self.connect(self.head, self.filt1, self.vsnk_gr) + +def main(): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=10000, + help="Number of samples to process [default=%default]") + parser.add_option("-s", "--samplerate", type="eng_float", default=8000, + help="System sample rate [default=%default]") + parser.add_option("-B", "--bandwidth", type="eng_float", default=1000, + help="Filter bandwidth [default=%default]") + parser.add_option("-T", "--transition", type="eng_float", default=100, + help="Transition band [default=%default]") + parser.add_option("-A", "--attenuation", type="eng_float", default=80, + help="Stopband attenuation [default=%default]") + parser.add_option("-D", "--decimation", type="int", default=1, + help="Decmation factor [default=%default]") + (options, args) = parser.parse_args () + + put = example_fir_filter_ccc(options.nsamples, + options.samplerate, + options.bandwidth, + options.transition, + options.attenuation, + options.decimation) + put.run() + + data_src = scipy.array(put.vsnk_src.data()) + data_snk = scipy.array(put.vsnk_out.data()) + data_gr = scipy.array(put.vsnk_gr.data()) + + # Plot the signals PSDs + nfft = 1024 + f1 = pylab.figure(1, figsize=(12,10)) + s1 = f1.add_subplot(1,1,1) + s1.psd(data_src, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_snk, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + + + f2 = pylab.figure(2, figsize=(12,10)) + s2 = f2.add_subplot(1,1,1) + s2.plot(data_src) + s2.plot(data_snk.real, 'g') + s2.plot(data_gr.real, 'r--') + + pylab.show() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/examples/fir_filter_fff.py b/gr-filter/examples/fir_filter_fff.py index 538aded44..1ed889fcf 100755 --- a/gr-filter/examples/fir_filter_fff.py +++ b/gr-filter/examples/fir_filter_fff.py @@ -83,6 +83,13 @@ def main(): s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, Fs=options.samplerate) + + f2 = pylab.figure(2, figsize=(12,10)) + s2 = f2.add_subplot(1,1,1) + s2.plot(data_src) + s2.plot(data_snk.real, 'g') + s2.plot(data_gr.real, 'r--') + pylab.show() if __name__ == "__main__": diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 4889312da..f96128805 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -76,7 +76,9 @@ add_custom_target(filter_generated_includes DEPENDS install(FILES api.h fir_filter.h + fft_filter.h ${generated_includes} + fft_filter_ccc.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter COMPONENT "fft_devel" ) diff --git a/gr-filter/include/filter/fft_filter.h b/gr-filter/include/filter/fft_filter.h new file mode 100644 index 000000000..fccea595f --- /dev/null +++ b/gr-filter/include/filter/fft_filter.h @@ -0,0 +1,105 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_FFT_FILTER_H +#define INCLUDED_FILTER_FFT_FILTER_H + +#include +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + /*! + * \brief Fast FFT filter with gr_complex input, gr_complex output and gr_complex taps + * \ingroup filter_blk + */ + class FILTER_API fft_filter_ccc + { + private: + int d_ntaps; + int d_nsamples; + int d_fftsize; // fftsize = ntaps + nsamples - 1 + int d_decimation; + fft::fft_complex *d_fwdfft; // forward "plan" + fft::fft_complex *d_invfft; // inverse "plan" + int d_nthreads; // number of FFTW threads to use + std::vector d_tail; // state carried between blocks for overlap-add + std::vector d_new_taps; + gr_complex *d_xformed_taps; // Fourier xformed taps + + void compute_sizes(int ntaps); + int tailsize() const { return d_ntaps - 1; } + + public: + /*! + * \brief Construct an FFT filter for complex vectors with the given taps and decimation rate. + * + * This is the basic implementation for performing FFT filter for fast convolution + * in other blocks for complex vectors (such as gr_fft_filter_ccc). + * + * \param decimation The decimation rate of the filter (int) + * \param taps The filter taps (complex) + * \param nthreads The number of threads for the FFT to use (int) + */ + fft_filter_ccc(int decimation, + const std::vector &taps, + int nthreads=1); + + ~fft_filter_ccc(); + + /*! + * \brief Set new taps for the filter. + * + * Sets new taps and resets the class properties to handle different sizes + * \param taps The filter taps (complex) + */ + int set_taps(const std::vector &taps); + + /*! + * \brief Set number of threads to use. + */ + void set_nthreads(int n); + + /*! + * \brief Get number of threads being used. + */ + int nthreads() const; + + /*! + * \brief Perform the filter operation + * + * \param nitems The number of items to produce + * \param input The input vector to be filtered + * \param output The result of the filter operation + */ + int filter(int nitems, const gr_complex *input, gr_complex *output); + }; + + } /* namespace impl */ + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_H */ diff --git a/gr-filter/include/filter/fft_filter_ccc.h b/gr-filter/include/filter/fft_filter_ccc.h new file mode 100644 index 000000000..a309ffc22 --- /dev/null +++ b/gr-filter/include/filter/fft_filter_ccc.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_FFT_FILTER_CCC_H +#define INCLUDED_FILTER_FFT_FILTER_CCC_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fft_filter_ccc : virtual public gr_sync_decimator + { + public: + // gr::filter::fft_filter::sptr + typedef boost::shared_ptr sptr; + + /*! + * \brief Fast FFT filter with gr_complex input, gr_complex output and gr_complex taps + * \ingroup filter_blk + * + * \param decimation >= 1 + * \param taps complex filter taps + * \param nthreads number of threads for the FFT to use + */ + static FILTER_API sptr make(int decimation, + const std::vector &taps, + int nthreads=1); + + virtual void set_taps(const std::vector &taps) = 0; + virtual std::vector taps() const = 0; + + /*! + * \brief Set number of threads to use. + */ + virtual void set_nthreads(int n) = 0; + + /*! + * \brief Get number of threads being used. + */ + virtual int nthreads() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_CCC_H */ diff --git a/gr-filter/include/filter/fft_filter_fff.h b/gr-filter/include/filter/fft_filter_fff.h new file mode 100644 index 000000000..309a55135 --- /dev/null +++ b/gr-filter/include/filter/fft_filter_fff.h @@ -0,0 +1,88 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_FFT_FILTER_FFF_H +#define INCLUDED_GR_FFT_FILTER_FFF_H + +#include +#include + +class gr_fft_filter_fff; +typedef boost::shared_ptr gr_fft_filter_fff_sptr; +GR_CORE_API gr_fft_filter_fff_sptr +gr_make_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads=1); + +class gri_fft_filter_fff_generic; +//class gri_fft_filter_fff_sse; + +/*! + * \brief Fast FFT filter with float input, float output and float taps + * \ingroup filter_blk + */ +class GR_CORE_API gr_fft_filter_fff : public gr_sync_decimator +{ + private: + friend GR_CORE_API gr_fft_filter_fff_sptr + gr_make_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads); + + int d_nsamples; + bool d_updated; +#if 1 // don't enable the sse version until handling it is worked out + gri_fft_filter_fff_generic *d_filter; +#else + gri_fft_filter_fff_sse *d_filter; +#endif + std::vector d_new_taps; + + /*! + * Construct a FFT filter with the given taps + * + * \param decimation >= 1 + * \param taps float filter taps + * \param nthreads number of threads for the FFT to use + */ + gr_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads=1); + + public: + ~gr_fft_filter_fff (); + + void set_taps (const std::vector &taps); + std::vector taps () const; + + /*! + * \brief Set number of threads to use. + */ + void set_nthreads(int n); + + /*! + * \brief Get number of threads being used. + */ + int nthreads() const; + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_GR_FFT_FILTER_FFF_H */ diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index c307fea46..525532fe6 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -20,13 +20,16 @@ * Boston, MA 02110-1301, USA. */ +#ifndef INCLUDED_FILTER_FIR_FILTER_H +#define INCLUDED_FILTER_FIR_FILTER_H + #include #include #include namespace gr { namespace filter { - namespace impl { + namespace kernel { class FILTER_API fir_filter_fff { @@ -110,3 +113,5 @@ namespace gr { } /* namespace impl */ } /* namespace filter */ } /* namespace gr */ + +#endif /* INCLUDED_FILTER_FIR_FILTER_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 784a3c109..aa03c4615 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -106,7 +106,9 @@ link_directories(${FFTW3F_LIBRARY_DIRS}) ######################################################################## list(APPEND filter_sources fir_filter.cc + fft_filter.cc ${generated_sources} + fft_filter_ccc_impl.cc ) list(APPEND filter_libs diff --git a/gr-filter/lib/fft_filter.cc b/gr-filter/lib/fft_filter.cc new file mode 100644 index 000000000..86b2a2fdb --- /dev/null +++ b/gr-filter/lib/fft_filter.cc @@ -0,0 +1,174 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + #define VERBOSE 0 + + fft_filter_ccc::fft_filter_ccc(int decimation, + const std::vector &taps, + int nthreads) + : d_fftsize(-1), d_decimation(decimation), d_fwdfft(0), + d_invfft(0), d_nthreads(nthreads) + { + set_taps(taps); + } + + fft_filter_ccc::~fft_filter_ccc() + { + delete d_fwdfft; + delete d_invfft; + fft::free(d_xformed_taps); + } + + /* + * determines d_ntaps, d_nsamples, d_fftsize, d_xformed_taps + */ + int + fft_filter_ccc::set_taps(const std::vector &taps) + { + int i = 0; + compute_sizes(taps.size()); + + d_tail.resize(tailsize()); + for(i = 0; i < tailsize(); i++) + d_tail[i] = 0; + + gr_complex *in = d_fwdfft->get_inbuf(); + gr_complex *out = d_fwdfft->get_outbuf(); + + float scale = 1.0 / d_fftsize; + + // Compute forward xform of taps. + // Copy taps into first ntaps slots, then pad with zeros + for(i = 0; i < d_ntaps; i++) + in[i] = taps[i] * scale; + + for(; i < d_fftsize; i++) + in[i] = 0; + + d_fwdfft->execute(); // do the xform + + // now copy output to d_xformed_taps + for(i = 0; i < d_fftsize; i++) + d_xformed_taps[i] = out[i]; + + return d_nsamples; + } + + // determine and set d_ntaps, d_nsamples, d_fftsize + void + fft_filter_ccc::compute_sizes(int ntaps) + { + int old_fftsize = d_fftsize; + d_ntaps = ntaps; + d_fftsize = (int) (2 * pow(2.0, ceil(log(double(ntaps)) / log(2.0)))); + d_nsamples = d_fftsize - d_ntaps + 1; + + if(VERBOSE) { + std::cerr << "fft_filter_ccc: ntaps = " << d_ntaps + << " fftsize = " << d_fftsize + << " nsamples = " << d_nsamples << std::endl; + } + + // compute new plans + if(d_fftsize != old_fftsize) { + delete d_fwdfft; + delete d_invfft; + d_fwdfft = new fft::fft_complex(d_fftsize, true, d_nthreads); + d_invfft = new fft::fft_complex(d_fftsize, false, d_nthreads); + d_xformed_taps = fft::malloc_complex(d_fftsize); + } + } + + void + fft_filter_ccc::set_nthreads(int n) + { + d_nthreads = n; + if(d_fwdfft) + d_fwdfft->set_nthreads(n); + if(d_invfft) + d_invfft->set_nthreads(n); + } + + int + fft_filter_ccc::nthreads() const + { + return d_nthreads; + } + + int + fft_filter_ccc::filter (int nitems, const gr_complex *input, gr_complex *output) + { + int dec_ctr = 0; + int j = 0; + int ninput_items = nitems * d_decimation; + + for(int i = 0; i < ninput_items; i += d_nsamples) { + memcpy(d_fwdfft->get_inbuf(), &input[i], d_nsamples * sizeof(gr_complex)); + + for(j = d_nsamples; j < d_fftsize; j++) + d_fwdfft->get_inbuf()[j] = 0; + + d_fwdfft->execute(); // compute fwd xform + + gr_complex *a = d_fwdfft->get_outbuf(); + gr_complex *b = d_xformed_taps; + gr_complex *c = d_invfft->get_inbuf(); + + volk_32fc_x2_multiply_32fc_a(c, a, b, d_fftsize); + + d_invfft->execute(); // compute inv xform + + // add in the overlapping tail + + for(j = 0; j < tailsize(); j++) + d_invfft->get_outbuf()[j] += d_tail[j]; + + // copy nsamples to output + j = dec_ctr; + while(j < d_nsamples) { + *output++ = d_invfft->get_outbuf()[j]; + j += d_decimation; + } + dec_ctr = (j - d_nsamples); + + // stash the tail + memcpy(&d_tail[0], d_invfft->get_outbuf() + d_nsamples, + tailsize() * sizeof(gr_complex)); + } + + return nitems; + } + } /* namespace impl */ + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fft_filter_ccc_impl.cc b/gr-filter/lib/fft_filter_ccc_impl.cc new file mode 100644 index 000000000..2721f128e --- /dev/null +++ b/gr-filter/lib/fft_filter_ccc_impl.cc @@ -0,0 +1,119 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fft_filter_ccc_impl.h" +#include + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + fft_filter_ccc::sptr fft_filter_ccc::make(int decimation, + const std::vector &taps, + int nthreads) + { + return gnuradio::get_initial_sptr(new fft_filter_ccc_impl + (decimation, taps, nthreads)); + } + + fft_filter_ccc_impl::fft_filter_ccc_impl(int decimation, + const std::vector &taps, + int nthreads) + : gr_sync_decimator("fft_filter_ccc", + gr_make_io_signature (1, 1, sizeof(gr_complex)), + gr_make_io_signature (1, 1, sizeof(gr_complex)), + decimation), + d_updated(false) + { + set_history(1); + + d_filter = new kernel::fft_filter_ccc(decimation, taps, nthreads); + + d_new_taps = taps; + d_nsamples = d_filter->set_taps(taps); + set_output_multiple(d_nsamples); + } + + fft_filter_ccc_impl::~fft_filter_ccc_impl() + { + delete d_filter; + } + + void + fft_filter_ccc_impl::set_taps(const std::vector &taps) + { + d_new_taps = taps; + d_updated = true; + } + + std::vector + fft_filter_ccc_impl::taps() const + { + return d_new_taps; + } + + void + fft_filter_ccc_impl::set_nthreads(int n) + { + if(d_filter) + d_filter->set_nthreads(n); + } + + int + fft_filter_ccc_impl::nthreads() const + { + if(d_filter) + return d_filter->nthreads(); + else + return 0; + } + + int + fft_filter_ccc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + if (d_updated){ + d_nsamples = d_filter->set_taps(d_new_taps); + d_updated = false; + set_output_multiple(d_nsamples); + return 0; // output multiple may have changed + } + + d_filter->filter(noutput_items, in, out); + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fft_filter_ccc_impl.h b/gr-filter/lib/fft_filter_ccc_impl.h new file mode 100644 index 000000000..2d8d61c5e --- /dev/null +++ b/gr-filter/lib/fft_filter_ccc_impl.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_FILTER_FFT_FILTER_CCC_IMPL_H +#define INCLUDED_FILTER_FFT_FILTER_CCC_IMPL_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fft_filter_ccc_impl : public fft_filter_ccc + { + private: + int d_nsamples; + bool d_updated; + kernel::fft_filter_ccc *d_filter; + std::vector d_new_taps; + + public: + fft_filter_ccc_impl(int decimation, + const std::vector &taps, + int nthreads=1); + + ~fft_filter_ccc_impl(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + + void set_nthreads(int n); + int nthreads() const; + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_CCC_IMPL_H */ diff --git a/gr-filter/lib/fft_filter_fff_impl.cc b/gr-filter/lib/fft_filter_fff_impl.cc new file mode 100644 index 000000000..a09feb7f1 --- /dev/null +++ b/gr-filter/lib/fft_filter_fff_impl.cc @@ -0,0 +1,123 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +gr_fft_filter_fff_sptr gr_make_fft_filter_fff (int decimation, + const std::vector &taps, + int nthreads) +{ + return gnuradio::get_initial_sptr(new gr_fft_filter_fff (decimation, taps, nthreads)); +} + + +gr_fft_filter_fff::gr_fft_filter_fff (int decimation, + const std::vector &taps, + int nthreads) + : gr_sync_decimator ("fft_filter_fff", + gr_make_io_signature (1, 1, sizeof (float)), + gr_make_io_signature (1, 1, sizeof (float)), + decimation), + d_updated(false) +{ + set_history(1); + +#if 1 // don't enable the sse version until handling it is worked out + d_filter = new gri_fft_filter_fff_generic(decimation, taps, nthreads); +#else + d_filter = new gri_fft_filter_fff_sse(decimation, taps); +#endif + + d_new_taps = taps; + d_nsamples = d_filter->set_taps(taps); + set_output_multiple(d_nsamples); +} + +gr_fft_filter_fff::~gr_fft_filter_fff () +{ + delete d_filter; +} + +void +gr_fft_filter_fff::set_taps (const std::vector &taps) +{ + d_new_taps = taps; + d_updated = true; +} + +std::vector +gr_fft_filter_fff::taps () const +{ + return d_new_taps; +} + +void +gr_fft_filter_fff::set_nthreads(int n) +{ + if(d_filter) + d_filter->set_nthreads(n); +} + +int +gr_fft_filter_fff::nthreads() const +{ + if(d_filter) + return d_filter->nthreads(); + else + return 0; +} + +int +gr_fft_filter_fff::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + float *out = (float *) output_items[0]; + + if (d_updated){ + d_nsamples = d_filter->set_taps(d_new_taps); + d_updated = false; + set_output_multiple(d_nsamples); + return 0; // output multiple may have changed + } + + assert(noutput_items % d_nsamples == 0); + + d_filter->filter(noutput_items, in, out); + + //assert((out - (float *) output_items[0]) == noutput_items); + + return noutput_items; +} diff --git a/gr-filter/lib/fft_filter_fff_impl.h b/gr-filter/lib/fft_filter_fff_impl.h new file mode 100644 index 000000000..309a55135 --- /dev/null +++ b/gr-filter/lib/fft_filter_fff_impl.h @@ -0,0 +1,88 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_FFT_FILTER_FFF_H +#define INCLUDED_GR_FFT_FILTER_FFF_H + +#include +#include + +class gr_fft_filter_fff; +typedef boost::shared_ptr gr_fft_filter_fff_sptr; +GR_CORE_API gr_fft_filter_fff_sptr +gr_make_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads=1); + +class gri_fft_filter_fff_generic; +//class gri_fft_filter_fff_sse; + +/*! + * \brief Fast FFT filter with float input, float output and float taps + * \ingroup filter_blk + */ +class GR_CORE_API gr_fft_filter_fff : public gr_sync_decimator +{ + private: + friend GR_CORE_API gr_fft_filter_fff_sptr + gr_make_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads); + + int d_nsamples; + bool d_updated; +#if 1 // don't enable the sse version until handling it is worked out + gri_fft_filter_fff_generic *d_filter; +#else + gri_fft_filter_fff_sse *d_filter; +#endif + std::vector d_new_taps; + + /*! + * Construct a FFT filter with the given taps + * + * \param decimation >= 1 + * \param taps float filter taps + * \param nthreads number of threads for the FFT to use + */ + gr_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads=1); + + public: + ~gr_fft_filter_fff (); + + void set_taps (const std::vector &taps); + std::vector taps () const; + + /*! + * \brief Set number of threads to use. + */ + void set_nthreads(int n); + + /*! + * \brief Get number of threads being used. + */ + int nthreads() const; + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_GR_FFT_FILTER_FFF_H */ diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 5f0e0912d..321f6981e 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -26,7 +26,7 @@ namespace gr { namespace filter { - namespace impl { + namespace kernel { fir_filter_fff::fir_filter_fff(int decimation, const std::vector &taps) @@ -55,7 +55,7 @@ namespace gr { d_ntaps = (int)taps.size(); d_taps = fft::malloc_float(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[i] = taps[i]; + d_taps[d_ntaps-i-1] = taps[i]; } } @@ -64,7 +64,7 @@ namespace gr { { std::vector t; for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[i]); + t.push_back(d_taps[d_ntaps-i-1]); return t; } @@ -134,7 +134,7 @@ namespace gr { d_ntaps = (int)taps.size(); d_taps = fft::malloc_complex(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[i] = gr_complex(taps[i],0); + d_taps[d_ntaps-i-1] = gr_complex(taps[i],0); } } @@ -143,7 +143,7 @@ namespace gr { { std::vector t; for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[i].real()); + t.push_back(d_taps[d_ntaps-i-1].real()); return t; } @@ -213,7 +213,7 @@ namespace gr { d_ntaps = (int)taps.size(); d_taps = fft::malloc_complex(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[i] = taps[i]; + d_taps[d_ntaps-i-1] = taps[i]; } } @@ -222,7 +222,7 @@ namespace gr { { std::vector t; for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[i]); + t.push_back(d_taps[d_ntaps-i-1]); return t; } diff --git a/gr-filter/lib/fir_filter_XXX_impl.cc.t b/gr-filter/lib/fir_filter_XXX_impl.cc.t index 70c3fba3f..c3637042d 100644 --- a/gr-filter/lib/fir_filter_XXX_impl.cc.t +++ b/gr-filter/lib/fir_filter_XXX_impl.cc.t @@ -26,8 +26,6 @@ #include "@IMPL_NAME@.h" #include -#include -#include namespace gr { namespace filter { @@ -46,9 +44,9 @@ namespace gr { gr_make_io_signature(1, 1, sizeof(@O_TYPE@)), decimation) { - d_fir = new impl::@BASE_NAME@(decimation, taps); + d_fir = new kernel::@BASE_NAME@(decimation, taps); d_updated = false; - set_history(d_fir->ntaps()+1); + set_history(d_fir->ntaps()); } @IMPL_NAME@::~@IMPL_NAME@() @@ -78,7 +76,7 @@ namespace gr { @O_TYPE@ *out = (@O_TYPE@*)output_items[0]; if (d_updated) { - set_history(d_fir->ntaps()+1); + set_history(d_fir->ntaps()); d_updated = false; return 0; // history requirements may have changed. } diff --git a/gr-filter/lib/fir_filter_XXX_impl.h.t b/gr-filter/lib/fir_filter_XXX_impl.h.t index a40c49bf2..d5bf7104d 100644 --- a/gr-filter/lib/fir_filter_XXX_impl.h.t +++ b/gr-filter/lib/fir_filter_XXX_impl.h.t @@ -35,7 +35,7 @@ namespace gr { class FILTER_API @IMPL_NAME@ : public @BASE_NAME@ { private: - impl::@BASE_NAME@ *d_fir; + kernel::@BASE_NAME@ *d_fir; bool d_updated; public: diff --git a/gr-filter/python/qa_fft_filter.py b/gr-filter/python/qa_fft_filter.py new file mode 100755 index 000000000..33d3d870b --- /dev/null +++ b/gr-filter/python/qa_fft_filter.py @@ -0,0 +1,380 @@ +#!/usr/bin/env python +# +# Copyright 2004,2005,2007,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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, or (at your option) +# any later version. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import sys +import random + +def make_random_complex_tuple(L): + result = [] + for x in range(L): + result.append(complex(random.uniform(-1000,1000), + random.uniform(-1000,1000))) + return tuple(result) + +def make_random_float_tuple(L): + result = [] + for x in range(L): + result.append(float(int(random.uniform(-1000,1000)))) + return tuple(result) + + +def reference_filter_ccc(dec, taps, input): + """ + compute result using conventional fir filter + """ + tb = gr.top_block() + #src = gr.vector_source_c(((0,) * (len(taps) - 1)) + input) + src = gr.vector_source_c(input) + op = filter.fir_filter_ccc(dec, taps) + dst = gr.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + return dst.data() + +def reference_filter_fff(dec, taps, input): + """ + compute result using conventional fir filter + """ + tb = gr.top_block() + #src = gr.vector_source_f(((0,) * (len(taps) - 1)) + input) + src = gr.vector_source_f(input) + op = filter.fir_filter_fff(dec, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + return dst.data() + + +def print_complex(x): + for i in x: + i = complex(i) + sys.stdout.write("(%6.3f,%6.3fj), " % (i.real, i.imag)) + sys.stdout.write('\n') + + +class test_fft_filter(gr_unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def assert_fft_ok2(self, expected_result, result_data): + expected_result = expected_result[:len(result_data)] + self.assertComplexTuplesAlmostEqual2 (expected_result, result_data, + abs_eps=1e-9, rel_eps=4e-4) + + def assert_fft_float_ok2(self, expected_result, result_data, abs_eps=1e-9, rel_eps=4e-4): + expected_result = expected_result[:len(result_data)] + self.assertFloatTuplesAlmostEqual2 (expected_result, result_data, + abs_eps, rel_eps) + + def test_ccc_001(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (1,) + expected_result = tuple([complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_ccc_002(self): + # Test nthreads + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + nthreads = 2 + expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps, nthreads) + dst = gr.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + def test_ccc_003(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_ccc_004(self): + random.seed(0) + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_complex_tuple(ntaps) + expected_result = reference_filter_ccc(1, taps, src_data) + + src = gr.vector_source_c(src_data) + op = filter.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + del tb + self.assert_fft_ok2(expected_result, result_data) + + def test_ccc_005(self): + random.seed(0) + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_complex_tuple(ntaps) + expected_result = reference_filter_ccc(dec, taps, src_data) + + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(dec, taps) + dst = gr.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + del tb + result_data = dst.data() + + self.assert_fft_ok2(expected_result, result_data) + + def test_ccc_006(self): + # Test decimating with nthreads=2 + random.seed(0) + nthreads = 2 + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_complex_tuple(ntaps) + expected_result = reference_filter_ccc(dec, taps, src_data) + + src = gr.vector_source_c(src_data) + op = filter.fft_filter_ccc(dec, taps, nthreads) + dst = gr.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + del tb + result_data = dst.data() + + self.assert_fft_ok2(expected_result, result_data) + + # ---------------------------------------------------------------- + # test _fff version + # ---------------------------------------------------------------- + + def test_fff_001(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (1,) + expected_result = tuple([float(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_fff_002(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + expected_result = tuple([2 * float(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5) + + def test_fff_003(self): + # Test 02 with nthreads + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + nthreads = 2 + expected_result = tuple([2 * float(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps, nthreads) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5) + + def xtest_fff_004(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4096 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(1, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + + #print "src_len =", src_len, " ntaps =", ntaps + try: + self.assert_fft_float_ok2(expected_result, result_data, abs_eps=1.0) + except: + expected = open('expected', 'w') + for x in expected_result: + expected.write(`x` + '\n') + actual = open('actual', 'w') + for x in result_data: + actual.write(`x` + '\n') + raise + + def xtest_fff_005(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4*1024 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(1, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + + self.assert_fft_float_ok2(expected_result, result_data, abs_eps=2.0) + + def xtest_fff_006(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(dec, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(dec, taps) + dst = gr.vector_sink_f() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + + self.assert_fft_float_ok2(expected_result, result_data) + + def xtest_fff_007(self): + # test decimation with nthreads + random.seed(0) + nthreads = 2 + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(dec, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(dec, taps, nthreads) + dst = gr.vector_sink_f() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + + self.assert_fft_float_ok2(expected_result, result_data) + + def test_fff_get0(self): + random.seed(0) + for i in xrange(25): + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + + op = gr.fft_filter_fff(1, taps) + result_data = op.taps() + #print result_data + + self.assertEqual(taps, result_data) + + def test_ccc_get0(self): + random.seed(0) + for i in xrange(25): + ntaps = int(random.uniform(2, 100)) + taps = make_random_complex_tuple(ntaps) + + op = gr.fft_filter_ccc(1, taps) + result_data = op.taps() + #print result_data + + self.assertComplexTuplesAlmostEqual(taps, result_data, 4) + + +if __name__ == '__main__': + gr_unittest.run(test_fft_filter, "test_fft_filter.xml") + diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py index 2c88e7830..f0f08afca 100755 --- a/gr-filter/python/qa_fir_filter.py +++ b/gr-filter/python/qa_fir_filter.py @@ -32,7 +32,7 @@ class test_filter(gr_unittest.TestCase): def test_fir_filter_fff_001(self): src_data = [1, 2, 3, 4] - expected_data = [0, 0.5, 1.5, 2.5] + expected_data = [0.5, 1.5, 2.5, 3.5] src = gr.vector_source_f(src_data) op = filter.fir_filter_fff(1, [0.5, 0.5]) dst = gr.vector_sink_f() @@ -43,7 +43,7 @@ class test_filter(gr_unittest.TestCase): def test_fir_filter_ccf_001(self): src_data = [1+1j, 2+2j, 3+3j, 4+4j] - expected_data = [0+0j, 0.5+0.5j, 1.5+1.5j, 2.5+2.5j] + expected_data = [0.5+0.5j, 1.5+1.5j, 2.5+2.5j, 3.5+3.5j] src = gr.vector_source_c(src_data) op = filter.fir_filter_ccf(1, [0.5, 0.5]) dst = gr.vector_sink_c() @@ -54,7 +54,7 @@ class test_filter(gr_unittest.TestCase): def test_fir_filter_ccc_001(self): src_data = [1+1j, 2+2j, 3+3j, 4+4j] - expected_data = [0+0j, -0.5+1.5j, -1.5+4.5j, -2.5+7.5j] + expected_data = [-0.5+1.5j, -1.5+4.5j, -2.5+7.5j, -3.5+10.5j] src = gr.vector_source_c(src_data) op = filter.fir_filter_ccc(1, [0.5+1j, 0.5+1j]) dst = gr.vector_sink_c() @@ -63,6 +63,22 @@ class test_filter(gr_unittest.TestCase): result_data = dst.data() self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + def test_fir_filter_ccc_002(self): + src_data = 10*[1+1j, 2+2j, 3+3j, 4+4j] + + # results derived from original gr.fir_filter_ccc + expected_data = ((7.537424837948042e-20+7.537424837948042e-20j), (9.131923434324563e-05+9.131923434324563e-05j), (0.0003317668742965907+0.0003317668742965907j), (0.0007230418268591166+0.0007230418268591166j), (0.0012087896466255188+0.0012087896466255188j), (0.0013292605290189385+0.0013292605290189385j), (0.001120875240303576+0.001120875240303576j), (0.000744672492146492+0.000744672492146492j), (0.000429437990533188+0.000429437990533188j), (2.283908543176949e-05+2.283908543176949e-05j), (-0.0002245186478830874-0.0002245186478830874j), (-0.0001157080550910905-0.0001157080550910905j), (0.00041409023106098175+0.00041409023106098175j), (0.0009017843985930085+0.0009017843985930085j), (0.0012520025484263897+0.0012520025484263897j), (0.0014116164529696107+0.0014116164529696107j), (0.001393353333696723+0.001393353333696723j), (0.000912194955162704+0.000912194955162704j), (0.00022649182938039303+0.00022649182938039303j), (-0.00031363096786662936-0.00031363096786662936j), (-0.0003966730728279799-0.0003966730728279799j), (-0.00023757052258588374-0.00023757052258588374j), (0.00021952332463115454+0.00021952332463115454j), (0.0009092430118471384+0.0009092430118471384j), (0.001662317430600524+0.001662317430600524j), (0.0019024648936465383+0.0019024648936465383j), (0.0015955769922584295+0.0015955769922584295j), (0.0009144138311967254+0.0009144138311967254j), (0.0001872836146503687+0.0001872836146503687j), (-0.000581968342885375-0.000581968342885375j), (-0.0009886166080832481-0.0009886166080832481j), (-0.0007480768254026771-0.0007480768254026771j), (0.00018211957649327815+0.00018211957649327815j), (0.0012042406015098095+0.0012042406015098095j), (0.0020200139842927456+0.0020200139842927456j), (0.0023816542234271765+0.0023816542234271765j), (0.002195809967815876+0.002195809967815876j), (0.0012113333214074373+0.0012113333214074373j), (-0.00014088614261709154-0.00014088614261709154j), (-0.0012574587017297745-0.0012574587017297745j)) + + taps = gr.firdes.low_pass(1, 1, 0.1, 0.01) + src = gr.vector_source_c(src_data) + op = filter.fir_filter_ccc(1, taps) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + if __name__ == '__main__': gr_unittest.run(test_filter, "test_filter.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 28268528a..94ad9cd64 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -31,12 +31,15 @@ #include "filter/fir_filter_fff.h" #include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" +#include "filter/fft_filter_ccc.h" %} %include "filter/fir_filter_fff.h" %include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" +%include "filter/fft_filter_ccc.h" GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); -- cgit