/* -*- c++ -*- */ /* * Copyright (C) 2011,2013,2016-2017 Free Software Foundation, Inc. * * This file is part of GNU Radio * * SPDX-License-Identifier: GPL-3.0-or-later * */ #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 intended 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) { // So the loop can be reverted one step, if needed. d_prev_avg_period = d_avg_period; d_prev_inst_period = d_inst_period; d_prev_phase = d_phase; // Integral arm of PI filter d_avg_period = d_avg_period + d_beta * error; // Limit the integral arm output here, as a large negative // error input can lead to a negative d_avg_period, which // will cause an infitine loop in the phase wrap method. period_limit(); // Proportional arm of PI filter and final sum of PI filter arms d_inst_period = d_avg_period + d_alpha * error; // Limit the filter output here, for the errant case of a large // negative error input, that can lead to a negative d_inst_period, // which results in an incorrect phase increment, as it is assumed // to be moving forward to the next symbol. if (d_inst_period <= 0.f) d_inst_period = d_avg_period; // Compute the new, unwrapped clock phase d_phase = d_phase + d_inst_period; } /*! \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() { d_avg_period = d_prev_avg_period; d_inst_period = d_prev_inst_period; d_phase = d_prev_phase; } /*! \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() { float period = d_avg_period; // One could argue d_inst_period instead float limit = period / 2.0f; while (d_phase > limit) d_phase -= period; while (d_phase <= -limit) d_phase += period; } /*! \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 is called in advance_loop to keep the * estimated average clock period, T_avg, in the specified range. * It is set as a separate virtual method in case another way is desired * as this is fairly heavy-handed. */ virtual void period_limit() { if (d_avg_period > d_max_avg_period) d_avg_period = d_max_avg_period; else if (d_avg_period < d_min_avg_period) d_avg_period = d_min_avg_period; } /******************************************************************* * 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 automatically 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 documentation 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 documentation 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 documentation for set_ted_gain() for more details. */ float get_ted_gain() const; /*! * \brief Returns the PI filter proportional gain, alpha. * * \details * See the documentation for set_alpha() for more details. */ float get_alpha() const; /*! * \brief Returns the PI filter integral gain, beta. * * \details * See the documentation 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 documentation 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 documentation 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 */