GNU Radio 3.5.1 C++ API
gr_pfb_clock_sync_fff.h
Go to the documentation of this file.
00001 /* -*- c++ -*- */
00002 /*
00003  * Copyright 2009,2010 Free Software Foundation, Inc.
00004  * 
00005  * This file is part of GNU Radio
00006  * 
00007  * GNU Radio is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 3, or (at your option)
00010  * any later version.
00011  * 
00012  * GNU Radio is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with GNU Radio; see the file COPYING.  If not, write to
00019  * the Free Software Foundation, Inc., 51 Franklin Street,
00020  * Boston, MA 02110-1301, USA.
00021  */
00022 
00023 
00024 #ifndef INCLUDED_GR_PFB_CLOCK_SYNC_FFF_H
00025 #define INCLUDED_GR_PFB_CLOCK_SYNC_FFF_H
00026 
00027 #include <gr_core_api.h>
00028 #include <gr_block.h>
00029 
00030 class gr_pfb_clock_sync_fff;
00031 typedef boost::shared_ptr<gr_pfb_clock_sync_fff> gr_pfb_clock_sync_fff_sptr;
00032 GR_CORE_API gr_pfb_clock_sync_fff_sptr gr_make_pfb_clock_sync_fff (double sps, float gain,
00033                                                        const std::vector<float> &taps,
00034                                                        unsigned int filter_size=32,
00035                                                        float init_phase=0,
00036                                                        float max_rate_deviation=1.5);
00037 
00038 class gr_fir_fff;
00039 
00040 /*!
00041  * \class gr_pfb_clock_sync_fff
00042  *
00043  * \brief Timing synchronizer using polyphase filterbanks
00044  *
00045  * \ingroup filter_blk
00046  * \ingroup pfb_blk
00047  * 
00048  * This block performs timing synchronization for PAM signals by
00049  * minimizing the derivative of the filtered signal, which in turn
00050  * maximizes the SNR and minimizes ISI.
00051  *
00052  * This approach works by setting up two filterbanks; one filterbank
00053  * contains the signal's pulse shaping matched filter (such as a root
00054  * raised cosine filter), where each branch of the filterbank contains
00055  * a different phase of the filter.  The second filterbank contains
00056  * the derivatives of the filters in the first filterbank. Thinking of
00057  * this in the time domain, the first filterbank contains filters that
00058  * have a sinc shape to them. We want to align the output signal to be
00059  * sampled at exactly the peak of the sinc shape. The derivative of
00060  * the sinc contains a zero at the maximum point of the sinc (sinc(0)
00061  * = 1, sinc(0)' = 0).  Furthermore, the region around the zero point
00062  * is relatively linear. We make use of this fact to generate the
00063  * error signal.
00064  *
00065  * If the signal out of the derivative filters is d_i[n] for the ith
00066  * filter, and the output of the matched filter is x_i[n], we
00067  * calculate the error as: e[n] = (Re{x_i[n]} * Re{d_i[n]} +
00068  * Im{x_i[n]} * Im{d_i[n]}) / 2.0 This equation averages the error in
00069  * the real and imaginary parts. There are two reasons we multiply by
00070  * the signal itself. First, if the symbol could be positive or
00071  * negative going, but we want the error term to always tell us to go
00072  * in the same direction depending on which side of the zero point we
00073  * are on. The sign of x_i[n] adjusts the error term to do
00074  * this. Second, the magnitude of x_i[n] scales the error term
00075  * depending on the symbol's amplitude, so larger signals give us a
00076  * stronger error term because we have more confidence in that
00077  * symbol's value.  Using the magnitude of x_i[n] instead of just the
00078  * sign is especially good for signals with low SNR.
00079  *
00080  * The error signal, e[n], gives us a value proportional to how far
00081  * away from the zero point we are in the derivative signal. We want
00082  * to drive this value to zero, so we set up a second order loop. We
00083  * have two variables for this loop; d_k is the filter number in the
00084  * filterbank we are on and d_rate is the rate which we travel through
00085  * the filters in the steady state. That is, due to the natural clock
00086  * differences between the transmitter and receiver, d_rate represents
00087  * that difference and would traverse the filter phase paths to keep
00088  * the receiver locked. Thinking of this as a second-order PLL, the
00089  * d_rate is the frequency and d_k is the phase. So we update d_rate
00090  * and d_k using the standard loop equations based on two error
00091  * signals, d_alpha and d_beta.  We have these two values set based on
00092  * each other for a critically damped system, so in the block
00093  * constructor, we just ask for "gain," which is d_alpha while d_beta
00094  * is equal to (gain^2)/4.
00095  *
00096  * The block's parameters are:
00097  *
00098  * \li \p sps: The clock sync block needs to know the number of samples per
00099  * symbol, because it defaults to return a single point representing
00100  * the symbol. The sps can be any positive real number and does not
00101  * need to be an integer.
00102  *
00103  * \li \p loop_bw: The loop bandwidth is used to set the gain of the
00104  * inner control loop (see:
00105  * http://gnuradio.squarespace.com/blog/2011/8/13/control-loop-gain-values.html).
00106  * This should be set small (a value of around 2pi/100 is suggested in
00107  * that blog post as the step size for the number of radians around
00108  * the unit circle to move relative to the error).
00109  *
00110  * \li \p taps: One of the most important parameters for this block is
00111  * the taps of the filter. One of the benefits of this algorithm is
00112  * that you can put the matched filter in here as the taps, so you get
00113  * both the matched filter and sample timing correction in one go. So
00114  * create your normal matched filter. For a typical digital
00115  * modulation, this is a root raised cosine filter. The number of taps
00116  * of this filter is based on how long you expect the channel to be;
00117  * that is, how many symbols do you want to combine to get the current
00118  * symbols energy back (there's probably a better way of stating
00119  * that). It's usually 5 to 10 or so. That gives you your filter, but
00120  * now we need to think about it as a filter with different phase
00121  * profiles in each filter. So take this number of taps and multiply
00122  * it by the number of filters. This is the number you would use to
00123  * create your prototype filter. When you use this in the PFB
00124  * filerbank, it segments these taps into the filterbanks in such a
00125  * way that each bank now represents the filter at different phases,
00126  * equally spaced at 2pi/N, where N is the number of filters.
00127  *
00128  * \li \p filter_size (default=32): The number of filters can also be
00129  * set and defaults to 32. With 32 filters, you get a good enough
00130  * resolution in the phase to produce very small, almost unnoticeable,
00131  * ISI.  Going to 64 filters can reduce this more, but after that
00132  * there is very little gained for the extra complexity.
00133  *
00134  * \li \p init_phase (default=0): The initial phase is another
00135  * settable parameter and refers to the filter path the algorithm
00136  * initially looks at (i.e., d_k starts at init_phase). This value
00137  * defaults to zero, but it might be useful to start at a different
00138  * phase offset, such as the mid-point of the filters.
00139  *
00140  * \li \p max_rate_deviation (default=1.5): The next parameter is the
00141  * max_rate_devitation, which defaults to 1.5. This is how far we
00142  * allow d_rate to swing, positive or negative, from 0. Constraining
00143  * the rate can help keep the algorithm from walking too far away to
00144  * lock during times when there is no signal.
00145  *
00146  * \li \p osps: note that unlike the ccf version of this algorithm,
00147  * this block does \a not have a setting for the number of output
00148  * samples per symbol. This is mostly because it should not be
00149  * necessary as the reason for having multiple output sps is to
00150  * perform equalization and the equalizers will take in complex
00151  * numbers in order to do magnitude and phase correction.
00152  */
00153 
00154 class GR_CORE_API gr_pfb_clock_sync_fff : public gr_block
00155 {
00156  private:
00157   /*!
00158    * Build the polyphase filterbank timing synchronizer.
00159    * \param sps (double) The number of samples per second in the incoming signal
00160    * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default.
00161    * \param taps (vector<int>) The filter taps.
00162    * \param filter_size (uint) The number of filters in the filterbank (default = 32).
00163    * \param init_phase (float) The initial phase to look at, or which filter to start 
00164    *                           with (default = 0).
00165    * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5).
00166    *
00167    */
00168   friend GR_CORE_API gr_pfb_clock_sync_fff_sptr gr_make_pfb_clock_sync_fff (double sps, float gain,
00169                                                                 const std::vector<float> &taps,
00170                                                                 unsigned int filter_size,
00171                                                                 float init_phase,
00172                                                                 float max_rate_deviation);
00173 
00174   bool                     d_updated;
00175   double                   d_sps;
00176   double                   d_sample_num;
00177   float                    d_alpha;
00178   float                    d_beta;
00179   int                      d_nfilters;
00180   std::vector<gr_fir_fff*> d_filters;
00181   std::vector<gr_fir_fff*> d_diff_filters;
00182   std::vector< std::vector<float> > d_taps;
00183   std::vector< std::vector<float> > d_dtaps;
00184   float                    d_k;
00185   float                    d_rate;
00186   float                    d_rate_i;
00187   float                    d_rate_f;
00188   float                    d_max_dev;
00189   int                      d_filtnum;
00190   int                      d_taps_per_filter;
00191 
00192   /*!
00193    * Build the polyphase filterbank timing synchronizer.
00194    */
00195   gr_pfb_clock_sync_fff (double sps, float gain,
00196                          const std::vector<float> &taps,
00197                          unsigned int filter_size,
00198                          float init_phase,
00199                          float max_rate_deviation);
00200   
00201   void create_diff_taps(const std::vector<float> &newtaps,
00202                         std::vector<float> &difftaps);
00203 
00204 public:
00205   ~gr_pfb_clock_sync_fff ();
00206   
00207   /*!
00208    * Resets the filterbank's filter taps with the new prototype filter
00209    */
00210   void set_taps (const std::vector<float> &taps,
00211                  std::vector< std::vector<float> > &ourtaps,
00212                  std::vector<gr_fir_fff*> &ourfilter);
00213 
00214   /*!
00215    * Returns the taps of the matched filter
00216    */
00217   std::vector<float> channel_taps(int channel);
00218 
00219   /*!
00220    * Returns the taps in the derivative filter
00221    */
00222   std::vector<float> diff_channel_taps(int channel);
00223 
00224   /*!
00225    * Print all of the filterbank taps to screen.
00226    */
00227   void print_taps();
00228 
00229   /*!
00230    * Print all of the filterbank taps of the derivative filter to screen.
00231    */
00232   void print_diff_taps();
00233 
00234   /*!
00235    * Set the gain value alpha for the control loop
00236    */  
00237   void set_alpha(float alpha)
00238   {
00239     d_alpha = alpha;
00240   }
00241 
00242   /*!
00243    * Set the gain value beta for the control loop
00244    */  
00245   void set_beta(float beta)
00246   {
00247     d_beta = beta;
00248   }
00249 
00250   /*!
00251    * Set the maximum deviation from 0 d_rate can have
00252    */  
00253   void set_max_rate_deviation(float m)
00254   {
00255     d_max_dev = m;
00256   }
00257 
00258   bool check_topology(int ninputs, int noutputs);
00259 
00260   int general_work (int noutput_items,
00261                     gr_vector_int &ninput_items,
00262                     gr_vector_const_void_star &input_items,
00263                     gr_vector_void_star &output_items);
00264 };
00265 
00266 #endif