GNU Radio C++ API
gr_pfb_clock_sync_ccf.h
Go to the documentation of this file.
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_core_api.h>
28 #include <gr_block.h>
29 
33  const std::vector<float> &taps,
34  unsigned int filter_size=32,
35  float init_phase=0,
36  float max_rate_deviation=1.5,
37  int osps=1);
38 
39 class gr_fir_ccf;
40 
41 /*!
42  * \class gr_pfb_clock_sync_ccf
43  *
44  * \brief Timing synchronizer using polyphase filterbanks
45  *
46  * \ingroup filter_blk
47  * \ingroup pfb_blk
48  *
49  * This block performs timing synchronization for PAM signals by
50  * minimizing the derivative of the filtered signal, which in turn
51  * maximizes the SNR and minimizes ISI.
52  *
53  * This approach works by setting up two filterbanks; one filterbank
54  * contains the signal's pulse shaping matched filter (such as a root
55  * raised cosine filter), where each branch of the filterbank contains
56  * a different phase of the filter. The second filterbank contains
57  * the derivatives of the filters in the first filterbank. Thinking of
58  * this in the time domain, the first filterbank contains filters that
59  * have a sinc shape to them. We want to align the output signal to be
60  * sampled at exactly the peak of the sinc shape. The derivative of
61  * the sinc contains a zero at the maximum point of the sinc (sinc(0)
62  * = 1, sinc(0)' = 0). Furthermore, the region around the zero point
63  * is relatively linear. We make use of this fact to generate the
64  * error signal.
65  *
66  * If the signal out of the derivative filters is d_i[n] for the ith
67  * filter, and the output of the matched filter is x_i[n], we
68  * calculate the error as: e[n] = (Re{x_i[n]} * Re{d_i[n]} +
69  * Im{x_i[n]} * Im{d_i[n]}) / 2.0 This equation averages the error in
70  * the real and imaginary parts. There are two reasons we multiply by
71  * the signal itself. First, if the symbol could be positive or
72  * negative going, but we want the error term to always tell us to go
73  * in the same direction depending on which side of the zero point we
74  * are on. The sign of x_i[n] adjusts the error term to do
75  * this. Second, the magnitude of x_i[n] scales the error term
76  * depending on the symbol's amplitude, so larger signals give us a
77  * stronger error term because we have more confidence in that
78  * symbol's value. Using the magnitude of x_i[n] instead of just the
79  * sign is especially good for signals with low SNR.
80  *
81  * The error signal, e[n], gives us a value proportional to how far
82  * away from the zero point we are in the derivative signal. We want
83  * to drive this value to zero, so we set up a second order loop. We
84  * have two variables for this loop; d_k is the filter number in the
85  * filterbank we are on and d_rate is the rate which we travel through
86  * the filters in the steady state. That is, due to the natural clock
87  * differences between the transmitter and receiver, d_rate represents
88  * that difference and would traverse the filter phase paths to keep
89  * the receiver locked. Thinking of this as a second-order PLL, the
90  * d_rate is the frequency and d_k is the phase. So we update d_rate
91  * and d_k using the standard loop equations based on two error
92  * signals, d_alpha and d_beta. We have these two values set based on
93  * each other for a critically damped system, so in the block
94  * constructor, we just ask for "gain," which is d_alpha while d_beta
95  * is equal to (gain^2)/4.
96  *
97  * The clock sync block needs to know the number of samples per symbol
98  * (sps), because it only returns a single point representing the
99  * symbol. The sps can be any positive real number and does not need
100  * to be an integer. The filter taps must also be specified. The taps
101  * are generated by first conceiving of the prototype filter that
102  * would be the signal's matched filter. Then interpolate this by the
103  * number of filters in the filterbank. These are then distributed
104  * among all of the filters. So if the prototype filter was to have 45
105  * taps in it, then each path of the filterbank will also have 45
106  * taps. This is easily done by building the filter with the sample
107  * rate multiplied by the number of filters to use.
108  *
109  * The number of filters can also be set and defaults to 32. With 32
110  * filters, you get a good enough resolution in the phase to produce
111  * very small, almost unnoticeable, ISI. Going to 64 filters can
112  * reduce this more, but after that there is very little gained for
113  * the extra complexity.
114  *
115  * The initial phase is another settable parameter and refers to the
116  * filter path the algorithm initially looks at (i.e., d_k starts at
117  * init_phase). This value defaults to zero, but it might be useful to
118  * start at a different phase offset, such as the mid- point of the
119  * filters.
120  *
121  * The final parameter is the max_rate_devitation, which defaults to
122  * 1.5. This is how far we allow d_rate to swing, positive or
123  * negative, from 0. Constraining the rate can help keep the algorithm
124  * from walking too far away to lock during times when there is no
125  * signal.
126  *
127  */
128 
130 {
131  private:
132  /*!
133  * Build the polyphase filterbank timing synchronizer.
134  * \param sps (double) The number of samples per symbol in the incoming signal
135  * \param loop_bw (float) The bandwidth of the control loop; set's alpha and beta.
136  * \param taps (vector<int>) The filter taps.
137  * \param filter_size (uint) The number of filters in the filterbank (default = 32).
138  * \param init_phase (float) The initial phase to look at, or which filter to start
139  * with (default = 0).
140  * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5).
141  * \param osps (int) The number of output samples per symbol (default=1).
142  *
143  */
144  friend GR_CORE_API gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float loop_bw,
145  const std::vector<float> &taps,
146  unsigned int filter_size,
147  float init_phase,
148  float max_rate_deviation,
149  int osps);
150 
151  bool d_updated;
152  double d_sps;
153  double d_sample_num;
154  float d_loop_bw;
155  float d_damping;
156  float d_alpha;
157  float d_beta;
158 
159  int d_nfilters;
160  int d_taps_per_filter;
161  std::vector<gr_fir_ccf*> d_filters;
162  std::vector<gr_fir_ccf*> d_diff_filters;
163  std::vector< std::vector<float> > d_taps;
164  std::vector< std::vector<float> > d_dtaps;
165 
166  float d_k;
167  float d_rate;
168  float d_rate_i;
169  float d_rate_f;
170  float d_max_dev;
171  int d_filtnum;
172  int d_osps;
173  float d_error;
174 
175  /*!
176  * Build the polyphase filterbank timing synchronizer.
177  */
178  gr_pfb_clock_sync_ccf (double sps, float loop_bw,
179  const std::vector<float> &taps,
180  unsigned int filter_size,
181  float init_phase,
182  float max_rate_deviation,
183  int osps);
184 
185  void create_diff_taps(const std::vector<float> &newtaps,
186  std::vector<float> &difftaps);
187 
188 public:
190 
191  /*! \brief update the system gains from omega and eta
192  *
193  * This function updates the system gains based on the loop
194  * bandwidth and damping factor of the system.
195  * These two factors can be set separately through their own
196  * set functions.
197  */
198  void update_gains();
199 
200  /*!
201  * Resets the filterbank's filter taps with the new prototype filter
202  */
203  void set_taps (const std::vector<float> &taps,
204  std::vector< std::vector<float> > &ourtaps,
205  std::vector<gr_fir_ccf*> &ourfilter);
206 
207  /*!
208  * Returns all of the taps of the matched filter
209  */
210  std::vector< std::vector<float> > get_taps();
211 
212  /*!
213  * Returns all of the taps of the derivative filter
214  */
215  std::vector< std::vector<float> > get_diff_taps();
216 
217  /*!
218  * Returns the taps of the matched filter for a particular channel
219  */
220  std::vector<float> get_channel_taps(int channel);
221 
222  /*!
223  * Returns the taps in the derivative filter for a particular channel
224  */
225  std::vector<float> get_diff_channel_taps(int channel);
226 
227  /*!
228  * Return the taps as a formatted string for printing
229  */
230  std::string get_taps_as_string();
231 
232  /*!
233  * Return the derivative filter taps as a formatted string for printing
234  */
235  std::string get_diff_taps_as_string();
236 
237 
238  /*******************************************************************
239  SET FUNCTIONS
240  *******************************************************************/
241 
242 
243  /*!
244  * \brief Set the loop bandwidth
245  *
246  * Set the loop filter's bandwidth to \p bw. This should be between
247  * 2*pi/200 and 2*pi/100 (in rads/samp). It must also be a positive
248  * number.
249  *
250  * When a new damping factor is set, the gains, alpha and beta, of the loop
251  * are recalculated by a call to update_gains().
252  *
253  * \param bw (float) new bandwidth
254  *
255  */
256  void set_loop_bandwidth(float bw);
257 
258  /*!
259  * \brief Set the loop damping factor
260  *
261  * Set the loop filter's damping factor to \p df. The damping factor
262  * should be sqrt(2)/2.0 for critically damped systems.
263  * Set it to anything else only if you know what you are doing. It must
264  * be a number between 0 and 1.
265  *
266  * When a new damping factor is set, the gains, alpha and beta, of the loop
267  * are recalculated by a call to update_gains().
268  *
269  * \param df (float) new damping factor
270  *
271  */
272  void set_damping_factor(float df);
273 
274  /*!
275  * \brief Set the loop gain alpha
276  *
277  * Set's the loop filter's alpha gain parameter.
278  *
279  * This value should really only be set by adjusting the loop bandwidth
280  * and damping factor.
281  *
282  * \param alpha (float) new alpha gain
283  *
284  */
285  void set_alpha(float alpha);
286 
287  /*!
288  * \brief Set the loop gain beta
289  *
290  * Set's the loop filter's beta gain parameter.
291  *
292  * This value should really only be set by adjusting the loop bandwidth
293  * and damping factor.
294  *
295  * \param beta (float) new beta gain
296  *
297  */
298  void set_beta(float beta);
299 
300  /*!
301  * Set the maximum deviation from 0 d_rate can have
302  */
304  {
305  d_max_dev = m;
306  }
307 
308  /*******************************************************************
309  GET FUNCTIONS
310  *******************************************************************/
311 
312  /*!
313  * \brief Returns the loop bandwidth
314  */
315  float get_loop_bandwidth() const;
316 
317  /*!
318  * \brief Returns the loop damping factor
319  */
320  float get_damping_factor() const;
321 
322  /*!
323  * \brief Returns the loop gain alpha
324  */
325  float get_alpha() const;
326 
327  /*!
328  * \brief Returns the loop gain beta
329  */
330  float get_beta() const;
331 
332  /*!
333  * \brief Returns the current clock rate
334  */
335  float get_clock_rate() const;
336 
337  /*******************************************************************
338  *******************************************************************/
339 
340  bool check_topology(int ninputs, int noutputs);
341 
342  int general_work (int noutput_items,
343  gr_vector_int &ninput_items,
344  gr_vector_const_void_star &input_items,
345  gr_vector_void_star &output_items);
346 };
347 
348 #endif