root / gnuradio-core / src / lib / filter / gr_pfb_clock_sync_ccf.h @ d702e27d
History | View | Annotate | Download (8.8 kB)
| 1 | /* -*- c++ -*- */
|
|---|---|
| 2 | /*
|
| 3 | * Copyright 2009,2010 Free Software Foundation, Inc. |
| 4 | * |
| 5 | * This file is part of GNU Radio |
| 6 | * |
| 7 | * GNU Radio is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 3, or (at your option) |
| 10 | * any later version. |
| 11 | * |
| 12 | * GNU Radio is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with GNU Radio; see the file COPYING. If not, write to |
| 19 | * the Free Software Foundation, Inc., 51 Franklin Street, |
| 20 | * Boston, MA 02110-1301, USA. |
| 21 | */ |
| 22 | |
| 23 | |
| 24 | #ifndef INCLUDED_GR_PFB_CLOCK_SYNC_CCF_H
|
| 25 | #define INCLUDED_GR_PFB_CLOCK_SYNC_CCF_H
|
| 26 | |
| 27 | #include <gr_block.h> |
| 28 | |
| 29 | class gr_pfb_clock_sync_ccf; |
| 30 | typedef boost::shared_ptr<gr_pfb_clock_sync_ccf> gr_pfb_clock_sync_ccf_sptr;
|
| 31 | gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, |
| 32 | const std::vector<float> &taps, |
| 33 | unsigned int filter_size=32, |
| 34 | float init_phase=0, |
| 35 | float max_rate_deviation=1.5); |
| 36 | |
| 37 | class gr_fir_ccf; |
| 38 | |
| 39 | /*!
|
| 40 | * \class gr_pfb_clock_sync_ccf |
| 41 | * |
| 42 | * \brief Timing synchronizer using polyphase filterbanks |
| 43 | * |
| 44 | * \ingroup filter_blk |
| 45 | * |
| 46 | * This block performs timing synchronization for PAM signals by minimizing the |
| 47 | * derivative of the filtered signal, which in turn maximizes the SNR and |
| 48 | * minimizes ISI. |
| 49 | * |
| 50 | * This approach works by setting up two filterbanks; one filterbanke contains the |
| 51 | * signal's pulse shaping matched filter (such as a root raised cosine filter), |
| 52 | * where each branch of the filterbank contains a different phase of the filter. |
| 53 | * The second filterbank contains the derivatives of the filters in the first |
| 54 | * filterbank. Thinking of this in the time domain, the first filterbank contains |
| 55 | * filters that have a sinc shape to them. We want to align the output signal to |
| 56 | * be sampled at exactly the peak of the sinc shape. The derivative of the sinc |
| 57 | * contains a zero at the maximum point of the sinc (sinc(0) = 1, sinc(0)' = 0). |
| 58 | * Furthermore, the region around the zero point is relatively linear. We make |
| 59 | * use of this fact to generate the error signal. |
| 60 | * |
| 61 | * If the signal out of the derivative filters is d_i[n] for the ith filter, and |
| 62 | * the output of the matched filter is x_i[n], we calculate the error as: |
| 63 | * e[n] = (Re{x_i[n]} * Re{d_i[n]} + Im{x_i[n]} * Im{d_i[n]}) / 2.0
|
| 64 | * This equation averages the error in the real and imaginary parts. There are two |
| 65 | * reasons we multiply by the signal itself. First, if the symbol could be positive |
| 66 | * or negative going, but we want the error term to always tell us to go in the |
| 67 | * same direction depending on which side of the zero point we are on. The sign of |
| 68 | * x_i[n] adjusts the error term to do this. Second, the magnitude of x_i[n] scales |
| 69 | * the error term depending on the symbol's amplitude, so larger signals give us |
| 70 | * a stronger error term because we have more confidence in that symbol's value. |
| 71 | * Using the magnitude of x_i[n] instead of just the sign is especially good for |
| 72 | * signals with low SNR. |
| 73 | * |
| 74 | * The error signal, e[n], gives us a value proportional to how far away from the zero |
| 75 | * point we are in the derivative signal. We want to drive this value to zero, so we |
| 76 | * set up a second order loop. We have two variables for this loop; d_k is the filter |
| 77 | * number in the filterbank we are on and d_rate is the rate which we travel through |
| 78 | * the filters in the steady state. That is, due to the natural clock differences between |
| 79 | * the transmitter and receiver, d_rate represents that difference and would traverse |
| 80 | * the filter phase paths to keep the receiver locked. Thinking of this as a second-order |
| 81 | * PLL, the d_rate is the frequency and d_k is the phase. So we update d_rate and d_k |
| 82 | * using the standard loop equations based on two error signals, d_alpha and d_beta. |
| 83 | * We have these two values set based on each other for a critically damped system, so in |
| 84 | * the block constructor, we just ask for "gain," which is d_alpha while d_beta is |
| 85 | * equal to (gain^2)/4. |
| 86 | * |
| 87 | * The clock sync block needs to know the number of samples per second (sps), because it |
| 88 | * only returns a single point representing the sample. The sps can be any positive real |
| 89 | * number and does not need to be an integer. The filter taps must also be specified. The |
| 90 | * taps are generated by first conceiving of the prototype filter that would be the signal's |
| 91 | * matched filter. Then interpolate this by the number of filters in the filterbank. These |
| 92 | * are then distributed among all of the filters. So if the prototype filter was to have |
| 93 | * 45 taps in it, then each path of the filterbank will also have 45 taps. This is easily |
| 94 | * done by building the filter with the sample rate multiplied by the number of filters |
| 95 | * to use. |
| 96 | * |
| 97 | * The number of filters can also be set and defaults to 32. With 32 filters, you get a |
| 98 | * good enough resolution in the phase to produce very small, almost unnoticeable, ISI. |
| 99 | * Going to 64 filters can reduce this more, but after that there is very little gained |
| 100 | * for the extra complexity. |
| 101 | * |
| 102 | * The initial phase is another settable parameter and refers to the filter path the |
| 103 | * algorithm initially looks at (i.e., d_k starts at init_phase). This value defaults |
| 104 | * to zero, but it might be useful to start at a different phase offset, such as the mid- |
| 105 | * point of the filters. |
| 106 | * |
| 107 | * The final parameter is the max_rate_devitation, which defaults to 1.5. This is how far |
| 108 | * we allow d_rate to swing, positive or negative, from 0. Constraining the rate can help |
| 109 | * keep the algorithm from walking too far away to lock during times when there is no signal. |
| 110 | * |
| 111 | */ |
| 112 | |
| 113 | class gr_pfb_clock_sync_ccf : public gr_block |
| 114 | {
|
| 115 | private:
|
| 116 | /*!
|
| 117 | * Build the polyphase filterbank timing synchronizer. |
| 118 | * \param sps (double) The number of samples per second in the incoming signal |
| 119 | * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default. |
| 120 | * \param taps (vector<int>) The filter taps. |
| 121 | * \param filter_size (uint) The number of filters in the filterbank (default = 32). |
| 122 | * \param init_phase (float) The initial phase to look at, or which filter to start |
| 123 | * with (default = 0). |
| 124 | * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5). |
| 125 | * |
| 126 | */ |
| 127 | friend gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, |
| 128 | const std::vector<float> &taps, |
| 129 | unsigned int filter_size, |
| 130 | float init_phase,
|
| 131 | float max_rate_deviation);
|
| 132 | |
| 133 | bool d_updated;
|
| 134 | double d_sps;
|
| 135 | double d_sample_num;
|
| 136 | float d_alpha;
|
| 137 | float d_beta;
|
| 138 | int d_nfilters;
|
| 139 | std::vector<gr_fir_ccf*> d_filters; |
| 140 | std::vector<gr_fir_ccf*> d_diff_filters; |
| 141 | std::vector< std::vector<float> > d_taps;
|
| 142 | std::vector< std::vector<float> > d_dtaps;
|
| 143 | float d_k;
|
| 144 | float d_rate;
|
| 145 | float d_rate_i;
|
| 146 | float d_rate_f;
|
| 147 | float d_max_dev;
|
| 148 | int d_filtnum;
|
| 149 | int d_taps_per_filter;
|
| 150 | |
| 151 | /*!
|
| 152 | * Build the polyphase filterbank timing synchronizer. |
| 153 | */ |
| 154 | gr_pfb_clock_sync_ccf (double sps, float gain, |
| 155 | const std::vector<float> &taps, |
| 156 | unsigned int filter_size, |
| 157 | float init_phase,
|
| 158 | float max_rate_deviation);
|
| 159 | |
| 160 | void create_diff_taps(const std::vector<float> &newtaps, |
| 161 | std::vector<float> &difftaps);
|
| 162 | |
| 163 | public:
|
| 164 | ~gr_pfb_clock_sync_ccf (); |
| 165 | |
| 166 | /*!
|
| 167 | * Resets the filterbank's filter taps with the new prototype filter |
| 168 | */ |
| 169 | void set_taps (const std::vector<float> &taps, |
| 170 | std::vector< std::vector<float> > &ourtaps,
|
| 171 | std::vector<gr_fir_ccf*> &ourfilter); |
| 172 | |
| 173 | /*!
|
| 174 | * Returns the taps of the matched filter |
| 175 | */ |
| 176 | std::vector<float> channel_taps(int channel); |
| 177 | |
| 178 | /*!
|
| 179 | * Returns the taps in the derivative filter |
| 180 | */ |
| 181 | std::vector<float> diff_channel_taps(int channel); |
| 182 | |
| 183 | /*!
|
| 184 | * Print all of the filterbank taps to screen. |
| 185 | */ |
| 186 | void print_taps();
|
| 187 | |
| 188 | /*!
|
| 189 | * Print all of the filterbank taps of the derivative filter to screen. |
| 190 | */ |
| 191 | void print_diff_taps();
|
| 192 | |
| 193 | /*!
|
| 194 | * Set the gain value alpha for the control loop |
| 195 | */ |
| 196 | void set_alpha(float alpha) |
| 197 | {
|
| 198 | d_alpha = alpha; |
| 199 | } |
| 200 | |
| 201 | /*!
|
| 202 | * Set the gain value beta for the control loop |
| 203 | */ |
| 204 | void set_beta(float beta) |
| 205 | {
|
| 206 | d_beta = beta; |
| 207 | } |
| 208 | |
| 209 | /*!
|
| 210 | * Set the maximum deviation from 0 d_rate can have |
| 211 | */ |
| 212 | void set_max_rate_deviation(float m) |
| 213 | {
|
| 214 | d_max_dev = m; |
| 215 | } |
| 216 | |
| 217 | bool check_topology(int ninputs, int noutputs); |
| 218 | |
| 219 | int general_work (int noutput_items, |
| 220 | gr_vector_int &ninput_items, |
| 221 | gr_vector_const_void_star &input_items, |
| 222 | gr_vector_void_star &output_items); |
| 223 | }; |
| 224 | |
| 225 | #endif
|