GNU Radio 3.7.1 C++ API
|
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 */