GNU Radio Manual and C++ API Reference 3.9.8.0
The Free & Open Software Radio Ecosystem
 
Loading...
Searching...
No Matches
pfb_clock_sync_fff.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#ifndef INCLUDED_DIGITAL_PFB_CLOCK_SYNC_FFF_H
12#define INCLUDED_DIGITAL_PFB_CLOCK_SYNC_FFF_H
13
14#include <gnuradio/block.h>
17
18namespace gr {
19namespace digital {
20
21/*!
22 * \brief Timing synchronizer using polyphase filterbanks
23 * \ingroup synchronizers_blk
24 * \ingroup deprecated_blk
25 *
26 * \details
27 * This block performs timing synchronization for PAM signals by
28 * minimizing the derivative of the filtered signal, which in turn
29 * maximizes the SNR and minimizes ISI.
30 *
31 * This approach works by setting up two filterbanks; one
32 * filterbank contains the signal's pulse shaping matched filter
33 * (such as a root raised cosine filter), where each branch of the
34 * filterbank contains a different phase of the filter. The
35 * second filterbank contains the derivatives of the filters in
36 * the first filterbank. Thinking of this in the time domain, the
37 * first filterbank contains filters that have a sinc shape to
38 * them. We want to align the output signal to be sampled at
39 * exactly the peak of the sinc shape. The derivative of the sinc
40 * contains a zero at the maximum point of the sinc (sinc(0) = 1,
41 * sinc(0)' = 0). Furthermore, the region around the zero point
42 * is relatively linear. We make use of this fact to generate the
43 * error signal.
44 *
45 * If the signal out of the derivative filters is d_i[n] for the
46 * ith filter, and the output of the matched filter is x_i[n], we
47 * calculate the error as: e[n] = (Re{x_i[n]} * Re{d_i[n]} +
48 * Im{x_i[n]} * Im{d_i[n]}) / 2.0 This equation averages the error
49 * in the real and imaginary parts. There are two reasons we
50 * multiply by the signal itself. First, if the symbol could be
51 * positive or negative going, but we want the error term to
52 * always tell us to go in the same direction depending on which
53 * side of the zero point we are on. The sign of x_i[n] adjusts
54 * the error term to do this. Second, the magnitude of x_i[n]
55 * scales the error term depending on the symbol's amplitude, so
56 * larger signals give us a stronger error term because we have
57 * more confidence in that symbol's value. Using the magnitude of
58 * x_i[n] instead of just the sign is especially good for signals
59 * with low SNR.
60 *
61 * The error signal, e[n], gives us a value proportional to how
62 * far away from the zero point we are in the derivative
63 * signal. We want to drive this value to zero, so we set up a
64 * second order loop. We have two variables for this loop; d_k is
65 * the filter number in the filterbank we are on and d_rate is the
66 * rate which we travel through the filters in the steady
67 * state. That is, due to the natural clock differences between
68 * the transmitter and receiver, d_rate represents that difference
69 * and would traverse the filter phase paths to keep the receiver
70 * locked. Thinking of this as a second-order PLL, the d_rate is
71 * the frequency and d_k is the phase. So we update d_rate and d_k
72 * using the standard loop equations based on two error signals,
73 * d_alpha and d_beta. We have these two values set based on each
74 * other for a critically damped system, so in the block
75 * constructor, we just ask for "gain," which is d_alpha while
76 * d_beta is equal to (gain^2)/4.
77 *
78 * The block's parameters are:
79 *
80 * \li \p sps: The clock sync block needs to know the number of
81 * samples per symbol, because it defaults to return a single
82 * point representing the symbol. The sps can be any positive real
83 * number and does not need to be an integer.
84 *
85 * \li \p loop_bw: The loop bandwidth is used to set the gain of
86 * the inner control loop (see:
87 * http://gnuradio.squarespace.com/blog/2011/8/13/control-loop-gain-values.html).
88 * This should be set small (a value of around 2pi/100 is
89 * suggested in that blog post as the step size for the number of
90 * radians around the unit circle to move relative to the error).
91 *
92 * \li \p taps: One of the most important parameters for this
93 * block is the taps of the filter. One of the benefits of this
94 * algorithm is that you can put the matched filter in here as the
95 * taps, so you get both the matched filter and sample timing
96 * correction in one go. So create your normal matched filter. For
97 * a typical digital modulation, this is a root raised cosine
98 * filter. The number of taps of this filter is based on how long
99 * you expect the channel to be; that is, how many symbols do you
100 * want to combine to get the current symbols energy back (there's
101 * probably a better way of stating that). It's usually 5 to 10 or
102 * so. That gives you your filter, but now we need to think about
103 * it as a filter with different phase profiles in each filter. So
104 * take this number of taps and multiply it by the number of
105 * filters. This is the number you would use to create your
106 * prototype filter. When you use this in the PFB filerbank, it
107 * segments these taps into the filterbanks in such a way that
108 * each bank now represents the filter at different phases,
109 * equally spaced at 2pi/N, where N is the number of filters.
110 *
111 * \li \p filter_size (default=32): The number of filters can also
112 * be set and defaults to 32. With 32 filters, you get a good
113 * enough resolution in the phase to produce very small, almost
114 * unnoticeable, ISI. Going to 64 filters can reduce this more,
115 * but after that there is very little gained for the extra
116 * complexity.
117 *
118 * \li \p init_phase (default=0): The initial phase is another
119 * settable parameter and refers to the filter path the algorithm
120 * initially looks at (i.e., d_k starts at init_phase). This value
121 * defaults to zero, but it might be useful to start at a
122 * different phase offset, such as the mid-point of the filters.
123 *
124 * \li \p max_rate_deviation (default=1.5): The next parameter is
125 * the max_rate_devitation, which defaults to 1.5. This is how far
126 * we allow d_rate to swing, positive or negative, from
127 * 0. Constraining the rate can help keep the algorithm from
128 * walking too far away to lock during times when there is no
129 * signal.
130 *
131 * \li \p osps (default=1): The osps is the number of output
132 * samples per symbol. By default, the algorithm produces 1 sample
133 * per symbol, sampled at the exact sample value. This osps value
134 * was added to better work with equalizers, which do a better job
135 * of modeling the channel if they have 2 samps/sym.
136 *
137 * Reference:
138 * f. j. harris and M. Rice, "Multirate Digital Filters for Symbol
139 * Timing Synchronization in Software Defined Radios", IEEE
140 * Selected Areas in Communications, Vol. 19, No. 12, Dec., 2001.
141 *
142 * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.127.1757
143 */
145{
146public:
147 // gr::digital::pfb_clock_sync_fff::sptr
148 typedef std::shared_ptr<pfb_clock_sync_fff> sptr;
149
150 /*!
151 * Build the polyphase filterbank timing synchronizer.
152 * \param sps (double) The number of samples per second in the incoming signal
153 * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by
154 * default. \param taps (vector<int>) The filter taps. \param filter_size (uint) The
155 * number of filters in the filterbank (default = 32). \param init_phase (float) The
156 * initial phase to look at, or which filter to start with (default = 0). \param
157 * max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5). \param
158 * osps (int) The number of output samples per symbol (default=1).
159 *
160 */
161 static sptr make(double sps,
162 float gain,
163 const std::vector<float>& taps,
164 unsigned int filter_size = 32,
165 float init_phase = 0,
166 float max_rate_deviation = 1.5,
167 int osps = 1);
168
169 /*! \brief update the system gains from omega and eta
170 *
171 * This function updates the system gains based on the loop
172 * bandwidth and damping factor of the system.
173 * These two factors can be set separately through their own
174 * set functions.
175 */
176 virtual void update_gains() = 0;
177
178 /*!
179 * Resets the filterbank's filter taps with the new prototype filter.
180 */
181 virtual void update_taps(const std::vector<float>& taps) = 0;
182
183 /*!
184 * Returns all of the taps of the matched filter
185 */
186 virtual std::vector<std::vector<float>> taps() const = 0;
187
188 /*!
189 * Returns all of the taps of the derivative filter
190 */
191 virtual std::vector<std::vector<float>> diff_taps() const = 0;
192
193 /*!
194 * Returns the taps of the matched filter for a particular channel
195 */
196 virtual std::vector<float> channel_taps(int channel) const = 0;
197
198 /*!
199 * Returns the taps in the derivative filter for a particular channel
200 */
201 virtual std::vector<float> diff_channel_taps(int channel) const = 0;
202
203 /*!
204 * Return the taps as a formatted string for printing
205 */
206 virtual std::string taps_as_string() const = 0;
207
208 /*!
209 * Return the derivative filter taps as a formatted string for printing
210 */
211 virtual std::string diff_taps_as_string() const = 0;
212
213
214 /*******************************************************************
215 SET FUNCTIONS
216 *******************************************************************/
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} /* namespace digital */
308} /* namespace gr */
309
310#endif /* INCLUDED_DIGITAL_PFB_CLOCK_SYNC_FFF_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_fff.h:145
virtual std::string taps_as_string() const =0
virtual void set_beta(float beta)=0
Set the loop gain beta.
virtual void set_max_rate_deviation(float m)=0
virtual void set_damping_factor(float df)=0
Set the loop damping factor.
virtual void update_taps(const std::vector< float > &taps)=0
virtual void set_loop_bandwidth(float bw)=0
Set the loop bandwidth.
virtual std::vector< std::vector< float > > taps() const =0
std::shared_ptr< pfb_clock_sync_fff > sptr
Definition: pfb_clock_sync_fff.h:148
virtual void set_alpha(float alpha)=0
Set the loop gain alpha.
virtual std::string diff_taps_as_string() const =0
virtual float loop_bandwidth() const =0
Returns the loop bandwidth.
static sptr make(double sps, float gain, const std::vector< float > &taps, unsigned int filter_size=32, float init_phase=0, float max_rate_deviation=1.5, int osps=1)
virtual std::vector< float > diff_channel_taps(int channel) const =0
virtual float beta() const =0
Returns the loop gain beta.
virtual float clock_rate() const =0
Returns the current clock rate.
virtual std::vector< float > channel_taps(int channel) const =0
virtual float alpha() const =0
Returns the loop gain alpha.
virtual void update_gains()=0
update the system gains from omega and eta
virtual float damping_factor() const =0
Returns the loop damping factor.
virtual std::vector< std::vector< float > > diff_taps() const =0
#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