From aa8ec1794a97931a77c9f97e59527d9702fb530a Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Thu, 8 Oct 2026 11:39:22 +0200 Subject: [PATCH] fix: a zero-width band fires on equality or not at all, and the text now says which The earlier correction moved the claim rather than fixing it: four places still said the float-equality test "never fires on a real-valued signal", and a +/-1 square is a real-valued signal -- a standalone check against a band of [1, 1] fired 19 times, because `inside` is true at +1 and false at -1, so the signal changes side on every transition. All four now state the measured behaviour: an exact float-equality test against the level, which an ordinary signal never satisfies and a signal sitting at the level satisfies on every transition. Neither answer is a band, which is the reason the second spin is clamped an increment away rather than the detector special-cased -- the same conclusion, now resting on a true premise. Co-Authored-By: Claude Opus 4.6 --- README.md | 7 +++++-- src/condition.cpp | 7 +++++-- src/help/08-trigger.txt | 6 ++++-- src/ui.cpp | 7 ++++--- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a5660dc..dbb301c 100644 --- a/README.md +++ b/README.md @@ -687,8 +687,11 @@ modes are unchanged, and only the question "did an event happen at this sample" because that is the first sample at which the width is known. `<` a width shorter than the signal's own pulses is the glitch trigger: nothing but a runt satisfies it. - **window** — the signal entering or leaving a band between two levels. A band is two levels, - so the panel refuses a zero-width one: it reduces the condition to a float-equality test, - which on a real-valued signal never fires. + so the panel refuses a zero-width one: it reduces the condition to an exact float-equality + test against the level, which an ordinary signal never satisfies and a signal sitting *at* + the level satisfies on every transition — a ±1 square against a band of `[1, 1]` fired 19 + times in a standalone check. Neither answer is a band, which is why the second spin is + clamped an increment away rather than the detector special-cased. - **dropout** — the *absence* of a crossing for longer than a timeout. One event per outage: the condition re-arms only when a crossing returns. diff --git a/src/condition.cpp b/src/condition.cpp index 7a7681a..2d3c13e 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -119,8 +119,11 @@ double EventDetector::feed_window(double value, double t) noexcept { if (inside == inside_) return kNoEvent; inside_ = inside; // The event is the *change of side*, so `Rising` reads as "into the band" and `Falling` - // as "out of it". A band of zero width reduces the condition to an equality test on a - // real-valued signal, which is a float-equality test and therefore unusable: the panel refuses one. + // as "out of it". A band of zero width is not a band: `inside` reduces to an exact + // float-equality test against the level, so the condition fires on none of an ordinary + // signal's samples and on every transition of one that sits exactly at the level -- a + // +/-1 square against a band of [1, 1] fired 19 times in a standalone check. Neither + // answer is a band, so the panel refuses one. const bool fired = (cond_.edge == TriggerEdge::Rising && inside) || (cond_.edge == TriggerEdge::Falling && !inside) || cond_.edge == TriggerEdge::Both; diff --git a/src/help/08-trigger.txt b/src/help/08-trigger.txt index 05891f9..4ca69c3 100644 --- a/src/help/08-trigger.txt +++ b/src/help/08-trigger.txt @@ -34,8 +34,10 @@ share. Arm starts watching; a arms and disarms from the keyboard behaviour. Pulse width — a crossing and its return, qualified by how long the excursion lasted: less than, greater than, within or outside a pair of widths; the event is timed at the closing edge. Window — the signal entering or leaving a band between two levels; the - panel keeps the two levels at least one increment apart, because a zero-width band reduces the - condition to a float-equality test that never fires on a real-valued signal. Dropout — + panel keeps the two levels at least one increment apart, because a zero-width band is not a + band: the condition becomes an exact float-equality test against the level, which an ordinary + signal never satisfies and a signal sitting at the level satisfies on every transition. + Dropout — the absence of a crossing for longer than a timeout; one event per outage, re-arms when a crossing returns. • The shape's own rows are the only ones shown; the rest are hidden, so the panel never offers diff --git a/src/ui.cpp b/src/ui.cpp index 40fe6b0..29bce0f 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -6983,9 +6983,10 @@ GtkWidget* build_trigger_panel(App& app) { app.trigger_level2_spin = spin(-1.0e12, 1.0e12, 0.01, 4); gtk_widget_set_tooltip_text(app.trigger_level2_spin, "the band's other edge; it is kept at least one increment away " - "from the level, because a band of zero width reduces the " - "condition to a float-equality test, which on a real-valued " - "signal never fires"); + "from the level, because a band of zero width is not a band: the " + "condition becomes an exact float-equality test against the level, " + "which an ordinary signal never satisfies and a signal sitting at " + "the level satisfies on every transition"); app.trigger_level2_row = labeled_box("to level", app.trigger_level2_spin); gtk_box_append(GTK_BOX(box), app.trigger_level2_row);