48 virtual void rate(s64 delta_subns, u32 elapsed_usec) {}
55 virtual s64
update(s64 next, u32 elapsed_usec) = 0;
73 : m_prev(0), m_usec(0) {}
77 { m_prev = 0; m_usec = 0; }
80 s64
update(s64 next, u32 elapsed_usec)
override
81 { m_prev = next; m_usec = elapsed_usec;
return next; }
85 inline s64
prev()
const {
return m_prev; }
86 inline u32
usec()
const {
return m_usec; }
95 template<
typename T,
unsigned MAX_WINDOW>
103 void read(T* dst,
unsigned count)
const {
104 if (m_wridx >= count) {
105 memcpy(dst, m_window + m_wridx - count, count*
sizeof(T));
107 unsigned wrap = count - m_wridx;
108 memcpy(dst, m_window + MAX_WINDOW - wrap, wrap*
sizeof(T));
109 memcpy(dst + wrap, m_window, m_wridx*
sizeof(T));
114 void push(
const T& next) {
115 m_window[m_wridx] = next;
116 if (++m_wridx >= MAX_WINDOW) m_wridx = 0;
120 memset(m_window, 0, MAX_WINDOW *
sizeof(T));
125 T m_window[MAX_WINDOW];
129 s64 boxcar_filter(
const s64* data,
unsigned order);
130 s64 median_filter(s64* data,
unsigned samps);
140 inline s64 get_mean()
const {
return m_mean;}
141 inline u64 get_sigma()
const {
return m_sigma;}
142 inline void set_min(u64 min_subns) {m_min = min_subns;}
143 inline void set_tau(u32 tau_msec) {m_tau_usec = 1000*tau_msec;}
146 void reset()
override;
147 s64
update(s64 next, u32 elapsed_usec)
override;
158 template<
unsigned MAX_ORDER>
162 : m_order(0) {set_order(order);}
164 inline void set_order(
unsigned x)
165 {
if (x <= MAX_ORDER) m_order = x; }
168 { m_window.reset(); }
170 s64
update(s64 next, u32 elapsed_usec)
override {
171 if (next == INT64_MAX)
return INT64_MAX;
172 s64 temp[MAX_WINDOW];
174 m_window.read(temp, 1u << m_order);
175 return satcat5::ptp::boxcar_filter(temp, m_order);
179 static constexpr
unsigned MAX_WINDOW = 1u << MAX_ORDER;
186 template<
unsigned MAX_ORDER>
190 : m_order(0) {set_order(order);}
192 inline void set_order(
unsigned x)
193 {
if (x <= MAX_ORDER) m_order = x|1; }
196 { m_window.reset(); }
198 s64
update(s64 next, u32 elapsed_usec)
override {
199 if (next == INT64_MAX)
return INT64_MAX;
202 m_window.read(temp, m_order);
203 return satcat5::ptp::median_filter(temp, m_order);
228 : kp(satcat5::util::round_u64z(k1(tau_secs, 0.707) / fw_gain()))
229 , ki(satcat5::util::round_u64z(k2(tau_secs, 0.707) / fw_gain()))
235 bool ok()
const {
return (kp > 7) && (ki > 7);}
239 static constexpr
unsigned SCALE = 60;
244 static constexpr
double alpha(
double zeta)
245 {
return 0.25 / (zeta * zeta); }
246 static constexpr
double k1(
double tau,
double zeta)
247 {
return 1.273239545 / (tau * (1.0 + alpha(zeta))); }
248 static constexpr
double k2(
double tau,
double zeta)
249 {
return alpha(zeta) * k1(tau, zeta) * k1(tau, zeta); }
254 static constexpr
double fw_gain()
255 {
return double(satcat5::ptp::USEC_PER_SEC)
256 / 6.28318530717958647693
257 / satcat5::util::pow2d(
SCALE); }
280 void reset()
override;
281 void rate(s64 delta, u32 elapsed_usec)
override;
282 s64
update(s64 next, u32 elapsed_usec)
override;
299 : kp(satcat5::util::round_u64z(k1(tau_secs) / fw_gain()))
300 , ki(satcat5::util::round_u64z(k2(tau_secs) / fw_gain()))
301 , kr(satcat5::util::round_u64z(kratio(tau_secs)))
307 bool ok()
const {
return (kp > 7) && (ki > 7) && (kr > 7);}
320 static constexpr
double k1(
double tau)
321 {
return 0.830373616 / tau; }
322 static constexpr
double k2(
double tau)
323 {
return (4.0/9.0) * k1(tau) * k1(tau); }
324 static constexpr
double k3(
double tau)
325 {
return (2.0/27.0) * k1(tau) * k1(tau) * k1(tau); }
327 static constexpr
double kratio(
double tau)
328 {
return k3(tau) / k2(tau)
329 * satcat5::util::pow2d(
SCALE2)
330 / double(satcat5::ptp::USEC_PER_SEC); }
335 static constexpr
double fw_gain()
336 {
return double(satcat5::ptp::USEC_PER_SEC)
337 / 6.28318530717958647693
362 void reset()
override;
363 void rate(s64 delta, u32 elapsed_usec)
override;
364 s64
update(s64 next, u32 elapsed_usec)
override;
386 :
alpha(satcat5::util::INT128_ZERO)
387 ,
beta(satcat5::util::INT128_ZERO) {}
404 : ki(satcat5::util::round_u64z(ki_gain() / tau_secs))
405 , kw(satcat5::util::round_u64z(kw_gain() * 2.0 / tau_secs))
411 bool ok()
const {
return (ki > 7) && (kw > 7);}
414 static constexpr
double ki_gain()
415 {
return double(satcat5::ptp::USEC_PER_SEC); }
416 static constexpr
double kw_gain()
418 / double(satcat5::ptp::USEC_PER_SEC); }
433 void rate(s64 delta, u32 elapsed_usec)
override;
441 s64 update_inner(
const u32* dt,
const s64* y);
453 template<
unsigned MAX_WINDOW>
459 , m_count(0), m_elapsed(0) {}
460 static_assert(MAX_WINDOW >= 2,
"MAX_WINDOW must be at least 2.");
465 if (2 <= window && window <= MAX_WINDOW) m_window = window;
472 m_accum = satcat5::util::INT128_ZERO;
476 s64
update(s64 next, u32 elapsed_usec)
override {
479 m_elapsed += elapsed_usec;
480 if (next == INT64_MAX)
return INT64_MAX;
481 m_dly.push(m_elapsed);
485 if (m_count < MAX_WINDOW) ++m_count;
486 if (m_count < m_window)
return INT64_MAX;
487 u32 temp_dly[MAX_WINDOW]; m_dly.read(temp_dly, m_window);
488 s64 temp_dat[MAX_WINDOW]; m_dat.read(temp_dat, m_window);
490 return update_inner(temp_dly, temp_dat);
511 : m_filters(ctrl), m_first(true), m_rate(0)
512 , m_accum(satcat5::util::INT128_ZERO) {}
521 void reset()
override;
522 void rate(s64 delta, u32 elapsed_usec)
override;
523 s64
update(s64 next, u32 elapsed_usec)
override;
527 s64
predict(u32 elapsed_usec)
const;
531 static constexpr
unsigned SCALE = 32;
550 : m_scale(satcat5::util::round_s64z(fw_gain(scale_ns) / ref_clk_hz)) {}
553 bool ok()
const {
return satcat5::util::abs_s64(m_scale) > 1000000;}
559 s64
invert(s64 rate)
const;
563 static constexpr
unsigned SHIFT = 48;
564 static constexpr
double fw_gain(
unsigned scale_ns) {
565 return satcat5::util::pow2d(scale_ns + SHIFT)
566 / double(satcat5::ptp::SUBNS_PER_NSEC);
Amplitude-based outlier rejection.
void reset() override
Flush previous inputs and reset to a neutral state.
s64 update(s64 next, u32 elapsed_usec) override
Method called for each new input sample.
An FIR low-pass filter using "boxcar" averaging over 2^N samples.
s64 update(s64 next, u32 elapsed_usec) override
Method called for each new input sample.
void reset() override
Flush previous inputs and reset to a neutral state.
Helper class for "ControllerLR" is never used directly.
void set_coeff(const satcat5::ptp::CoeffLR &coeff)
Adjust loop bandwidth.
ControllerLR_Inner(const satcat5::ptp::CoeffLR &coeff, unsigned window)
Private constructor and destructor.
void rate(s64 delta, u32 elapsed_usec) override
Partial API from ptp::Filter.
Loop-filter for a linear-regression (LR) controller.
ControllerLR(const satcat5::ptp::CoeffLR &coeff)
Constructor sets loop bandwidth, which can be changed later.
void reset() override
Remaining API from ptp::Filter.
s64 update(s64 next, u32 elapsed_usec) override
Implement the required API from ptp::Filter.
void set_window(unsigned window)
Adjust window-size.
Loop-filter for a proportional-integral (PI) controller.
void rate(s64 delta, u32 elapsed_usec) override
Required API from ptp::Filter.
s64 update(s64 next, u32 elapsed_usec) override
Required API from ptp::Filter.
void set_slew(u64 slew)
Adjust maximum slew-rate.
ControllerPI(const satcat5::ptp::CoeffPI &coeff)
Constructor sets loop bandwidth, which can be changed later.
void reset() override
Required API from ptp::Filter.
void set_coeff(const satcat5::ptp::CoeffPI &coeff)
Adjust tracking-loop bandwidth.
Loop-filter for a proportional-double-integral (PII) controller.
s64 update(s64 next, u32 elapsed_usec) override
Required API from ptp::Filter.
ControllerPII(const satcat5::ptp::CoeffPII &coeff)
Constructor sets loop bandwidth, which can be changed later.
void rate(s64 delta, u32 elapsed_usec) override
Required API from ptp::Filter.
void reset() override
Required API from ptp::Filter.
void set_slew(u64 slew)
Adjust maximum slew-rate.
void set_coeff(const satcat5::ptp::CoeffPII &coeff)
Adjust tracking-loop bandwidth.
DebugFilter remembers the last received call to update.
s64 prev() const
Accessors for received parameters.
void reset() override
Reset history.
u32 usec() const
Accessors for received parameters.
s64 update(s64 next, u32 elapsed_usec) override
Simple passthrough, storing both parameters.
Define the basic chain-of-filters API.
virtual void rate(s64 delta_subns, u32 elapsed_usec)
Optional handler for fast-acquisition; override if required.
constexpr Filter()
Private constructor and destructor.
virtual void reset()=0
Flush previous inputs and reset to a neutral state.
virtual s64 update(s64 next, u32 elapsed_usec)=0
Method called for each new input sample.
An inline filter that iteratively estimates linear trends.
constexpr LinearPrediction(satcat5::ptp::Filter *ctrl=0)
Create this object, and optionally add the first filter.
s64 predict(u32 elapsed_usec) const
Extrapolate trendline relative to most recent update() event.
void rate(s64 delta, u32 elapsed_usec) override
Required API from ptp::Filter.
s64 update(s64 next, u32 elapsed_usec) override
Required API from ptp::Filter.
void add_filter(satcat5::ptp::Filter *filter)
Add to the chain of processing filters.
void reset() override
Required API from ptp::Filter.
Convert normalized frequency offset to ticks-per-clock.
s64 invert(s64 rate) const
Inverse conversion (ticks-per-clock ==> normalized rate)
bool ok() const
Is the scale coefficient large enough to mitigate rounding error?
s64 convert(s64 offset) const
Forward conversion (normalized rate ==> ticks-per-clock)
constexpr RateConversion(double ref_clk_hz, unsigned scale_ns)
Specify the nominal clock frequency and the scale (see above).
A sliding-window circular buffer, retaining the last N samples.
Helper functions for manipulating singly-linked lists.
void push_back(T *item)
Add a new item at the tail of the list.
Templated functions for manipulating singly-linked lists.
High-precision "Time" object for use with PTP / IEEE1588.
Loop-filter coefficients for use with the "ControllerLR" class.
constexpr CoeffLR(double tau_secs)
Calculate tracking-loop coefficients.
bool ok() const
Are all coefficients large enough to mitigate rounding error?
Loop-filter coefficients for use with the "ControllerPI" class.
constexpr CoeffPI(double tau_secs)
Calculate tracking-loop coefficients.
bool ok() const
Are all coefficients large enough to mitigate rounding error?
static constexpr unsigned SCALE
Fixed-point scaling of each coefficient by 2^-N.
Loop-filter coefficients for use with the "ControllerPII" class.
static constexpr unsigned SCALE2
Fixed-point scaling of each coefficient by 2^-N.
static constexpr unsigned SCALE
Fixed-point scaling of each coefficient by 2^-N.
bool ok() const
Are all coefficients large enough to mitigate rounding error?
static constexpr unsigned SCALE1
Fixed-point scaling of each coefficient by 2^-N.
constexpr CoeffPII(double tau_secs)
Calculate tracking-loop coefficients.
Stateless linear regression calculator.
s64 extrapolate(s64 x) const
Extrapolate relative to the most recent sample.
satcat5::util::int128_t beta
Parameters for the best-fit line.
constexpr LinearRegression()
Placeholder constructor.
satcat5::util::int128_t alpha
Parameters for the best-fit line.
static constexpr unsigned TSCALE
Parameters for the best-fit line.
constexpr double pow2d(unsigned n)
Calculate 2^N for very large N, returning a double.