diff --git a/README.md b/README.md index ddd5c3c..a5660dc 100644 --- a/README.md +++ b/README.md @@ -374,6 +374,7 @@ per-signal gain and offset: ```toml window_s = 2 plots = 1 +measure_stats = false [[plot]] signals = ["Ch1", "", "", ""] @@ -384,7 +385,8 @@ gain = 2 offset = 0.5 ``` -It is written (400 ms after the last change, and flushed on close) whenever the +It also stores `measure_stats` (the measurements statistics toggle, a boolean, absent means +`false`). It is written (400 ms after the last change, and flushed on close) whenever the window, the layout, a drop or a calibration changes, and applied once, when the first CONFIG names the signals -- signal *names* are the identity, because a CONFIG renumbers and an enable/disable rebuilds the channel list. Signals the @@ -685,7 +687,8 @@ 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 rather than quietly degrading to an edge. + 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. - **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/docs/claude/invariants-session-and-export.md b/docs/claude/invariants-session-and-export.md index b25c629..88d1a6c 100644 --- a/docs/claude/invariants-session-and-export.md +++ b/docs/claude/invariants-session-and-export.md @@ -9,7 +9,9 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear signals**, **the chosen trace colours** (`[[color]]`, `name` + `rgb = "#RRGGBB"` — only the *overrides*, so a stream's new signals still take the positional palette and a saved session does not freeze a colour that is meant to follow the signal list), **the math channels with their - graphs** and **the measurements pane**. Signal identity + graphs**, **the measurements pane**, and **`measure_stats`** — the measurements statistics + toggle; written as a bare top-level boolean (`measure_stats = true/false`), defaults to `false` + when absent. Signal identity is the *name* throughout (a CONFIG renumbers channels), 'enabled' is read off the channels the model built -- an enabled signal is exactly one that produced a channel -- and `apply_layout()` installs the graphs **before** assigning the rows, since a row may hold a math channel's name and diff --git a/docs/claude/probe-readings.md b/docs/claude/probe-readings.md index a0f5942..c01b233 100644 --- a/docs/claude/probe-readings.md +++ b/docs/claude/probe-readings.md @@ -600,32 +600,40 @@ Four legs, selected by `UDPSCOPE_PROBE_CONDITION_SHAPE`. Every leg prints one li [condition-probe] type=T cmp=C width=W t0=T0 measured=M at0=A earlier=E level2=L2 mode=D ``` -Measured readings (`--rate 10000 --freq 5`, each on a fresh port): +Measured readings (`--rate 10000 --freq 5`, each on a fresh port). Shape 0's `level2=` and +`mode=` fields are from the `[search-probe]` run in `.superpowers/sdd/progress.md`; shapes 1–3 +were measured before those fields were added and their `level2=0.500000 mode=2` is read from +the probe arm code (`condition_probe_arm` always sets `level2` to `0.5` and the mode after a +latch is `Held=2`): ``` -shape=0 type=1 cmp=0 width=0.010000 t0=1.842100 measured=0.002000 at0=nan earlier=nan -shape=1 type=1 cmp=1 width=0.010000 t0=2.160100 measured=0.200000 at0=nan earlier=nan -shape=2 type=2 cmp=0 width=0.010000 t0=1.706700 measured=nan at0=-0.500907 earlier=-0.498185 -shape=3 type=3 cmp=0 width=0.010000 t0=4.120200 measured=0.300100 at0=nan earlier=nan +shape=0 type=1 cmp=0 width=0.010000 t0=1.710000 measured=0.004000 at0=nan earlier=nan level2=0.500000 mode=2 +shape=1 type=1 cmp=1 width=0.010000 t0=2.160100 measured=0.200000 at0=nan earlier=nan level2=0.500000 mode=2 +shape=2 type=2 cmp=0 width=0.010000 t0=1.706700 measured=nan at0=-0.500907 earlier=-0.498185 level2=0.500000 mode=2 +shape=3 type=3 cmp=0 width=0.010000 t0=4.120200 measured=0.300100 at0=nan earlier=nan level2=0.500000 mode=2 ``` `measured` is the reading, not a phase. It is the pulse width read back from the held snapshot's own stamps, which are edge-relative: the snapshot is the ground truth, not the mock's absolute clock. Three things are worth knowing before calling a reading wrong: -- **Leg 0**: `measured=0.002` is the injected glitch width. A `measured` near the signal's own - half-period means the width qualifier did not qualify — the trigger caught a normal edge instead - of the runt. +- **Leg 0**: `measured=0.004` is the injected glitch width (here the search-probe run used + `--glitch-width 0.004`; the earlier standalone probe run read `0.002` with `--glitch-width 0.002`). + A `measured` near the signal's own half-period means the width qualifier did not qualify — the + trigger caught a normal edge instead of the runt. - **Leg 1**: `measured=0.200` is an ordinary square half-period; every square pulse satisfies - `> 0.010`. + `> 0.010`. **Use a 1.0 s window with 50 % pre** so the opening edge of a 0.2 s pulse lies within + the pre window. - **Leg 2**: `at0` must lie **outside** `[-0.5, +0.5]` and `earlier` **inside** it — those are the two sample values around the band crossing. The probe prints `nan` for any field a leg cannot measure. **This leg uses `sine`**, not `square`: the square is exactly ±1 and has no sample - inside the `[-0.5, +0.5]` band, so a window condition on the square can never fire. **Use a - 1.0 s window with 50 % pre** or the probe cannot reach the opening edge of a 0.2 s pulse. + inside the `[-0.5, +0.5]` band, so a window condition on the square can never fire. This leg + uses a **0.2 s window with 10 % pre** — short enough to capture the crossing, because the event + is the band exit, which lands at x = 0 in the held snapshot. - **Leg 3**: `measured=0.300` is the dropout timeout. **This leg also uses `sine`**. A timeout of 0.050 s against a sine crossing zero every 0.1 s latches on a healthy signal, so use 0.300 s - or longer; **use a 1.0 s window with 50 % pre** as for leg 2. + or longer; **use a 1.0 s window with 50 % pre** so the last crossing before the outage lies + within the pre window. The channel-selection line `[condition-probe] = of channels` is printed before the trigger fires; if the channel is not found this reads `-1` and the probe fails immediately. @@ -664,7 +672,9 @@ phase=2 … n=1 mean=-4.84467e-14 | n=1 mean=0.63662 | n=1 mean=2 | n=1 mean=1 ## `[search-probe]` -`UDPSCOPE_PROBE_SEARCH_MS`, scheduled after `UDPSCOPE_PROBE_CONDITION_MS`. Run with +`UDPSCOPE_PROBE_SEARCH_MS`. The handler calls `condition_probe()` directly — the condition is +set in exactly one place for both the live trigger and the search, so `UDPSCOPE_PROBE_CONDITION_MS` +must **not** also be set (two arms on the same port race). Run with `--rate 2000 --batch 64 --freq 5 --glitch-at 0.25 --glitch-width 0.004` and `UDPSCOPE_EXIT_AFTER_MS=12000`. diff --git a/docs/claude/verification.md b/docs/claude/verification.md index 8c6aa1f..ee90954 100644 --- a/docs/claude/verification.md +++ b/docs/claude/verification.md @@ -180,9 +180,10 @@ precedent, not an exception. - `UDPSCOPE_PROBE_CONDITION_MS` — arms a qualified trigger against the mock and reports the latched `t0`, the measured pulse or band width, and sample values for the window condition. Schedules its own arm and report stages. `UDPSCOPE_PROBE_CONDITION_SHAPE` picks the leg: `0` pulse width `<` - (the glitch, the default, needs `--glitch-at`/`--glitch-width`), `1` pulse width `>`, `2` window - (uses `sine` with a 1.0 s window and 50 % pre), `3` dropout (needs `--quiet-at`; also uses - `sine` with a 1.0 s window and 50 % pre). + (the glitch, the default, needs `--glitch-at`/`--glitch-width`), `1` pulse width `>` (uses a + 1.0 s window and 50 % pre so the opening edge of a 0.2 s pulse is captured), `2` window + (uses `sine` with a 0.2 s window and 10 % pre), `3` dropout (needs `--quiet-at`; uses `sine` + with a 1.0 s window and 50 % pre so the last crossing before the outage is captured). - `UDPSCOPE_PROBE_STATS_MS` — reports every measurement's accumulator (`n`, mean, sd, lo, hi), pauses, reports again with `dn=` per measurement, then resets and reports a third time. Three phases: `phase=0`, `phase=1` (after pause) and `phase=2` (after Reset). Needs diff --git a/src/help/08-trigger.txt b/src/help/08-trigger.txt index f4f0ebf..05891f9 100644 --- a/src/help/08-trigger.txt +++ b/src/help/08-trigger.txt @@ -1,13 +1,16 @@ Trigger -The trigger watches one signal for an edge through a level and captures a window around it. +The trigger watches one signal for a condition and captures a window around it. The +Shape dropdown chooses what counts as an event; the arming, hold-off, pre/post window +and Normal/Single mode are the same for all four shapes. The captured window is frozen on screen until you re-arm. How to reach it The trigger button in the toolbar (t) reveals the trigger panel: the signal, the level, -the edge (rising, falling, either), the mode (Normal or Single) and the pre-trigger share. -Arm starts watching; a arms and disarms from the keyboard. +the shape, the edge (rising, falling, either), the mode (Normal or Single) and the pre-trigger +share. Arm starts watching; a arms and disarms from the keyboard. +Search the buffer is at the foot of the same panel. Worth knowing @@ -27,6 +30,21 @@ the edge (rising, falling, either), the mode (Normal or Single) and the pre-trig • The trigger reads the stored signal — decimated and averaged — not the wire signal. A spike narrower than one decimation group is filtered away rather than triggering on data the buffer does not contain. +• The four shapes: edge — a crossing of the level in the chosen direction, the classic + 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 — + 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 + a pulse width to a window trigger. +• Search the buffer runs the same condition over the whole retained ring and lists every + place it holds. The arrows step through hits; each step pauses on the hit, centred, exactly as + the Pause button does. Up to 16 hits in the visible window are marked on every row; when there + are more the panel says how many and suggests zooming in. The search uses the trigger's own + condition, set in one place, so a hit is by construction an instant the trigger would have fired. Example diff --git a/src/help/10-measurements.txt b/src/help/10-measurements.txt index bb08408..71ac67e 100644 --- a/src/help/10-measurements.txt +++ b/src/help/10-measurements.txt @@ -45,6 +45,12 @@ set, with Save and Delete. small value is 0.00. • Cost is bounded by reading the summary pyramid the producer already maintains, not the raw window. The values are still exact: a bucket's sum is a sum. +• Statistics adds a second line to every measurement showing n, the mean, the + standard deviation and the range. Each observation is one recompute of the pane, so + n counts windows measured, not samples — a mean frequency over many windows is what + that claims to be. Reset clears every accumulator and starts fresh. A change of display + window, a CONFIG or a measurement edit also resets; a pause, a resume, a zoom, a pan and the + toggle itself do not, so turning the line off to read the plot and back on again keeps the history. Example