summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gr-filter/include/filter/CMakeLists.txt3
-rw-r--r--gr-filter/include/filter/polyphase_filterbank.h148
-rw-r--r--gr-filter/lib/CMakeLists.txt1
-rw-r--r--gr-filter/lib/pfb_channelizer_ccf_impl.cc102
-rw-r--r--gr-filter/lib/pfb_channelizer_ccf_impl.h24
-rw-r--r--gr-filter/lib/polyphase_filterbank.cc114
6 files changed, 302 insertions, 90 deletions
diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt
index c64178696..0c067fcba 100644
--- a/gr-filter/include/filter/CMakeLists.txt
+++ b/gr-filter/include/filter/CMakeLists.txt
@@ -76,10 +76,11 @@ add_custom_target(filter_generated_includes DEPENDS
install(FILES
api.h
firdes.h
- pm_remez.h
fir_filter.h
fir_filter_with_buffer.h
fft_filter.h
+ pm_remez.h
+ polyphase_filterbank.h
${generated_includes}
dc_blocker_cc.h
dc_blocker_ff.h
diff --git a/gr-filter/include/filter/polyphase_filterbank.h b/gr-filter/include/filter/polyphase_filterbank.h
new file mode 100644
index 000000000..50d087340
--- /dev/null
+++ b/gr-filter/include/filter/polyphase_filterbank.h
@@ -0,0 +1,148 @@
+/* -*- c++ -*- */
+/*
+ * 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.
+ */
+
+
+#ifndef INCLUDED_FILTER_POLYPHASE_FILTERBANK_H
+#define INCLUDED_FILTER_POLYPHASE_FILTERBANK_H
+
+#include <filter/api.h>
+#include <filter/fir_filter.h>
+#include <fft/fft.h>
+
+namespace gr {
+ namespace filter {
+ namespace kernel {
+
+ /*!
+ * \class polyphase_filterbank
+ *
+ * \brief Polyphase filterbank parent class
+ *
+ * \ingroup filter_blk
+ * \ingroup pfb_blk
+ *
+ * This block takes in complex inputs and channelizes it to
+ * <EM>M</EM> channels of equal bandwidth. Each of the resulting
+ * channels is decimated to the new rate that is the input
+ * sampling rate <EM>fs</EM> divided by the number of channels,
+ * <EM>M</EM>.
+ *
+ * The PFB channelizer code takes the taps generated above and
+ * builds a set of filters. The set contains <EM>M</EM> number
+ * of filters and each filter contains ceil(taps.size()/decim)
+ * number of taps. Each tap from the filter prototype is
+ * sequentially inserted into the next filter. When all of the
+ * input taps are used, the remaining filters in the filterbank
+ * are filled out with 0's to make sure each filter has the same
+ * number of taps.
+ *
+ * Each filter operates using the gr_fir filter classs of GNU
+ * Radio, which takes the input stream at <EM>i</EM> and
+ * performs the inner product calculation to <EM>i+(n-1)</EM>
+ * where <EM>n</EM> is the number of filter taps. To efficiently
+ * handle this in the GNU Radio structure, each filter input
+ * must come from its own input stream. So the channelizer must
+ * be provided with <EM>M</EM> streams where the input stream
+ * has been deinterleaved. This is most easily done using the
+ * gr_stream_to_streams block.
+ *
+ * The output is then produced as a vector, where index
+ * <EM>i</EM> in the vector is the next sample from the
+ * <EM>i</EM>th channel. This is most easily handled by sending
+ * the output to a gr_vector_to_streams block to handle the
+ * conversion and passing <EM>M</EM> streams out.
+ *
+ * The input and output formatting is done using a hier_block2
+ * called pfb_channelizer_ccf. This can take in a single stream
+ * and outputs <EM>M</EM> streams based on the behavior
+ * described above.
+ *
+ * The filter's taps should be based on the input sampling rate.
+ *
+ * For example, using the GNU Radio's firdes utility to building
+ * filters, we build a low-pass filter with a sampling rate of
+ * <EM>fs</EM>, a 3-dB bandwidth of <EM>BW</EM> and a transition
+ * bandwidth of <EM>TB</EM>. We can also specify the out-of-band
+ * attenuation to use, <EM>ATT</EM>, and the filter window
+ * function (a Blackman-harris window in this case). The first
+ * input is the gain of the filter, which we specify here as
+ * unity.
+ *
+ * <B><EM>self._taps = gr.firdes.low_pass_2(1, fs, BW, TB,
+ * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS)</EM></B>
+ *
+ * More on the theory of polyphase filterbanks can be found in
+ * the following book.
+ *
+ * <B><EM>f. harris, "Multirate Signal Processing for
+ * Communication Systems," Upper Saddle River, NJ:
+ * Prentice Hall, Inc. 2004.</EM></B>
+ *
+ */
+
+ class FILTER_API polyphase_filterbank
+ {
+ protected:
+ unsigned int d_nfilts;
+ std::vector<kernel::fir_filter_ccf*> d_filters;
+ std::vector< std::vector<float> > d_taps;
+ unsigned int d_taps_per_filter;
+ fft::fft_complex *d_fft;
+
+ public:
+ /*!
+ * Build the polyphase filterbank decimator.
+ * \param nfilts (unsigned integer) Specifies the number of
+ * channels <EM>M</EM>
+ * \param taps (vector/list of floats) The prototype filter to
+ * populate the filterbank.
+ */
+ polyphase_filterbank(unsigned int nfilts,
+ const std::vector<float> &taps);
+
+ ~polyphase_filterbank();
+
+ /*!
+ * Update the filterbank's filter taps from a prototype
+ * filter.
+ *
+ * \param taps (vector/list of floats) The prototype filter to
+ * populate the filterbank.
+ */
+ void set_taps(const std::vector<float> &taps);
+
+ /*!
+ * Print all of the filterbank taps to screen.
+ */
+ void print_taps();
+
+ /*!
+ * Return a vector<vector<>> of the filterbank taps
+ */
+ std::vector<std::vector<float> > taps() const;
+ };
+
+ } /* namespace kernel */
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* INCLUDED_FILTER_POLYPHASE_FILTERBANK_H */
diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt
index 5930ec9bb..b7577ad5e 100644
--- a/gr-filter/lib/CMakeLists.txt
+++ b/gr-filter/lib/CMakeLists.txt
@@ -110,6 +110,7 @@ list(APPEND filter_sources
fft_filter.cc
firdes.cc
pm_remez.cc
+ polyphase_filterbank.cc
${generated_sources}
dc_blocker_cc_impl.cc
dc_blocker_ff_impl.cc
diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.cc b/gr-filter/lib/pfb_channelizer_ccf_impl.cc
index 4006db789..4e06cbc5f 100644
--- a/gr-filter/lib/pfb_channelizer_ccf_impl.cc
+++ b/gr-filter/lib/pfb_channelizer_ccf_impl.cc
@@ -26,26 +26,26 @@
#include "pfb_channelizer_ccf_impl.h"
#include <gr_io_signature.h>
-#include <cstdio>
namespace gr {
namespace filter {
- pfb_channelizer_ccf::sptr pfb_channelizer_ccf::make(unsigned int numchans,
+ pfb_channelizer_ccf::sptr pfb_channelizer_ccf::make(unsigned int nfilts,
const std::vector<float> &taps,
float oversample_rate)
{
- return gnuradio::get_initial_sptr(new pfb_channelizer_ccf_impl(numchans, taps,
+ return gnuradio::get_initial_sptr(new pfb_channelizer_ccf_impl(nfilts, taps,
oversample_rate));
}
- pfb_channelizer_ccf_impl::pfb_channelizer_ccf_impl(unsigned int numchans,
+ pfb_channelizer_ccf_impl::pfb_channelizer_ccf_impl(unsigned int nfilts,
const std::vector<float> &taps,
float oversample_rate)
: gr_block("pfb_channelizer_ccf",
- gr_make_io_signature(numchans, numchans, sizeof(gr_complex)),
- gr_make_io_signature(1, numchans, sizeof(gr_complex))),
- d_updated(false), d_numchans(numchans), d_oversample_rate(oversample_rate)
+ gr_make_io_signature(nfilts, nfilts, sizeof(gr_complex)),
+ gr_make_io_signature(1, nfilts, sizeof(gr_complex))),
+ polyphase_filterbank(nfilts, taps),
+ d_updated(false), d_oversample_rate(oversample_rate)
{
// The over sampling rate must be rationally related to the number of channels
// in that it must be N/i for i in [1,N], which gives an outputsample rate
@@ -53,110 +53,62 @@ namespace gr {
// This tests the specified input sample rate to see if it conforms to this
// requirement within a few significant figures.
double intp = 0;
- double fltp = modf(numchans / oversample_rate, &intp);
+ double fltp = modf(nfilts / oversample_rate, &intp);
if(fltp != 0.0)
throw std::invalid_argument("pfb_channelizer: oversample rate must be N/i for i in [1, N]");
set_relative_rate(1.0/intp);
- d_filters = std::vector<kernel::fir_filter_ccf*>(d_numchans);
- d_channel_map.resize(d_numchans);
-
- // Create an FIR filter for each channel and zero out the taps
- std::vector<float> vtaps(0, d_numchans);
- for(unsigned int i = 0; i < d_numchans; i++) {
- d_filters[i] = new kernel::fir_filter_ccf(1, vtaps);
+ // Default channel map
+ d_channel_map.resize(d_nfilts);
+ for(unsigned int i = 0; i < d_nfilts; i++) {
d_channel_map[i] = i;
}
- // Now, actually set the filters' taps
- set_taps(taps);
-
- // Create the FFT to handle the output de-spinning of the channels
- d_fft = new fft::fft_complex(d_numchans, false);
-
// Although the filters change, we use this look up table
// to set the index of the FFT input buffer, which equivalently
// performs the FFT shift operation on every other turn.
- d_rate_ratio = (int)rintf(d_numchans / d_oversample_rate);
- d_idxlut = new int[d_numchans];
- for(unsigned int i = 0; i < d_numchans; i++) {
- d_idxlut[i] = d_numchans - ((i + d_rate_ratio) % d_numchans) - 1;
+ d_rate_ratio = (int)rintf(d_nfilts / d_oversample_rate);
+ d_idxlut = new int[d_nfilts];
+ for(unsigned int i = 0; i < d_nfilts; i++) {
+ d_idxlut[i] = d_nfilts - ((i + d_rate_ratio) % d_nfilts) - 1;
}
// Calculate the number of filtering rounds to do to evenly
// align the input vectors with the output channels
d_output_multiple = 1;
- while((d_output_multiple * d_rate_ratio) % d_numchans != 0)
+ while((d_output_multiple * d_rate_ratio) % d_nfilts != 0)
d_output_multiple++;
set_output_multiple(d_output_multiple);
+
+ set_history (d_taps_per_filter+1);
}
pfb_channelizer_ccf_impl::~pfb_channelizer_ccf_impl()
{
delete [] d_idxlut;
- delete d_fft;
-
- for(unsigned int i = 0; i < d_numchans; i++) {
- delete d_filters[i];
- }
}
void
- pfb_channelizer_ccf_impl::set_taps (const std::vector<float> &taps)
+ pfb_channelizer_ccf_impl::set_taps(const std::vector<float> &taps)
{
gruel::scoped_lock guard(d_mutex);
- 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]);
- }
-
- // Set the history to ensure enough input items for each filter
+ polyphase_filterbank::set_taps(taps);
set_history (d_taps_per_filter+1);
-
d_updated = true;
}
void
pfb_channelizer_ccf_impl::print_taps()
{
- unsigned int i, j;
- for(i = 0; i < 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");
- }
+ polyphase_filterbank::print_taps();
}
- std::vector< std::vector<float> >
+ std::vector<std::vector<float> >
pfb_channelizer_ccf_impl::taps() const
{
- return d_taps;
+ return polyphase_filterbank::taps();
}
void
@@ -167,7 +119,7 @@ namespace gr {
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_numchans) || (min < 0)) {
+ if((max >= d_nfilts) || (min < 0)) {
throw std::invalid_argument("pfb_channelizer_ccf_impl::set_channel_map: map range out of bounds.\n");
}
d_channel_map = map;
@@ -202,7 +154,7 @@ namespace gr {
int toconsume = (int)rintf(noutput_items/d_oversample_rate);
while(n <= toconsume) {
j = 0;
- i = (i + d_rate_ratio) % d_numchans;
+ i = (i + d_rate_ratio) % d_nfilts;
last = i;
while(i >= 0) {
in = (gr_complex*)input_items[j];
@@ -211,7 +163,7 @@ namespace gr {
i--;
}
- i = d_numchans-1;
+ i = d_nfilts-1;
while(i > last) {
in = (gr_complex*)input_items[j];
d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n-1]);
@@ -219,7 +171,7 @@ namespace gr {
i--;
}
- n += (i+d_rate_ratio) >= (int)d_numchans;
+ n += (i+d_rate_ratio) >= (int)d_nfilts;
// despin through FFT
d_fft->execute();
diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.h b/gr-filter/lib/pfb_channelizer_ccf_impl.h
index ee9327663..8cabc4e34 100644
--- a/gr-filter/lib/pfb_channelizer_ccf_impl.h
+++ b/gr-filter/lib/pfb_channelizer_ccf_impl.h
@@ -24,30 +24,26 @@
#define INCLUDED_FILTER_PFB_CHANNELIZER_CCF_IMPL_H
#include <filter/pfb_channelizer_ccf.h>
+#include <filter/polyphase_filterbank.h>
#include <filter/fir_filter.h>
#include <fft/fft.h>
namespace gr {
namespace filter {
- class FILTER_API pfb_channelizer_ccf_impl : public pfb_channelizer_ccf
+ class FILTER_API pfb_channelizer_ccf_impl : public pfb_channelizer_ccf, kernel::polyphase_filterbank
{
private:
- bool d_updated;
- unsigned int d_numchans;
- float d_oversample_rate;
- std::vector<kernel::fir_filter_ccf*> d_filters;
- std::vector< std::vector<float> > d_taps;
- unsigned int d_taps_per_filter;
- fft::fft_complex *d_fft;
- int *d_idxlut;
- int d_rate_ratio;
- int d_output_multiple;
- std::vector<int> d_channel_map;
- gruel::mutex d_mutex; // mutex to protect set/work access
+ bool d_updated;
+ float d_oversample_rate;
+ int *d_idxlut;
+ int d_rate_ratio;
+ int d_output_multiple;
+ std::vector<int> d_channel_map;
+ gruel::mutex d_mutex; // mutex to protect set/work access
public:
- pfb_channelizer_ccf_impl(unsigned int numchans,
+ pfb_channelizer_ccf_impl(unsigned int nfilts,
const std::vector<float> &taps,
float oversample_rate);
diff --git a/gr-filter/lib/polyphase_filterbank.cc b/gr-filter/lib/polyphase_filterbank.cc
new file mode 100644
index 000000000..9d881d76e
--- /dev/null
+++ b/gr-filter/lib/polyphase_filterbank.cc
@@ -0,0 +1,114 @@
+/* -*- c++ -*- */
+/*
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <filter/polyphase_filterbank.h>
+#include <cstdio>
+
+namespace gr {
+ namespace filter {
+ namespace kernel {
+
+ polyphase_filterbank::polyphase_filterbank(unsigned int nfilts,
+ const std::vector<float> &taps)
+ : d_nfilts(nfilts)
+ {
+ d_filters = std::vector<kernel::fir_filter_ccf*>(d_nfilts);
+
+ // Create an FIR filter for each channel and zero out the taps
+ std::vector<float> vtaps(0, d_nfilts);
+ for(unsigned int i = 0; i < d_nfilts; i++) {
+ d_filters[i] = new kernel::fir_filter_ccf(1, vtaps);
+ }
+
+ // Now, actually set the filters' taps
+ set_taps(taps);
+
+ // Create the FFT to handle the output de-spinning of the channels
+ d_fft = new fft::fft_complex(d_nfilts, false);
+ }
+
+ polyphase_filterbank::~polyphase_filterbank()
+ {
+ delete d_fft;
+ for(unsigned int i = 0; i < d_nfilts; i++) {
+ delete d_filters[i];
+ }
+ }
+
+ void
+ polyphase_filterbank::set_taps (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_nfilts);
+
+ // Create d_numchan vectors to store each channel's taps
+ d_taps.resize(d_nfilts);
+
+ // 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_nfilts*d_taps_per_filter) {
+ tmp_taps.push_back(0.0);
+ }
+
+ // Partition the filter
+ for(i = 0; i < d_nfilts; 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_nfilts]; // 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
+ polyphase_filterbank::print_taps()
+ {
+ unsigned int i, j;
+ for(i = 0; i < d_nfilts; 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> >
+ polyphase_filterbank::taps() const
+ {
+ return d_taps;
+ }
+
+ } /* namespace kernel */
+ } /* namespace filter */
+} /* namespace gr */