feat(udpscope): producer-clock calibration and hrt tick-rate fit
Adds TimeBase.h/cpp with ClockOffset (latched wall-clock offset with one-sided recalibration on positive drift only, so early-arriving packets do not wobble the trace) and HrtRateFit (sliding-window OLS that recovers an unknown hrt tick rate from receive timestamps). Also adds TimeSignalScale() which maps UDPS type codes to seconds-per-count. 9 new tests, all 28 pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
ea9689591d
commit
41ab151a2f
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @file TimeBase.h
|
||||
* @brief Producer-clock to wall-clock reconstruction.
|
||||
*
|
||||
* Framework-free. A UDPS stream's accurate timestamps come from a producer
|
||||
* clock — either a declared time signal or the packet's embedded hrt — and both
|
||||
* need mapping onto the client's wall clock before they can be plotted.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
|
||||
namespace udpscope {
|
||||
|
||||
/**
|
||||
* @brief Seconds per count of a time signal, from its type code.
|
||||
*
|
||||
* The protocol carries uint64 time signals in nanoseconds and everything else
|
||||
* in microseconds; this mirrors UDPSourceSession so the two agree on a stream.
|
||||
*/
|
||||
double TimeSignalScale(uint8_t typeCode);
|
||||
|
||||
/**
|
||||
* @brief A latched producer-to-wall offset.
|
||||
*
|
||||
* Established from the first sample and then held, so network jitter does not
|
||||
* wobble the trace. Only a drift beyond kRecalibThresholdS — a producer restart
|
||||
* or re-phase, not delivery noise — forces a new calibration.
|
||||
*/
|
||||
class ClockOffset {
|
||||
public:
|
||||
static constexpr double kRecalibThresholdS = 0.5;
|
||||
|
||||
/** @return producerSec mapped onto wall clock. */
|
||||
double map(double producerSec, double wallSec);
|
||||
|
||||
bool valid() const { return valid_; }
|
||||
void reset() { valid_ = false; offset_ = 0.0; }
|
||||
double offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
double offset_ = 0.0;
|
||||
bool valid_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Recovers the producer's hrt tick rate by least squares against arrival
|
||||
* time.
|
||||
*
|
||||
* The protocol does not carry the tick rate, and StreamHub's approach of using
|
||||
* the local MARTe HighResolutionTimer frequency is only valid when the client
|
||||
* runs on the producer's host. A remote bench scope cannot assume that, so the
|
||||
* rate is measured: hrt against recv_time is a straight line whose slope is
|
||||
* ticks per second.
|
||||
*/
|
||||
class HrtRateFit {
|
||||
public:
|
||||
static constexpr size_t kMinSamples = 32;
|
||||
static constexpr size_t kWindow = 256;
|
||||
|
||||
void add(uint64_t hrt, double wallSec);
|
||||
bool ready() const { return n_ >= kMinSamples && rate_ > 0.0; }
|
||||
double ticksPerSecond() const { return rate_; }
|
||||
double toSeconds(uint64_t hrt) const;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
void refit();
|
||||
|
||||
struct Sample { double hrt; double wall; };
|
||||
std::deque<Sample> samples_;
|
||||
size_t n_ = 0;
|
||||
double rate_ = 0.0;
|
||||
};
|
||||
|
||||
} /* namespace udpscope */
|
||||
Reference in New Issue
Block a user