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