summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xgr-filter/examples/reconstruction.py6
-rw-r--r--gr-filter/grc/filter_pfb_channelizer.xml13
-rw-r--r--gr-filter/grc/filter_pfb_synthesizer.xml8
-rw-r--r--gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h12
-rw-r--r--gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h55
-rw-r--r--gr-filter/lib/pfb_channelizer_ccf_impl.cc72
-rw-r--r--gr-filter/lib/pfb_channelizer_ccf_impl.h2
-rw-r--r--gr-filter/lib/pfb_synthesizer_ccf_impl.cc268
-rw-r--r--gr-filter/lib/polyphase_filterbank.cc2
-rwxr-xr-xgr-filter/python/filter/qa_pfb_channelizer.py16
-rwxr-xr-xgr-filter/python/filter/qa_pfb_decimator.py11
-rwxr-xr-xgr-filter/python/filter/qa_pfb_interpolator.py20
-rwxr-xr-xgr-filter/python/filter/qa_pfb_synthesizer.py34
13 files changed, 316 insertions, 203 deletions
diff --git a/gr-filter/examples/reconstruction.py b/gr-filter/examples/reconstruction.py
index fd8ba87a2f..0a83b5a4e0 100755
--- a/gr-filter/examples/reconstruction.py
+++ b/gr-filter/examples/reconstruction.py
@@ -72,7 +72,7 @@ def main():
src = blocks.vector_source_b(data.astype(scipy.uint8).tolist(), False)
mod = digital.bpsk_mod(samples_per_symbol=2)
- chan = filter.channel_model(npwr)
+ chan = channels.channel_model(npwr)
rrc = filter.fft_filter_ccc(1, rrc_taps)
# Split it up into pieces
@@ -122,7 +122,7 @@ def main():
s12.set_title("Original Signal in Time")
start = 1
- skip = 4
+ skip = 2
s13 = f1.add_subplot(2,2,3)
s13.plot(sin.real[start::skip], sin.imag[start::skip], "o")
s13.set_title("Constellation")
@@ -153,7 +153,7 @@ def main():
s32.plot(sout.imag[1000:1500], "o-r")
s32.set_title("Reconstructed Signal in Time")
- start = 2
+ start = 0
skip = 4
s33 = f3.add_subplot(2,2,3)
s33.plot(sout.real[start::skip], sout.imag[start::skip], "o")
diff --git a/gr-filter/grc/filter_pfb_channelizer.xml b/gr-filter/grc/filter_pfb_channelizer.xml
index 26349cd10d..f8a51d2204 100644
--- a/gr-filter/grc/filter_pfb_channelizer.xml
+++ b/gr-filter/grc/filter_pfb_channelizer.xml
@@ -16,14 +16,13 @@
$atten)
self.$(id).set_channel_map($ch_map)
</make>
- <!-- Set taps not implemented yet
- <callback>set_taps($taps)</callback>
- -->
+ <callback>set_taps($taps)</callback>
<callback>set_channel_map($ch_map)</callback>
<param>
<name>Channels</name>
<key>nchans</key>
+ <value>1</value>
<type>int</type>
</param>
<param>
@@ -50,6 +49,13 @@ self.$(id).set_channel_map($ch_map)
<value>[]</value>
<type>int_vector</type>
</param>
+ <param>
+ <name>Bus Connections</name>
+ <key>bus_conns</key>
+ <value>[[0,],]</value>
+ <type>raw</type>
+ <hide>part</hide>
+ </param>
<sink>
<name>in</name>
<type>complex</type>
@@ -59,4 +65,5 @@ self.$(id).set_channel_map($ch_map)
<type>complex</type>
<nports>$nchans</nports>
</source>
+ <bus_structure_source>$bus_conns</bus_structure_source>
</block>
diff --git a/gr-filter/grc/filter_pfb_synthesizer.xml b/gr-filter/grc/filter_pfb_synthesizer.xml
index e84b25e62e..e7e1ae3951 100644
--- a/gr-filter/grc/filter_pfb_synthesizer.xml
+++ b/gr-filter/grc/filter_pfb_synthesizer.xml
@@ -45,6 +45,13 @@ self.$(id).set_channel_map($ch_map)
<value>[]</value>
<type>int_vector</type>
</param>
+ <param>
+ <name>Bus Connections</name>
+ <key>bus_conns</key>
+ <value>[[0,],]</value>
+ <type>raw</type>
+ <hide>part</hide>
+ </param>
<sink>
<name>in</name>
<type>complex</type>
@@ -54,4 +61,5 @@ self.$(id).set_channel_map($ch_map)
<name>out</name>
<type>complex</type>
</source>
+ <bus_structure_sink>$bus_conns</bus_structure_sink>
</block>
diff --git a/gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h b/gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h
index f199f85b83..96ffd60daa 100644
--- a/gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h
+++ b/gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h
@@ -101,8 +101,14 @@ namespace gr {
* <B><EM>f. harris, "Multirate Signal Processing for Communication
* Systems," Upper Saddle River, NJ: Prentice Hall, Inc. 2004.</EM></B>
*
+ * When dealing with oversampling, the above book is still a good
+ * reference along with this paper:
+ *
+ * <B><EM>E. Venosa, X. Chen, and fred harris, “Polyphase analysis
+ * filter bank down-converts unequal channel bandwidths with
+ * arbitrary center frequencies - design I,” in SDR’10-WinnComm,
+ * 2010.</EM></B>
*/
-
class FILTER_API pfb_channelizer_ccf : virtual public block
{
public:
@@ -151,7 +157,7 @@ namespace gr {
* Print all of the filterbank taps to screen.
*/
virtual void print_taps() = 0;
-
+
/*!
* Return a vector<vector<>> of the filterbank taps
*/
@@ -189,7 +195,7 @@ namespace gr {
* the map is [0...M-1] with M = N.
*/
virtual void set_channel_map(const std::vector<int> &map) = 0;
-
+
/*!
* Gets the current channel map.
*/
diff --git a/gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h b/gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h
index f1fbad7272..32465b61ac 100644
--- a/gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h
+++ b/gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h
@@ -34,6 +34,57 @@ namespace gr {
* \brief Polyphase synthesis filterbank with
* gr_complex input, gr_complex output and float taps
* \ingroup channelizers_blk
+ *
+ * \details
+ *
+ * The PFB sythesis filterbank combines multiple baseband signals
+ * into a single channelized signal. Each input stream is,
+ * essentially, modulated onto an output channel according the the
+ * channel mapping (see set_channel_map for details).
+ *
+ * Setting this filterbank up means selecting the number of output
+ * channels, the prototype filter, and whether to handle channels
+ * at 2x the sample rate (this is generally used only for
+ * reconstruction filtering).
+ *
+ * The number of channels sets the maximum number of channels to
+ * use, but not all input streams must be connected. For M total
+ * channels, we can connect inputs 0 to N where N < M-1. Because
+ * of the way GNU Radio handles stream connections, we must
+ * connect the channels consecutively, and so we must use the
+ * set_channel_map if the desired output channels are not the same
+ * as the the default mapping. This features gives us the
+ * flexibility to output to any given channel. Generally, we try
+ * to not use the channels at the edge of the spectrum to avoid
+ * issues with filtering and roll-off of the transmitter or
+ * receiver.
+ *
+ * When using the 2x sample rate mode, we specify the number of
+ * channels that will be used. However, the actual output signal
+ * will be twice this number of channels. This is mainly important
+ * to know when setting the channel map. For M channels, the
+ * channel mapping can specy from 0 to 2M-1 channels to output
+ * onto.
+ *
+ * For more details about this and the concepts of reconstruction
+ * filtering, see:
+ *
+ * <B><EM>f. harris, "Multirate Signal Processing for Communication
+ * Systems," Upper Saddle River, NJ: Prentice Hall, Inc. 2004.</EM></B>
+ *
+ * <B><EM>X. Chen, E. Venosa, and fred harris, “Polyphase analysis
+ * filter bank up-converts unequal channel bandwidths with
+ * arbitrary center frequencies - design ii,” in SDR’10-WinnComm,
+ * 2010.</EM></B>
+ *
+ * <B><EM>E. Venosa, X. Chen, and fred harris, “Polyphase analysis
+ * filter bank down-converts unequal channel bandwidths with
+ * arbitrary center frequencies - design I,” in SDR’10-WinnComm,
+ * 2010.</EM></B>
+ *
+ * <B><EM>f. j. harris, C. Dick, X. Chen, and E. Venosa, “Wideband 160-
+ * channel polyphase filter bank cable TV channeliser,” in IET
+ * Signal Processing, 2010.</EM></B>
*/
class FILTER_API pfb_synthesizer_ccf : virtual public sync_interpolator
{
@@ -50,8 +101,8 @@ namespace gr {
* \param twox (bool) use 2x oversampling or not (default is no)
*/
static sptr make(unsigned int numchans,
- const std::vector<float> &taps,
- bool twox=false);
+ const std::vector<float> &taps,
+ bool twox=false);
/*!
* Resets the filterbank's filter taps with the new prototype filter
diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.cc b/gr-filter/lib/pfb_channelizer_ccf_impl.cc
index c28434b6cb..62223e40da 100644
--- a/gr-filter/lib/pfb_channelizer_ccf_impl.cc
+++ b/gr-filter/lib/pfb_channelizer_ccf_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2009,2010,2012 Free Software Foundation, Inc.
+ * Copyright 2009,2010,2012,2014 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -26,25 +26,28 @@
#include "pfb_channelizer_ccf_impl.h"
#include <gnuradio/io_signature.h>
+#include <stdio.h>
namespace gr {
namespace filter {
-
- pfb_channelizer_ccf::sptr pfb_channelizer_ccf::make(unsigned int nfilts,
- const std::vector<float> &taps,
- float oversample_rate)
+
+ 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(nfilts, taps,
- oversample_rate));
+ return gnuradio::get_initial_sptr
+ (new pfb_channelizer_ccf_impl(nfilts, taps,
+ oversample_rate));
}
pfb_channelizer_ccf_impl::pfb_channelizer_ccf_impl(unsigned int nfilts,
const std::vector<float> &taps,
float oversample_rate)
: block("pfb_channelizer_ccf",
- io_signature::make(nfilts, nfilts, sizeof(gr_complex)),
- io_signature::make(1, nfilts, sizeof(gr_complex))),
- polyphase_filterbank(nfilts, taps),
+ io_signature::make(nfilts, nfilts, sizeof(gr_complex)),
+ io_signature::make(1, nfilts, sizeof(gr_complex))),
+ polyphase_filterbank(nfilts, taps, false),
d_updated(false), d_oversample_rate(oversample_rate)
{
// The over sampling rate must be rationally related to the number of channels
@@ -59,15 +62,22 @@ namespace gr {
set_relative_rate(1.0/intp);
- // Default channel map
+ // Default channel map. The channel map specifies which input
+ // goes to which output channel; so out[0] comes from
+ // channel_map[0].
d_channel_map.resize(d_nfilts);
for(unsigned int i = 0; i < d_nfilts; i++) {
d_channel_map[i] = i;
}
- // 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.
+ // We use a look up table to set the index of the FFT input
+ // buffer, which equivalently performs the FFT shift operation
+ // on every other turn when the rate_ratio>1. Also, this
+ // performs the index 'flip' where the first input goes into the
+ // last filter. In the pfb_decimator_ccf, we directly index the
+ // input_items buffers starting with this last; here we start
+ // with the first and put it into the fft object properly for
+ // the same effect.
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++) {
@@ -81,10 +91,8 @@ namespace gr {
d_output_multiple++;
set_output_multiple(d_output_multiple);
- // History is the length of each filter arm plus 1.
- // The +1 comes from the channel mapping in the work function
- // where we start n=1 so that we can look at in[n-1]
- set_history(d_taps_per_filter+1);
+ // Use set_taps to also set the history requirement
+ set_taps(taps);
}
pfb_channelizer_ccf_impl::~pfb_channelizer_ccf_impl()
@@ -118,7 +126,7 @@ namespace gr {
pfb_channelizer_ccf_impl::set_channel_map(const std::vector<int> &map)
{
gr::thread::scoped_lock guard(d_mutex);
-
+
if(map.size() > 0) {
unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end());
if(max >= d_nfilts) {
@@ -142,9 +150,9 @@ namespace gr {
{
gr::thread::scoped_lock guard(d_mutex);
- gr_complex *in = (gr_complex *) input_items[0];
- gr_complex *out = (gr_complex *) output_items[0];
-
+ 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.
@@ -152,6 +160,18 @@ namespace gr {
size_t noutputs = output_items.size();
+ // The following algorithm looks more complex in order to handle
+ // the cases where we want more that 1 sps for each
+ // channel. Otherwise, this would boil down into a single loop
+ // that operates from input_items[0] to [d_nfilts].
+
+ // When dealing with osps>1, we start not at the last filter,
+ // but nfilts/osps and then wrap around to the next symbol into
+ // the other set of filters.
+ // For details of this operation, see:
+ // fred harris, Multirate Signal Processing For Communication
+ // Systems. Upper Saddle River, NJ: Prentice Hall, 2004.
+
int n=1, i=-1, j=0, oo=0, last;
int toconsume = (int)rintf(noutput_items/d_oversample_rate);
while(n <= toconsume) {
@@ -159,16 +179,16 @@ namespace gr {
i = (i + d_rate_ratio) % d_nfilts;
last = i;
while(i >= 0) {
- in = (gr_complex*)input_items[j];
- d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n]);
+ in = (gr_complex*)input_items[j];
+ d_fft->get_inbuf()[d_idxlut[j]] = d_fir_filters[i]->filter(&in[n]);
j++;
i--;
}
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]);
+ in = (gr_complex*)input_items[j];
+ d_fft->get_inbuf()[d_idxlut[j]] = d_fir_filters[i]->filter(&in[n-1]);
j++;
i--;
}
diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.h b/gr-filter/lib/pfb_channelizer_ccf_impl.h
index 6d727676b2..bfc53f8848 100644
--- a/gr-filter/lib/pfb_channelizer_ccf_impl.h
+++ b/gr-filter/lib/pfb_channelizer_ccf_impl.h
@@ -31,7 +31,7 @@
namespace gr {
namespace filter {
-
+
class FILTER_API pfb_channelizer_ccf_impl : public pfb_channelizer_ccf, kernel::polyphase_filterbank
{
private:
diff --git a/gr-filter/lib/pfb_synthesizer_ccf_impl.cc b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc
index 5f0e330ab4..bdfc158a5d 100644
--- a/gr-filter/lib/pfb_synthesizer_ccf_impl.cc
+++ b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2010,2012 Free Software Foundation, Inc.
+ * Copyright 2010,2012,2014 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -33,45 +33,43 @@ namespace gr {
pfb_synthesizer_ccf::sptr
pfb_synthesizer_ccf::make(unsigned int numchans,
- const std::vector<float> &taps,
- bool twox)
+ const std::vector<float> &taps, bool twox)
{
return gnuradio::get_initial_sptr
- (new pfb_synthesizer_ccf_impl(numchans, taps, twox));
+ (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)
+ pfb_synthesizer_ccf_impl::pfb_synthesizer_ccf_impl
+ (unsigned int numchans, const std::vector<float> &taps, bool twox)
: sync_interpolator("pfb_synthesizer_ccf",
- io_signature::make(1, numchans, sizeof(gr_complex)),
- io_signature::make(1, 1, sizeof(gr_complex)),
- (twox ? numchans/2 : numchans)),
- d_updated(false), d_numchans(numchans), d_state(0)
+ io_signature::make(1, numchans, sizeof(gr_complex)),
+ io_signature::make(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");
+ throw std::invalid_argument("pfb_synthesizer_ccf_impl: number of channels must be even for 2x oversampling.\n");
}
- d_filters = std::vector<kernel::fir_filter_with_buffer_ccf*>(d_numchans);
- d_channel_map.resize(d_numchans);
+ 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_numchans);
- for(unsigned int i = 0; i < d_numchans; i++) {
- d_filters[i] = new kernel::fir_filter_with_buffer_ccf(vtaps);
- d_channel_map[i] = i;
+ // Create an FIR filter for each channel and zero out the taps
+ // and set the default channel map
+ 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_numchans, false);
- memset(d_fft->get_inbuf(), 0, d_numchans*sizeof(gr_complex));
+ 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);
}
@@ -79,8 +77,8 @@ namespace gr {
pfb_synthesizer_ccf_impl::~pfb_synthesizer_ccf_impl()
{
delete d_fft;
- for(unsigned int i = 0; i < d_numchans; i++) {
- delete d_filters[i];
+ for(unsigned int i = 0; i < d_twox*d_numchans; i++) {
+ delete d_filters[i];
}
}
@@ -89,20 +87,29 @@ namespace gr {
{
gr::thread::scoped_lock guard(d_mutex);
+ // The different modes, 1x or 2x the sampling rate, have
+ // different filtering partitions.
if(d_twox == 1)
- set_taps1(taps);
+ 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);
+ set_taps2(taps);
+ // Because we keep our own buffers inside the filters, we don't
+ // need history.
+ set_history(1);
d_updated = true;
}
void
pfb_synthesizer_ccf_impl::set_taps1(const std::vector<float> &taps)
{
+ // In this partitioning, we do a normal polyphase paritioning by
+ // deinterleaving the taps into each filter:
+ //
+ // Prototype filter: [t0, t1, t2, t3, t4, t5, t6]
+ // filter 0: [t0, t3, t6]
+ // filter 1: [t1, t4, 0 ]
+ // filter 2: [t2, t5, 0 ]
unsigned int i,j;
unsigned int ntaps = taps.size();
@@ -113,70 +120,77 @@ namespace gr {
// 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;
+ std::vector<float> tmp_taps = taps;
while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) {
- tmp_taps.push_back(0.0);
+ 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]);
+ // 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
+ }
+
+ // Set the filter taps for each channel
+ d_filters[i]->set_taps(d_taps[i]);
}
}
void
- pfb_synthesizer_ccf_impl::set_taps2 (const std::vector<float> &taps)
+ pfb_synthesizer_ccf_impl::set_taps2(const std::vector<float> &taps)
{
+ // In this partitioning, create 2M filters each in Z^2; the
+ // second half of the filters are also delayed by Z^1:
+ //
+ // Prototype filter: [t0, t1, t2, t3, t4, t5, t6]
+ // filter 0: [t0, 0, t6]
+ // filter 1: [t2, 0, 0]
+ // filter 2: [t4, 0, 0]
+ // filter 3: [ 0, t1, 0]
+ // filter 4: [ 0, t3, 0 ]
+ // filter 5: [ 0, t5, 0 ]
+
unsigned int i,j;
int state = 0;
- unsigned int ntaps = 2*taps.size();
+ 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);
+ 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;
+ std::vector<float> tmp_taps = taps;
while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) {
- tmp_taps.push_back(0.0);
+ tmp_taps.push_back(0.0);
+ //tmp_taps.insert(tmp_taps.begin(), 0.0);
}
// Partition the filter
- unsigned int halfchans = d_numchans/2;
- for(i = 0; i < halfchans; 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[halfchans+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*halfchans];
- d_taps[halfchans + i][j] = 0;
- state = 1;
- }
- else {
- d_taps[i][j] = 0;
- d_taps[halfchans + i][j] = tmp_taps[i + j*halfchans];
- 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[halfchans + i]->set_taps(d_taps[halfchans + i]);
+ 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++) {
+ if(state == 0) {
+ d_taps[i][j] = 0;
+ d_taps[d_numchans + i][j] = tmp_taps[i + j*d_numchans];
+ state = 1;
+ }
+ else {
+ d_taps[i][j] = tmp_taps[i + j*d_numchans];
+ d_taps[d_numchans + i][j] = 0;
+ state = 0;
+ }
+ }
+
+ // Set the filter taps for each channel
+ d_filters[i]->set_taps(d_taps[i]);
+ d_filters[d_numchans + i]->set_taps(d_taps[d_numchans + i]);
}
}
@@ -184,15 +198,16 @@ namespace gr {
pfb_synthesizer_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");
+ 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
{
@@ -205,14 +220,15 @@ namespace gr {
gr::thread::scoped_lock guard(d_mutex);
if(map.size() > 0) {
- unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end());
- if(max >= d_numchans) {
- 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_numchans*sizeof(gr_complex));
+ 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("pfb_synthesizer_ccf_impl::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));
}
}
@@ -224,63 +240,63 @@ namespace gr {
int
pfb_synthesizer_ccf_impl::work(int noutput_items,
- gr_vector_const_void_star &input_items,
- gr_vector_void_star &output_items)
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
{
gr::thread::scoped_lock guard(d_mutex);
- gr_complex *in = (gr_complex*)input_items[0];
- gr_complex *out = (gr_complex*)output_items[0];
+ 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.
+ 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
+ // Algorithm 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;
- }
+ 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 {
- unsigned int halfchans = d_numchans/2;
- for(n = 0; n < noutput_items/halfchans; 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 < halfchans; i++) {
- out[i] = d_filters[i]->filter(d_fft->get_outbuf()[d_state*halfchans+i]);
- out[i] += d_filters[halfchans+i]->filter(d_fft->get_outbuf()[(d_state^1)*halfchans+i]);
- }
- d_state ^= 1;
-
- out += halfchans;
- }
+ for(n = 0; n < noutput_items/d_numchans; n++) {
+ for(i = 0; i < ninputs; i++) {
+ //in = (gr_complex*)input_items[ninputs-i-1];
+ 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;
diff --git a/gr-filter/lib/polyphase_filterbank.cc b/gr-filter/lib/polyphase_filterbank.cc
index ac20eb4014..5fffc02dce 100644
--- a/gr-filter/lib/polyphase_filterbank.cc
+++ b/gr-filter/lib/polyphase_filterbank.cc
@@ -79,8 +79,6 @@ namespace gr {
tmp_taps.push_back(0.0);
}
- std::reverse(tmp_taps.begin(), tmp_taps.end());
-
// 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
diff --git a/gr-filter/python/filter/qa_pfb_channelizer.py b/gr-filter/python/filter/qa_pfb_channelizer.py
index 4c108e8443..46c6e7b5ae 100755
--- a/gr-filter/python/filter/qa_pfb_channelizer.py
+++ b/gr-filter/python/filter/qa_pfb_channelizer.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2012,2013 Free Software Foundation, Inc.
+# Copyright 2012-2014 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -67,18 +67,18 @@ class test_pfb_channelizer(gr_unittest.TestCase):
self.tb.connect((s2ss,i), (pfb,i))
self.tb.connect((pfb, i), snks[i])
- self.tb.run()
+ self.tb.run()
Ntest = 50
L = len(snks[0].data())
# Adjusted phase rotations for data
- p0 = -2*math.pi * 0 / M
- p1 = -2*math.pi * 1 / M
- p2 = -2*math.pi * 2 / M
- p3 = -2*math.pi * 3 / M
- p4 = -2*math.pi * 4 / M
-
+ p0 = 0.11058379158914133
+ p1 = 4.5108246571401693
+ p2 = 3.9739891674564594
+ p3 = 2.2820531095511924
+ p4 = 1.3782797467397869
+
# Filter delay is the normal delay of each arm
tpf = math.ceil(len(taps) / float(M))
delay = -(tpf - 1.0) / 2.0
diff --git a/gr-filter/python/filter/qa_pfb_decimator.py b/gr-filter/python/filter/qa_pfb_decimator.py
index 745ee538d9..4366e85eec 100755
--- a/gr-filter/python/filter/qa_pfb_decimator.py
+++ b/gr-filter/python/filter/qa_pfb_decimator.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2012,2013 Free Software Foundation, Inc.
+# Copyright 2012-2014 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -61,8 +61,13 @@ def run_test(tb, channel, fft_rotate, fft_filter):
L = len(snk.data())
- # Each channel is rotated by 2pi/M
- phase = -2*math.pi * channel / M
+ # Adjusted phase rotations for data
+ phase = [ 0.11058476216852586,
+ 4.5108246571401693,
+ 3.9739891674564594,
+ 2.2820531095511924,
+ 1.3782797467397869]
+ phase = phase[channel]
# Filter delay is the normal delay of each arm
tpf = math.ceil(len(taps) / float(M))
diff --git a/gr-filter/python/filter/qa_pfb_interpolator.py b/gr-filter/python/filter/qa_pfb_interpolator.py
index 33c22675af..b7ed4feef6 100755
--- a/gr-filter/python/filter/qa_pfb_interpolator.py
+++ b/gr-filter/python/filter/qa_pfb_interpolator.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2012,2013 Free Software Foundation, Inc.
+# Copyright 2012-2014 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -42,9 +42,9 @@ class test_pfb_interpolator(gr_unittest.TestCase):
N = 1000 # number of samples to use
M = 5 # Number of channels
fs = 1000 # baseband sampling rate
- ifs = M*fs # input samp rate to decimator
+ ofs = M*fs # output samp rate of interpolator
- taps = filter.firdes.low_pass_2(M, ifs, fs/2, fs/10,
+ taps = filter.firdes.low_pass_2(M, ofs, fs/4, fs/10,
attenuation_dB=80,
window=filter.firdes.WIN_BLACKMAN_hARRIS)
@@ -57,20 +57,16 @@ class test_pfb_interpolator(gr_unittest.TestCase):
self.tb.connect(signal, pfb)
self.tb.connect(pfb, snk)
- self.tb.run()
+ self.tb.run()
Ntest = 50
L = len(snk.data())
- # Can only get channel 0 out; no phase rotation
- phase = 0
+ # Phase rotation through the filters
+ phase = 4.8870112969978994
- # Calculate the filter delay
- delay = -(len(taps) - 1) / 2.0 - (M-1)
- delay = int(delay)
-
- # Create a time scale that's delayed to match the filter delay
- t = map(lambda x: float(x)/ifs, xrange(delay, L+delay))
+ # Create a time scale
+ t = map(lambda x: float(x)/ofs, xrange(0, L))
# Create known data as complex sinusoids for the baseband freq
# of the extracted channel is due to decimator output order.
diff --git a/gr-filter/python/filter/qa_pfb_synthesizer.py b/gr-filter/python/filter/qa_pfb_synthesizer.py
index baba904088..0b3f8b27a2 100755
--- a/gr-filter/python/filter/qa_pfb_synthesizer.py
+++ b/gr-filter/python/filter/qa_pfb_synthesizer.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2012,2013 Free Software Foundation, Inc.
+# Copyright 2012-2014 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -62,7 +62,14 @@ class test_pfb_synthesizer(gr_unittest.TestCase):
self.tb.connect(pfb, snk)
- self.tb.run()
+ self.tb.run()
+
+ # Adjusted phase rotations for data
+ p0 = 0
+ p1 = 2*math.pi*1.0/M
+ p2 = 2*math.pi*2.0/M
+ p3 = 2*math.pi*3.0/M
+ p4 = 2*math.pi*4.0/M
Ntest = 1000
L = len(snk.data())
@@ -73,20 +80,19 @@ class test_pfb_synthesizer(gr_unittest.TestCase):
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])
+ expected_data[i] = math.cos(2.*math.pi*freqs[0]*t[i] + p3) + \
+ 1j*math.sin(2.*math.pi*freqs[0]*t[i] + p3) + \
+ math.cos(2.*math.pi*freqs[1]*t[i] + p4) + \
+ 1j*math.sin(2.*math.pi*freqs[1]*t[i] + p4) + \
+ math.cos(2.*math.pi*freqs[2]*t[i] + p0) + \
+ 1j*math.sin(2.*math.pi*freqs[2]*t[i] + p0) + \
+ math.cos(2.*math.pi*freqs[3]*t[i] + p1) + \
+ 1j*math.sin(2.*math.pi*freqs[3]*t[i] + p1) + \
+ math.cos(2.*math.pi*freqs[4]*t[i] + p2) + \
+ 1j*math.sin(2.*math.pi*freqs[4]*t[i] + p2)
dst_data = snk.data()
- offset = 25
- self.assertComplexTuplesAlmostEqual(expected_data[2000-offset:2000-offset+Ntest],
+ self.assertComplexTuplesAlmostEqual(expected_data[2000:2000+Ntest],
dst_data[2000:2000+Ntest], 4)
if __name__ == '__main__':