diff options
-rwxr-xr-x | gr-filter/examples/reconstruction.py | 6 | ||||
-rw-r--r-- | gr-filter/grc/filter_pfb_channelizer.xml | 13 | ||||
-rw-r--r-- | gr-filter/grc/filter_pfb_synthesizer.xml | 8 | ||||
-rw-r--r-- | gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h | 12 | ||||
-rw-r--r-- | gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h | 55 | ||||
-rw-r--r-- | gr-filter/lib/pfb_channelizer_ccf_impl.cc | 72 | ||||
-rw-r--r-- | gr-filter/lib/pfb_channelizer_ccf_impl.h | 2 | ||||
-rw-r--r-- | gr-filter/lib/pfb_synthesizer_ccf_impl.cc | 268 | ||||
-rw-r--r-- | gr-filter/lib/polyphase_filterbank.cc | 2 | ||||
-rwxr-xr-x | gr-filter/python/filter/qa_pfb_channelizer.py | 16 | ||||
-rwxr-xr-x | gr-filter/python/filter/qa_pfb_decimator.py | 11 | ||||
-rwxr-xr-x | gr-filter/python/filter/qa_pfb_interpolator.py | 20 | ||||
-rwxr-xr-x | gr-filter/python/filter/qa_pfb_synthesizer.py | 34 |
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__': |