diff options
Diffstat (limited to 'gnuradio-core/src')
-rw-r--r-- | gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc | 12 | ||||
-rw-r--r-- | gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h | 25 | ||||
-rw-r--r-- | gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.cc | 91 | ||||
-rw-r--r-- | gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.h | 58 | ||||
-rw-r--r-- | gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.i | 10 | ||||
-rw-r--r-- | gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h | 95 | ||||
-rw-r--r-- | gnuradio-core/src/lib/filter/gr_pfb_clock_sync_fff.h | 95 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h | 10 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_ofdm_sampler.cc | 3 | ||||
-rw-r--r-- | gnuradio-core/src/lib/io/Makefile.am | 2 | ||||
-rw-r--r-- | gnuradio-core/src/lib/io/gri_logger.cc | 178 | ||||
-rw-r--r-- | gnuradio-core/src/lib/io/gri_logger.h | 59 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py | 8 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/gruimpl/hexint.py | 12 |
14 files changed, 358 insertions, 300 deletions
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc b/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc index 48eb849ab..5a6e753ab 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc +++ b/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -79,8 +79,8 @@ gr_pfb_arb_resampler_ccf::gr_pfb_arb_resampler_ccf (float rate, // Now, actually set the filters' taps std::vector<float> dtaps; create_diff_taps(taps, dtaps); - set_taps(taps, d_taps, d_filters); - set_taps(dtaps, d_dtaps, d_diff_filters); + create_taps(taps, d_taps, d_filters); + create_taps(dtaps, d_dtaps, d_diff_filters); } gr_pfb_arb_resampler_ccf::~gr_pfb_arb_resampler_ccf () @@ -91,9 +91,9 @@ gr_pfb_arb_resampler_ccf::~gr_pfb_arb_resampler_ccf () } void -gr_pfb_arb_resampler_ccf::set_taps (const std::vector<float> &newtaps, - std::vector< std::vector<float> > &ourtaps, - std::vector<gr_fir_ccf*> &ourfilter) +gr_pfb_arb_resampler_ccf::create_taps (const std::vector<float> &newtaps, + std::vector< std::vector<float> > &ourtaps, + std::vector<gr_fir_ccf*> &ourfilter) { int i,j; diff --git a/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h b/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h index b99ad286b..cf5a79d4e 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h +++ b/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -139,18 +139,23 @@ class gr_pfb_arb_resampler_ccf : public gr_block void create_diff_taps(const std::vector<float> &newtaps, std::vector<float> &difftaps); - -public: - ~gr_pfb_arb_resampler_ccf (); - + /*! * Resets the filterbank's filter taps with the new prototype filter - * \param taps (vector/list of floats) The prototype filter to populate the filterbank. The taps - * should be generated at the interpolated sampling rate. + * \param newtaps (vector of floats) The prototype filter to populate the filterbank. + * The taps should be generated at the interpolated sampling rate. + * \param ourtaps (vector of floats) Reference to our internal member of holding the taps. + * \param ourfilter (vector of filters) Reference to our internal filter to set the taps for. */ - void set_taps (const std::vector<float> &newtaps, - std::vector< std::vector<float> > &ourtaps, - std::vector<gr_fir_ccf*> &ourfilter); + void create_taps (const std::vector<float> &newtaps, + std::vector< std::vector<float> > &ourtaps, + std::vector<gr_fir_ccf*> &ourfilter); + + +public: + ~gr_pfb_arb_resampler_ccf (); + + // FIXME: See about a set_taps function during runtime. /*! * Print all of the filterbank taps to screen. 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 7e34551c8..5fda47880 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.cc +++ b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -33,20 +33,33 @@ #include <cstring> gr_pfb_channelizer_ccf_sptr gr_make_pfb_channelizer_ccf (unsigned int numchans, - const std::vector<float> &taps) + const std::vector<float> &taps, + float oversample_rate) { - return gr_pfb_channelizer_ccf_sptr (new gr_pfb_channelizer_ccf (numchans, taps)); + return gr_pfb_channelizer_ccf_sptr (new gr_pfb_channelizer_ccf (numchans, taps, + oversample_rate)); } gr_pfb_channelizer_ccf::gr_pfb_channelizer_ccf (unsigned int numchans, - const std::vector<float> &taps) - : gr_sync_block ("pfb_channelizer_ccf", - gr_make_io_signature (numchans, numchans, sizeof(gr_complex)), - gr_make_io_signature (1, 1, numchans*sizeof(gr_complex))), - d_updated (false) + const std::vector<float> &taps, + float oversample_rate) + : gr_block ("pfb_channelizer_ccf", + gr_make_io_signature (numchans, numchans, sizeof(gr_complex)), + gr_make_io_signature (1, 1, numchans*sizeof(gr_complex))), + d_updated (false), d_numchans(numchans), d_oversample_rate(oversample_rate) { - d_numchans = numchans; + // The over sampling rate must be rationally related to the number of channels + // in that it must be N/i for i in [1,N], which gives an outputsample rate + // of [fs/N, fs] where fs is the input sample rate. + // This tests the specified input sample rate to see if it conforms to this + // requirement within a few significant figures. + double intp = 0; + double x = (10000.0*rint(numchans / oversample_rate)) / 10000.0; + double fltp = modf(numchans / oversample_rate, &intp); + if(fltp != 0.0) + throw std::invalid_argument("gr_pfb_channelizer: oversample rate must be N/i for i in [1, N]"); + d_filters = std::vector<gr_fir_ccf*>(d_numchans); // Create an FIR filter for each channel and zero out the taps @@ -60,10 +73,28 @@ gr_pfb_channelizer_ccf::gr_pfb_channelizer_ccf (unsigned int numchans, // Create the FFT to handle the output de-spinning of the channels d_fft = new gri_fft_complex (d_numchans, false); + + // Although the filters change, we use this look up table + // to set the index of the FFT input buffer, which equivalently + // performs the FFT shift operation on every other turn. + d_rate_ratio = (int)rintf(d_numchans / d_oversample_rate); + d_idxlut = new int[d_numchans]; + for(unsigned int i = 0; i < d_numchans; i++) { + d_idxlut[i] = d_numchans - ((i + d_rate_ratio) % d_numchans) - 1; + } + + // Calculate the number of filtering rounds to do to evenly + // align the input vectors with the output channels + d_output_multiple = 1; + while((d_output_multiple * d_rate_ratio) % d_numchans != 0) + d_output_multiple++; + set_output_multiple(d_output_multiple); } gr_pfb_channelizer_ccf::~gr_pfb_channelizer_ccf () { + delete [] d_idxlut; + for(unsigned int i = 0; i < d_numchans; i++) { delete d_filters[i]; } @@ -101,7 +132,7 @@ gr_pfb_channelizer_ccf::set_taps (const std::vector<float> &taps) } // Set the history to ensure enough input items for each filter - set_history (d_taps_per_filter); + set_history (d_taps_per_filter+1); d_updated = true; } @@ -121,9 +152,10 @@ gr_pfb_channelizer_ccf::print_taps() int -gr_pfb_channelizer_ccf::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) +gr_pfb_channelizer_ccf::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) { gr_complex *in = (gr_complex *) input_items[0]; gr_complex *out = (gr_complex *) output_items[0]; @@ -133,20 +165,35 @@ gr_pfb_channelizer_ccf::work (int noutput_items, return 0; // history requirements may have changed. } - for(int i = 0; i < noutput_items; i++) { - // Move through filters from bottom to top - for(int j = d_numchans-1; j >= 0; j--) { - // Take in the items from the first input stream to d_numchans - in = (gr_complex*)input_items[d_numchans - 1 - j]; + int n=1, i=-1, j=0, last; + int toconsume = (int)rintf(noutput_items/d_oversample_rate); + while(n <= toconsume) { + j = 0; + i = (i + d_rate_ratio) % d_numchans; + last = i; + while(i >= 0) { + in = (gr_complex*)input_items[j]; + d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n]); + j++; + i--; + } - // Filter current input stream from bottom filter to top - d_fft->get_inbuf()[j] = d_filters[j]->filter(&in[i]); + i = d_numchans-1; + while(i > last) { + in = (gr_complex*)input_items[j]; + d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n-1]); + j++; + i--; } + n += (i+d_rate_ratio) >= (int)d_numchans; + // despin through FFT d_fft->execute(); - memcpy(&out[d_numchans*i], d_fft->get_outbuf(), d_numchans*sizeof(gr_complex)); + memcpy(out, d_fft->get_outbuf(), d_numchans*sizeof(gr_complex)); + out += d_numchans; } - + + consume_each(toconsume); return noutput_items; } 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 b2e67e817..751673bc7 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.h +++ b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -24,12 +24,13 @@ #ifndef INCLUDED_GR_PFB_CHANNELIZER_CCF_H #define INCLUDED_GR_PFB_CHANNELIZER_CCF_H -#include <gr_sync_block.h> +#include <gr_block.h> class gr_pfb_channelizer_ccf; typedef boost::shared_ptr<gr_pfb_channelizer_ccf> gr_pfb_channelizer_ccf_sptr; gr_pfb_channelizer_ccf_sptr gr_make_pfb_channelizer_ccf (unsigned int numchans, - const std::vector<float> &taps); + const std::vector<float> &taps, + float oversample_rate=1); class gr_fir_ccf; class gri_fft_complex; @@ -88,6 +89,19 @@ class gri_fft_complex; * <B><EM>self._taps = gr.firdes.low_pass_2(1, fs, BW, TB, * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS)</EM></B> * + * The filter output can also be overs ampled. The over sampling rate + * is the ratio of the the actual output sampling rate to the normal + * output sampling rate. It must be rationally related to the number + * of channels as N/i for i in [1,N], which gives an outputsample rate + * of [fs/N, fs] where fs is the input sample rate and N is the number + * of channels. + * + * For example, for 6 channels with fs = 6000 Hz, the normal rate is + * 6000/6 = 1000 Hz. Allowable oversampling rates are 6/6, 6/5, 6/4, + * 6/3, 6/2, and 6/1 where the output sample rate of a 6/1 oversample + * ratio is 6000 Hz, or 6 times the normal 1000 Hz. A rate of 6/5 = 1.2, + * so the output rate would be 1200 Hz. + * * The theory behind this block can be found in Chapter 6 of * the following book. * @@ -96,31 +110,50 @@ class gri_fft_complex; * */ -class gr_pfb_channelizer_ccf : public gr_sync_block +class gr_pfb_channelizer_ccf : public gr_block { private: /*! * Build the polyphase filterbank decimator. * \param numchans (unsigned integer) Specifies the number of channels <EM>M</EM> * \param taps (vector/list of floats) The prototype filter to populate the filterbank. + * \param oversample_rate (float) The over sampling rate is the ratio of the the actual + * output sampling rate to the normal output sampling rate. + * It must be rationally related to the number of channels + * as N/i for i in [1,N], which gives an outputsample rate + * of [fs/N, fs] where fs is the input sample rate and N is + * the number of channels. + * + * For example, for 6 channels with fs = 6000 Hz, the normal + * rate is 6000/6 = 1000 Hz. Allowable oversampling rates + * are 6/6, 6/5, 6/4, 6/3, 6/2, and 6/1 where the output + * sample rate of a 6/1 oversample ratio is 6000 Hz, or + * 6 times the normal 1000 Hz. */ friend gr_pfb_channelizer_ccf_sptr gr_make_pfb_channelizer_ccf (unsigned int numchans, - const std::vector<float> &taps); + const std::vector<float> &taps, + float oversample_rate); + bool d_updated; + unsigned int d_numchans; + float d_oversample_rate; std::vector<gr_fir_ccf*> d_filters; std::vector< std::vector<float> > d_taps; - gri_fft_complex *d_fft; - unsigned int d_numchans; unsigned int d_taps_per_filter; - bool d_updated; + gri_fft_complex *d_fft; + int *d_idxlut; + int d_rate_ratio; + int d_output_multiple; /*! * Build the polyphase filterbank decimator. * \param numchans (unsigned integer) Specifies the number of channels <EM>M</EM> * \param taps (vector/list of floats) The prototype filter to populate the filterbank. + * \param oversample_rate (float) The output over sampling rate. */ gr_pfb_channelizer_ccf (unsigned int numchans, - const std::vector<float> &taps); + const std::vector<float> &taps, + float oversample_rate); public: ~gr_pfb_channelizer_ccf (); @@ -136,9 +169,10 @@ public: */ void print_taps(); - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); }; #endif 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 4bef90e22..63e3e0fe6 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.i +++ b/gnuradio-core/src/lib/filter/gr_pfb_channelizer_ccf.i @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -23,13 +23,15 @@ GR_SWIG_BLOCK_MAGIC(gr,pfb_channelizer_ccf); gr_pfb_channelizer_ccf_sptr gr_make_pfb_channelizer_ccf (unsigned int numchans, - const std::vector<float> &taps); + const std::vector<float> &taps, + float oversample_rate=1); -class gr_pfb_channelizer_ccf : public gr_sync_block +class gr_pfb_channelizer_ccf : public gr_block { private: gr_pfb_channelizer_ccf (unsigned int numchans, - const std::vector<float> &taps); + const std::vector<float> &taps, + float oversample_rate); public: ~gr_pfb_channelizer_ccf (); diff --git a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h index 70857173b..4e6ef5fc4 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h +++ b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h @@ -43,6 +43,71 @@ class gr_fir_ccf; * * \ingroup filter_blk * + * This block performs timing synchronization for PAM signals by minimizing the + * derivative of the filtered signal, which in turn maximizes the SNR and + * minimizes ISI. + * + * This approach works by setting up two filterbanks; one filterbanke contains the + * signal's pulse shaping matched filter (such as a root raised cosine filter), + * where each branch of the filterbank contains a different phase of the filter. + * The second filterbank contains the derivatives of the filters in the first + * filterbank. Thinking of this in the time domain, the first filterbank contains + * filters that have a sinc shape to them. We want to align the output signal to + * be sampled at exactly the peak of the sinc shape. The derivative of the sinc + * contains a zero at the maximum point of the sinc (sinc(0) = 1, sinc(0)' = 0). + * Furthermore, the region around the zero point is relatively linear. We make + * use of this fact to generate the error signal. + * + * If the signal out of the derivative filters is d_i[n] for the ith filter, and + * the output of the matched filter is x_i[n], we calculate the error as: + * e[n] = (Re{x_i[n]} * Re{d_i[n]} + Im{x_i[n]} * Im{d_i[n]}) / 2.0 + * This equation averages the error in the real and imaginary parts. There are two + * reasons we multiply by the signal itself. First, if the symbol could be positive + * or negative going, but we want the error term to always tell us to go in the + * same direction depending on which side of the zero point we are on. The sign of + * x_i[n] adjusts the error term to do this. Second, the magnitude of x_i[n] scales + * the error term depending on the symbol's amplitude, so larger signals give us + * a stronger error term because we have more confidence in that symbol's value. + * Using the magnitude of x_i[n] instead of just the sign is especially good for + * signals with low SNR. + * + * The error signal, e[n], gives us a value proportional to how far away from the zero + * point we are in the derivative signal. We want to drive this value to zero, so we + * set up a second order loop. We have two variables for this loop; d_k is the filter + * number in the filterbank we are on and d_rate is the rate which we travel through + * the filters in the steady state. That is, due to the natural clock differences between + * the transmitter and receiver, d_rate represents that difference and would traverse + * the filter phase paths to keep the receiver locked. Thinking of this as a second-order + * PLL, the d_rate is the frequency and d_k is the phase. So we update d_rate and d_k + * using the standard loop equations based on two error signals, d_alpha and d_beta. + * We have these two values set based on each other for a critically damped system, so in + * the block constructor, we just ask for "gain," which is d_alpha while d_beta is + * equal to (gain^2)/4. + * + * The clock sync block needs to know the number of samples per second (sps), because it + * only returns a single point representing the sample. The sps can be any positive real + * number and does not need to be an integer. The filter taps must also be specified. The + * taps are generated by first conceiving of the prototype filter that would be the signal's + * matched filter. Then interpolate this by the number of filters in the filterbank. These + * are then distributed among all of the filters. So if the prototype filter was to have + * 45 taps in it, then each path of the filterbank will also have 45 taps. This is easily + * done by building the filter with the sample rate multiplied by the number of filters + * to use. + * + * The number of filters can also be set and defaults to 32. With 32 filters, you get a + * good enough resolution in the phase to produce very small, almost unnoticeable, ISI. + * Going to 64 filters can reduce this more, but after that there is very little gained + * for the extra complexity. + * + * The initial phase is another settable parameter and refers to the filter path the + * algorithm initially looks at (i.e., d_k starts at init_phase). This value defaults + * to zero, but it might be useful to start at a different phase offset, such as the mid- + * point of the filters. + * + * The final parameter is the max_rate_devitation, which defaults to 1.5. This is how far + * we allow d_rate to swing, positive or negative, from 0. Constraining the rate can help + * keep the algorithm from walking too far away to lock during times when there is no signal. + * */ class gr_pfb_clock_sync_ccf : public gr_block @@ -50,6 +115,14 @@ class gr_pfb_clock_sync_ccf : public gr_block private: /*! * Build the polyphase filterbank timing synchronizer. + * \param sps (double) The number of samples per second in the incoming signal + * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default. + * \param taps (vector<int>) The filter taps. + * \param filter_size (uint) The number of filters in the filterbank (default = 32). + * \param init_phase (float) The initial phase to look at, or which filter to start + * with (default = 0). + * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5). + * */ friend gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, const std::vector<float> &taps, @@ -96,24 +169,46 @@ public: void set_taps (const std::vector<float> &taps, std::vector< std::vector<float> > &ourtaps, std::vector<gr_fir_ccf*> &ourfilter); + + /*! + * Returns the taps of the matched filter + */ std::vector<float> channel_taps(int channel); + + /*! + * Returns the taps in the derivative filter + */ std::vector<float> diff_channel_taps(int channel); /*! * Print all of the filterbank taps to screen. */ void print_taps(); + + /*! + * Print all of the filterbank taps of the derivative filter to screen. + */ void print_diff_taps(); + /*! + * Set the gain value alpha for the control loop + */ void set_alpha(float alpha) { d_alpha = alpha; } + + /*! + * Set the gain value beta for the control loop + */ void set_beta(float beta) { d_beta = beta; } + /*! + * Set the maximum deviation from 0 d_rate can have + */ void set_max_rate_deviation(float m) { d_max_dev = m; diff --git a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_fff.h b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_fff.h index 10eec4f54..fa1279a7c 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_fff.h +++ b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_fff.h @@ -43,6 +43,71 @@ class gr_fir_fff; * * \ingroup filter_blk * + * This block performs timing synchronization for PAM signals by minimizing the + * derivative of the filtered signal, which in turn maximizes the SNR and + * minimizes ISI. + * + * This approach works by setting up two filterbanks; one filterbanke contains the + * signal's pulse shaping matched filter (such as a root raised cosine filter), + * where each branch of the filterbank contains a different phase of the filter. + * The second filterbank contains the derivatives of the filters in the first + * filterbank. Thinking of this in the time domain, the first filterbank contains + * filters that have a sinc shape to them. We want to align the output signal to + * be sampled at exactly the peak of the sinc shape. The derivative of the sinc + * contains a zero at the maximum point of the sinc (sinc(0) = 1, sinc(0)' = 0). + * Furthermore, the region around the zero point is relatively linear. We make + * use of this fact to generate the error signal. + * + * If the signal out of the derivative filters is d_i[n] for the ith filter, and + * the output of the matched filter is x_i[n], we calculate the error as: + * e[n] = (Re{x_i[n]} * Re{d_i[n]} + Im{x_i[n]} * Im{d_i[n]}) / 2.0 + * This equation averages the error in the real and imaginary parts. There are two + * reasons we multiply by the signal itself. First, if the symbol could be positive + * or negative going, but we want the error term to always tell us to go in the + * same direction depending on which side of the zero point we are on. The sign of + * x_i[n] adjusts the error term to do this. Second, the magnitude of x_i[n] scales + * the error term depending on the symbol's amplitude, so larger signals give us + * a stronger error term because we have more confidence in that symbol's value. + * Using the magnitude of x_i[n] instead of just the sign is especially good for + * signals with low SNR. + * + * The error signal, e[n], gives us a value proportional to how far away from the zero + * point we are in the derivative signal. We want to drive this value to zero, so we + * set up a second order loop. We have two variables for this loop; d_k is the filter + * number in the filterbank we are on and d_rate is the rate which we travel through + * the filters in the steady state. That is, due to the natural clock differences between + * the transmitter and receiver, d_rate represents that difference and would traverse + * the filter phase paths to keep the receiver locked. Thinking of this as a second-order + * PLL, the d_rate is the frequency and d_k is the phase. So we update d_rate and d_k + * using the standard loop equations based on two error signals, d_alpha and d_beta. + * We have these two values set based on each other for a critically damped system, so in + * the block constructor, we just ask for "gain," which is d_alpha while d_beta is + * equal to (gain^2)/4. + * + * The clock sync block needs to know the number of samples per second (sps), because it + * only returns a single point representing the sample. The sps can be any positive real + * number and does not need to be an integer. The filter taps must also be specified. The + * taps are generated by first conceiving of the prototype filter that would be the signal's + * matched filter. Then interpolate this by the number of filters in the filterbank. These + * are then distributed among all of the filters. So if the prototype filter was to have + * 45 taps in it, then each path of the filterbank will also have 45 taps. This is easily + * done by building the filter with the sample rate multiplied by the number of filters + * to use. + * + * The number of filters can also be set and defaults to 32. With 32 filters, you get a + * good enough resolution in the phase to produce very small, almost unnoticeable, ISI. + * Going to 64 filters can reduce this more, but after that there is very little gained + * for the extra complexity. + * + * The initial phase is another settable parameter and refers to the filter path the + * algorithm initially looks at (i.e., d_k starts at init_phase). This value defaults + * to zero, but it might be useful to start at a different phase offset, such as the mid- + * point of the filters. + * + * The final parameter is the max_rate_devitation, which defaults to 1.5. This is how far + * we allow d_rate to swing, positive or negative, from 0. Constraining the rate can help + * keep the algorithm from walking too far away to lock during times when there is no signal. + * */ class gr_pfb_clock_sync_fff : public gr_block @@ -50,6 +115,14 @@ class gr_pfb_clock_sync_fff : public gr_block private: /*! * Build the polyphase filterbank timing synchronizer. + * \param sps (double) The number of samples per second in the incoming signal + * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default. + * \param taps (vector<int>) The filter taps. + * \param filter_size (uint) The number of filters in the filterbank (default = 32). + * \param init_phase (float) The initial phase to look at, or which filter to start + * with (default = 0). + * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5). + * */ friend gr_pfb_clock_sync_fff_sptr gr_make_pfb_clock_sync_fff (double sps, float gain, const std::vector<float> &taps, @@ -96,24 +169,46 @@ public: void set_taps (const std::vector<float> &taps, std::vector< std::vector<float> > &ourtaps, std::vector<gr_fir_fff*> &ourfilter); + + /*! + * Returns the taps of the matched filter + */ std::vector<float> channel_taps(int channel); + + /*! + * Returns the taps in the derivative filter + */ std::vector<float> diff_channel_taps(int channel); /*! * Print all of the filterbank taps to screen. */ void print_taps(); + + /*! + * Print all of the filterbank taps of the derivative filter to screen. + */ void print_diff_taps(); + /*! + * Set the gain value alpha for the control loop + */ void set_alpha(float alpha) { d_alpha = alpha; } + + /*! + * Set the gain value beta for the control loop + */ void set_beta(float beta) { d_beta = beta; } + /*! + * Set the maximum deviation from 0 d_rate can have + */ void set_max_rate_deviation(float m) { d_max_dev = m; diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h index 178e18f3e..db060793e 100644 --- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h +++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h @@ -48,12 +48,12 @@ class gri_fft_complex; * * The FLL works by filtering the upper and lower band edges into x_u(t) and x_l(t), respectively. * These are combined to form cc(t) = x_u(t) + x_l(t) and ss(t) = x_u(t) - x_l(t). Combining - * these to form the signal e(t) = Re{cc(t) \times ss(t)^*} (where ^* is the complex conjugate) + * these to form the signal e(t) = Re{cc(t) \\times ss(t)^*} (where ^* is the complex conjugate) * provides an error signal at the DC term that is directly proportional to the carrier frequency. * We then make a second-order loop using the error signal that is the running average of e(t). * * In theory, the band-edge filter is the derivative of the matched filter in frequency, - * (H_be(f) = \frac{H(f)}{df}. In practice, this comes down to a quarter sine wave at the point + * (H_be(f) = \\frac{H(f)}{df}. In practice, this comes down to a quarter sine wave at the point * of the matched filter's rolloff (if it's a raised-cosine, the derivative of a cosine is a sine). * Extend this sine by another quarter wave to make a half wave around the band-edges is equivalent * in time to the sum of two sinc functions. The baseband filter fot the band edges is therefore @@ -93,7 +93,11 @@ class gr_fll_band_edge_cc : public gr_sync_block /*! * Build the FLL - * \param taps (vector/list of gr_complex) The taps of the band-edge filter + * \param samps_per_sym (float) number of samples per symbol + * \param rolloff (float) Rolloff (excess bandwidth) of signal filter + * \param filter_size (int) number of filter taps to generate + * \param alpha (float) Alpha gain in the control loop + * \param beta (float) Beta gain in the control loop */ gr_fll_band_edge_cc(float samps_per_sym, float rolloff, int filter_size, float alpha, float beta); diff --git a/gnuradio-core/src/lib/general/gr_ofdm_sampler.cc b/gnuradio-core/src/lib/general/gr_ofdm_sampler.cc index 74bd65a50..7f6b2b01c 100644 --- a/gnuradio-core/src/lib/general/gr_ofdm_sampler.cc +++ b/gnuradio-core/src/lib/general/gr_ofdm_sampler.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2007,2008 Free Software Foundation, Inc. + * Copyright 2007,2008,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -45,6 +45,7 @@ gr_ofdm_sampler::gr_ofdm_sampler (unsigned int fft_length, gr_make_io_signature2 (2, 2, sizeof (gr_complex)*fft_length, sizeof(char)*fft_length)), d_state(STATE_NO_SIG), d_timeout_max(timeout), d_fft_length(fft_length), d_symbol_length(symbol_length) { + set_relative_rate(1.0/(double) fft_length); // buffer allocator hint } void diff --git a/gnuradio-core/src/lib/io/Makefile.am b/gnuradio-core/src/lib/io/Makefile.am index 9eacd137d..c52554645 100644 --- a/gnuradio-core/src/lib/io/Makefile.am +++ b/gnuradio-core/src/lib/io/Makefile.am @@ -39,7 +39,6 @@ libio_la_SOURCES = \ gr_oscope_guts.cc \ gr_oscope_sink_f.cc \ gr_oscope_sink_x.cc \ - gri_logger.cc \ i2c.cc \ i2c_bitbang.cc \ i2c_bbio.cc \ @@ -72,7 +71,6 @@ grinclude_HEADERS = \ gr_oscope_sink_f.h \ gr_oscope_sink_x.h \ gr_trigger_mode.h \ - gri_logger.h \ i2c.h \ i2c_bitbang.h \ i2c_bbio.h \ diff --git a/gnuradio-core/src/lib/io/gri_logger.cc b/gnuradio-core/src/lib/io/gri_logger.cc deleted file mode 100644 index 473a7c5ed..000000000 --- a/gnuradio-core/src/lib/io/gri_logger.cc +++ /dev/null @@ -1,178 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006,2009 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#if 0 // This needs reimplementation with boost threads and synchronization - -#include <gri_logger.h> -#include <stdio.h> -#include <stdarg.h> -#include <stdexcept> -#include <boost/weak_ptr.hpp> -#include <string.h> - - -/* - * This class creates the thread that reads from the ringbuffer and - * and writes to the file. This is opaque to the user. - */ -class gri_log_poster : public omni_thread -{ - FILE *d_fp; - gr_buffer_sptr d_writer; - gr_buffer_reader_sptr d_reader; - omni_semaphore d_ringbuffer_ready; - volatile bool d_time_to_die; - volatile bool d_writer_overrun; - - virtual void* run_undetached(void * arg); - -public: - gri_log_poster(const char *filename); - ~gri_log_poster(); - - void kill() { d_time_to_die = true; post(); } - gr_buffer_sptr writer() const { return d_writer; } - void post() { d_ringbuffer_ready.post(); } - void note_writer_overrun() { d_writer_overrun = true; } -}; - -gri_log_poster::gri_log_poster(const char *filename) - : omni_thread(), - d_ringbuffer_ready(1, 1), // binary semaphore - d_time_to_die(false), - d_writer_overrun(false) -{ - if ((d_fp = fopen(filename, "w")) == 0){ - perror (filename); - throw std::runtime_error("can't open file"); - } - - // Create a 1MB buffer. - d_writer = gr_make_buffer(1 * 1024 * 1024, sizeof(unsigned char)); - d_reader = gr_buffer_add_reader(d_writer, 0); - - start_undetached(); // start the thread -} - -gri_log_poster::~gri_log_poster() -{ - if (d_fp != 0){ - fclose(d_fp); - d_fp = 0; - } -} - -/* - * This is the body of the logging thread. - */ -void * -gri_log_poster::run_undetached(void *arg) -{ - int nbytes; - - //fprintf(stderr, "Enter: run_undetached!\n"); - - while (!d_time_to_die){ - while ((nbytes = d_reader->items_available()) > 0){ - fwrite(d_reader->read_pointer(), 1, nbytes, d_fp); - d_reader->update_read_pointer(nbytes); - } - fflush(d_fp); - d_ringbuffer_ready.wait(); - - if (d_writer_overrun){ - fputs(">>>>> gri_logger: writer overrun. Info lost <<<<<\n", d_fp); - d_writer_overrun = false; - } - } - - // fprintf(stderr, "Exit: run_undetached!\n"); - return 0; -} - -// ------------------------------------------------------------------------ - -static boost::weak_ptr<gri_logger> s_singleton; // weak pointer IQ test ;-) -static omni_mutex s_singleton_mutex; - -gri_logger_sptr -gri_logger::singleton() -{ - omni_mutex_lock l(s_singleton_mutex); - gri_logger_sptr r; - - if (r = s_singleton.lock()) - return r; - - r = gri_logger_sptr(new gri_logger("gri_logger.log")); - s_singleton = r; - return r; -} - - -gri_logger::gri_logger(const char *filename) -{ - d_poster = new gri_log_poster(filename); -} - -gri_logger::~gri_logger() -{ - d_poster->kill(); - d_poster->join(NULL); -} - -void -gri_logger::write(const void *buf, size_t count) -{ - omni_mutex_lock l(d_write_mutex); - gr_buffer_sptr writer = d_poster->writer(); - - // either write it all, or drop it on the ground - if (count <= (size_t) writer->space_available()){ - memcpy(writer->write_pointer(), buf, count); - writer->update_write_pointer(count); - d_poster->post(); - } - else { - d_poster->note_writer_overrun(); - } -} - -void -gri_logger::printf(const char *format, ...) -{ - va_list ap; - char buf[4096]; - int n; - - va_start(ap, format); - n = vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); - if (n > -1 && n < (ssize_t) sizeof(buf)) - write(buf, n); -} - -#endif diff --git a/gnuradio-core/src/lib/io/gri_logger.h b/gnuradio-core/src/lib/io/gri_logger.h deleted file mode 100644 index 0a1414540..000000000 --- a/gnuradio-core/src/lib/io/gri_logger.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006,2009 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ -#ifndef INCLUDED_GRI_LOGGER_H -#define INCLUDED_GRI_LOGGER_H - -#if 0 // This needs reimplementation with boost threads and synchronization - -#include <stddef.h> -#include <gnuradio/omnithread.h> -#include <gr_buffer.h> - -class gri_log_poster; -class gri_logger; -typedef boost::shared_ptr<gri_logger> gri_logger_sptr; - - -/*! - * \brief non-blocking logging to a file. - * - * In reality, this may block, but only for a bounded time. - * Trust me, it's safe to use from portaudio and JACK callbacks. - */ -class gri_logger -{ - gri_log_poster *d_poster; - omni_mutex d_write_mutex; - -public: - static gri_logger_sptr singleton(); - - gri_logger(const char *filename); - ~gri_logger(); - - void write(const void *buf, size_t count); - void printf(const char *format, ...); -}; - -#endif - -#endif /* INCLUDED_GRI_LOGGER_H */ diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py index c45ae4d1a..a479ed48e 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2009 Free Software Foundation, Inc. +# Copyright 2009,2010 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -29,16 +29,18 @@ class pfb_channelizer_ccf(gr.hier_block2): This simplifies the interface by allowing a single input stream to connect to this block. It will then output a stream for each channel. ''' - def __init__(self, numchans, taps): + def __init__(self, numchans, taps, oversample_rate=1): gr.hier_block2.__init__(self, "pfb_channelizer_ccf", 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._taps = taps + self._oversample_rate = oversample_rate self.s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, self._numchans) - self.pfb = gr.pfb_channelizer_ccf(self._numchans, self._taps) + self.pfb = gr.pfb_channelizer_ccf(self._numchans, self._taps, + self._oversample_rate) self.v2s = gr.vector_to_streams(gr.sizeof_gr_complex, self._numchans) self.connect(self, self.s2ss) diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py b/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py index 8d46e8192..f2808c448 100644 --- a/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py +++ b/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py @@ -30,3 +30,15 @@ def hexint(mask): if mask >= 2**31: return int(mask-2**32) return mask + +def hexshort(mask): + """ + Convert unsigned masks into signed shorts. + + This allows us to use hex constants like 0x8000 when talking to + our hardware and not get screwed by them getting treated as python + longs. + """ + if mask >= 2**15: + return int(mask-2**16) + return mask |