summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.cc45
-rw-r--r--gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.h46
-rw-r--r--gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.i5
-rw-r--r--gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.cc191
-rw-r--r--gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.h53
-rw-r--r--gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.i10
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py22
-rw-r--r--gnuradio-examples/python/pfb/CMakeLists.txt1
-rwxr-xr-xgnuradio-examples/python/pfb/channelize.py5
-rwxr-xr-xgnuradio-examples/python/pfb/reconstruction.py131
-rw-r--r--grc/blocks/blks2_pfb_channelizer.xml7
-rw-r--r--grc/blocks/gr_pfb_synthesis_filterbank.xml14
12 files changed, 467 insertions, 63 deletions
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.cc b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.cc
index db16a634b..24fc35a19 100644
--- a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.cc
+++ b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.cc
@@ -46,7 +46,7 @@ gr_pfb_channelizer_ccf::gr_pfb_channelizer_ccf (unsigned int numchans,
float oversample_rate)
: gr_block ("pfb_channelizer_ccf",
gr_make_io_signature (numchans, numchans, sizeof(gr_complex)),
- gr_make_io_signature (1, 1, numchans*sizeof(gr_complex))),
+ gr_make_io_signature (1, numchans, sizeof(gr_complex))),
d_updated (false), d_numchans(numchans), d_oversample_rate(oversample_rate)
{
// The over sampling rate must be rationally related to the number of channels
@@ -62,11 +62,13 @@ gr_pfb_channelizer_ccf::gr_pfb_channelizer_ccf (unsigned int numchans,
set_relative_rate(1.0/intp);
d_filters = std::vector<gr_fir_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] = gr_fir_util::create_gr_fir_ccf(vtaps);
+ d_channel_map[i] = i;
}
// Now, actually set the filters' taps
@@ -104,6 +106,7 @@ gr_pfb_channelizer_ccf::~gr_pfb_channelizer_ccf ()
void
gr_pfb_channelizer_ccf::set_taps (const std::vector<float> &taps)
{
+ gruel::scoped_lock guard(d_mutex);
unsigned int i,j;
unsigned int ntaps = taps.size();
@@ -151,6 +154,31 @@ gr_pfb_channelizer_ccf::print_taps()
}
}
+std::vector< std::vector<float> >
+gr_pfb_channelizer_ccf::taps() const
+{
+ return d_taps;
+}
+
+void
+gr_pfb_channelizer_ccf::set_channel_map(const std::vector<int> &map)
+{
+ gruel::scoped_lock guard(d_mutex);
+
+ 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)) {
+ throw std::invalid_argument("gr_pfb_channelizer_ccf::set_channel_map: map range out of bounds.\n");
+ }
+ d_channel_map = map;
+}
+
+std::vector<int>
+gr_pfb_channelizer_ccf::channel_map() const
+{
+ return d_channel_map;
+}
+
int
gr_pfb_channelizer_ccf::general_work (int noutput_items,
@@ -158,6 +186,8 @@ gr_pfb_channelizer_ccf::general_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];
@@ -166,7 +196,9 @@ gr_pfb_channelizer_ccf::general_work (int noutput_items,
return 0; // history requirements may have changed.
}
- int n=1, i=-1, j=0, last;
+ size_t noutputs = output_items.size();
+
+ int n=1, i=-1, j=0, oo=0, last;
int toconsume = (int)rintf(noutput_items/d_oversample_rate);
while(n <= toconsume) {
j = 0;
@@ -191,8 +223,13 @@ gr_pfb_channelizer_ccf::general_work (int noutput_items,
// despin through FFT
d_fft->execute();
- memcpy(out, d_fft->get_outbuf(), d_numchans*sizeof(gr_complex));
- out += d_numchans;
+
+ // Send to output channels
+ for(unsigned int nn = 0; nn < noutputs; nn++) {
+ out = (gr_complex*)output_items[nn];
+ out[oo] = d_fft->get_outbuf()[d_channel_map[nn]];
+ }
+ oo++;
}
consume_each(toconsume);
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.h b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.h
index 8fd5c4f78..040b93e73 100644
--- a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.h
+++ b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.h
@@ -26,6 +26,7 @@
#include <gr_core_api.h>
#include <gr_block.h>
+#include <gruel/thread.h>
class gr_pfb_channelizer_ccf;
typedef boost::shared_ptr<gr_pfb_channelizer_ccf> gr_pfb_channelizer_ccf_sptr;
@@ -146,6 +147,8 @@ class GR_CORE_API gr_pfb_channelizer_ccf : public gr_block
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
/*!
* Build the polyphase filterbank decimator.
@@ -170,6 +173,49 @@ public:
* 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;
+
+ /*!
+ * Set the channel map. Channels are numbers as:
+ *
+ * N/2+1 | ... | N-1 | 0 | 1 | 2 | ... | N/2
+ * <------------------- 0 -------------------->
+ * freq
+ *
+ * So output stream 0 comes from channel 0, etc. Setting a new
+ * channel map allows the user to specify which channel in frequency
+ * he/she wants to got to which output stream.
+ *
+ * The map should have the same number of elements as the number of
+ * output connections from the block. The minimum value of the map
+ * is 0 (for the 0th channel) and the maximum number is N-1 where N
+ * is the number of channels.
+ *
+ * We specify M as the number of output connections made where M <=
+ * N, so only M out of N channels are driven to an output
+ * stream. The number of items in the channel map should be at least
+ * M long. If there are more channels specified, any value in the
+ * map over M-1 will be ignored. If the size of the map is less than
+ * M the behavior is unknown (we don't wish to check every entry
+ * into the work function).
+ *
+ * This means that if the channelizer is splitting the signal up
+ * into N channels but only M channels are specified in the map
+ * (where M <= N), then M output streams must be connected and the
+ * map and the channel numbers used must be less than N-1. Output
+ * channel number can be reused, too. By default, the map is
+ * [0...M-1] with M = N.
+ */
+ void set_channel_map(const std::vector<int> &map);
+
+ /*!
+ * Gets the current channel map.
+ */
+ std::vector<int> channel_map() const;
int general_work (int noutput_items,
gr_vector_int &ninput_items,
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.i b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.i
index 63e3e0fe6..f5edba5b7 100644
--- a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.i
+++ b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.i
@@ -37,4 +37,9 @@ class gr_pfb_channelizer_ccf : public gr_block
~gr_pfb_channelizer_ccf ();
void set_taps (const std::vector<float> &taps);
+ void print_taps();
+ std::vector<std::vector<float> > taps() const;
+
+ void set_channel_map(const std::vector<int> &map);
+ std::vector<int> channel_map() const;
};
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.cc b/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.cc
index 9fad1bd0d..f999a2b92 100644
--- a/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.cc
+++ b/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.cc
@@ -31,45 +31,66 @@
#include <cstring>
gr_pfb_synthesis_filterbank_ccf_sptr gr_make_pfb_synthesis_filterbank_ccf
- (unsigned int numchans, const std::vector<float> &taps)
+ (unsigned int numchans, const std::vector<float> &taps, bool twox)
{
return gr_pfb_synthesis_filterbank_ccf_sptr
- (new gr_pfb_synthesis_filterbank_ccf (numchans, taps));
+ (new gr_pfb_synthesis_filterbank_ccf (numchans, taps, twox));
}
gr_pfb_synthesis_filterbank_ccf::gr_pfb_synthesis_filterbank_ccf
- (unsigned int numchans, const std::vector<float> &taps)
+ (unsigned int numchans, const std::vector<float> &taps, bool twox)
: gr_sync_interpolator ("pfb_synthesis_filterbank_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_updated (false), d_numchans(numchans), d_state(0)
{
- d_filters = std::vector<gri_fir_filter_with_buffer_ccf*>(d_numchans);
+ // 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("gr_pfb_synthesis_filterbank_ccf: number of channels must be even for 2x oversampling.\n");
+ }
+
+ d_filters = std::vector<gri_fir_filter_with_buffer_ccf*>(d_twox*d_numchans);
+ d_channel_map.resize(d_twox*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++) {
+ std::vector<float> vtaps(0, d_twox*d_numchans);
+ for(unsigned int i = 0; i < d_twox*d_numchans; i++) {
d_filters[i] = new gri_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 gri_fft_complex (d_numchans, true);
+ d_fft = new gri_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);
}
gr_pfb_synthesis_filterbank_ccf::~gr_pfb_synthesis_filterbank_ccf ()
{
- for(unsigned int i = 0; i < d_numchans; i++) {
+ for(unsigned int i = 0; i < d_twox*d_numchans; i++) {
delete d_filters[i];
}
}
void
-gr_pfb_synthesis_filterbank_ccf::set_taps (const std::vector<float> &taps)
+gr_pfb_synthesis_filterbank_ccf::set_taps(const std::vector<float> &taps)
+{
+ gruel::scoped_lock guard(d_mutex);
+ if(d_twox == 1)
+ set_taps1(taps);
+ else
+ set_taps2(taps);
+}
+
+void
+gr_pfb_synthesis_filterbank_ccf::set_taps1(const std::vector<float> &taps)
{
unsigned int i,j;
@@ -106,10 +127,62 @@ gr_pfb_synthesis_filterbank_ccf::set_taps (const std::vector<float> &taps)
}
void
+gr_pfb_synthesis_filterbank_ccf::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]);
+ }
+
+ // Set the history to ensure enough input items for each filter
+ set_history (d_taps_per_filter+1);
+
+ d_updated = true;
+}
+
+void
gr_pfb_synthesis_filterbank_ccf::print_taps()
{
unsigned int i, j;
- for(i = 0; i < d_numchans; i++) {
+ 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]);
@@ -119,16 +192,43 @@ gr_pfb_synthesis_filterbank_ccf::print_taps()
}
+std::vector< std::vector<float> >
+gr_pfb_synthesis_filterbank_ccf::taps() const
+{
+ return d_taps;
+}
+
+void
+gr_pfb_synthesis_filterbank_ccf::set_channel_map(const std::vector<int> &map)
+{
+ gruel::scoped_lock guard(d_mutex);
+
+ 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_synthesis_filterbank_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>
+gr_pfb_synthesis_filterbank_ccf::channel_map() const
+{
+ return d_channel_map;
+}
+
int
gr_pfb_synthesis_filterbank_ccf::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];
- int numsigs = input_items.size();
- int ndiff = d_numchans - numsigs;
- unsigned int nhalf = (unsigned int)ceil((float)numsigs/2.0f);
if (d_updated) {
d_updated = false;
@@ -136,33 +236,48 @@ gr_pfb_synthesis_filterbank_ccf::work (int noutput_items,
}
unsigned int n, i;
- for(n = 0; n < noutput_items/d_numchans; n++) {
- // fill up the populated channels based on the
- // number of real input streams
- for(i = 0; i < nhalf; i++) {
- in = (gr_complex*)input_items[i];
- d_fft->get_inbuf()[i] = (in+i)[n];
- }
-
- // Make the ndiff channels around N/2 0
- for(; i < nhalf+ndiff; i++) {
- d_fft->get_inbuf()[i] = gr_complex(0,0);
- }
+ size_t ninputs = input_items.size();
- // Finish off channels with data
- for(; i < d_numchans; i++) {
- in = (gr_complex*)input_items[i-ndiff];
- d_fft->get_inbuf()[i] = (in+i)[n];
+ // 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;
}
-
- // spin through IFFT
- d_fft->execute();
-
- for(i = 0; i < d_numchans; i++) {
- out[d_numchans-i-1] = d_filters[d_numchans-i-1]->filter(d_fft->get_outbuf()[i]);
+ }
+
+ // 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;
}
-
- out += d_numchans;
}
return noutput_items;
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.h b/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.h
index 1f772b1dd..d7f9d26c7 100644
--- a/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.h
+++ b/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.h
@@ -27,11 +27,12 @@
#include <gr_core_api.h>
#include <gr_sync_interpolator.h>
#include <gri_fir_filter_with_buffer_ccf.h>
+#include <gruel/thread.h>
class gr_pfb_synthesis_filterbank_ccf;
typedef boost::shared_ptr<gr_pfb_synthesis_filterbank_ccf> gr_pfb_synthesis_filterbank_ccf_sptr;
GR_CORE_API gr_pfb_synthesis_filterbank_ccf_sptr gr_make_pfb_synthesis_filterbank_ccf
- (unsigned int numchans, const std::vector<float> &taps);
+ (unsigned int numchans, const std::vector<float> &taps, bool twox=false);
class gri_fft_complex;
@@ -55,9 +56,10 @@ class GR_CORE_API gr_pfb_synthesis_filterbank_ccf : public gr_sync_interpolator
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)
*/
friend GR_CORE_API gr_pfb_synthesis_filterbank_ccf_sptr gr_make_pfb_synthesis_filterbank_ccf
- (unsigned int numchans, const std::vector<float> &taps);
+ (unsigned int numchans, const std::vector<float> &taps, bool twox);
bool d_updated;
unsigned int d_numchans;
@@ -65,7 +67,20 @@ class GR_CORE_API gr_pfb_synthesis_filterbank_ccf : public gr_sync_interpolator
gri_fft_complex *d_fft;
std::vector< gri_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);
/*!
* Build the polyphase synthesis filterbank.
@@ -73,9 +88,11 @@ class GR_CORE_API gr_pfb_synthesis_filterbank_ccf : public gr_sync_interpolator
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)
*/
gr_pfb_synthesis_filterbank_ccf (unsigned int numchans,
- const std::vector<float> &taps);
+ const std::vector<float> &taps,
+ bool twox);
public:
~gr_pfb_synthesis_filterbank_ccf ();
@@ -91,6 +108,36 @@ public:
* 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;
+
+ /*!
+ * 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.
+ */
+ void set_channel_map(const std::vector<int> &map);
+
+ /*!
+ * Gets the current channel map.
+ */
+ std::vector<int> channel_map() const;
int work (int noutput_items,
gr_vector_const_void_star &input_items,
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.i b/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.i
index 02a9f0255..c24abecf0 100644
--- a/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.i
+++ b/gnuradio-core/src/lib/filter/gr_pfb_synthesis_filterbank_ccf.i
@@ -23,16 +23,22 @@
GR_SWIG_BLOCK_MAGIC(gr,pfb_synthesis_filterbank_ccf);
gr_pfb_synthesis_filterbank_ccf_sptr gr_make_pfb_synthesis_filterbank_ccf
- (unsigned int numchans, const std::vector<float> &taps);
+ (unsigned int numchans, const std::vector<float> &taps, bool twox=false);
class gr_pfb_synthesis_filterbank_ccf : public gr_sync_interpolator
{
private:
gr_pfb_synthesis_filterbank_ccf (unsigned int numchans,
- const std::vector<float> &taps);
+ const std::vector<float> &taps,
+ bool twox=false);
public:
~gr_pfb_synthesis_filterbank_ccf ();
void set_taps (const std::vector<float> &taps);
+ void print_taps();
+ std::vector< std::vector<float> > taps() const;
+
+ void set_channel_map(const std::vector<int> &map);
+ std::vector<int> channel_map() const;
};
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py
index 3ddc1749a..dea71b286 100644
--- a/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py
@@ -34,7 +34,7 @@ class pfb_channelizer_ccf(gr.hier_block2):
gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
gr.io_signature(numchans, numchans, gr.sizeof_gr_complex)) # Output signature
- self._numchans = numchans
+ self._nchans = numchans
self._oversample_rate = oversample_rate
if taps is not None:
@@ -47,7 +47,7 @@ class pfb_channelizer_ccf(gr.hier_block2):
made = False
while not made:
try:
- self._taps = optfir.low_pass(1, self._numchans, bw, bw+tb, ripple, atten)
+ self._taps = optfir.low_pass(1, self._nchans, bw, bw+tb, ripple, atten)
made = True
except RuntimeError:
ripple += 0.01
@@ -58,22 +58,16 @@ class pfb_channelizer_ccf(gr.hier_block2):
if(ripple >= 1.0):
raise RuntimeError("optfir could not generate an appropriate filter.")
- self.s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, self._numchans)
- self.pfb = gr.pfb_channelizer_ccf(self._numchans, self._taps,
+ self.s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, self._nchans)
+ self.pfb = gr.pfb_channelizer_ccf(self._nchans, self._taps,
self._oversample_rate)
- self.v2s = gr.vector_to_streams(gr.sizeof_gr_complex, self._numchans)
-
self.connect(self, self.s2ss)
- for i in xrange(self._numchans):
+ for i in xrange(self._nchans):
self.connect((self.s2ss,i), (self.pfb,i))
+ self.connect((self.pfb,i), (self,i))
- # Get independent streams from the filterbank and send them out
- self.connect(self.pfb, self.v2s)
-
- for i in xrange(self._numchans):
- self.connect((self.v2s,i), (self,i))
-
-
+ def set_channel_map(self, newmap):
+ self.pfb.set_channel_map(newmap)
diff --git a/gnuradio-examples/python/pfb/CMakeLists.txt b/gnuradio-examples/python/pfb/CMakeLists.txt
index 55dbb16ac..88fdf2ead 100644
--- a/gnuradio-examples/python/pfb/CMakeLists.txt
+++ b/gnuradio-examples/python/pfb/CMakeLists.txt
@@ -29,6 +29,7 @@ GR_PYTHON_INSTALL(PROGRAMS
resampler.py
synth_filter.py
synth_to_chan.py
+ reconstruction.py
DESTINATION ${GR_PKG_DATA_DIR}/examples/pfb
COMPONENT "gnuradio_examples"
)
diff --git a/gnuradio-examples/python/pfb/channelize.py b/gnuradio-examples/python/pfb/channelize.py
index 999e5d20e..2fcb14a36 100755
--- a/gnuradio-examples/python/pfb/channelize.py
+++ b/gnuradio-examples/python/pfb/channelize.py
@@ -68,7 +68,7 @@ class pfb_top_block(gr.top_block):
self.head = gr.head(gr.sizeof_gr_complex, self._N)
# Construct the channelizer filter
- self.pfb = blks2.pfb_channelizer_ccf(self._M, self._taps)
+ self.pfb = blks2.pfb_channelizer_ccf(self._M, self._taps, 1)
# Construct a vector sink for the input signal to the channelizer
self.snk_i = gr.vector_sink_c()
@@ -77,6 +77,9 @@ class pfb_top_block(gr.top_block):
self.connect(self.add, self.head, self.pfb)
self.connect(self.add, self.snk_i)
+ # Use this to play with the channel mapping
+ #self.pfb.set_channel_map([5,6,7,8,0,1,2,3,4])
+
# Create a vector sink for each of M output channels of the filter and connect it
self.snks = list()
for i in xrange(self._M):
diff --git a/gnuradio-examples/python/pfb/reconstruction.py b/gnuradio-examples/python/pfb/reconstruction.py
new file mode 100755
index 000000000..2b6f9a831
--- /dev/null
+++ b/gnuradio-examples/python/pfb/reconstruction.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+import scipy, math, pylab
+from scipy import fftpack
+from gnuradio import gr, digital, blks2
+
+fftlen = 8192
+
+def main():
+ N = 10000
+ fs = 2000.0
+ Ts = 1.0/fs
+ t = scipy.arange(0, N*Ts, Ts)
+
+ # When playing with the number of channels, be careful about the filter
+ # specs and the channel map of the synthesizer set below.
+ nchans = 10
+
+ # Build the filter(s)
+ bw = 1000
+ tb = 400
+ proto_taps = gr.firdes.low_pass_2(1, nchans*fs, bw, tb, 80,
+ gr.firdes.WIN_BLACKMAN_hARRIS)
+ print "Filter length: ", len(proto_taps)
+
+
+ # Create a modulated signal
+ npwr = 0.01
+ data = scipy.random.randint(0, 256, N)
+ rrc_taps = gr.firdes.root_raised_cosine(1, 2, 1, 0.35, 41)
+
+ src = gr.vector_source_b(data.astype(scipy.uint8).tolist(), False)
+ mod = digital.bpsk_mod(samples_per_symbol=2)
+ chan = gr.channel_model(npwr)
+ rrc = gr.fft_filter_ccc(1, rrc_taps)
+
+ # Split it up into pieces
+ channelizer = blks2.pfb_channelizer_ccf(nchans, proto_taps, 2)
+
+ # Put the pieces back together again
+ syn_taps = [nchans*t for t in proto_taps]
+ synthesizer = gr.pfb_synthesis_filterbank_ccf(nchans, syn_taps, True)
+ src_snk = gr.vector_sink_c()
+ snk = gr.vector_sink_c()
+
+ # Remap the location of the channels
+ # Can be done in synth or channelizer (watch out for rotattions in
+ # the channelizer)
+ synthesizer.set_channel_map([ 0, 1, 2, 3, 4,
+ 15, 16, 17, 18, 19])
+
+ tb = gr.top_block()
+ tb.connect(src, mod, chan, rrc, channelizer)
+ tb.connect(rrc, src_snk)
+
+ vsnk = []
+ for i in xrange(nchans):
+ tb.connect((channelizer,i), (synthesizer, i))
+
+ vsnk.append(gr.vector_sink_c())
+ tb.connect((channelizer,i), vsnk[i])
+
+ tb.connect(synthesizer, snk)
+ tb.run()
+
+ sin = scipy.array(src_snk.data()[1000:])
+ sout = scipy.array(snk.data()[1000:])
+
+
+ # Plot original signal
+ fs_in = nchans*fs
+ f1 = pylab.figure(1, figsize=(16,12), facecolor='w')
+ s11 = f1.add_subplot(2,2,1)
+ s11.psd(sin, NFFT=fftlen, Fs=fs_in)
+ s11.set_title("PSD of Original Signal")
+ s11.set_ylim([-200, -20])
+
+ s12 = f1.add_subplot(2,2,2)
+ s12.plot(sin.real[1000:1500], "o-b")
+ s12.plot(sin.imag[1000:1500], "o-r")
+ s12.set_title("Original Signal in Time")
+
+ start = 1
+ skip = 4
+ s13 = f1.add_subplot(2,2,3)
+ s13.plot(sin.real[start::skip], sin.imag[start::skip], "o")
+ s13.set_title("Constellation")
+ s13.set_xlim([-2, 2])
+ s13.set_ylim([-2, 2])
+
+ # Plot channels
+ nrows = int(scipy.sqrt(nchans))
+ ncols = int(scipy.ceil(float(nchans)/float(nrows)))
+
+ f2 = pylab.figure(2, figsize=(16,12), facecolor='w')
+ for n in xrange(nchans):
+ s = f2.add_subplot(nrows, ncols, n+1)
+ s.psd(vsnk[n].data(), NFFT=fftlen, Fs=fs_in)
+ s.set_title("Channel {0}".format(n))
+ s.set_ylim([-200, -20])
+
+ # Plot reconstructed signal
+ fs_out = 2*nchans*fs
+ f3 = pylab.figure(3, figsize=(16,12), facecolor='w')
+ s31 = f3.add_subplot(2,2,1)
+ s31.psd(sout, NFFT=fftlen, Fs=fs_out)
+ s31.set_title("PSD of Reconstructed Signal")
+ s31.set_ylim([-200, -20])
+
+ s32 = f3.add_subplot(2,2,2)
+ s32.plot(sout.real[1000:1500], "o-b")
+ s32.plot(sout.imag[1000:1500], "o-r")
+ s32.set_title("Reconstructed Signal in Time")
+
+ start = 2
+ skip = 4
+ s33 = f3.add_subplot(2,2,3)
+ s33.plot(sout.real[start::skip], sout.imag[start::skip], "o")
+ s33.set_title("Constellation")
+ s33.set_xlim([-2, 2])
+ s33.set_ylim([-2, 2])
+
+ pylab.show()
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except KeyboardInterrupt:
+ pass
+
diff --git a/grc/blocks/blks2_pfb_channelizer.xml b/grc/blocks/blks2_pfb_channelizer.xml
index aee9dd512..5a1b20edb 100644
--- a/grc/blocks/blks2_pfb_channelizer.xml
+++ b/grc/blocks/blks2_pfb_channelizer.xml
@@ -18,6 +18,8 @@
<!-- Set taps not implemented yet
<callback>set_taps($taps)</callback>
-->
+ <callback>set_channel_map(ch_$map)</callback>
+
<param>
<name>Channels</name>
<key>nchans</key>
@@ -41,6 +43,11 @@
<value>100</value>
<type>real</type>
</param>
+ <param>
+ <name>Channel Map</name>
+ <key>ch_map</key>
+ <type>int_vector</type>
+ </param>
<sink>
<name>in</name>
<type>complex</type>
diff --git a/grc/blocks/gr_pfb_synthesis_filterbank.xml b/grc/blocks/gr_pfb_synthesis_filterbank.xml
index a8b944c6a..6ea54632c 100644
--- a/grc/blocks/gr_pfb_synthesis_filterbank.xml
+++ b/grc/blocks/gr_pfb_synthesis_filterbank.xml
@@ -10,9 +10,10 @@
<import>from gnuradio import gr</import>
<import>from gnuradio.gr import firdes</import>
<make>gr.pfb_synthesis_filterbank_ccf(
- $numchans, $taps)
+ $numchans, $taps, $twox)
</make>
<callback>set_taps($taps)</callback>
+ <callback>set_channel_map($ch_map)</callback>
<param>
<name>Channels</name>
@@ -31,6 +32,17 @@
<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>
+ <type>int_vector</type>
+ </param>
<sink>
<name>in</name>
<type>complex</type>