GNU Radio 3.4.0 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_CCF_H 00025 #define INCLUDED_GR_PFB_CLOCK_SYNC_CCF_H 00026 00027 #include <gr_block.h> 00028 00029 class gr_pfb_clock_sync_ccf; 00030 typedef boost::shared_ptr<gr_pfb_clock_sync_ccf> gr_pfb_clock_sync_ccf_sptr; 00031 gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, 00032 const std::vector<float> &taps, 00033 unsigned int filter_size=32, 00034 float init_phase=0, 00035 float max_rate_deviation=1.5); 00036 00037 class gr_fir_ccf; 00038 00039 /*! 00040 * \class gr_pfb_clock_sync_ccf 00041 * 00042 * \brief Timing synchronizer using polyphase filterbanks 00043 * 00044 * \ingroup filter_blk 00045 * 00046 * This block performs timing synchronization for PAM signals by minimizing the 00047 * derivative of the filtered signal, which in turn maximizes the SNR and 00048 * minimizes ISI. 00049 * 00050 * This approach works by setting up two filterbanks; one filterbanke contains the 00051 * signal's pulse shaping matched filter (such as a root raised cosine filter), 00052 * where each branch of the filterbank contains a different phase of the filter. 00053 * The second filterbank contains the derivatives of the filters in the first 00054 * filterbank. Thinking of this in the time domain, the first filterbank contains 00055 * filters that have a sinc shape to them. We want to align the output signal to 00056 * be sampled at exactly the peak of the sinc shape. The derivative of the sinc 00057 * contains a zero at the maximum point of the sinc (sinc(0) = 1, sinc(0)' = 0). 00058 * Furthermore, the region around the zero point is relatively linear. We make 00059 * use of this fact to generate the error signal. 00060 * 00061 * If the signal out of the derivative filters is d_i[n] for the ith filter, and 00062 * the output of the matched filter is x_i[n], we calculate the error as: 00063 * e[n] = (Re{x_i[n]} * Re{d_i[n]} + Im{x_i[n]} * Im{d_i[n]}) / 2.0 00064 * This equation averages the error in the real and imaginary parts. There are two 00065 * reasons we multiply by the signal itself. First, if the symbol could be positive 00066 * or negative going, but we want the error term to always tell us to go in the 00067 * same direction depending on which side of the zero point we are on. The sign of 00068 * x_i[n] adjusts the error term to do this. Second, the magnitude of x_i[n] scales 00069 * the error term depending on the symbol's amplitude, so larger signals give us 00070 * a stronger error term because we have more confidence in that symbol's value. 00071 * Using the magnitude of x_i[n] instead of just the sign is especially good for 00072 * signals with low SNR. 00073 * 00074 * The error signal, e[n], gives us a value proportional to how far away from the zero 00075 * point we are in the derivative signal. We want to drive this value to zero, so we 00076 * set up a second order loop. We have two variables for this loop; d_k is the filter 00077 * number in the filterbank we are on and d_rate is the rate which we travel through 00078 * the filters in the steady state. That is, due to the natural clock differences between 00079 * the transmitter and receiver, d_rate represents that difference and would traverse 00080 * the filter phase paths to keep the receiver locked. Thinking of this as a second-order 00081 * PLL, the d_rate is the frequency and d_k is the phase. So we update d_rate and d_k 00082 * using the standard loop equations based on two error signals, d_alpha and d_beta. 00083 * We have these two values set based on each other for a critically damped system, so in 00084 * the block constructor, we just ask for "gain," which is d_alpha while d_beta is 00085 * equal to (gain^2)/4. 00086 * 00087 * The clock sync block needs to know the number of samples per symbol (sps), because it 00088 * only returns a single point representing the sample. The sps can be any positive real 00089 * number and does not need to be an integer. The filter taps must also be specified. The 00090 * taps are generated by first conceiving of the prototype filter that would be the signal's 00091 * matched filter. Then interpolate this by the number of filters in the filterbank. These 00092 * are then distributed among all of the filters. So if the prototype filter was to have 00093 * 45 taps in it, then each path of the filterbank will also have 45 taps. This is easily 00094 * done by building the filter with the sample rate multiplied by the number of filters 00095 * to use. 00096 * 00097 * The number of filters can also be set and defaults to 32. With 32 filters, you get a 00098 * good enough resolution in the phase to produce very small, almost unnoticeable, ISI. 00099 * Going to 64 filters can reduce this more, but after that there is very little gained 00100 * for the extra complexity. 00101 * 00102 * The initial phase is another settable parameter and refers to the filter path the 00103 * algorithm initially looks at (i.e., d_k starts at init_phase). This value defaults 00104 * to zero, but it might be useful to start at a different phase offset, such as the mid- 00105 * point of the filters. 00106 * 00107 * The final parameter is the max_rate_devitation, which defaults to 1.5. This is how far 00108 * we allow d_rate to swing, positive or negative, from 0. Constraining the rate can help 00109 * keep the algorithm from walking too far away to lock during times when there is no signal. 00110 * 00111 */ 00112 00113 class gr_pfb_clock_sync_ccf : public gr_block 00114 { 00115 private: 00116 /*! 00117 * Build the polyphase filterbank timing synchronizer. 00118 * \param sps (double) The number of samples per symbol in the incoming signal 00119 * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default. 00120 * \param taps (vector<int>) The filter taps. 00121 * \param filter_size (uint) The number of filters in the filterbank (default = 32). 00122 * \param init_phase (float) The initial phase to look at, or which filter to start 00123 * with (default = 0). 00124 * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5). 00125 * 00126 */ 00127 friend gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, 00128 const std::vector<float> &taps, 00129 unsigned int filter_size, 00130 float init_phase, 00131 float max_rate_deviation); 00132 00133 bool d_updated; 00134 double d_sps; 00135 double d_sample_num; 00136 float d_alpha; 00137 float d_beta; 00138 int d_nfilters; 00139 std::vector<gr_fir_ccf*> d_filters; 00140 std::vector<gr_fir_ccf*> d_diff_filters; 00141 std::vector< std::vector<float> > d_taps; 00142 std::vector< std::vector<float> > d_dtaps; 00143 float d_k; 00144 float d_rate; 00145 float d_rate_i; 00146 float d_rate_f; 00147 float d_max_dev; 00148 int d_filtnum; 00149 int d_taps_per_filter; 00150 00151 /*! 00152 * Build the polyphase filterbank timing synchronizer. 00153 */ 00154 gr_pfb_clock_sync_ccf (double sps, float gain, 00155 const std::vector<float> &taps, 00156 unsigned int filter_size, 00157 float init_phase, 00158 float max_rate_deviation); 00159 00160 void create_diff_taps(const std::vector<float> &newtaps, 00161 std::vector<float> &difftaps); 00162 00163 public: 00164 ~gr_pfb_clock_sync_ccf (); 00165 00166 /*! 00167 * Resets the filterbank's filter taps with the new prototype filter 00168 */ 00169 void set_taps (const std::vector<float> &taps, 00170 std::vector< std::vector<float> > &ourtaps, 00171 std::vector<gr_fir_ccf*> &ourfilter); 00172 00173 /*! 00174 * Returns the taps of the matched filter 00175 */ 00176 std::vector<float> channel_taps(int channel); 00177 00178 /*! 00179 * Returns the taps in the derivative filter 00180 */ 00181 std::vector<float> diff_channel_taps(int channel); 00182 00183 /*! 00184 * Print all of the filterbank taps to screen. 00185 */ 00186 void print_taps(); 00187 00188 /*! 00189 * Print all of the filterbank taps of the derivative filter to screen. 00190 */ 00191 void print_diff_taps(); 00192 00193 /*! 00194 * Set the gain value alpha for the control loop 00195 */ 00196 void set_alpha(float alpha) 00197 { 00198 d_alpha = alpha; 00199 } 00200 00201 /*! 00202 * Set the gain value beta for the control loop 00203 */ 00204 void set_beta(float beta) 00205 { 00206 d_beta = beta; 00207 } 00208 00209 /*! 00210 * Set the maximum deviation from 0 d_rate can have 00211 */ 00212 void set_max_rate_deviation(float m) 00213 { 00214 d_max_dev = m; 00215 } 00216 00217 bool check_topology(int ninputs, int noutputs); 00218 00219 int general_work (int noutput_items, 00220 gr_vector_int &ninput_items, 00221 gr_vector_const_void_star &input_items, 00222 gr_vector_void_star &output_items); 00223 }; 00224 00225 #endif