GNU Radio 3.4.0 C++ API
gr_pfb_clock_sync_ccf.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_CCF_H
00025 #define INCLUDED_GR_PFB_CLOCK_SYNC_CCF_H
00026 
00027 #include <gr_block.h>
00028 
00029 class gr_pfb_clock_sync_ccf;
00030 typedef boost::shared_ptr<gr_pfb_clock_sync_ccf> gr_pfb_clock_sync_ccf_sptr;
00031 gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain,
00032                                                        const std::vector<float> &taps,
00033                                                        unsigned int filter_size=32,
00034                                                        float init_phase=0,
00035                                                        float max_rate_deviation=1.5);
00036 
00037 class gr_fir_ccf;
00038 
00039 /*!
00040  * \class gr_pfb_clock_sync_ccf
00041  *
00042  * \brief Timing synchronizer using polyphase filterbanks
00043  *
00044  * \ingroup filter_blk
00045  * 
00046  * This block performs timing synchronization for PAM signals by minimizing the
00047  * derivative of the filtered signal, which in turn maximizes the SNR and 
00048  * minimizes ISI.
00049  *
00050  * This approach works by setting up two filterbanks; one filterbanke contains the 
00051  * signal's pulse shaping matched filter (such as a root raised cosine filter),
00052  * where each branch of the filterbank contains a different phase of the filter.
00053  * The second filterbank contains the derivatives of the filters in the first 
00054  * filterbank. Thinking of this in the time domain, the first filterbank contains
00055  * filters that have a sinc shape to them. We want to align the output signal to
00056  * be sampled at exactly the peak of the sinc shape. The derivative of the sinc
00057  * contains a zero at the maximum point of the sinc (sinc(0) = 1, sinc(0)' = 0).
00058  * Furthermore, the region around the zero point is relatively linear. We make
00059  * use of this fact to generate the error signal.
00060  *
00061  * If the signal out of the derivative filters is d_i[n] for the ith filter, and
00062  * the output of the matched filter is x_i[n], we calculate the error as:
00063  *    e[n] = (Re{x_i[n]} * Re{d_i[n]} + Im{x_i[n]} * Im{d_i[n]}) / 2.0
00064  * This equation averages the error in the real and imaginary parts. There are two
00065  * reasons we multiply by the signal itself. First, if the symbol could be positive
00066  * or negative going, but we want the error term to always tell us to go in the 
00067  * same direction depending on which side of the zero point we are on. The sign of
00068  * x_i[n] adjusts the error term to do this. Second, the magnitude of x_i[n] scales
00069  * the error term depending on the symbol's amplitude, so larger signals give us
00070  * a stronger error term because we have more confidence in that symbol's value.
00071  * Using the magnitude of x_i[n] instead of just the sign is especially good for
00072  * signals with low SNR.
00073  *
00074  * The error signal, e[n], gives us a value proportional to how far away from the zero
00075  * point we are in the derivative signal. We want to drive this value to zero, so we
00076  * set up a second order loop. We have two variables for this loop; d_k is the filter
00077  * number in the filterbank we are on and d_rate is the rate which we travel through
00078  * the filters in the steady state. That is, due to the natural clock differences between
00079  * the transmitter and receiver, d_rate represents that difference and would traverse
00080  * the filter phase paths to keep the receiver locked. Thinking of this as a second-order
00081  * PLL, the d_rate is the frequency and d_k is the phase. So we update d_rate and d_k
00082  * using the standard loop equations based on two error signals, d_alpha and d_beta.
00083  * We have these two values set based on each other for a critically damped system, so in
00084  * the block constructor, we just ask for "gain," which is d_alpha while d_beta is
00085  * equal to (gain^2)/4.
00086  *
00087  * The clock sync block needs to know the number of samples per symbol (sps), because it
00088  * only returns a single point representing the sample. The sps can be any positive real
00089  * number and does not need to be an integer. The filter taps must also be specified. The
00090  * taps are generated by first conceiving of the prototype filter that would be the signal's
00091  * matched filter. Then interpolate this by the number of filters in the filterbank. These
00092  * are then distributed among all of the filters. So if the prototype filter was to have
00093  * 45 taps in it, then each path of the filterbank will also have 45 taps. This is easily
00094  * done by building the filter with the sample rate multiplied by the number of filters
00095  * to use.
00096  *
00097  * The number of filters can also be set and defaults to 32. With 32 filters, you get a
00098  * good enough resolution in the phase to produce very small, almost unnoticeable, ISI.
00099  * Going to 64 filters can reduce this more, but after that there is very little gained
00100  * for the extra complexity.
00101  *
00102  * The initial phase is another settable parameter and refers to the filter path the
00103  * algorithm initially looks at (i.e., d_k starts at init_phase). This value defaults 
00104  * to zero, but it might be useful to start at a different phase offset, such as the mid-
00105  * point of the filters.
00106  *
00107  * The final parameter is the max_rate_devitation, which defaults to 1.5. This is how far
00108  * we allow d_rate to swing, positive or negative, from 0. Constraining the rate can help
00109  * keep the algorithm from walking too far away to lock during times when there is no signal.
00110  *
00111  */
00112 
00113 class gr_pfb_clock_sync_ccf : public gr_block
00114 {
00115  private:
00116   /*!
00117    * Build the polyphase filterbank timing synchronizer.
00118    * \param sps (double) The number of samples per symbol in the incoming signal
00119    * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default.
00120    * \param taps (vector<int>) The filter taps.
00121    * \param filter_size (uint) The number of filters in the filterbank (default = 32).
00122    * \param init_phase (float) The initial phase to look at, or which filter to start 
00123    *                           with (default = 0).
00124    * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5).
00125    *
00126    */
00127   friend gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain,
00128                                                                 const std::vector<float> &taps,
00129                                                                 unsigned int filter_size,
00130                                                                 float init_phase,
00131                                                                 float max_rate_deviation);
00132 
00133   bool                     d_updated;
00134   double                   d_sps;
00135   double                   d_sample_num;
00136   float                    d_alpha;
00137   float                    d_beta;
00138   int                      d_nfilters;
00139   std::vector<gr_fir_ccf*> d_filters;
00140   std::vector<gr_fir_ccf*> d_diff_filters;
00141   std::vector< std::vector<float> > d_taps;
00142   std::vector< std::vector<float> > d_dtaps;
00143   float                    d_k;
00144   float                    d_rate;
00145   float                    d_rate_i;
00146   float                    d_rate_f;
00147   float                    d_max_dev;
00148   int                      d_filtnum;
00149   int                      d_taps_per_filter;
00150 
00151   /*!
00152    * Build the polyphase filterbank timing synchronizer.
00153    */
00154   gr_pfb_clock_sync_ccf (double sps, float gain,
00155                          const std::vector<float> &taps,
00156                          unsigned int filter_size,
00157                          float init_phase,
00158                          float max_rate_deviation);
00159   
00160   void create_diff_taps(const std::vector<float> &newtaps,
00161                         std::vector<float> &difftaps);
00162 
00163 public:
00164   ~gr_pfb_clock_sync_ccf ();
00165   
00166   /*!
00167    * Resets the filterbank's filter taps with the new prototype filter
00168    */
00169   void set_taps (const std::vector<float> &taps,
00170                  std::vector< std::vector<float> > &ourtaps,
00171                  std::vector<gr_fir_ccf*> &ourfilter);
00172 
00173   /*!
00174    * Returns the taps of the matched filter
00175    */
00176   std::vector<float> channel_taps(int channel);
00177 
00178   /*!
00179    * Returns the taps in the derivative filter
00180    */
00181   std::vector<float> diff_channel_taps(int channel);
00182 
00183   /*!
00184    * Print all of the filterbank taps to screen.
00185    */
00186   void print_taps();
00187 
00188   /*!
00189    * Print all of the filterbank taps of the derivative filter to screen.
00190    */
00191   void print_diff_taps();
00192 
00193   /*!
00194    * Set the gain value alpha for the control loop
00195    */  
00196   void set_alpha(float alpha)
00197   {
00198     d_alpha = alpha;
00199   }
00200 
00201   /*!
00202    * Set the gain value beta for the control loop
00203    */  
00204   void set_beta(float beta)
00205   {
00206     d_beta = beta;
00207   }
00208 
00209   /*!
00210    * Set the maximum deviation from 0 d_rate can have
00211    */  
00212   void set_max_rate_deviation(float m)
00213   {
00214     d_max_dev = m;
00215   }
00216   
00217   bool check_topology(int ninputs, int noutputs);
00218 
00219   int general_work (int noutput_items,
00220                     gr_vector_int &ninput_items,
00221                     gr_vector_const_void_star &input_items,
00222                     gr_vector_void_star &output_items);
00223 };
00224 
00225 #endif