GNU Radio v3.6.2-149-ga6d285d9 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 <digital_api.h> 00028 #include <gr_block.h> 00029 00030 class digital_pfb_clock_sync_ccf; 00031 typedef boost::shared_ptr<digital_pfb_clock_sync_ccf> digital_pfb_clock_sync_ccf_sptr; 00032 DIGITAL_API digital_pfb_clock_sync_ccf_sptr 00033 digital_make_pfb_clock_sync_ccf(double sps, float loop_bw, 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_ccf; 00041 00042 /*! 00043 * \class digital_pfb_clock_sync_ccf 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 per symbol. By default, 00149 * the algorithm produces 1 sample per symbol, sampled at the exact 00150 * sample value. This osps value was added to better work with 00151 * equalizers, which do a better job of modeling the channel if they 00152 * have 2 samps/sym. 00153 */ 00154 00155 class DIGITAL_API digital_pfb_clock_sync_ccf : public gr_block 00156 { 00157 private: 00158 /*! 00159 * Build the polyphase filterbank timing synchronizer. 00160 * \param sps (double) The number of samples per symbol in the incoming signal 00161 * \param loop_bw (float) The bandwidth of the control loop; set's alpha and beta. 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_ccf_sptr 00171 digital_make_pfb_clock_sync_ccf(double sps, float loop_bw, 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_ccf*> d_filters; 00189 std::vector<gr_fir_ccf*> 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_ccf(double sps, float loop_bw, 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_ccf(); 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_ccf*> &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