feat: a measurement accumulates how it varies, so one reading can be told from a trend
This commit is contained in:
1 parent
d79810c7ba
commit
1a8218887c
2 files changed
+79
-8
No files matched your search
@@ -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
|
||||
Reference in new issue
Block a user