test: the noise probe reads a measurement's band and HF reject

Five measurements on the same spiked `sine`, asking for corners 0, 50, 0, 0, 50 on one
channel: two windows under the (channel, fc) key, which is what exercises it.

    measure pkpk bare      noise=10.0 hf=0.0  value=2.987692   the stimulus, impulses included
    measure pkpk hf        noise=10.0 hf=50.0 value=1.990075   the waveform: pass one is filtered
    measure period noband  noise=0.0  hf=0.0  value=0.021290   supposed to be WRONG -- the control
    measure period band    noise=25.0 hf=0.0  value=0.200000   the band alone
    measure period hf      noise=0.0  hf=50.0 value=0.200000   the reject alone
    measure windows=2                                          the corner is part of the key

`pkpk hf` is the reading that says the filter reached *pass one* and not only pass two; a
value near 3 means it did not. `period noband` is deliberately the defect the band exists to
fix, and a future reader who "fixes" it has removed the check.

The band leg asks for **25 %** rather than the 10 % default, and that is arithmetic rather
than a workaround: the band is a share of the *window's* peak-to-peak, which with the
impulses in it is about 3, so 10 % is a half-band of 0.3 -- less than the 0.5 an impulse
moves the signal by. `MeasureOptions::noise_pct`'s own rationale says 10 % holds to a 0.3
interferer, and this mock's is 0.5, so the default could not clear it. The default band is
what the `period noband` and `period hf` legs are about.

verification.md now says the noise and measurement probes must not share a run: both push
onto `app.measures` and the later one would read the earlier one's rows.
This commit is contained in:
Martino Ferrari committed 2026-10-13 15:46:24 +02:00
1 parent 84aebf4116
commit 2c0e21fd20
3 files changed
+84

No files matched your search

+49
View File
@@ -3560,6 +3560,55 @@ gboolean noise_probe(gpointer user) {
"[noise-probe] trigger delay: clean=%zu filtered=%zu delay=%.6f want=%.6f ratio=%.3f\n",
e_leg.hits, f_leg.hits, delay, want, want > 0.0 ? delay / want : 0.0);
std::fflush(stderr);
// Five measurements on the spiked sine, as *two* windows: three legs at `hf=0` and two at `hf=50`. The
// pane keys its walks by (channel, corner), so this is also what checks that a second corner does not
// reuse the first one's walk.
std::string name;
{
std::lock_guard<std::mutex> lock(a->model->mutex());
if (a->model->channels().empty()) return G_SOURCE_REMOVE;
name = a->model->channels().front().name; // `sine`; signal 0 is the timestamp
}
struct Leg {
const char* label;
MeasureType type;
double noise;
double hf;
};
// The impulses dominate the window's extremes, so `peak to peak` reads the *stimulus* (about 3)
// unfiltered and the *waveform* (about 2) filtered -- the one reading that says the filter reached pass
// one and not only pass two.
const Leg legs[] = {
{"pkpk bare", MeasureType::PeakToPeak, 10.0, 0.0},
{"pkpk hf", MeasureType::PeakToPeak, 10.0, 50.0},
{"period noband", MeasureType::Period, 0.0, 0.0},
// 25 % and not the 10 % default: the band is a share of the *window's* peak-to-peak, which with the
// impulses in it is about 3, so 10 % is a half-band of 0.3 -- less than the 0.5 an impulse moves the
// signal by, and the `MeasureOptions::noise_pct` comment says as much ("10 % holds to an interferer
// amplitude of 0.3"). The default is what `period noband` and `period hf` are about; this leg asks for
// a band that clears the stimulus, which is what makes the reading legible.
{"period band", MeasureType::Period, 25.0, 0.0},
{"period hf", MeasureType::Period, 0.0, 50.0},
};
const std::size_t base = a->measures.size();
for (const Leg& leg : legs) {
MeasureOptions options;
options.noise_pct = leg.noise;
options.hf_fc = leg.hf;
a->measures.push_back({name, leg.type, options});
}
rebuild_measures_pane(*a);
update_measures(*a, /*force=*/true, /*accumulate=*/true);
for (std::size_t i = 0; i < std::size(legs); ++i) {
const std::size_t at = base + i;
// A *value*, not a reference: the conditional's other arm is a temporary.
const MeasureResult r = at < a->measure_results.size() ? a->measure_results[at] : MeasureResult{};
std::fprintf(stderr, "[noise-probe] measure %-14s noise=%.1f hf=%.1f valid=%d value=%.6f\n",
legs[i].label, legs[i].noise, legs[i].hf, int(r.valid), r.primary);
}
std::fprintf(stderr, "[noise-probe] measure windows=%zu\n", a->measure_samples.size());
std::fflush(stderr);
a->search.clear();
update_search_panel(*a);
return G_SOURCE_REMOVE;