fix: condition-probe assertion uses snapshot width, not mock-clock phase
The old `phase=` field compared `trigger_time()` against the mock's absolute `glitch_at + glitch_width` target. The app's stream epoch is the first packet it received, not the mock's t = 0; the two clocks differ by however many packets the CONNECT handshake cost, making the comparison meaningless. Replace it with a snapshot-relative measurement: scan backwards from x = 0 in the held event snapshot for the most recent upward crossing of `condition.level` (the same `prev < level && val >= level` test the detector uses) and negate the crossing's stamp to get the measured pulse width. A `measured` near 0.002 s is the glitch caught exactly; near 0.200 s means an ordinary edge was latched. Remove the now-dead `glitch_at`, `glitch_width`, `square_period` fields from `ConditionProbe`. No other files changed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
d147678b1c
commit
0876ecfe22
1 file changed
+49
-15
+49
-15
@@ -8403,17 +8403,26 @@ gboolean fields_probe(gpointer user) {
|
||||
|
||||
/// Headless self-check for the qualified conditions: arms a pulse-width condition on the
|
||||
/// mock's `square`, whose narrow pulse the mock injects once per square period, and reports
|
||||
/// the latched t0 against where the pulse's closing edge must be.
|
||||
/// the measured pulse width from the held event snapshot.
|
||||
///
|
||||
/// The assertion is the *phase* rather than an absolute t0: the probe arms at whatever stream
|
||||
/// time the app happened to reach, so which glitch it catches is not fixed, but every glitch
|
||||
/// closes at `--glitch-at + --glitch-width + k * (2 / --freq)`. A `phase` of a few sample
|
||||
/// periods is the pulse being found exactly; a phase near half the square period means the
|
||||
/// condition fired on an ordinary edge instead.
|
||||
/// The assertion is snapshot-relative rather than an absolute t0. The app's stream clock
|
||||
/// epoch is the first packet it received, not the mock's t = 0; the two clocks differ by
|
||||
/// however many packets the CONNECT handshake cost (typically a whole number of 100-sample /
|
||||
/// 10 ms packets), so comparing trigger_time() against a mock-clock target is meaningless.
|
||||
///
|
||||
/// A held event snapshot's stamps are edge-relative: stamps == 0 is the latching event,
|
||||
/// which for a pulse-width condition is the pulse's *closing* edge. The pulse's opening
|
||||
/// edge therefore sits at stamps ≈ -glitch_width. The probe scans backwards from x = 0 to
|
||||
/// find the most recent upward crossing of level 0 (the same prev < level && value >= level
|
||||
/// test the detector uses) and negates the crossing's stamp to get the measured pulse width.
|
||||
///
|
||||
/// A `measured` near 0.002 s means the condition caught the 2 ms glitch pulse exactly.
|
||||
/// A `measured` near 0.200 s (the ordinary square half-period) means the condition fired on
|
||||
/// an ordinary edge rather than the narrow pulse -- that is the wrong answer to distinguish
|
||||
/// against. If no snapshot is available or no upward crossing is found before x = 0, the
|
||||
/// line still prints with `measured=nan` rather than being skipped, so a failure is still
|
||||
/// one readable line.
|
||||
struct ConditionProbe {
|
||||
double glitch_at = 0.25;
|
||||
double glitch_width = 0.002;
|
||||
double square_period = 0.4;
|
||||
int waited = 0;
|
||||
};
|
||||
ConditionProbe g_condition;
|
||||
@@ -8428,13 +8437,38 @@ gboolean condition_probe_report(gpointer user) {
|
||||
std::fflush(stderr);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
const double want = g_condition.glitch_at + g_condition.glitch_width;
|
||||
double phase = std::fmod(t0 - want, g_condition.square_period);
|
||||
if (phase < 0.0) phase += g_condition.square_period;
|
||||
if (phase > 0.5 * g_condition.square_period) phase -= g_condition.square_period;
|
||||
std::fprintf(stderr, "[condition-probe] type=%d cmp=%d width=%.6f t0=%.6f phase=%.6f mode=%d\n",
|
||||
// Measure the pulse width from the snapshot's own edge-relative stamps rather than
|
||||
// comparing t0 against a mock-clock target. stamps == 0 is the closing edge (the
|
||||
// latching event for a pulse-width condition); the opening edge sits at stamps ≈ -width.
|
||||
// Scan backwards from the last sample at or before x = 0 for the most recent upward
|
||||
// crossing of the condition level, then negate the crossing stamp to get the width.
|
||||
double measured = std::numeric_limits<double>::quiet_NaN();
|
||||
const double level = a->model->trigger().condition.level;
|
||||
const std::size_t trig_ch = static_cast<std::size_t>(a->model->trigger().channel);
|
||||
if (std::shared_ptr<const EventSnapshot> snap = a->model->display_snapshot()) {
|
||||
if (snap != nullptr && trig_ch < snap->values.size()) {
|
||||
const std::vector<f64>& st = snap->stamps;
|
||||
const std::vector<f32>& vals = snap->values[trig_ch];
|
||||
if (vals.size() == st.size() && !st.empty()) {
|
||||
// Find the last index whose stamp is <= 0.
|
||||
const auto it = std::upper_bound(st.begin(), st.end(), 0.0);
|
||||
std::size_t end = static_cast<std::size_t>(it - st.begin());
|
||||
// Scan backwards for the most recent upward crossing (prev < level, value >= level).
|
||||
for (std::size_t i = end; i >= 2; --i) {
|
||||
const double prev = static_cast<double>(vals[i - 2]);
|
||||
const double val = static_cast<double>(vals[i - 1]);
|
||||
if (prev < level && val >= level) {
|
||||
measured = -st[i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::fprintf(stderr, "[condition-probe] type=%d cmp=%d width=%.6f t0=%.6f measured=%.6f mode=%d\n",
|
||||
int(a->model->trigger().condition.type), int(a->model->trigger().condition.cmp),
|
||||
a->model->trigger().condition.width_s, t0, phase, int(a->model->display_mode()));
|
||||
a->model->trigger().condition.width_s, t0, measured,
|
||||
int(a->model->display_mode()));
|
||||
std::fflush(stderr);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user