summaryrefslogtreecommitdiff
path: root/gr-digital/lib/clock_tracking_loop.h
blob: 1a20713c4855bfc21f45a3a91d0f0be0c4b98b60 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
/* -*- c++ -*- */
/*
 * Copyright (C) 2011,2013,2016-2017 Free Software Foundation, Inc.
 *
 * This file is part of GNU Radio
 *
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifndef INCLUDED_DIGITAL_CLOCK_TRACKING_LOOP_H
#define INCLUDED_DIGITAL_CLOCK_TRACKING_LOOP_H

namespace gr {
  namespace digital {

    /*!
     * \brief A second-order control loop implementation class.
     *
     * \details
     * This class implements most of a second order symbol clock
     * tracking loop and is inteded to act as a parent class to blocks
     * which need a symbol clock tracking loop to determine the optimal
     * instant to sample a received symbol from an input sample
     * stream (i.e. *_clock_recovery* and *_clock_sync* blocks).
     * It takes in an expected timing error detector gain, a normalized loop
     * bandwidth and damping factor, as well as clock period bounds, and
     * provides the functions that control the update of the loop.
     *
     * This control loop runs at the rate of the output clock, so
     * each step of the loop produces estimates about the output clock,
     * and the clock phase/timing error input must come at a rate of
     * once per output clock.
     *
     * This class does not include a timing error detector, and the 
     * caller is expected to provide the clock phase/timing error input
     * for each step of the loop.
     *
     * The loop's low pass filter is a Proportional-Integral (PI) filter.
     * The proportional and integral gains of the filter are termed alpha
     * and beta respectively. These gains are calculated using the input
     * expected timing error detector gain, loop bandwidth and damping factor.
     * If needed, the alpha and beta gain values can be set using their
     * respective #set_alpha or #set_beta functions.
     *
     * The class estimates the average clock period, T_avg; the instantaneous
     * clock period, T_inst; and the instantaneous clock phase, tau; of a
     * symbol clock based on an error signal from an external clock phase/
     * timing error detector which provides one error signal sample per
     * clock (one error sample at the end of every T_inst clock cycle).
     * The error calculation is unique for each TED algorithm and is
     * calculated externally and passed to the advance_loop function,
     * which uses this to update its estimates.
     *
     * This class also provides the functions #phase_wrap and
     * #period_limit to easily keep the clock phase estimate, tau, and the
     * average clock period estimate, T_avg, within set bounds (phase_wrap
     * keeps the phase within +/-T_avg/2).
     *
     * The clock tracking loop, with its PI filter, when properly implemented, has
     * a digital loop phase-transfer function, in terms of the timing error
     * detector gain, \f$K_{ted}\f$; proportional gain, \f$\alpha\f$; and the
     * integral gain, \f$\beta\f$, as follows:
     * 
     * \f{align*}
     *    H(z) &= \dfrac {\Theta_o(z)}{\Theta_i(z)}
     *          = K_{ted}(\alpha + \beta)z^{-1} \cdot
     *            \dfrac{
     *                   1
     *                   - \dfrac{\alpha}{\alpha + \beta} z^{-1}
     *                  }
     *                  {
     *                   1
     *                   - 2 \left(1 - K_{ted}\dfrac{\alpha + \beta}{2}\right) z^{-1}
     *                   + (1 - K_{ted}\alpha) z^{-2}
     *                  } \\
     * \f}
     *
     * Mapping the above phase-transfer function to the standard form of a transfer
     * function for an analog second order control loop mapped to the digital domain
     * with the mapping \f$z = e^{sT}\f$ applied to the s-plane poles,
     * \f$s_{1,2} = -\zeta\omega_{n} \pm \omega_{n}\sqrt{\zeta^{2}-1}\f$, one obtains an
     * alternate form of the transfer function, directly related to the damping factor
     * \f$\zeta\f$, the natural radian frequency \f$\omega_{n}\f$, the damped radian frequency
     * of oscillation \f$\omega_{d}\f$, and the symbol clock period \f$T\f$:
     * 
     * \f{align*}
     *   H(z) &=
     *      \begin{cases}
     *         \dfrac{
     *                [2 -2\cos(\omega_{d}T)e^{-\zeta\omega_{n}T}] z 
     *                -2\sinh(\zeta\omega_{n}T)e^{-\zeta\omega_{n}T} 
     *               }
     *               {
     *                z^{2}
     *                - 2 \cos(\omega_{d}T) e^{-\zeta\omega_{n}T} z
     *                + e^{-2\zeta\omega_{n}T}
     *               }
     *               & \quad \text{for} \quad \zeta < 1 \quad \text{with}
     *               \quad \omega_{d}T = \omega_{n}T \sqrt{1 - \zeta^{2}}
     *               \\
     * \\
     *         \dfrac{
     *                [2 -2(1)e^{-\zeta\omega_{n}T}] z 
     *                -2\sinh(\zeta\omega_{n}T)e^{-\zeta\omega_{n}T} 
     *               }
     *               {
     *                z^{2}
     *                - 2(1)e^{-\zeta\omega_{n}T} z
     *                + e^{-2\zeta\omega_{n}T}
     *               }
     *               & \quad \text{for} \quad \zeta = 1 \quad \text{with}
     *               \quad \omega_{d}T = 0
     *               \\
     * \\
     *         \dfrac{
     *                [2 -2\cosh(\omega_{d}T)e^{-\zeta\omega_{n}T}] z 
     *                -2\sinh(\zeta\omega_{n}T)e^{-\zeta\omega_{n}T} 
     *               }
     *               {
     *                z^{2}
     *                - 2 \cosh(\omega_{d}T) e^{-\zeta\omega_{n}T} z
     *                + e^{-2\zeta\omega_{n}T}
     *               }
     *               & \quad \text{for} \quad \zeta > 1 \quad \text{with}
     *               \quad \omega_{d}T = \omega_{n}T \sqrt{\zeta^{2} - 1}
     *               \\
     *      \end{cases}
     * \\
     * \f}
     * 
     * The PI filter gains, expressed in terms of the damping factor, \f$\zeta\f$;
     * the natural radian frequency, \f$\omega_{n}\f$; the damped radian frequency of
     * oscillation, \f$\omega_{d}\f$; the timing error detector gain \f$K_{ted}\f$;
     * and the clock period \f$T\f$ are:
     * 
     * \f{align*}
     *   \alpha &= \dfrac{2}{K_{ted}}e^{-\zeta\omega_{n}T} \sinh(\zeta\omega_{n}T) \\
     * \\
     *   \beta  &=
     *      \begin{cases}
     *         \dfrac{2}{K_{ted}} \left(1 -
     *          e^{-\zeta\omega_{n}T} [\sinh(\zeta\omega_{n}T) + \cos(\omega_{d}T)]
     *          \right) &
     *         \text{for} \quad \zeta < 1 \quad (under \: damped)\\ \\
     *         \dfrac{2}{K_{ted}} \left(1 -
     *          e^{-\zeta\omega_{n}T} [\sinh(\zeta\omega_{n}T) + 1]
     *          \right) &
     *         \text{for} \quad \zeta = 1 \quad (critically \: damped)\\ \\
     *         \dfrac{2}{K_{ted}} \left(1 -
     *          e^{-\zeta\omega_{n}T} [\sinh(\zeta\omega_{n}T) +\cosh(\omega_{d}T)]
     *          \right) &
     *         \text{for} \quad \zeta > 1 \quad (over \: damped)\\
     *      \end{cases} \\
     * \\
     * \f}
     * 
     * It should be noted that the clock period \f$T\f$ is being estimated by the clock
     * tracking loop and can vary over time, so that setting the loop bandwidth
     * directly can be a problem.  However, we specify loop bandwidth in terms
     * of the normalized digital natural radian frequency \f$\omega_{n\_norm}\f$ of the loop.
     * \f$\omega_{n\_norm}\f$ can only usefully be a small positive number, close to
     * zero.  The damping factor, \f$\zeta\f$, dictates the maximum value
     * \f$\omega_{n\_norm}\f$ can practically take on.  In the extreme a case of
     * \f$\zeta = 0.0\f$, \f$\omega_{n\_norm}\f$ is practically limited to the range
     * \f$(0, \pi)\f$, as \f$\pi\f$ then corresponds to the Nyquist frequency
     * of the clock.  However, whatever the damping factor, large values of
     * \f$\omega_{n\_norm}\f$ are usually not useful and yield poor results.
     * 
     * \f{align*}
     *     \omega_{n}T = \omega_{n\_norm} = 2 \pi f_{n\_norm} = 2 \pi f_{n} T =
     *     \pi \dfrac{f_{n}}{\left(\dfrac{F_{c}}{2}\right)}
     * \f}
     * 
     * In practice, the timing error detector (TED) of the symbol clock
     * tracking loop is implemented with an estimator of symbol clock phase
     * error, which has some gain \f$K_{ted}\f$.  The gain, \f$K_{ted}\f$, is
     * defined as the slope of a TED's S-curve plot at a symbol clock phase
     * offset of \f$\tau = 0\f$.  The S-curve shape and central slope, and
     * hence the gain \f$K_{ted}\f$, depend on the TED's estimator espression,
     * the input signal level, the pulse shaping filter, and the \f$E_s/N_0\f$
     * of the incomping signal.  The user must determine the TED's
     * S-curve by analysis or simulation of the particular situation, in order
     * to determine an appropriate value for \f$K_{ted}\f$.
     *
     * * A note on symbol clock phase vs. interpolating resampler sample phase,
     * since most GNURadio symbol synchronization blocks seem to have the same
     * implementation error:
     *
     * In general, the symbol clock phase, that the symbol clock tracking loop
     * estimates and tracks, cannot be used alone to derive the interpolating resampler
     * sample phase used in symbol synchronization, except in the very special case of
     * the symbol clock period being exactly divisible by the input sample stream
     * sample period.  Since this is never guaranteed in tracking real symbol clocks,
     * one should not use the symbol clock phase alone to compute the interpolating
     * resampler sample phase.
     *
     * Consider, in the analog time domain, the optimum symbol sampling instants
     * \f$t_{k}\f$, of an analog input signal \f$x(t)\f$, at an optimal symbol clock
     * phase \f$\tau_{0}\f$ and the symbol clock period \f$T_{c}\f$:
     * 
     * \f{align*}
     *    t_{k} &= \tau_{0} + k T_{c} \\
     *    y_{k} &= x(t_{k}) = x(\tau_{0} + k T_{c}) \\
     * \f}
     * 
     * If one divides the \f$t_{k}\f$ times by the input sample stream sample period
     * \f$T_{i}\f$, the correct interpolating resampler sample phase \f$\tau_{0\_i}\f$ will
     * get a contribution from the term \f$T_{c\_remainder}\f$ (which is an error) as shown
     * below:
     * 
     * \f{align*}
     *    \dfrac{t_{k}}{T_{i}} &= \dfrac{\tau_{0}}{T_{i}} + \dfrac{k T_{c}}{T_{i}} \\
     *     &= (m + \tau_{0\_remainder}) + (n + T_{c\_remainder}) \\
     *     &= \tau_{0\_remainder} + T_{c\_remainder} + (m + n) \\
     *     &= \tau_{0\_i} + k^{\prime}
     * \f}
     * 
     * So instead of using the symbol clock sample phase alone to obtain the
     * interpolating resampler sample phase, one should use the previous interpolating
     * resampler sample phase and the instantaneous clock period estimate provided by
     * the symbol clock tracking loop.
     *
     */
    class clock_tracking_loop
    {
    protected:
      // Estimate of the average clock period, T_avg, in units of
      // input sample clocks (so this is the average number of
      // input samples per output symbol, aka samples/symbol).
      // To convert to seconds, divide by the input sample rate: F_s_input.
      float d_avg_period;

      // Limits on how far the average clock period estimate can wander,
      // and the nominal average clock period, in units of input sample clocks.
      // To convert to seconds, divide by the input sample rate: F_s_input.
      float d_max_avg_period, d_min_avg_period;
      float d_nom_avg_period;

      // Instantaneous clock period estimate, T_inst, in units of
      // input sample clocks (so this is the intantaneous number of
      // input samples per output symbol, aka instantaneous samples/symbol).
      // To convert to seconds, divide by the input sample rate: F_s_input.
      float d_inst_period;

      // Instantaneous clock phase estimate, tau, in units of
      // input sample clocks.
      // To convert to seconds, divide by the input sample rate: F_s_input.
      // To wrap, add or subtract a multiple of the estimate of the 
      // average clock period, T_avg.
      // To convert to a normalized (but not wrapped) clock phase estimate,
      // divide by the estimate of the average clock period, T_avg.  
      // To further convert the normalized clock phase estimate to radians,
      // multiply the normalized clock phase estimate by 2*pi.
      float d_phase;

      // Damping factor of the 2nd order loop transfer function.
      // Zeta in the range (0.0, 1.0) yields an under-damped loop.
      // Zeta in the range (1.0, Inf) yields an over-damped loop.
      // Zeta equal to 1.0 yields a crtically-damped loop.
      float d_zeta;

      // Normalized natural radian frequency of the 2nd order loop transfer
      // function.  It should be a small positive number, corresponding to
      // the normalized natural radian frequency of the loop as digital
      // low-pass filter that is filtering the clock phase/timing error signal.
      // omega_n_norm = omega_n*T  = 2*pi*f_n*T = 2*pi*f_n_norm
      float d_omega_n_norm;

      // Expected gain of the timing error detector in use, given the
      // TED estimator expression, the expected input amplitude, the
      // input pulse shape, and the expected input Es/No.  (This value is the
      // slope of the TED's S-curve plot at a timing offset of tau = 0, and
      // must be determined by analysis and/or simulation by the user.)
      float d_ted_gain;

      // Proportional gain of the PI loop filter (aka gain_mu)
      // (aka gain_mu in some clock recovery blocks)
      float d_alpha;

      // Integral gain of the PI loop filter
      // (aka gain_omega in some clock recovery blocks)
      float d_beta;

      // For reverting the loop state one iteration (only)
      float d_prev_avg_period;
      float d_prev_inst_period;
      float d_prev_phase;

    public:
      clock_tracking_loop(void) {}

      /*! \brief Construct a clock_tracking_loop object.
       *
       * \details
       * This function instantiates a clock_tracking_loop object. 
       *
       * \param loop_bw
       * Normalized approximate loop bandwidth.
       * It should be a small positive number, corresponding to the normalized
       * natural radian frequency of the loop as digital low-pass filter that is
       * filtering the clock phase/timing error.
       *
       * Technically this parameter corresponds to the natural radian frequency
       * of the 2nd order loop transfer function (scaled by Fs),
       * which is the radius of the pole locations in the s-plane of an
       * underdamped analog 2nd order system.
       *
       * \param max_period
       * Maximum limit for the estimated clock period, in units of
       * input stream sample periods. (i.e. maximum samples/symbol)
       *
       * \param min_period
       * Minimum limit for the estimated clock period, in units of
       * input stream sample periods. (i.e. minimum samples/symbol)
       *
       * \param nominal_period
       * Nominal value for the estimated clock period, in units of
       * input stream sample periods. (i.e. nominal samples/symbol)
       * If not specified, this value will be set to the average of
       * min_period and max_period,
       *
       * \param damping
       * Damping factor of the 2nd order loop transfer function.
       * Damping in the range (0.0, 1.0) yields an under-damped loop.
       * Damping in the range (1.0, Inf) yields an over-damped loop.
       * Damping equal to 1.0 yields a crtically-damped loop.
       * Under-damped loops are not generally useful for clock tracking.
       * This parameter defaults to 1.0, if not specified.
       *
       * \param ted_gain
       * Expected gain of the timing error detector, given the TED in use
       * and the anticipated input amplitude, pulse shape, and Es/No.
       * This value is the slope of the TED's S-curve at timing offset tau = 0.
       * This value is normally computed by the user analytically or by
       * simulation in a tool outside of GNURadio.
       * This value must be correct for the loop filter gains to be computed
       * properly from the desired input loop bandwidth and damping factor.
       * This parameter defaults to 1.0, if not specified.
       */
      clock_tracking_loop(float loop_bw,
                          float max_period, float min_period,
                          float nominal_period = 0.0f,
                          float damping = 1.0f,
                          float ted_gain = 1.0f);

      virtual ~clock_tracking_loop();

      /*! \brief Update the gains from the ted_gain, loop bw and damping factor.
       *
       * \details
       * This function updates the gains based on the loop
       * bandwidth and damping factor of the system. These two
       * factors can be set separately through their own set
       * functions.
       */
      void update_gains();

      /*! \brief Advance the loop based on the current gain
       *  settings and the input error signal.
       */
      void advance_loop(float error);

      /*! \brief Undo a single, prior advance_loop() call.
       *
       * \details
       * Reverts a single, prior call to advance_loop().
       * It cannot usefully called again, until after the next call
       * to advance_loop().
       *
       * This method is needed so clock recovery/sync blocks can
       * perform correctly given the constraints of GNURadio's streaming
       * engine, interpolation filtering, and tag propagation.
       */
      void revert_loop();

      /*! \brief
       * Keep the clock phase estimate, tau, between -T_avg/2 and T_avg/2.
       *
       * \details
       * This function keeps the clock phase estimate, tau, between
       * -T_avg/2 and T_avg/2, by wrapping it modulo the estimated
       * average clock period, T_avg.  (N.B. Wrapping an estimated phase
       * by an *estimated*, *average* period.)
       *
       * This function can be called after advance_loop to keep the
       * phase value small.  It is set as a separate method in case
       * another way is desired as this is fairly heavy-handed.
       * Clock recovery/sync blocks usually do not need the phase of the
       * clock, and this class doesn't actually use the phase at all,
       * so calling this is optional.
       */
      void phase_wrap();

      /*! \brief
       * Keep the estimated average clock period, T_avg, between T_avg_min
       * and T_avg_max.
       *
       * \details
       * This function keeps the estimated average clock period, T_avg,
       * between T_avg_min and T_avg_max. It accomplishes this by hard limiting.
       * This is needed because T_avg is essentially computed by the
       * integrator portion of an IIR filter, so T_avg could potentially
       * wander very far during periods of noise/nonsense input.
       *
       * This function should be called after advance_loop to keep the
       * estimated average clock period, T_avg, in the specified range.
       * It is set as a separate method in case another way is desired as
       * this is fairly heavy-handed.
       */
      void period_limit();

      /*******************************************************************
       * SET FUNCTIONS
       *******************************************************************/

      /*!
       * \brief Set the normalized approximate loop bandwidth.
       *
       * \details
       * Set the normalized approximate loop bandwidth.
       * Useful values are usually close to 0.0, e.g. 2*pi*0.045.
       *
       * It should be a small positive number, corresponding to the normalized
       * natural radian frequency of the loop as digital low-pass filter that is
       * filtering the clock phase/timing error.
       *
       * Technically this parameter corresponds to the natural radian frequency
       * of the 2nd order loop transfer function (scaled by Fs),
       * which is the radius of the pole locations in the s-plane of an
       * underdamped analog 2nd order system.
       *
       * The input parameter corresponds to omega_n_norm in the following
       * relation:
       *
       *     omega_n_norm = omega_n*T = 2*pi*f_n*T = 2*pi*f_n_norm
       *
       * where T is the period of the clock being estimated by this
       * clock tracking loop, and omega_n is the natural radian frequency
       * of the 2nd order loop transfer function.
       *
       * When a new loop bandwidth is set, the gains, alpha and beta,
       * of the loop are recalculated by a call to update_gains().
       *
       * \param bw    normalized approximate loop bandwidth
       */
      virtual void set_loop_bandwidth(float bw);

      /*!
       * \brief Set the loop damping factor.
       *
       * \details
       * Set the damping factor of the loop.
       * Damping in the range (0.0, 1.0) yields an under-damped loop.
       * Damping in the range (1.0, Inf) yields an over-damped loop.
       * Damping equal to 1.0 yields a crtically-damped loop.
       * Under-damped loops are not generally useful for clock tracking.
       * For clock tracking, as a first guess, set the damping factor to 2.0,
       * or 1.5 or 1.0.
       *
       * Damping factor of the 2nd order loop transfer function.
       * When a new damping factor is set, the gains, alpha and beta,
       * of the loop are recalculated by a call to update_gains().
       *
       * \param df    loop damping factor
       */
      void set_damping_factor(float df);

      /*!
       * \brief Set the expected gain of the Timing Error Detector.
       *
       * \details
       * Sets the expected gain of the timing error detector, given the TED in
       * use and the anticipated input amplitude, pulse shape, and Es/No.
       * This value is the slope of the TED's S-curve at timing offset tau = 0.
       * This value is normally computed by the user analytically or by
       * simulation in a tool outside of GNURadio.
       * This value must be correct for the loop filter gains to be computed
       * properly from the desired input loop bandwidth and damping factor.
       *
       * When a new ted_gain is set, the gains, alpha and beta,
       * of the loop are automatcally recalculated.
       *
       * \param ted_gain    expected gain of the timing error detector
       */
      void set_ted_gain (float ted_gain);

      /*!
       * \brief Set the PI filter proportional gain, alpha.
       *
       * \details
       * Sets the PI filter proportional gain, alpha.
       * This gain directly mutliplies the clock phase/timing error
       * term in the PI filter when advancing the loop.
       * It most directly affects the instantaneous clock period estimate,
       * T_inst, and instantaneous clock phase estimate, tau.
       *
       * This value would normally be adjusted by setting the loop
       * bandwidth and damping factor and calling update_gains(). However,
       * it can be set here directly if desired.
       *
       * Setting this parameter directly is probably only feasible if
       * the user is directly observing the estimates of average clock
       * period and instantaneous clock period over time in response to
       * an impulsive change in the input stream (i.e. watching the loop
       * transient behavior at the start of a data burst).
       *
       * \param alpha    PI filter proportional gain
       */
      void set_alpha(float alpha);

      /*!
       * \brief Set the PI filter integral gain, beta.
       *
       * \details
       * Sets the PI filter integral gain, beta.
       * This gain is used when integrating the clock phase/timing error
       * term in the PI filter when advancing the loop.
       * It most directly affects the average clock period estimate,
       * T_avg.
       *
       * This value would normally be adjusted by setting the loop
       * bandwidth and damping factor and calling update_gains(). However,
       * it can be set here directly if desired.
       *
       * Setting this parameter directly is probably only feasible if
       * the user is directly observing the estimates of average clock
       * period and instantaneous clock period over time in response to
       * an impulsive change in the input stream (i.e. watching the loop
       * transient behavior at the start of a data burst).
       *
       * \param beta    PI filter integral gain
       */
      void set_beta(float beta);

      /*!
       * \brief Set the average clock period estimate, T_avg.
       *
       * \details
       * Directly sets the average clock period estimate, T_avg,
       * in units of input stream sample clocks (so the average number of
       * input samples per output symbol, aka samples/symbol).
       *
       * The average clock period estimate, T_avg, is normally updated by
       * the advance_loop() and period_limit() calls. This method is used
       * manually reset the estimate when needed.
       *
       * \param period
       * Average clock period, T_avg, in units of input stream sample clocks.
       */
      void set_avg_period(float period);

      /*!
       * \brief Set the instantaneous clock period estimate, T_inst.
       *
       * \details
       * Directly sets the instantaneous clock period estimate, T_inst,
       * in units of input stream sample clocks (so the instantaneous number of
       * input samples per output symbol, aka instantaneous samples/symbol).
       *
       * The instantaneous clock period estimate, T_inst, is normally updated by
       * the advance_loop() call. This method is used manually reset the
       * estimate when needed.
       *
       * \param period
       * Instantaneous clock period, T_inst, in units of input stream sample
       * clocks.
       */
      void set_inst_period(float period);

      /*!
       * \brief Set the instantaneous clock phase estimate, tau.
       *
       * \details
       * Directly sets the instantaneous clock phase estimate, tau,
       * in units of input stream sample clocks.
       *
       * The instantaneous clock phase estimate, tau, is normally updated by
       * the advance_loop() call. This method is used manually reset the
       * estimate when needed.
       *
       * \param phase 
       * Instantaneous clock phase, tau, in units of input stream sample clocks.
       *
       */
      void set_phase(float phase);

      /*!
       * \brief Set the maximum average clock period estimate limit, T_avg_max.
       *
       * \details
       * Sets the maximum average clock period estimate limit, T_avg_max
       * in units of input stream sample clocks (so the maximum average number
       * of input samples per output symbol, aka maximum samples/symbol).
       *
       * This limit is needed because T_avg is essentially computed by the
       * integrator portion of an IIR filter, so T_avg could potentially
       * wander very far during periods of noise/nonsense input.
       *
       * \param period
       * Maximum average clock period, T_avg_max, in units of input stream
       * sample clocks.
       */
      void set_max_avg_period(float period);

      /*!
       * \brief Set the minimum average clock period estimate limit, T_avg_min.
       *
       * \details
       * Sets the minimum average clock period estimate limit, T_avg_min
       * in units of input stream sample clocks (so the minimum average number
       * of input samples per output symbol, aka minimum samples/symbol).
       *
       * This limit is needed because T_avg is essentially computed by the
       * integrator portion of an IIR filter, so T_avg could potentially
       * wander very far during periods of noise/nonsense input.
       *
       * \param period
       * Minimum average clock period, T_avg_min, in units of input stream
       * sample clocks.
       */
      void set_min_avg_period(float period);

      /*!
       * \brief Set the nominal average clock period estimate limit, T_avg_nom.
       *
       * \details
       * Sets the nominal average clock period estimate limit, T_avg_nom
       * in units of input stream sample clocks (so the nominal average number
       * of input samples per output symbol, aka minimum samples/symbol).
       *
       * \param period
       * Nominal average clock period, T_avg_nom, in units of input stream
       * sample clocks.
       */
      void set_nom_avg_period(float period);

      /*******************************************************************
       * GET FUNCTIONS
       *******************************************************************/

      /*!
       * \brief Returns the normalized approximate loop bandwidth.
       *
       * \details
       * See the documenation for set_loop_bandwidth() for more details.
       *
       * Note that if set_alpha() or set_beta() were called to directly
       * set gains, the value returned by this method will be inaccurate/stale.
       */
      float get_loop_bandwidth() const;

      /*!
       * \brief Returns the loop damping factor.
       *
       * \details
       * See the documenation for set_damping_factor() for more details.
       *
       * Note that if set_alpha() or set_beta() were called to directly
       * set gains, the value returned by this method will be inaccurate/stale.
       */
      float get_damping_factor() const;

      /*!
       * \brief Returns the user providded expected gain of the Timing Error
       * Detector.
       *
       * \details
       * See the documenation for set_ted_gain() for more details.
       */
      float get_ted_gain() const;

      /*!
       * \brief Returns the PI filter proportional gain, alpha.
       *
       * \details
       * See the documenation for set_alpha() for more details.
       */
      float get_alpha() const;

      /*!
       * \brief Returns the PI filter integral gain, beta.
       *
       * \details
       * See the documenation for set_beta() for more details.
       */
      float get_beta() const;

      /*!
       * \brief Get the average clock period estimate, T_avg.
       *
       * \details
       * Gets the average clock period estimate, T_avg,
       * in units of input stream sample clocks (so the average number of
       * input samples per output symbol, aka samples/symbol).
       *
       * To convert to seconds, divide by the input stream sample rate:
       * F_s_input.
       */
      float get_avg_period() const;

      /*!
       * \brief Get the instantaneous clock period estimate, T_inst.
       *
       * \details
       * Gets the instantaneous clock period estimate, T_inst,
       * in units of input stream sample clocks (so the instantaneous number of
       * input samples per output symbol, aka instantaneous samples/symbol).
       *
       * To convert to seconds, divide by the input stream sample rate:
       * F_s_input.
       */
      float get_inst_period() const;

      /*!
       * \brief Get the instantaneous clock phase estimate, tau.
       *
       * \details
       * Gets the instantaneous clock phase estimate, tau, in units of
       * input stream sample clocks.
       *
       * To convert to seconds, divide by the input stream sample rate:
       * F_s_input.
       *
       * To manually wrap, add or subtract a multiple of the estimate of the 
       * average clock period, T_avg.
       *
       * To convert to a normalized (but not wrapped) clock phase estimate,
       * divide by the estimate of the average clock period, T_avg.  
       * To further convert the normalized clock phase estimate to radians,
       * multiply the normalized clock phase estimate by 2*pi.
       */
      float get_phase() const;

      /*!
       * \brief Get the maximum average clock period estimate limit, T_avg_max.
       *
       * \details
       * See the documenation for set_max_avg_period() for more details.
       */
      float get_max_avg_period() const;

      /*!
       * \brief Get the minimum average clock period estimate limit, T_avg_min.
       *
       * \details
       * See the documenation for set_min_avg_period() for more details.
       */
      float get_min_avg_period() const;

      /*!
       * \brief Get the nominal average clock period, T_avg_nom.
       *
       * \details
       * Gets the nominal average clock period, T_avg_nom,
       * in units of input stream sample clocks (so the nominal average
       * number of input samples per output symbol, aka nominal samples/symbol).
       *
       * To convert to seconds, divide by the input stream sample rate:
       * F_s_input.
       */
      float get_nom_avg_period() const;

    };

  } /* namespace digital */
} /* namespace gr */

#endif /* INCLUDED_DIGITAL_CLOCK_TRACKING_LOOP_H */