GNU Radio 3.5.1 C++ API
|
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_FFF_H 00025 #define INCLUDED_GR_PFB_CLOCK_SYNC_FFF_H 00026 00027 #include <gr_core_api.h> 00028 #include <gr_block.h> 00029 00030 class gr_pfb_clock_sync_fff; 00031 typedef boost::shared_ptr<gr_pfb_clock_sync_fff> gr_pfb_clock_sync_fff_sptr; 00032 GR_CORE_API gr_pfb_clock_sync_fff_sptr gr_make_pfb_clock_sync_fff (double sps, float gain, 00033 const std::vector<float> &taps, 00034 unsigned int filter_size=32, 00035 float init_phase=0, 00036 float max_rate_deviation=1.5); 00037 00038 class gr_fir_fff; 00039 00040 /*! 00041 * \class gr_pfb_clock_sync_fff 00042 * 00043 * \brief Timing synchronizer using polyphase filterbanks 00044 * 00045 * \ingroup filter_blk 00046 * \ingroup pfb_blk 00047 * 00048 * This block performs timing synchronization for PAM signals by 00049 * minimizing the derivative of the filtered signal, which in turn 00050 * maximizes the SNR and minimizes ISI. 00051 * 00052 * This approach works by setting up two filterbanks; one filterbank 00053 * contains the signal's pulse shaping matched filter (such as a root 00054 * raised cosine filter), where each branch of the filterbank contains 00055 * a different phase of the filter. The second filterbank contains 00056 * the derivatives of the filters in the first filterbank. Thinking of 00057 * this in the time domain, the first filterbank contains filters that 00058 * have a sinc shape to them. We want to align the output signal to be 00059 * sampled at exactly the peak of the sinc shape. The derivative of 00060 * the sinc contains a zero at the maximum point of the sinc (sinc(0) 00061 * = 1, sinc(0)' = 0). Furthermore, the region around the zero point 00062 * is relatively linear. We make use of this fact to generate the 00063 * error signal. 00064 * 00065 * If the signal out of the derivative filters is d_i[n] for the ith 00066 * filter, and the output of the matched filter is x_i[n], we 00067 * calculate the error as: e[n] = (Re{x_i[n]} * Re{d_i[n]} + 00068 * Im{x_i[n]} * Im{d_i[n]}) / 2.0 This equation averages the error in 00069 * the real and imaginary parts. There are two reasons we multiply by 00070 * the signal itself. First, if the symbol could be positive or 00071 * negative going, but we want the error term to always tell us to go 00072 * in the same direction depending on which side of the zero point we 00073 * are on. The sign of x_i[n] adjusts the error term to do 00074 * this. Second, the magnitude of x_i[n] scales the error term 00075 * depending on the symbol's amplitude, so larger signals give us a 00076 * stronger error term because we have more confidence in that 00077 * symbol's value. Using the magnitude of x_i[n] instead of just the 00078 * sign is especially good for signals with low SNR. 00079 * 00080 * The error signal, e[n], gives us a value proportional to how far 00081 * away from the zero point we are in the derivative signal. We want 00082 * to drive this value to zero, so we set up a second order loop. We 00083 * have two variables for this loop; d_k is the filter number in the 00084 * filterbank we are on and d_rate is the rate which we travel through 00085 * the filters in the steady state. That is, due to the natural clock 00086 * differences between the transmitter and receiver, d_rate represents 00087 * that difference and would traverse the filter phase paths to keep 00088 * the receiver locked. Thinking of this as a second-order PLL, the 00089 * d_rate is the frequency and d_k is the phase. So we update d_rate 00090 * and d_k using the standard loop equations based on two error 00091 * signals, d_alpha and d_beta. We have these two values set based on 00092 * each other for a critically damped system, so in the block 00093 * constructor, we just ask for "gain," which is d_alpha while d_beta 00094 * is equal to (gain^2)/4. 00095 * 00096 * The block's parameters are: 00097 * 00098 * \li \p sps: The clock sync block needs to know the number of samples per 00099 * symbol, because it defaults to return a single point representing 00100 * the symbol. The sps can be any positive real number and does not 00101 * need to be an integer. 00102 * 00103 * \li \p loop_bw: The loop bandwidth is used to set the gain of the 00104 * inner control loop (see: 00105 * http://gnuradio.squarespace.com/blog/2011/8/13/control-loop-gain-values.html). 00106 * This should be set small (a value of around 2pi/100 is suggested in 00107 * that blog post as the step size for the number of radians around 00108 * the unit circle to move relative to the error). 00109 * 00110 * \li \p taps: One of the most important parameters for this block is 00111 * the taps of the filter. One of the benefits of this algorithm is 00112 * that you can put the matched filter in here as the taps, so you get 00113 * both the matched filter and sample timing correction in one go. So 00114 * create your normal matched filter. For a typical digital 00115 * modulation, this is a root raised cosine filter. The number of taps 00116 * of this filter is based on how long you expect the channel to be; 00117 * that is, how many symbols do you want to combine to get the current 00118 * symbols energy back (there's probably a better way of stating 00119 * that). It's usually 5 to 10 or so. That gives you your filter, but 00120 * now we need to think about it as a filter with different phase 00121 * profiles in each filter. So take this number of taps and multiply 00122 * it by the number of filters. This is the number you would use to 00123 * create your prototype filter. When you use this in the PFB 00124 * filerbank, it segments these taps into the filterbanks in such a 00125 * way that each bank now represents the filter at different phases, 00126 * equally spaced at 2pi/N, where N is the number of filters. 00127 * 00128 * \li \p filter_size (default=32): The number of filters can also be 00129 * set and defaults to 32. With 32 filters, you get a good enough 00130 * resolution in the phase to produce very small, almost unnoticeable, 00131 * ISI. Going to 64 filters can reduce this more, but after that 00132 * there is very little gained for the extra complexity. 00133 * 00134 * \li \p init_phase (default=0): The initial phase is another 00135 * settable parameter and refers to the filter path the algorithm 00136 * initially looks at (i.e., d_k starts at init_phase). This value 00137 * defaults to zero, but it might be useful to start at a different 00138 * phase offset, such as the mid-point of the filters. 00139 * 00140 * \li \p max_rate_deviation (default=1.5): The next parameter is the 00141 * max_rate_devitation, which defaults to 1.5. This is how far we 00142 * allow d_rate to swing, positive or negative, from 0. Constraining 00143 * the rate can help keep the algorithm from walking too far away to 00144 * lock during times when there is no signal. 00145 * 00146 * \li \p osps: note that unlike the ccf version of this algorithm, 00147 * this block does \a not have a setting for the number of output 00148 * samples per symbol. This is mostly because it should not be 00149 * necessary as the reason for having multiple output sps is to 00150 * perform equalization and the equalizers will take in complex 00151 * numbers in order to do magnitude and phase correction. 00152 */ 00153 00154 class GR_CORE_API gr_pfb_clock_sync_fff : public gr_block 00155 { 00156 private: 00157 /*! 00158 * Build the polyphase filterbank timing synchronizer. 00159 * \param sps (double) The number of samples per second in the incoming signal 00160 * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default. 00161 * \param taps (vector<int>) The filter taps. 00162 * \param filter_size (uint) The number of filters in the filterbank (default = 32). 00163 * \param init_phase (float) The initial phase to look at, or which filter to start 00164 * with (default = 0). 00165 * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5). 00166 * 00167 */ 00168 friend GR_CORE_API gr_pfb_clock_sync_fff_sptr gr_make_pfb_clock_sync_fff (double sps, float gain, 00169 const std::vector<float> &taps, 00170 unsigned int filter_size, 00171 float init_phase, 00172 float max_rate_deviation); 00173 00174 bool d_updated; 00175 double d_sps; 00176 double d_sample_num; 00177 float d_alpha; 00178 float d_beta; 00179 int d_nfilters; 00180 std::vector<gr_fir_fff*> d_filters; 00181 std::vector<gr_fir_fff*> d_diff_filters; 00182 std::vector< std::vector<float> > d_taps; 00183 std::vector< std::vector<float> > d_dtaps; 00184 float d_k; 00185 float d_rate; 00186 float d_rate_i; 00187 float d_rate_f; 00188 float d_max_dev; 00189 int d_filtnum; 00190 int d_taps_per_filter; 00191 00192 /*! 00193 * Build the polyphase filterbank timing synchronizer. 00194 */ 00195 gr_pfb_clock_sync_fff (double sps, float gain, 00196 const std::vector<float> &taps, 00197 unsigned int filter_size, 00198 float init_phase, 00199 float max_rate_deviation); 00200 00201 void create_diff_taps(const std::vector<float> &newtaps, 00202 std::vector<float> &difftaps); 00203 00204 public: 00205 ~gr_pfb_clock_sync_fff (); 00206 00207 /*! 00208 * Resets the filterbank's filter taps with the new prototype filter 00209 */ 00210 void set_taps (const std::vector<float> &taps, 00211 std::vector< std::vector<float> > &ourtaps, 00212 std::vector<gr_fir_fff*> &ourfilter); 00213 00214 /*! 00215 * Returns the taps of the matched filter 00216 */ 00217 std::vector<float> channel_taps(int channel); 00218 00219 /*! 00220 * Returns the taps in the derivative filter 00221 */ 00222 std::vector<float> diff_channel_taps(int channel); 00223 00224 /*! 00225 * Print all of the filterbank taps to screen. 00226 */ 00227 void print_taps(); 00228 00229 /*! 00230 * Print all of the filterbank taps of the derivative filter to screen. 00231 */ 00232 void print_diff_taps(); 00233 00234 /*! 00235 * Set the gain value alpha for the control loop 00236 */ 00237 void set_alpha(float alpha) 00238 { 00239 d_alpha = alpha; 00240 } 00241 00242 /*! 00243 * Set the gain value beta for the control loop 00244 */ 00245 void set_beta(float beta) 00246 { 00247 d_beta = beta; 00248 } 00249 00250 /*! 00251 * Set the maximum deviation from 0 d_rate can have 00252 */ 00253 void set_max_rate_deviation(float m) 00254 { 00255 d_max_dev = m; 00256 } 00257 00258 bool check_topology(int ninputs, int noutputs); 00259 00260 int general_work (int noutput_items, 00261 gr_vector_int &ninput_items, 00262 gr_vector_const_void_star &input_items, 00263 gr_vector_void_star &output_items); 00264 }; 00265 00266 #endif