feat: a measurement accumulates how it varies, so one reading can be told from a trend

This commit is contained in:
Martino Ferrari committed 2026-10-08 09:22:35 +02:00
1 parent d79810c7ba
commit 1a8218887c
2 files changed
+79 -8

No files matched your search

+43
View File
@@ -2,7 +2,9 @@
// SPDX-FileCopyrightText: 2026 Martino Ferrari
#pragma once
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <string>
#include <vector>
@@ -244,4 +246,45 @@ bool measure_uses_side(MeasureType type);
/// Whether a type draws its result on the plot when asked.
bool measure_can_show_on_plot(MeasureType type);
/// Running statistics over a measurement's successive results.
///
/// One observation per *recompute* of the measurements pane, never one per sample: a
/// measurement is a figure derived from a whole window, and counting its samples would
/// describe the window rather than the measurement's stability. `n` is therefore a count of
/// passes, which is what makes the readout comparable with a bench scope's.
///
/// The mean and spread are Welford's recurrence rather than running sums of `v` and `v*v`:
/// the results are absolute signal values, so a channel sitting at a large offset with a
/// small ripple -- the case the readout is *for* -- cancels to nothing in a naive
/// sum-of-squares long before the ripple is visible.
struct MeasureStatsAccum {
std::uint64_t n = 0;
double mean = 0.0; ///< Welford's running mean.
double m2 = 0.0; ///< Welford's sum of squared deviations from the running mean.
double lo = 0.0;
double hi = 0.0;
void add(double v) noexcept {
if (n == 0) {
lo = v;
hi = v;
} else {
if (v < lo) lo = v;
if (v > hi) hi = v;
}
++n;
const double delta = v - mean;
mean += delta / static_cast<double>(n);
m2 += delta * (v - mean);
}
void reset() noexcept { *this = MeasureStatsAccum{}; }
/// The sample standard deviation (n-1), and 0 rather than NaN for fewer than two
/// observations: the readout is a figure in a label, and a single pass has no spread.
double sd() const noexcept {
return n < 2 ? 0.0 : std::sqrt(m2 / static_cast<double>(n - 1));
}
};
} // namespace udpscope