From c89decef8ed03844c93dc3d54df22b7b5611deaf Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Thu, 27 Aug 2026 19:55:24 +0200 Subject: [PATCH] docs: say that HrtRateFit::toSeconds returns producer-epoch, not wall, seconds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fit keeps the slope and discards the intercept, so the result counts from the producer's boot. Tasks 4 and 7 compose it with ClockOffset::map, which is correct, but the bare name invites passing it straight to a plot axis. Also unwrapped the stalled-clock assertion from behind `if (fit.ready())` — that branch never runs, so the test confirmed nothing. Co-Authored-By: Claude Opus 4.6 --- Client/udpscope/TimeBase.h | 9 +++++++++ Client/udpscope/tests/TimeBaseTest.cpp | 10 ++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Client/udpscope/TimeBase.h b/Client/udpscope/TimeBase.h index 260618e..513f6d6 100644 --- a/Client/udpscope/TimeBase.h +++ b/Client/udpscope/TimeBase.h @@ -63,6 +63,15 @@ public: void add(uint64_t hrt, double wallSec); bool ready() const { return n_ >= kMinSamples && rate_ > 0.0; } double ticksPerSecond() const { return rate_; } + /** + * @brief Converts a tick count to seconds on the PRODUCER's own epoch. + * + * The fit recovers the slope only and discards the intercept, so this is + * `hrt / ticksPerSecond()` — not a wall-clock time. A producer's hrt counts + * from its own boot, not from the Unix epoch. Pass the result to + * ClockOffset::map() to land it on the wall clock; latching that arbitrary + * epoch difference is precisely what ClockOffset is for. + */ double toSeconds(uint64_t hrt) const; void reset(); diff --git a/Client/udpscope/tests/TimeBaseTest.cpp b/Client/udpscope/tests/TimeBaseTest.cpp index 53bd6f1..d612673 100644 --- a/Client/udpscope/tests/TimeBaseTest.cpp +++ b/Client/udpscope/tests/TimeBaseTest.cpp @@ -102,10 +102,12 @@ TEST(HrtRateFit, SurvivesAStalledClock) { for (int i = 0; i < 64; i++) { fit.add(12345u, 1000.0 + i * 0.01); /* hrt never advances */ } - /* A degenerate fit must not produce a rate that would divide by zero. */ - if (fit.ready()) { - EXPECT_GT(fit.ticksPerSecond(), 0.0); - } + /* A degenerate fit must not produce a rate that would divide by zero, so it + * must decline to be ready at all. Guarding this behind `if (fit.ready())` + * would make the test vacuous: the branch never runs and a fit that + * declared itself ready with a rate of 0 or NaN would pass unnoticed. */ + EXPECT_FALSE(fit.ready()); + EXPECT_DOUBLE_EQ(fit.ticksPerSecond(), 0.0); } TEST(TimeSignalScale, UsesNanosecondsForUint64AndMicrosecondsOtherwise) {