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