GNU Radio Manual and C++ API Reference  v3.9.2.0-89-gb7c7001e
The Free & Open Software Radio Ecosystem
pfb_clock_sync_ccf.h
Go to the documentation of this file.
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2009,2010,2012 Free Software Foundation, Inc.
4  *
5  * This file is part of GNU Radio
6  *
7  * SPDX-License-Identifier: GPL-3.0-or-later
8  *
9  */
10 
11 
12 #ifndef INCLUDED_DIGITAL_PFB_CLOCK_SYNC_CCF_H
13 #define INCLUDED_DIGITAL_PFB_CLOCK_SYNC_CCF_H
14 
15 #include <gnuradio/block.h>
16 #include <gnuradio/digital/api.h>
18 
19 namespace gr {
20 namespace digital {
21 
22 /*!
23  * \brief Timing synchronizer using polyphase filterbanks
24  * \ingroup synchronizers_blk
25  * \ingroup deprecated_blk
26  *
27  * \details
28  * This block performs timing synchronization for PAM signals by
29  * minimizing the derivative of the filtered signal, which in turn
30  * maximizes the SNR and minimizes ISI.
31  *
32  * This approach works by setting up two filterbanks; one
33  * filterbank contains the signal's pulse shaping matched filter
34  * (such as a root raised cosine filter), where each branch of the
35  * filterbank contains a different phase of the filter. The
36  * second filterbank contains the derivatives of the filters in
37  * the first filterbank. Thinking of this in the time domain, the
38  * first filterbank contains filters that have a sinc shape to
39  * them. We want to align the output signal to be sampled at
40  * exactly the peak of the sinc shape. The derivative of the sinc
41  * contains a zero at the maximum point of the sinc (sinc(0) = 1,
42  * sinc(0)' = 0). Furthermore, the region around the zero point
43  * is relatively linear. We make use of this fact to generate the
44  * error signal.
45  *
46  * If the signal out of the derivative filters is d_i[n] for the
47  * ith filter, and the output of the matched filter is x_i[n], we
48  * calculate the error as: e[n] = (Re{x_i[n]} * Re{d_i[n]} +
49  * Im{x_i[n]} * Im{d_i[n]}) / 2.0 This equation averages the error
50  * in the real and imaginary parts. There are two reasons we
51  * multiply by the signal itself. First, if the symbol could be
52  * positive or negative going, but we want the error term to
53  * always tell us to go in the same direction depending on which
54  * side of the zero point we are on. The sign of x_i[n] adjusts
55  * the error term to do this. Second, the magnitude of x_i[n]
56  * scales the error term depending on the symbol's amplitude, so
57  * larger signals give us a stronger error term because we have
58  * more confidence in that symbol's value. Using the magnitude of
59  * x_i[n] instead of just the sign is especially good for signals
60  * with low SNR.
61  *
62  * The error signal, e[n], gives us a value proportional to how
63  * far away from the zero point we are in the derivative
64  * signal. We want to drive this value to zero, so we set up a
65  * second order loop. We have two variables for this loop; d_k is
66  * the filter number in the filterbank we are on and d_rate is the
67  * rate which we travel through the filters in the steady
68  * state. That is, due to the natural clock differences between
69  * the transmitter and receiver, d_rate represents that difference
70  * and would traverse the filter phase paths to keep the receiver
71  * locked. Thinking of this as a second-order PLL, the d_rate is
72  * the frequency and d_k is the phase. So we update d_rate and d_k
73  * using the standard loop equations based on two error signals,
74  * d_alpha and d_beta. We have these two values set based on each
75  * other for a critically damped system, so in the block
76  * constructor, we just ask for "gain," which is d_alpha while
77  * d_beta is equal to (gain^2)/4.
78  *
79  * The block's parameters are:
80  *
81  * \li \p sps: The clock sync block needs to know the number of
82  * samples per symbol, because it defaults to return a single
83  * point representing the symbol. The sps can be any positive real
84  * number and does not need to be an integer.
85  *
86  * \li \p loop_bw: The loop bandwidth is used to set the gain of
87  * the inner control loop (see:
88  * http://gnuradio.squarespace.com/blog/2011/8/13/control-loop-gain-values.html).
89  * This should be set small (a value of around 2pi/100 is
90  * suggested in that blog post as the step size for the number of
91  * radians around the unit circle to move relative to the error).
92  *
93  * \li \p taps: One of the most important parameters for this
94  * block is the taps of the filter. One of the benefits of this
95  * algorithm is that you can put the matched filter in here as the
96  * taps, so you get both the matched filter and sample timing
97  * correction in one go. So create your normal matched filter. For
98  * a typical digital modulation, this is a root raised cosine
99  * filter. The number of taps of this filter is based on how long
100  * you expect the channel to be; that is, how many symbols do you
101  * want to combine to get the current symbols energy back (there's
102  * probably a better way of stating that). It's usually 5 to 10 or
103  * so. That gives you your filter, but now we need to think about
104  * it as a filter with different phase profiles in each filter. So
105  * take this number of taps and multiply it by the number of
106  * filters. This is the number you would use to create your
107  * prototype filter. When you use this in the PFB filerbank, it
108  * segments these taps into the filterbanks in such a way that
109  * each bank now represents the filter at different phases,
110  * equally spaced at 2pi/N, where N is the number of filters.
111  *
112  * \li \p filter_size (default=32): The number of filters can also
113  * be set and defaults to 32. With 32 filters, you get a good
114  * enough resolution in the phase to produce very small, almost
115  * unnoticeable, ISI. Going to 64 filters can reduce this more,
116  * but after that there is very little gained for the extra
117  * complexity.
118  *
119  * \li \p init_phase (default=0): The initial phase is another
120  * settable parameter and refers to the filter path the algorithm
121  * initially looks at (i.e., d_k starts at init_phase). This value
122  * defaults to zero, but it might be useful to start at a
123  * different phase offset, such as the mid-point of the filters.
124  *
125  * \li \p max_rate_deviation (default=1.5): The next parameter is
126  * the max_rate_devitation, which defaults to 1.5. This is how far
127  * we allow d_rate to swing, positive or negative, from
128  * 0. Constraining the rate can help keep the algorithm from
129  * walking too far away to lock during times when there is no
130  * signal.
131  *
132  * \li \p osps (default=1): The osps is the number of output
133  * samples per symbol. By default, the algorithm produces 1 sample
134  * per symbol, sampled at the exact sample value. This osps value
135  * was added to better work with equalizers, which do a better job
136  * of modeling the channel if they have 2 samps/sym.
137  *
138  * Reference:
139  * f. j. harris and M. Rice, "Multirate Digital Filters for Symbol
140  * Timing Synchronization in Software Defined Radios", IEEE
141  * Selected Areas in Communications, Vol. 19, No. 12, Dec., 2001.
142  *
143  * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.127.1757
144  */
145 class DIGITAL_API pfb_clock_sync_ccf : virtual public block
146 {
147 public:
148  // gr::digital::pfb_clock_sync_ccf::sptr
149  typedef std::shared_ptr<pfb_clock_sync_ccf> sptr;
150 
151  /*!
152  * Build the polyphase filterbank timing synchronizer.
153  * \param sps (double) The number of samples per symbol in the incoming signal
154  * \param loop_bw (float) The bandwidth of the control loop; set's alpha and beta.
155  * \param taps (vector<int>) The filter taps.
156  * \param filter_size (uint) The number of filters in the filterbank (default = 32).
157  * \param init_phase (float) The initial phase to look at, or which filter to start
158  * with (default = 0).
159  * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5).
160  * \param osps (int) The number of output samples per symbol (default=1).
161  */
162  static sptr make(double sps,
163  float loop_bw,
164  const std::vector<float>& taps,
165  unsigned int filter_size = 32,
166  float init_phase = 0,
167  float max_rate_deviation = 1.5,
168  int osps = 1);
169 
170  /*! \brief update the system gains from omega and eta
171  *
172  * This function updates the system gains based on the loop
173  * bandwidth and damping factor of the system.
174  * These two factors can be set separately through their own
175  * set functions.
176  */
177  virtual void update_gains() = 0;
178 
179  /*!
180  * Resets the filterbank's filter taps with the new prototype filter.
181  */
182  virtual void update_taps(const std::vector<float>& taps) = 0;
183 
184  /*!
185  * Returns all of the taps of the matched filter
186  */
187  virtual std::vector<std::vector<float>> taps() const = 0;
188 
189  /*!
190  * Returns all of the taps of the derivative filter
191  */
192  virtual std::vector<std::vector<float>> diff_taps() const = 0;
193 
194  /*!
195  * Returns the taps of the matched filter for a particular channel
196  */
197  virtual std::vector<float> channel_taps(int channel) const = 0;
198 
199  /*!
200  * Returns the taps in the derivative filter for a particular channel
201  */
202  virtual std::vector<float> diff_channel_taps(int channel) const = 0;
203 
204  /*!
205  * Return the taps as a formatted string for printing
206  */
207  virtual std::string taps_as_string() const = 0;
208 
209  /*!
210  * Return the derivative filter taps as a formatted string for printing
211  */
212  virtual std::string diff_taps_as_string() const = 0;
213 
214 
215  /*******************************************************************
216  SET FUNCTIONS
217  *******************************************************************/
218 
219  /*!
220  * \brief Set the loop bandwidth
221  *
222  * Set the loop filter's bandwidth to \p bw. This should be
223  * between 2*pi/200 and 2*pi/100 (in rads/samp). It must also be
224  * a positive number.
225  *
226  * When a new damping factor is set, the gains, alpha and beta,
227  * of the loop are recalculated by a call to update_gains().
228  *
229  * \param bw (float) new bandwidth
230  */
231  virtual void set_loop_bandwidth(float bw) = 0;
232 
233  /*!
234  * \brief Set the loop damping factor
235  *
236  * Set the loop filter's damping factor to \p df. The damping
237  * factor should be sqrt(2)/2.0 for critically damped systems.
238  * Set it to anything else only if you know what you are
239  * doing. It must be a number between 0 and 1.
240  *
241  * When a new damping factor is set, the gains, alpha and beta,
242  * of the loop are recalculated by a call to update_gains().
243  *
244  * \param df (float) new damping factor
245  */
246  virtual void set_damping_factor(float df) = 0;
247 
248  /*!
249  * \brief Set the loop gain alpha
250  *
251  * Set's the loop filter's alpha gain parameter.
252  *
253  * This value should really only be set by adjusting the loop
254  * bandwidth and damping factor.
255  *
256  * \param alpha (float) new alpha gain
257  */
258  virtual void set_alpha(float alpha) = 0;
259 
260  /*!
261  * \brief Set the loop gain beta
262  *
263  * Set's the loop filter's beta gain parameter.
264  *
265  * This value should really only be set by adjusting the loop
266  * bandwidth and damping factor.
267  *
268  * \param beta (float) new beta gain
269  */
270  virtual void set_beta(float beta) = 0;
271 
272  /*!
273  * Set the maximum deviation from 0 d_rate can have
274  */
275  virtual void set_max_rate_deviation(float m) = 0;
276 
277  /*******************************************************************
278  GET FUNCTIONS
279  *******************************************************************/
280 
281  /*!
282  * \brief Returns the loop bandwidth
283  */
284  virtual float loop_bandwidth() const = 0;
285 
286  /*!
287  * \brief Returns the loop damping factor
288  */
289  virtual float damping_factor() const = 0;
290 
291  /*!
292  * \brief Returns the loop gain alpha
293  */
294  virtual float alpha() const = 0;
295 
296  /*!
297  * \brief Returns the loop gain beta
298  */
299  virtual float beta() const = 0;
300 
301  /*!
302  * \brief Returns the current clock rate
303  */
304  virtual float clock_rate() const = 0;
305 
306  /*!
307  * \brief Returns the current error of the control loop.
308  */
309  virtual float error() const = 0;
310 
311  /*!
312  * \brief Returns the current rate of the control loop.
313  */
314  virtual float rate() const = 0;
315 
316  /*!
317  * \brief Returns the current phase arm of the control loop.
318  */
319  virtual float phase() const = 0;
320 };
321 
322 } /* namespace digital */
323 } /* namespace gr */
324 
325 #endif /* INCLUDED_DIGITAL_PFB_CLOCK_SYNC_CCF_H */
The abstract base class for all 'terminal' processing blocks.
Definition: gnuradio-runtime/include/gnuradio/block.h:60
Timing synchronizer using polyphase filterbanks.
Definition: pfb_clock_sync_ccf.h:146
static sptr make(double sps, float loop_bw, const std::vector< float > &taps, unsigned int filter_size=32, float init_phase=0, float max_rate_deviation=1.5, int osps=1)
virtual void set_damping_factor(float df)=0
Set the loop damping factor.
virtual float beta() const =0
Returns the loop gain beta.
virtual void update_taps(const std::vector< float > &taps)=0
virtual void set_loop_bandwidth(float bw)=0
Set the loop bandwidth.
virtual std::string diff_taps_as_string() const =0
virtual float loop_bandwidth() const =0
Returns the loop bandwidth.
virtual float phase() const =0
Returns the current phase arm of the control loop.
virtual std::vector< float > channel_taps(int channel) const =0
virtual std::string taps_as_string() const =0
virtual std::vector< float > diff_channel_taps(int channel) const =0
std::shared_ptr< pfb_clock_sync_ccf > sptr
Definition: pfb_clock_sync_ccf.h:149
virtual std::vector< std::vector< float > > taps() const =0
virtual float rate() const =0
Returns the current rate of the control loop.
virtual float damping_factor() const =0
Returns the loop damping factor.
virtual std::vector< std::vector< float > > diff_taps() const =0
virtual float clock_rate() const =0
Returns the current clock rate.
virtual void set_alpha(float alpha)=0
Set the loop gain alpha.
virtual void update_gains()=0
update the system gains from omega and eta
virtual void set_max_rate_deviation(float m)=0
virtual float alpha() const =0
Returns the loop gain alpha.
virtual float error() const =0
Returns the current error of the control loop.
virtual void set_beta(float beta)=0
Set the loop gain beta.
#define DIGITAL_API
Definition: gr-digital/include/gnuradio/digital/api.h:18
static constexpr float taps[NSTEPS+1][NTAPS]
Definition: interpolator_taps.h:9
GNU Radio logging wrapper for log4cpp library (C++ port of log4j)
Definition: basic_block.h:29