GNU Radio v3.6.2-149-ga6d285d9 C++ API
digital_pfb_clock_sync_fff.h
Go to the documentation of this file.
00001 /* -*- c++ -*- */
00002 /*
00003  * Copyright 2009,2010,2012 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_DIGITAL_PFB_CLOCK_SYNC_FFF_H
00025 #define INCLUDED_DIGITAL_PFB_CLOCK_SYNC_FFF_H
00026 
00027 #include <digital_api.h>
00028 #include <gr_block.h>
00029 
00030 class digital_pfb_clock_sync_fff;
00031 typedef boost::shared_ptr<digital_pfb_clock_sync_fff> digital_pfb_clock_sync_fff_sptr;
00032 DIGITAL_API digital_pfb_clock_sync_fff_sptr
00033 digital_make_pfb_clock_sync_fff(double sps, float gain,
00034                                 const std::vector<float> &taps,
00035                                 unsigned int filter_size=32,
00036                                 float init_phase=0,
00037                                 float max_rate_deviation=1.5,
00038                                 int osps=1);
00039 
00040 class gr_fir_fff;
00041 
00042 /*!
00043  * \class digital_pfb_clock_sync_fff
00044  *
00045  * \brief Timing synchronizer using polyphase filterbanks
00046  *
00047  * \ingroup filter_blk
00048  * \ingroup pfb_blk
00049  *
00050  * This block performs timing synchronization for PAM signals by
00051  * minimizing the derivative of the filtered signal, which in turn
00052  * maximizes the SNR and minimizes ISI.
00053  *
00054  * This approach works by setting up two filterbanks; one filterbank
00055  * contains the signal's pulse shaping matched filter (such as a root
00056  * raised cosine filter), where each branch of the filterbank contains
00057  * a different phase of the filter.  The second filterbank contains
00058  * the derivatives of the filters in the first filterbank. Thinking of
00059  * this in the time domain, the first filterbank contains filters that
00060  * have a sinc shape to them. We want to align the output signal to be
00061  * sampled at exactly the peak of the sinc shape. The derivative of
00062  * the sinc contains a zero at the maximum point of the sinc (sinc(0)
00063  * = 1, sinc(0)' = 0).  Furthermore, the region around the zero point
00064  * is relatively linear. We make use of this fact to generate the
00065  * error signal.
00066  *
00067  * If the signal out of the derivative filters is d_i[n] for the ith
00068  * filter, and the output of the matched filter is x_i[n], we
00069  * calculate the error as: e[n] = (Re{x_i[n]} * Re{d_i[n]} +
00070  * Im{x_i[n]} * Im{d_i[n]}) / 2.0 This equation averages the error in
00071  * the real and imaginary parts. There are two reasons we multiply by
00072  * the signal itself. First, if the symbol could be positive or
00073  * negative going, but we want the error term to always tell us to go
00074  * in the same direction depending on which side of the zero point we
00075  * are on. The sign of x_i[n] adjusts the error term to do
00076  * this. Second, the magnitude of x_i[n] scales the error term
00077  * depending on the symbol's amplitude, so larger signals give us a
00078  * stronger error term because we have more confidence in that
00079  * symbol's value.  Using the magnitude of x_i[n] instead of just the
00080  * sign is especially good for signals with low SNR.
00081  *
00082  * The error signal, e[n], gives us a value proportional to how far
00083  * away from the zero point we are in the derivative signal. We want
00084  * to drive this value to zero, so we set up a second order loop. We
00085  * have two variables for this loop; d_k is the filter number in the
00086  * filterbank we are on and d_rate is the rate which we travel through
00087  * the filters in the steady state. That is, due to the natural clock
00088  * differences between the transmitter and receiver, d_rate represents
00089  * that difference and would traverse the filter phase paths to keep
00090  * the receiver locked. Thinking of this as a second-order PLL, the
00091  * d_rate is the frequency and d_k is the phase. So we update d_rate
00092  * and d_k using the standard loop equations based on two error
00093  * signals, d_alpha and d_beta.  We have these two values set based on
00094  * each other for a critically damped system, so in the block
00095  * constructor, we just ask for "gain," which is d_alpha while d_beta
00096  * is equal to (gain^2)/4.
00097  *
00098  * The block's parameters are:
00099  *
00100  * \li \p sps: The clock sync block needs to know the number of samples per
00101  * symbol, because it defaults to return a single point representing
00102  * the symbol. The sps can be any positive real number and does not
00103  * need to be an integer.
00104  *
00105  * \li \p loop_bw: The loop bandwidth is used to set the gain of the
00106  * inner control loop (see:
00107  * http://gnuradio.squarespace.com/blog/2011/8/13/control-loop-gain-values.html).
00108  * This should be set small (a value of around 2pi/100 is suggested in
00109  * that blog post as the step size for the number of radians around
00110  * the unit circle to move relative to the error).
00111  *
00112  * \li \p taps: One of the most important parameters for this block is
00113  * the taps of the filter. One of the benefits of this algorithm is
00114  * that you can put the matched filter in here as the taps, so you get
00115  * both the matched filter and sample timing correction in one go. So
00116  * create your normal matched filter. For a typical digital
00117  * modulation, this is a root raised cosine filter. The number of taps
00118  * of this filter is based on how long you expect the channel to be;
00119  * that is, how many symbols do you want to combine to get the current
00120  * symbols energy back (there's probably a better way of stating
00121  * that). It's usually 5 to 10 or so. That gives you your filter, but
00122  * now we need to think about it as a filter with different phase
00123  * profiles in each filter. So take this number of taps and multiply
00124  * it by the number of filters. This is the number you would use to
00125  * create your prototype filter. When you use this in the PFB
00126  * filerbank, it segments these taps into the filterbanks in such a
00127  * way that each bank now represents the filter at different phases,
00128  * equally spaced at 2pi/N, where N is the number of filters.
00129  *
00130  * \li \p filter_size (default=32): The number of filters can also be
00131  * set and defaults to 32. With 32 filters, you get a good enough
00132  * resolution in the phase to produce very small, almost unnoticeable,
00133  * ISI.  Going to 64 filters can reduce this more, but after that
00134  * there is very little gained for the extra complexity.
00135  *
00136  * \li \p init_phase (default=0): The initial phase is another
00137  * settable parameter and refers to the filter path the algorithm
00138  * initially looks at (i.e., d_k starts at init_phase). This value
00139  * defaults to zero, but it might be useful to start at a different
00140  * phase offset, such as the mid-point of the filters.
00141  *
00142  * \li \p max_rate_deviation (default=1.5): The next parameter is the
00143  * max_rate_devitation, which defaults to 1.5. This is how far we
00144  * allow d_rate to swing, positive or negative, from 0. Constraining
00145  * the rate can help keep the algorithm from walking too far away to
00146  * lock during times when there is no signal.
00147  *
00148  * \li \p osps (default=1): The osps is the number of output samples
00149  * per symbol. By default, the algorithm produces 1 sample per symbol,
00150  * sampled at the exact sample value. This osps value was added to
00151  * better work with equalizers, which do a better job of modeling the
00152  * channel if they have 2 samps/sym.
00153  */
00154 
00155 class DIGITAL_API digital_pfb_clock_sync_fff : public gr_block
00156 {
00157  private:
00158   /*!
00159    * Build the polyphase filterbank timing synchronizer.
00160    * \param sps (double) The number of samples per second in the incoming signal
00161    * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default.
00162    * \param taps (vector<int>) The filter taps.
00163    * \param filter_size (uint) The number of filters in the filterbank (default = 32).
00164    * \param init_phase (float) The initial phase to look at, or which filter to start
00165    *                           with (default = 0).
00166    * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5).
00167    * \param osps (int) The number of output samples per symbol (default=1).
00168    *
00169    */
00170   friend DIGITAL_API digital_pfb_clock_sync_fff_sptr
00171     digital_make_pfb_clock_sync_fff(double sps, float gain,
00172                                     const std::vector<float> &taps,
00173                                     unsigned int filter_size,
00174                                     float init_phase,
00175                                     float max_rate_deviation,
00176                                     int osps);
00177 
00178   bool                              d_updated;
00179   double                            d_sps;
00180   double                            d_sample_num;
00181   float                             d_loop_bw;
00182   float                             d_damping;
00183   float                             d_alpha;
00184   float                             d_beta;
00185 
00186   int                               d_nfilters;
00187   int                               d_taps_per_filter;
00188   std::vector<gr_fir_fff*>          d_filters;
00189   std::vector<gr_fir_fff*>          d_diff_filters;
00190   std::vector< std::vector<float> > d_taps;
00191   std::vector< std::vector<float> > d_dtaps;
00192 
00193   float                             d_k;
00194   float                             d_rate;
00195   float                             d_rate_i;
00196   float                             d_rate_f;
00197   float                             d_max_dev;
00198   int                               d_filtnum;
00199   int                               d_osps;
00200   float                             d_error;
00201   int                               d_out_idx;
00202 
00203   /*!
00204    * Build the polyphase filterbank timing synchronizer.
00205    */
00206   digital_pfb_clock_sync_fff(double sps, float gain,
00207                              const std::vector<float> &taps,
00208                              unsigned int filter_size,
00209                              float init_phase,
00210                              float max_rate_deviation,
00211                              int osps);
00212 
00213   void create_diff_taps(const std::vector<float> &newtaps,
00214                         std::vector<float> &difftaps);
00215 
00216 public:
00217   ~digital_pfb_clock_sync_fff ();
00218 
00219   /*! \brief update the system gains from omega and eta
00220    *
00221    *  This function updates the system gains based on the loop
00222    *  bandwidth and damping factor of the system.
00223    *  These two factors can be set separately through their own
00224    *  set functions.
00225    */
00226   void update_gains();
00227 
00228   /*!
00229    * Resets the filterbank's filter taps with the new prototype filter
00230    */
00231   void set_taps(const std::vector<float> &taps,
00232                 std::vector< std::vector<float> > &ourtaps,
00233                 std::vector<gr_fir_fff*> &ourfilter);
00234 
00235   /*!
00236    * Returns all of the taps of the matched filter
00237    */
00238   std::vector< std::vector<float> > get_taps();
00239 
00240   /*!
00241    * Returns all of the taps of the derivative filter
00242    */
00243   std::vector< std::vector<float> > get_diff_taps();
00244 
00245   /*!
00246    * Returns the taps of the matched filter for a particular channel
00247    */
00248   std::vector<float> get_channel_taps(int channel);
00249 
00250   /*!
00251    * Returns the taps in the derivative filter for a particular channel
00252    */
00253   std::vector<float> get_diff_channel_taps(int channel);
00254 
00255   /*!
00256    * Return the taps as a formatted string for printing
00257    */
00258   std::string get_taps_as_string();
00259 
00260   /*!
00261    * Return the derivative filter taps as a formatted string for printing
00262    */
00263   std::string get_diff_taps_as_string();
00264 
00265 
00266   /*******************************************************************
00267     SET FUNCTIONS
00268   *******************************************************************/
00269 
00270 
00271   /*!
00272    * \brief Set the loop bandwidth
00273    *
00274    * Set the loop filter's bandwidth to \p bw. This should be between
00275    * 2*pi/200 and 2*pi/100  (in rads/samp). It must also be a positive
00276    * number.
00277    *
00278    * When a new damping factor is set, the gains, alpha and beta, of the loop
00279    * are recalculated by a call to update_gains().
00280    *
00281    * \param bw    (float) new bandwidth
00282    *
00283    */
00284   void set_loop_bandwidth(float bw);
00285 
00286   /*!
00287    * \brief Set the loop damping factor
00288    *
00289    * Set the loop filter's damping factor to \p df. The damping factor
00290    * should be sqrt(2)/2.0 for critically damped systems.
00291    * Set it to anything else only if you know what you are doing. It must
00292    * be a number between 0 and 1.
00293    *
00294    * When a new damping factor is set, the gains, alpha and beta, of the loop
00295    * are recalculated by a call to update_gains().
00296    *
00297    * \param df    (float) new damping factor
00298    *
00299    */
00300   void set_damping_factor(float df);
00301 
00302   /*!
00303    * \brief Set the loop gain alpha
00304    *
00305    * Set's the loop filter's alpha gain parameter.
00306    *
00307    * This value should really only be set by adjusting the loop bandwidth
00308    * and damping factor.
00309    *
00310    * \param alpha    (float) new alpha gain
00311    *
00312    */
00313   void set_alpha(float alpha);
00314 
00315   /*!
00316    * \brief Set the loop gain beta
00317    *
00318    * Set's the loop filter's beta gain parameter.
00319    *
00320    * This value should really only be set by adjusting the loop bandwidth
00321    * and damping factor.
00322    *
00323    * \param beta    (float) new beta gain
00324    *
00325    */
00326   void set_beta(float beta);
00327 
00328   /*!
00329    * Set the maximum deviation from 0 d_rate can have
00330    */
00331   void set_max_rate_deviation(float m)
00332   {
00333     d_max_dev = m;
00334   }
00335 
00336   /*******************************************************************
00337     GET FUNCTIONS
00338   *******************************************************************/
00339 
00340   /*!
00341    * \brief Returns the loop bandwidth
00342    */
00343   float get_loop_bandwidth() const;
00344 
00345   /*!
00346    * \brief Returns the loop damping factor
00347    */
00348   float get_damping_factor() const;
00349 
00350   /*!
00351    * \brief Returns the loop gain alpha
00352    */
00353   float get_alpha() const;
00354 
00355   /*!
00356    * \brief Returns the loop gain beta
00357    */
00358   float get_beta() const;
00359 
00360   /*!
00361    * \brief Returns the current clock rate
00362    */
00363   float get_clock_rate() const;
00364 
00365   /*******************************************************************
00366   *******************************************************************/
00367 
00368   bool check_topology(int ninputs, int noutputs);
00369 
00370   int general_work(int noutput_items,
00371                    gr_vector_int &ninput_items,
00372                    gr_vector_const_void_star &input_items,
00373                    gr_vector_void_star &output_items);
00374 };
00375 
00376 #endif