summaryrefslogtreecommitdiff
path: root/gr-filter
diff options
context:
space:
mode:
authorTom Rondeau2012-06-19 20:36:04 -0400
committerTom Rondeau2012-06-19 20:36:04 -0400
commitfad32dc397d731493dc88367d2b4648b35bb905b (patch)
tree9ed0d879ec2bf7099b09ba9d45ae6a2acdd32ec2 /gr-filter
parent5ab78aaeedb87f4a30c41dd9e7ede56b5dc01d04 (diff)
downloadgnuradio-fad32dc397d731493dc88367d2b4648b35bb905b.tar.gz
gnuradio-fad32dc397d731493dc88367d2b4648b35bb905b.tar.bz2
gnuradio-fad32dc397d731493dc88367d2b4648b35bb905b.zip
filter: adding PFB synthesis filter with GRC and QA.
Diffstat (limited to 'gr-filter')
-rw-r--r--gr-filter/grc/CMakeLists.txt1
-rw-r--r--gr-filter/grc/filter_block_tree.xml1
-rw-r--r--gr-filter/grc/pfb_synthesizer.xml57
-rw-r--r--gr-filter/include/filter/CMakeLists.txt1
-rw-r--r--gr-filter/include/filter/pfb_synthesizer_ccf.h107
-rw-r--r--gr-filter/lib/CMakeLists.txt1
-rw-r--r--gr-filter/lib/pfb_synthesizer_ccf_impl.cc288
-rw-r--r--gr-filter/lib/pfb_synthesizer_ccf_impl.h85
-rwxr-xr-xgr-filter/python/qa_pfb_synthesizer.py87
-rw-r--r--gr-filter/swig/filter_swig.i3
10 files changed, 631 insertions, 0 deletions
diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt
index 0747c7240..8bbb4c9e0 100644
--- a/gr-filter/grc/CMakeLists.txt
+++ b/gr-filter/grc/CMakeLists.txt
@@ -32,6 +32,7 @@ install(FILES
pfb_channelizer.xml
pfb_decimator.xml
pfb_interpolator.xml
+ pfb_synthesizer.xml
rational_resampler_base_xxx.xml
single_pole_iir_filter_xx.xml
DESTINATION ${GRC_BLOCKS_DIR}
diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml
index de840a3fd..b16966eae 100644
--- a/gr-filter/grc/filter_block_tree.xml
+++ b/gr-filter/grc/filter_block_tree.xml
@@ -43,6 +43,7 @@
<block>pfb_channelizer_ccf</block>
<block>pfb_decimator_ccf</block>
<block>pfb_interpolator_ccf</block>
+ <block>pfb_synthesizer_ccf</block>
<block>rational_resampler_base_xxx</block>
<block>single_pole_iir_filter_xx</block>
</cat>
diff --git a/gr-filter/grc/pfb_synthesizer.xml b/gr-filter/grc/pfb_synthesizer.xml
new file mode 100644
index 000000000..e84b25e62
--- /dev/null
+++ b/gr-filter/grc/pfb_synthesizer.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!--
+###################################################
+##Polyphase Synthesis Filterbank
+###################################################
+ -->
+<block>
+ <name>Polyphase Synthesizer</name>
+ <key>pfb_synthesizer_ccf</key>
+ <import>from gnuradio import filter</import>
+ <import>from gnuradio.filter import firdes</import>
+ <make>filter.pfb_synthesizer_ccf(
+ $numchans, $taps, $twox)
+self.$(id).set_channel_map($ch_map)
+ </make>
+ <callback>set_taps($taps)</callback>
+ <callback>set_channel_map($ch_map)</callback>
+
+ <param>
+ <name>Channels</name>
+ <key>numchans</key>
+ <value>2</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Connections</name>
+ <key>connections</key>
+ <value>2</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Taps</name>
+ <key>taps</key>
+ <type>real_vector</type>
+ </param>
+ <param>
+ <name>2x Sample Rate</name>
+ <key>twox</key>
+ <value>False</value>
+ <type>bool</type>
+ </param>
+ <param>
+ <name>Channel Map</name>
+ <key>ch_map</key>
+ <value>[]</value>
+ <type>int_vector</type>
+ </param>
+ <sink>
+ <name>in</name>
+ <type>complex</type>
+ <nports>$connections</nports>
+ </sink>
+ <source>
+ <name>out</name>
+ <type>complex</type>
+ </source>
+</block>
diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt
index 9174bfc84..b30a1fc76 100644
--- a/gr-filter/include/filter/CMakeLists.txt
+++ b/gr-filter/include/filter/CMakeLists.txt
@@ -106,6 +106,7 @@ install(FILES
pfb_channelizer_ccf.h
pfb_decimator_ccf.h
pfb_interpolator_ccf.h
+ pfb_synthesizer_ccf.h
single_pole_iir_filter_cc.h
single_pole_iir_filter_ff.h
DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter
diff --git a/gr-filter/include/filter/pfb_synthesizer_ccf.h b/gr-filter/include/filter/pfb_synthesizer_ccf.h
new file mode 100644
index 000000000..ec6fb49c2
--- /dev/null
+++ b/gr-filter/include/filter/pfb_synthesizer_ccf.h
@@ -0,0 +1,107 @@
+/* -*- 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_PFB_SYNTHESIZER_CCF_H
+#define INCLUDED_PFB_SYNTHESIZER_CCF_H
+
+#include <filter/api.h>
+#include <gr_sync_interpolator.h>
+
+namespace gr {
+ namespace filter {
+
+ /*!
+ * \class pfb_synthesizer_ccf
+ *
+ * \brief Polyphase synthesis filterbank with
+ * gr_complex input, gr_complex output and float taps
+ *
+ * \ingroup filter_blk
+ * \ingroup pfb_blk
+ */
+
+ class FILTER_API pfb_synthesizer_ccf : virtual public gr_sync_interpolator
+ {
+ public:
+ // gr::filter::pfb_synthesizer_ccf::sptr
+ typedef boost::shared_ptr<pfb_synthesizer_ccf> sptr;
+
+ /*!
+ * Build the polyphase synthesis filterbank.
+ * \param numchans (unsigned integer) Specifies the number of
+ * channels <EM>M</EM>
+ * \param taps (vector/list of floats) The prototype filter to
+ * populate the filterbank.
+ * \param twox (bool) use 2x oversampling or not (default is no)
+ */
+ static FILTER_API sptr make(unsigned int numchans,
+ const std::vector<float> &taps,
+ bool twox=false);
+
+ /*!
+ * Resets the filterbank's filter taps with the new prototype filter
+ * \param taps (vector/list of floats) The prototype filter to
+ * populate the filterbank.
+ */
+ virtual void set_taps(const std::vector<float> &taps) = 0;
+
+ /*!
+ * Print all of the filterbank taps to screen.
+ */
+ virtual void print_taps() = 0;
+
+ /*!
+ * Return a vector<vector<>> of the filterbank taps
+ */
+ virtual std::vector<std::vector<float> > taps() const = 0;
+
+ /*!
+ * Set the channel map. Channels are numbers as:
+ * N/2+1 | ... | N-1 | 0 | 1 | 2 | ... | N/2
+ * <------------------- 0 -------------------->
+ * freq
+ *
+ * So input stream 0 goes to channel 0, etc. Setting a new channel
+ * map allows the user to specify where in frequency he/she wants
+ * the input stream to go. This is especially useful to avoid
+ * putting signals into the channels on the edge of the spectrum
+ * which can either wrap around (in the case of odd number of
+ * channels) and be affected by filter rolloff in the transmitter.
+ *
+ * The map must be at least the number of streams being sent to the
+ * block. Less and the algorithm will not have enough data to
+ * properly setup the buffers. Any more channels specified will be
+ * ignored.
+ */
+ virtual void set_channel_map(const std::vector<int> &map) = 0;
+
+ /*!
+ * Gets the current channel map.
+ */
+ virtual std::vector<int> channel_map() const = 0;
+ };
+
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* INCLUDED_PFB_SYNTHESIZER_CCF_H */
diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt
index b3d456ec9..e29d69db5 100644
--- a/gr-filter/lib/CMakeLists.txt
+++ b/gr-filter/lib/CMakeLists.txt
@@ -133,6 +133,7 @@ list(APPEND filter_sources
pfb_channelizer_ccf_impl.cc
pfb_decimator_ccf_impl.cc
pfb_interpolator_ccf_impl.cc
+ pfb_synthesizer_ccf_impl.cc
single_pole_iir_filter_cc_impl.cc
single_pole_iir_filter_ff_impl.cc
)
diff --git a/gr-filter/lib/pfb_synthesizer_ccf_impl.cc b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc
new file mode 100644
index 000000000..1e9c099c1
--- /dev/null
+++ b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc
@@ -0,0 +1,288 @@
+/* -*- 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 "pfb_synthesizer_ccf_impl.h"
+#include <gr_io_signature.h>
+#include <cstdio>
+
+namespace gr {
+ namespace filter {
+
+ pfb_synthesizer_ccf::sptr
+ pfb_synthesizer_ccf::make(unsigned int numchans,
+ const std::vector<float> &taps,
+ bool twox)
+ {
+ return gnuradio::get_initial_sptr
+ (new pfb_synthesizer_ccf_impl(numchans, taps, twox));
+ }
+
+
+ pfb_synthesizer_ccf_impl::pfb_synthesizer_ccf_impl(unsigned int numchans,
+ const std::vector<float> &taps,
+ bool twox)
+ : gr_sync_interpolator("pfb_synthesizer_ccf",
+ gr_make_io_signature(1, numchans, sizeof(gr_complex)),
+ gr_make_io_signature(1, 1, sizeof(gr_complex)),
+ numchans),
+ d_updated (false), d_numchans(numchans), d_state(0)
+ {
+ // set up 2x multiplier; if twox==True, set to 2, otherwise to 1
+ d_twox = (twox ? 2 : 1);
+ if(d_numchans % d_twox != 0) {
+ throw std::invalid_argument("pfb_synthesizer_ccf: number of channels must be even for 2x oversampling.\n");
+ }
+
+ d_filters = std::vector<kernel::fir_filter_with_buffer_ccf*>(d_twox*d_numchans);
+ d_channel_map.resize(d_twox*d_numchans);
+
+ // Create a FIR filter for each channel and zero out the taps
+ std::vector<float> vtaps(0, d_twox*d_numchans);
+ for(unsigned int i = 0; i < d_twox*d_numchans; i++) {
+ d_filters[i] = new kernel::fir_filter_with_buffer_ccf(vtaps);
+ d_channel_map[i] = i;
+ }
+
+ // Now, actually set the filters' taps
+ set_taps(taps);
+
+ // Create the IFFT to handle the input channel rotations
+ d_fft = new fft::fft_complex(d_twox*d_numchans, false);
+ memset(d_fft->get_inbuf(), 0, d_twox*d_numchans*sizeof(gr_complex));
+
+ set_output_multiple(d_numchans);
+ }
+
+ pfb_synthesizer_ccf_impl::~pfb_synthesizer_ccf_impl()
+ {
+ for(unsigned int i = 0; i < d_twox*d_numchans; i++) {
+ delete d_filters[i];
+ }
+ }
+
+ void
+ pfb_synthesizer_ccf_impl::set_taps(const std::vector<float> &taps)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ if(d_twox == 1)
+ set_taps1(taps);
+ else
+ set_taps2(taps);
+
+ // Set the history to ensure enough input items for each filter
+ set_history(d_taps_per_filter+1);
+
+ d_updated = true;
+ }
+
+ void
+ pfb_synthesizer_ccf_impl::set_taps1(const std::vector<float> &taps)
+ {
+ unsigned int i,j;
+
+ unsigned int ntaps = taps.size();
+ d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_numchans);
+
+ // Create d_numchan vectors to store each channel's taps
+ d_taps.resize(d_numchans);
+
+ // Make a vector of the taps plus fill it out with 0's to fill
+ // each polyphase filter with exactly d_taps_per_filter
+ std::vector<float> tmp_taps;
+ tmp_taps = taps;
+ while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) {
+ tmp_taps.push_back(0.0);
+ }
+
+ // Partition the filter
+ for(i = 0; i < d_numchans; i++) {
+ // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out
+ d_taps[i] = std::vector<float>(d_taps_per_filter, 0);
+ for(j = 0; j < d_taps_per_filter; j++) {
+ d_taps[i][j] = tmp_taps[i + j*d_numchans]; // add taps to channels in reverse order
+ }
+
+ // Build a filter for each channel and add it's taps to it
+ d_filters[i]->set_taps(d_taps[i]);
+ }
+ }
+
+ void
+ pfb_synthesizer_ccf_impl::set_taps2 (const std::vector<float> &taps)
+ {
+ unsigned int i,j;
+ int state = 0;
+
+ unsigned int ntaps = taps.size();
+ d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_numchans);
+
+ // Create d_numchan vectors to store each channel's taps
+ d_taps.resize(d_twox*d_numchans);
+
+ // Make a vector of the taps plus fill it out with 0's to fill
+ // each polyphase filter with exactly d_taps_per_filter
+ std::vector<float> tmp_taps;
+ tmp_taps = taps;
+ while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) {
+ tmp_taps.push_back(0.0);
+ }
+
+ // Partition the filter
+ for(i = 0; i < d_numchans; i++) {
+ // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out
+ d_taps[i] = std::vector<float>(d_taps_per_filter, 0);
+ d_taps[d_numchans+i] = std::vector<float>(d_taps_per_filter, 0);
+ state = 0;
+ for(j = 0; j < d_taps_per_filter; j++) {
+ // add taps to channels in reverse order
+ // Zero out every other tap
+ if(state == 0) {
+ d_taps[i][j] = tmp_taps[i + j*d_numchans];
+ d_taps[d_numchans + i][j] = 0;
+ state = 1;
+ }
+ else {
+ d_taps[i][j] = 0;
+ d_taps[d_numchans + i][j] = tmp_taps[i + j*d_numchans];
+ state = 0;
+ }
+ }
+
+ // Build a filter for each channel and add it's taps to it
+ d_filters[i]->set_taps(d_taps[i]);
+ d_filters[d_numchans + i]->set_taps(d_taps[d_numchans + i]);
+ }
+ }
+
+ void
+ pfb_synthesizer_ccf_impl::print_taps()
+ {
+ unsigned int i, j;
+ for(i = 0; i < d_twox*d_numchans; i++) {
+ printf("filter[%d]: [", i);
+ for(j = 0; j < d_taps_per_filter; j++) {
+ printf(" %.4e", d_taps[i][j]);
+ }
+ printf("]\n\n");
+ }
+ }
+
+ std::vector< std::vector<float> >
+ pfb_synthesizer_ccf_impl::taps() const
+ {
+ return d_taps;
+ }
+
+ void
+ pfb_synthesizer_ccf_impl::set_channel_map(const std::vector<int> &map)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ if(map.size() > 0) {
+ unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end());
+ unsigned int min = (unsigned int)*std::min_element(map.begin(), map.end());
+ if((max >= d_twox*d_numchans) || (min < 0)) {
+ throw std::invalid_argument("gr_pfb_synthesizer_ccf::set_channel_map: map range out of bounds.\n");
+ }
+ d_channel_map = map;
+
+ // Zero out fft buffer so that unused channels are always 0
+ memset(d_fft->get_inbuf(), 0,d_twox*d_numchans*sizeof(gr_complex));
+ }
+ }
+
+ std::vector<int>
+ pfb_synthesizer_ccf_impl::channel_map() const
+ {
+ return d_channel_map;
+ }
+
+ int
+ pfb_synthesizer_ccf_impl::work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ gr_complex *in = (gr_complex*)input_items[0];
+ gr_complex *out = (gr_complex*)output_items[0];
+
+ if(d_updated) {
+ d_updated = false;
+ return 0; // history requirements may have changed.
+ }
+
+ unsigned int n, i;
+ size_t ninputs = input_items.size();
+
+ // Algoritm for critically sampled channels
+ if(d_twox == 1) {
+ for(n = 0; n < noutput_items/d_numchans; n++) {
+ for(i = 0; i < ninputs; i++) {
+ in = (gr_complex*)input_items[i];
+ d_fft->get_inbuf()[d_channel_map[i]] = in[n];
+ }
+
+ // spin through IFFT
+ d_fft->execute();
+
+ for(i = 0; i < d_numchans; i++) {
+ out[i] = d_filters[i]->filter(d_fft->get_outbuf()[i]);
+ }
+ out += d_numchans;
+ }
+ }
+
+ // Algorithm for oversampling by 2x
+ else {
+ for(n = 0; n < noutput_items/d_numchans; n++) {
+ for(i = 0; i < ninputs; i++) {
+ in = (gr_complex*)input_items[i];
+ d_fft->get_inbuf()[d_channel_map[i]] = in[n];
+ }
+
+ // spin through IFFT
+ d_fft->execute();
+
+ // Output is sum of two filters, but the input buffer to the filters must be circularly
+ // shifted by numchans every time through, done by using d_state to determine which IFFT
+ // buffer position to pull from.
+ for(i = 0; i < d_numchans; i++) {
+ out[i] = d_filters[i]->filter(d_fft->get_outbuf()[d_state*d_numchans+i]);
+ out[i] += d_filters[d_numchans+i]->filter(d_fft->get_outbuf()[(d_state^1)*d_numchans+i]);
+ }
+ d_state ^= 1;
+
+ out += d_numchans;
+ }
+ }
+
+ return noutput_items;
+ }
+
+ } /* namespace filter */
+} /* namespace gr */
diff --git a/gr-filter/lib/pfb_synthesizer_ccf_impl.h b/gr-filter/lib/pfb_synthesizer_ccf_impl.h
new file mode 100644
index 000000000..adffc143f
--- /dev/null
+++ b/gr-filter/lib/pfb_synthesizer_ccf_impl.h
@@ -0,0 +1,85 @@
+/* -*- 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_PFB_SYNTHESIZER_CCF_IMPL_H
+#define INCLUDED_PFB_SYNTHESIZER_CCF_IMPL_H
+
+#include <filter/pfb_synthesizer_ccf.h>
+#include <filter/fir_filter_with_buffer.h>
+#include <fft/fft.h>
+#include <gruel/thread.h>
+
+namespace gr {
+ namespace filter {
+
+ // While this is a polyphase_filterbank, we don't use the normal
+ // parent class because we have to use the fir_filter_with_buffer
+ // objects instead of normal filters.
+
+ class FILTER_API pfb_synthesizer_ccf_impl : public pfb_synthesizer_ccf
+ {
+ private:
+ bool d_updated;
+ unsigned int d_numchans;
+ unsigned int d_taps_per_filter;
+ fft::fft_complex *d_fft;
+ std::vector< kernel::fir_filter_with_buffer_ccf*> d_filters;
+ std::vector< std::vector<float> > d_taps;
+ int d_state;
+ std::vector<int> d_channel_map;
+ unsigned int d_twox;
+ gruel::mutex d_mutex; // mutex to protect set/work access
+
+ /*!
+ * \brief Tap setting algorithm for critically sampled channels
+ */
+ void set_taps1(const std::vector<float> &taps);
+
+ /*!
+ * \brief Tap setting algorithm for 2x over-sampled channels
+ */
+ void set_taps2(const std::vector<float> &taps);
+
+
+ public:
+ pfb_synthesizer_ccf_impl(unsigned int numchans,
+ const std::vector<float> &taps,
+ bool twox);
+ ~pfb_synthesizer_ccf_impl();
+
+ void set_taps(const std::vector<float> &taps);
+ std::vector<std::vector<float> > taps() const;
+ void print_taps();
+
+ void set_channel_map(const std::vector<int> &map);
+ std::vector<int> channel_map() 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_PFB_SYNTHESIZER_CCF_IMPL_H */
diff --git a/gr-filter/python/qa_pfb_synthesizer.py b/gr-filter/python/qa_pfb_synthesizer.py
new file mode 100755
index 000000000..8b69ccb12
--- /dev/null
+++ b/gr-filter/python/qa_pfb_synthesizer.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+#
+# Copyright 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 math
+
+class test_pfb_synthesizer(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_000(self):
+ N = 10000 # number of samples to use
+ M = 5 # Number of channels
+ fs = 1000 # baseband sampling rate
+ ofs = M*fs # input samp rate to decimator
+
+ taps = gr.firdes.low_pass_2(M, ofs, fs/2, fs/10,
+ attenuation_dB=80,
+ window=gr.firdes.WIN_BLACKMAN_hARRIS)
+
+ signals = list()
+ freqs = [0, 100, 200, -200, -100]
+ for i in xrange(len(freqs)):
+ signals.append(gr.sig_source_c(fs, gr.GR_SIN_WAVE, freqs[i], 1))
+
+ head = gr.head(gr.sizeof_gr_complex, N)
+ pfb = filter.pfb_synthesizer_ccf(M, taps)
+ snk = gr.vector_sink_c()
+
+ for i in xrange(M):
+ self.tb.connect(signals[i], (pfb,i))
+
+ self.tb.connect(pfb, head, snk)
+
+ self.tb.run()
+
+ Ntest = 1000
+ L = len(snk.data())
+ t = map(lambda x: float(x)/ofs, xrange(L))
+
+ # Create known data as sum of complex sinusoids at freqs
+ # of the output channels.
+ freqs = [-2200, -1100, 0, 1100, 2200]
+ expected_data = len(t)*[0,]
+ for i in xrange(len(t)):
+ expected_data[i] = math.cos(2.*math.pi*freqs[0]*t[i]) + \
+ 1j*math.sin(2.*math.pi*freqs[0]*t[i]) + \
+ math.cos(2.*math.pi*freqs[1]*t[i]) + \
+ 1j*math.sin(2.*math.pi*freqs[1]*t[i]) + \
+ math.cos(2.*math.pi*freqs[2]*t[i]) + \
+ 1j*math.sin(2.*math.pi*freqs[2]*t[i]) + \
+ math.cos(2.*math.pi*freqs[3]*t[i]) + \
+ 1j*math.sin(2.*math.pi*freqs[3]*t[i]) + \
+ math.cos(2.*math.pi*freqs[4]*t[i]) + \
+ 1j*math.sin(2.*math.pi*freqs[4]*t[i])
+ dst_data = snk.data()
+
+ offset = 25
+ self.assertComplexTuplesAlmostEqual(expected_data[2000-offset:2000-offset+Ntest],
+ dst_data[2000:2000+Ntest], 4)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_pfb_synthesizer, "test_pfb_synthesizer.xml")
diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i
index 5a8ebf975..bb4eab381 100644
--- a/gr-filter/swig/filter_swig.i
+++ b/gr-filter/swig/filter_swig.i
@@ -64,6 +64,7 @@
#include "filter/pfb_channelizer_ccf.h"
#include "filter/pfb_decimator_ccf.h"
#include "filter/pfb_interpolator_ccf.h"
+#include "filter/pfb_synthesizer_ccf.h"
#include "filter/rational_resampler_base_ccc.h"
#include "filter/rational_resampler_base_ccf.h"
#include "filter/rational_resampler_base_fcc.h"
@@ -110,6 +111,7 @@
%include "filter/pfb_channelizer_ccf.h"
%include "filter/pfb_decimator_ccf.h"
%include "filter/pfb_interpolator_ccf.h"
+%include "filter/pfb_synthesizer_ccf.h"
%include "filter/rational_resampler_base_ccc.h"
%include "filter/rational_resampler_base_ccf.h"
%include "filter/rational_resampler_base_fcc.h"
@@ -153,6 +155,7 @@ GR_SWIG_BLOCK_MAGIC2(filter, pfb_arb_resampler_fff);
GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf);
GR_SWIG_BLOCK_MAGIC2(filter, pfb_decimator_ccf);
GR_SWIG_BLOCK_MAGIC2(filter, pfb_interpolator_ccf);
+GR_SWIG_BLOCK_MAGIC2(filter, pfb_synthesizer_ccf);
GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_ccc);
GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_ccf);
GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_fcc);