measure: the split's own means are back as above/below/between
`top`, `bottom` and `amplitude` became the *flat levels* in e83ad86 -- the level the
signal dwells at, read from the density of the window's buckets -- and that change kept
the split's own two means as its internal fallback for a window the pyramid cannot
summarize. Those means are now three types of their own, so the two different questions
about one split can be asked side by side: `above %` and `below %` are the plain mean of
the samples on one side of a share -- the reading the old `top`/`bottom` gave -- and
`between % %` is the mean of the band between two shares, which no single split can
answer, because a band's two edges are two thresholds.
Nothing about the arithmetic is new, and nothing is a new pass. `Above`/`Below` are the
`sum[]`/`count[]` split `measure_accumulate()` already folds -- the very figures
`Top`/`Bottom` fall back to -- and `Between` is the **difference of two levels' upper
sides**, both of which come out of one pass two over one window. It rides the bucket
fast path unchanged: a bucket wholly inside the band is strictly above the lower level
and strictly below the upper one, so both accumulators fold it whole, and only the
buckets that straddle either edge are opened. `measure_level_pcts()` is the one place the
shares are read, and it clamps both and puts `between`'s pair **in order**, so "between
80 % and 20 %" is the band between 20 % and 80 % -- normalized rather than refused,
because the shares name two edges rather than a direction. Nothing to measure reads `--`,
as for every other type: a share at 100 % has no sample above it, and a band can hold
none at all.
Measured, `[bandavg-probe]`, fast mock at a 2 s window (`--rate 1300000 --batch 1000
--freq 1`):
sine `above 50` 0.636620 (2/pi), `below 50` -0.636620, `between 50 75` 0.255873
square `above 50` 0.986496 (30 harmonics) / 1.000000 (--square-harmonics 0),
`between 50 75` 0.302744 / --
ramp `between 50 75` 0.250000 and `above 50` 0.500000, `above 100` --
The first three are the figures the old `top`/`bottom` documented; the ramp's are what a
uniform density predicts (a band's mean is its own centre); the ideal square's empty band
is the nothing-to-measure rule. The pane offers the types and edits their percentages
through the dialog's second share -- which only `between` shows -- and a measurement's
pair survives add/edit/delete and the session file (`threshold2`), where a file written
before it existed loads at the default.
No raw pass: with the measurements probe adding one of every type at a 2 s window, `raw`
went from 113966 of 240719031 covered (0.0%) to 134978 of 255372033 (0.1%), 0.8 -> 1.1
ns a covered sample.
Docs: the band-average invariant (and the two assertions the flat-level change moved),
the probe readings for the new probe, the verification recipe, the manual's measurements
page and the README.
This commit is contained in:
1 parent
e83ad86b8f
commit
22e7a8efcc
14 files changed
+769
-98
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Invariants: the measurements pane
|
||||
|
||||
Why the pane is refreshed on a cadence, why neither pass may read its window raw, and how a crossing is counted.
|
||||
Why the pane is refreshed on a cadence, why neither pass may read its window raw, how a crossing is counted, and which mean over a split is which.
|
||||
|
||||
Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to learn; read the ones touching the area you are editing before you edit it.
|
||||
|
||||
@@ -66,9 +66,10 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
* **Exactness is the point, so nothing may stride or decimate.** `peak to peak` must still read
|
||||
exactly `2` on a unit sine, and it and `average` still come straight out of the one shared
|
||||
`measure_stats()` pass. A bucket's sum is a sum. (`top` has no *exact* reading to protect any
|
||||
more: it is a density estimate and not a mean, and the two places that used to assert
|
||||
`top == 0.636620` on a sine now assert what the flat level actually reads -- see the flat-level
|
||||
invariant below. Everything above is about the *bucket* path being a fast path and not an
|
||||
more: it is a density estimate and not a mean, so the assertion that used to be
|
||||
`top == 0.636620` on a sine now belongs to `above 50`, which *is* a mean over a side and reads
|
||||
exactly 2/pi again -- see the band-average invariant below, and the flat-level one for what
|
||||
`top` reads instead. Everything above is about the *bucket* path being a fast path and not an
|
||||
approximation, and that is unchanged.)
|
||||
* **The bucket index space must be the window's.** `ChannelBuffer::acquire()` calls
|
||||
`pyramid_.drop_before(values_->base())`, so a channel's pyramid is in the value ring's
|
||||
@@ -139,6 +140,60 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
`top=0.973824 bottom=-0.991731 amplitude=2.354173 peak to peak=2.354173`, `buckets=0
|
||||
raw=200.0%` -- two raw walks and no density estimate, which is the honest reading of a window
|
||||
the pyramid cannot summarize.
|
||||
- **`Above`/`Below`/`Between` are the *means over a side of a share*, and they are the readings
|
||||
`Top`/`Bottom` gave before a flat level replaced them.** That change kept the split's two means as
|
||||
its *internal fallback* for a window the pyramid cannot summarize (above); these three types are
|
||||
that fallback, exposed and named, which is what lets the two different questions about one split be
|
||||
asked side by side -- on a unit sine `above 50` reads `0.636620` (2/pi) where `top` reads
|
||||
`0.957988`, and on the mock's band-limited square `above 50` reads `0.986496` where the plateau is
|
||||
`1.000000` and `top` `0.999983`: the edge's ring is *inside* this mean and not inside that level.
|
||||
Five rules, and the first two are what a reader is most likely to assume wrongly:
|
||||
* **A band is not a wider split, and needs no walk of its own.** `Above`/`Below` are
|
||||
`MeasureLevel::sum[1]`/`count[1]` and `sum[0]`/`count[0]` at the measurement's own share.
|
||||
`Between` is the **difference of two levels' upper sides** -- `sum[1](lo) - sum[1](hi)` over
|
||||
`count[1](lo) - count[1](hi)`, i.e. the samples above the lower edge and *not* above the upper
|
||||
one -- and no single level's two sums can bound a band, because a band's two edges are two
|
||||
thresholds. Both accumulators come out of one pass two over one window (the pane registers one
|
||||
level per edge, `w.pct`, and both are deduplicated into it), so the difference is exact rather
|
||||
than approximate. `v == level` is the below side, as everywhere, so the band is `(lo, hi]`.
|
||||
* **It rides the bucket fast path unchanged.** A bucket wholly inside the band is strictly above
|
||||
the lower level *and* strictly below the upper one, so both accumulators fold it whole; only the
|
||||
buckets that straddle either edge are opened, exactly as for one level. `measure cost:` is the
|
||||
guard: adding all three types to a pane of every type left `raw` at `0.1%` of `covered`
|
||||
(`113966` of `240719031` with the ten types, `134978` of `255372033` with the thirteen), and a
|
||||
band is *two entries of the level list*, not a second pass.
|
||||
* **`measure_level_pcts()` is the only place the shares are read**, and it clamps both to `[0,100]`
|
||||
and puts `Between`'s pair **in order**: "between 80 % and 20 %" is the band between 20 % and
|
||||
80 %. Normalized rather than refused because the shares name two *edges*, and the band does not
|
||||
depend on which the user typed first; the pane's dedup key and both accumulators are keyed on the
|
||||
ordered pair, so one band is one walk however it was written.
|
||||
* **Nothing to measure reads `--`**, exactly as for every other type: a side holding no sample (a
|
||||
share at `100 %`, whose level is the window's own maximum) and an *interior* holding none are
|
||||
both `invalid`/`--` and never `0`. The ideal `--square-harmonics 0` square is where the second
|
||||
shows -- every sample is exactly +/-1, so the band `(0, 0.5]` holds nothing -- against the same
|
||||
band on the band-limited square, which holds the edges' transitions (`between 50 75` =
|
||||
`0.302744`, `between 25 75` = `0.000000`: the symmetric band's two halves cancel, the transition
|
||||
being odd about its own midpoint).
|
||||
* **The reading is a mean over the window's *samples*, so its sixth decimal follows the window's
|
||||
boundary.** A live window is closed at both ends, so its oldest and newest sample share a phase
|
||||
and the mean carries one sample that is not in an open interval -- about one unit in the sixth
|
||||
decimal out of 2.6 M samples. Measured over one fast-mock run, `[measure-trace]`: `above 50` on
|
||||
the sine printed `0.636620` on 48 lines and `0.636621` on 2, `below 50` `-0.636620` on 37 and
|
||||
`-0.636619` on 18, `above 50` on the square `0.986496` on 32 and `0.986495` on 18, and
|
||||
`between 50 75` on the ramp `0.250000`/`0.250001`/`0.249999`. The old `Top`/`Bottom` spread the
|
||||
same way -- it is a property of a mean over a finite window, not of these types, and a reading
|
||||
that has to be exact to six figures is read off `[bandavg-probe]`'s nine.
|
||||
Measured on the fast mock, 2 s window, the mock's default 30-harmonic square, `[bandavg-probe]`
|
||||
(nine significant digits, then what the pane prints):
|
||||
`above 50` on the sine `0.636620262` -> `636.62 mV`; `below 50` on the sine `-0.636619283`;
|
||||
`between 50 75` on the sine `0.255873597` -- the analytic `3(2-sqrt 3)/pi = 0.255873`, which is
|
||||
neither the window's mean (`0`) nor the band's edge (`0.5`); `above 50` on the square
|
||||
`0.986494746` / `0.986495505` (one or two samples at the window's end); `between 50 75` on the
|
||||
`ramp` `0.250000385` and `above 50` `0.500000` -- a uniform density makes every band's mean that
|
||||
band's own centre, so a quarter and a half of the range are the shape's prediction and not a
|
||||
coincidence; `above 100` on the `ramp` `--`; and the ideal square's `between 50 75` `--`.
|
||||
Editing the sine's band through the dialog to `50/90` takes it to `0.431361697`
|
||||
(`0.8 / (2 asin 0.8)`), which is the check that the second share is the one the engine reads.
|
||||
- **`MeasureOptions::noise_pct` is a percentage, clamped in `measure_set_level()` to half the room to the
|
||||
nearer extreme**, so a band can never invert its own thresholds. It moves the **edge decision only**: the
|
||||
sums still split at the bare level, which is the *fallback* `Top`/`Bottom` read on a window with no
|
||||
@@ -170,12 +225,13 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
decides that side and the measurement reads nothing at all, which is worse than reading
|
||||
something noisy. On a ringing 3.3 V logic signal at a 90 % split: `enters=0` with a fixed band,
|
||||
`enters=4 period=0.200000` with the clamp.
|
||||
* **The `sum`/`count` split stays at the bare `level`.** Two things read it and neither may be
|
||||
* **The `sum`/`count` split stays at the bare `level`.** Four things read it and none may be
|
||||
moved by a band: a crossing's *side* is decided by the sample's own value against the level
|
||||
(`v == level` is the below side, as it always was), and `Top`/`Bottom` fall back to the mean of
|
||||
each side on a window with no bucket histogram. The band is about *deciding a crossing*, not
|
||||
about where a sample lives, so folding it in would change a filtered window's `top` for no gain
|
||||
to any crossing count.
|
||||
(`v == level` is the below side, as it always was), `Top`/`Bottom` fall back to the mean of
|
||||
each side on a window with no bucket histogram, and `Above`/`Below`/`Between` read it on
|
||||
*every* window -- their whole definition is that split, so a band folded into it would move a
|
||||
documented exact reading (2/pi on a sine) for no gain to any crossing count. The band is about
|
||||
*deciding a crossing*, not about where a sample lives.
|
||||
* **`h = 0` must reduce exactly to the single-level walk**, including the `v == level` skip, so the
|
||||
band is a parameter of the engine rather than a mode of it.
|
||||
The cost is the noise's, not the band's: the clean fast mock reads `raw=119412 (0.1%)` of 215 M at
|
||||
|
||||
@@ -42,9 +42,14 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
was parsed into the last calibration record and the enable state was lost without a word.
|
||||
`mkdirs()` used to create only components with a trailing slash, so `~/.config/udpscope` was never
|
||||
made and the *first* save failed silently -- worth knowing when a layout change "does not stick".
|
||||
- **Measurement options persist under *named* keys -- `noise` and `hf` -- so a file written before them
|
||||
loads at the defaults**, which are exactly the behaviour it was written against: `noise = 10` is the
|
||||
hysteresis band the app has always counted with, and `hf = 0` is no filter.
|
||||
- **Measurement options persist under *named* keys -- `threshold`, `threshold2`, `noise` and `hf` -- so a
|
||||
file written before them loads at the defaults**, which are exactly the behaviour it was written against:
|
||||
`noise = 10` is the
|
||||
hysteresis band the app has always counted with, `hf = 0` is no filter, and a file with no
|
||||
`threshold2` -- every file written before `between` existed -- leaves that measurement at
|
||||
`threshold2 = 75`, which is the value the dialog offers a new one. The *pair* is stored as written
|
||||
rather than as ordered: `measure_level_pcts()` is what puts a reversed pair in order on read, so a
|
||||
file records what the user typed and a measurement file round-trips unchanged.
|
||||
- **The math node string is thirteen pipe fields, and the reader accepts twelve *or* thirteen.** `med_n` is
|
||||
the thirteenth, after `y`; a twelve-field node is a file written before the de-noise node existed, and its
|
||||
`med_n` stays at the struct's default -- which is what such a file meant. Field 0 is the op as a
|
||||
|
||||
@@ -218,6 +218,9 @@ Two lines of the self-check output are assertions in disguise:
|
||||
pane_h=H switch_on=X reset_sensitive=Y`
|
||||
(stderr, `UDPSCOPE_PROBE_MEASURES_MS`) — adds one measurement of every type on the first
|
||||
plottable signal and lets the tick compute them, so what it prints is what a user reads.
|
||||
The dump line carries one more field for the one type that reads *two* shares:
|
||||
`measure N: 'sig' between thr=50.0% thr2=75.0% above=1 show=1 -> ok ...`, where `thr2` appears
|
||||
only for `between` (see the band-average probe below).
|
||||
On the test stream's 1 Hz ±1 sine the values must be: average ≈ 0, `top` ≈ 0.96 and
|
||||
`bottom` ≈ -0.96, `amplitude` ≈ 1.92, max/min ≈ ±1, `peak to peak` **exactly 2**,
|
||||
`pulse duration` ≈ 0.5 s
|
||||
@@ -226,7 +229,9 @@ Two lines of the self-check output are assertions in disguise:
|
||||
window rather than from a mean over a side, and a sine has no plateau — so the densest band
|
||||
sits at its peak and the reading is near it, not the 0.636620 a mean over a half would give.
|
||||
`amplitude` is that pair's delta (`top - bottom`), which is why it reads about 1.92 rather than
|
||||
the half-swing it used to: a bench scope's `Vamplitude` is `Vtop - Vbase`. The measured
|
||||
the half-swing it used to: a bench scope's `Vamplitude` is `Vtop - Vbase`. The mean over a half
|
||||
is not gone, though: it is `above 50` on this stream (and `-above 50` is `below 50`), and its
|
||||
own probe asserts it — see `[bandavg-probe]` below. The measured
|
||||
figures, and the reason they are what they are, are in
|
||||
[invariants-measurements.md](invariants-measurements.md) — on the mock's `square` the
|
||||
separation is the visible one (`top`/`bottom` `0.999983`/`-0.999983`, `amplitude` `1.999966`,
|
||||
@@ -275,10 +280,15 @@ Two lines of the self-check output are assertions in disguise:
|
||||
(`an_unselectable_cursor_is_neither_hovered_nor_grabbed`) is the other half. The probe's last
|
||||
step still drags a mark *programmatically* and requires the next frame to put it back
|
||||
(`restored=1`), which now checks the placement path rather than the read-only property. The dialog report's `type= thr=
|
||||
side= show=` is the per-type behaviour: `period` and `frequency` must show **neither** the
|
||||
band= side= show= shares=A/B` is the per-type behaviour: `period` and `frequency` must show **neither** the
|
||||
split nor the side row (they compute from the mid level's rising crossings, and an option
|
||||
there is one the user can only get wrong), `average`/`min`/`max` no split either, and only
|
||||
`pulse duration` a side. A measurement whose signal is not acquired, or
|
||||
`pulse duration` a side. `band` is the *second* share's row, which only `between` shows -- it is
|
||||
the one type whose two edges are both parameters -- and `shares` is what the two spin fields are
|
||||
*showing*, read back out of the widgets: a share that is stored but not seeded into its field, or
|
||||
a field that is built but not read back on apply, is then visible in this line rather than only in
|
||||
the reading it produces. `above`/`below` show the threshold row and no band row, with their own
|
||||
label (`above % of the range`). A measurement whose signal is not acquired, or
|
||||
a window with too few crossings, must read `invalid`/`--` rather than a made-up number
|
||||
(`frequency` on a window holding one edge is the case: `duty` is reported only when a
|
||||
period exists to divide by). The probe then **walks the dialog's lifecycle**: open the edit
|
||||
@@ -1040,6 +1050,70 @@ phase=2 … n=0 mean=0 sd=0 lo=0 hi=0 (all four)
|
||||
- `phase=2` — after Reset. **`n` must be 0 or 1** (0 if the report ran before the next recompute,
|
||||
1 if after); anything larger means Reset cleared nothing.
|
||||
|
||||
## `[bandavg-probe]`
|
||||
|
||||
`UDPSCOPE_PROBE_BANDAVG_MS`. Needs `UDPSCOPE_ENABLE_ALL_MS` ahead of it, and it must **not** be
|
||||
scheduled with `UDPSCOPE_PROBE_MEASURE_MS` or `UDPSCOPE_PROBE_MEASURES_MS`: all three add
|
||||
measurements to the same pane, and the later one would report the earlier one's rows as its own.
|
||||
Run it against the fast mock (`--rate 1300000 --batch 1000 --freq 1`) **at a 2 s window** — the
|
||||
figures below are shares of the range, and a window that holds a fifth of a cycle is a different
|
||||
question, not a worse answer. Its own sequence is about 12 s, so `UDPSCOPE_EXIT_AFTER_MS=17000`.
|
||||
|
||||
It adds seven measurements, one per shape whose band mean can be computed by hand, and every one of
|
||||
them is a *mean over a side or a band* rather than a level. `--square-harmonics 30` and
|
||||
`--square-harmonics 0` are two runs — the second is the ideal step, where the band holds nothing:
|
||||
|
||||
| measurement | shape | reading | why that shape |
|
||||
|---|---|---|---|
|
||||
| `above 50` | 1 Hz unit sine | `0.636620` | 2/pi, the figure the old `top` documented |
|
||||
| `below 50` | 1 Hz unit sine | `-0.636620` | its other side: exactly `-above 50` |
|
||||
| `between 50/75` | 1 Hz unit sine | `0.255873` | `3(2-sqrt 3)/pi`, and neither 0 (the window's mean) nor 0.5 (the band's edge) |
|
||||
| `above 50` | `square`, 30 harmonics / `0` | `0.986496` / `1.000000` | the reading that commit recorded as the *old* `top`; the ideal step has no ring to put inside it |
|
||||
| `between 50/75` | `square`, 30 harmonics / `0` | `0.302744` / `--` | the two edges' transitions the band holds; **every** sample is exactly +/-1 on the ideal step, so it holds nothing |
|
||||
| `between 50/75` | `ramp` | `0.250000` | uniform density: a band's mean *is* that band's centre |
|
||||
| `above 100` | `ramp` | `--` | a share at the top of the range has no sample above it |
|
||||
|
||||
Four line kinds, and the reading is the third:
|
||||
|
||||
```
|
||||
[bandavg-probe] waiting stored=2108000 of 2600000 slots for a 2.000 s window
|
||||
[bandavg-probe] readings window channel=0 lo=-1.000000 hi=1.000000 samples=2600000 levels=2
|
||||
[bandavg-probe] readings above 'sine' shares=50% -> ok 0.636620262 text=[636.62 mV]
|
||||
[bandavg-probe] edit opened band_row=1 shares=50.0/75.0 (as stored)
|
||||
[bandavg-probe] after delete measures=6 -> 5 (want one fewer)
|
||||
```
|
||||
|
||||
- `waiting` — the probe reads nothing until the ring holds a **full window**: a share of a *partial*
|
||||
window is a different number (and one that keeps moving while the ring fills), and the mock
|
||||
delivers about 0.5 MS/s of the 1.3 MS/s it declares, so a ring the probe has just re-enabled needs
|
||||
seconds of wall clock before a 2 s window is there. The wait is read off the store
|
||||
(`stored_slots()` against `window / dt`), not guessed from a stopwatch. **A report taken before it
|
||||
finishes is the one reading this probe can produce that is not the share's** — the settled lines
|
||||
are the ones to quote.
|
||||
- `window` — the window each reading was taken over, per channel: `lo`/`hi` are that window's own
|
||||
extremes (so `lo`/`hi` of the split are visible here), `samples` is what it holds, and `levels`
|
||||
is how many level accumulators the pane registered for that channel. **`between` on the sine makes
|
||||
`levels=2` where `top`/`between` alone would make one**: a band is two entries of the level list,
|
||||
and a second measurement of the same band does not add a third.
|
||||
- the value line — the pane's own numbers, nine significant digits and then the row's formatted text
|
||||
(`text=`), with the shares it was given. `-> invalid` with `text=[--]` is the nothing-to-measure
|
||||
reading (the last two rows of the table). The value carries the measurement's **unit**; the three
|
||||
new types are signal-valued like `top`/`average`, so a sine reads `mV` and a quantity with no unit
|
||||
reads the bare number.
|
||||
- `edit opened` — the dialog's second-share row for a `between`: **`band_row` must be 1** (only
|
||||
`between` offers it) and `shares` must be the pair that was *stored*, read back out of the two
|
||||
spin fields. The probe then sets them to `50/90` and saves, and the next report is the proof the
|
||||
field is wired: the sine's `between` reads `0.431361697` (`0.8 / (2 asin 0.8)`), where neither of
|
||||
its sides would.
|
||||
- `after delete` — the cycle's last leg through the same dialog: **one fewer** measurement, process
|
||||
alive. The two shares are the thing a delete could drop on the floor.
|
||||
- The `[measure-trace]` line the probe starts carries the same five readings every 250 ms, which is
|
||||
where the *sixth* decimal's spread is visible (see the band-average invariant in
|
||||
[invariants-measurements.md](invariants-measurements.md)): `above 50` on the sine reads
|
||||
`0.636620` on almost every line and `0.636621` on a few, `above 50` on the square `0.986496` on
|
||||
most and `0.986495` on the rest. **A line's `--` on a *lagging* trace is the trace's, not the
|
||||
reading's** — the pane is refreshed on a cadence, so quote an earlier line beside its own `view=`.
|
||||
|
||||
## `[search-probe]`
|
||||
|
||||
`UDPSCOPE_PROBE_SEARCH_MS`. The handler calls `condition_probe()` directly — the condition is
|
||||
|
||||
@@ -120,13 +120,45 @@ precedent, not an exception.
|
||||
thing that reads a second time: `top`/`bottom`/`amplitude` fold a histogram out of the **same**
|
||||
fetched bucket list, so they add buckets and not samples (measured: `raw=0.1%` and `2.1%` with
|
||||
the interferer, against `0.1%`/`2.1%` before those types had a flat level, for 0.44→0.50 ms and
|
||||
0.52→0.65 ms a pass -- the buckets are the budget).
|
||||
0.52→0.65 ms a pass -- the buckets are the budget). It is the guard on the band averages for the
|
||||
same reason: `between` is a *second level in the same walk*, not a second pass, and measured on
|
||||
the fast mock at a 2 s window with the measurements probe adding one of **every** type, `raw`
|
||||
stayed at a fraction of a percent of `covered` —
|
||||
`covered=240719031 buckets=3760828 raw=113966 (0.0%)` for the ten types before, against
|
||||
`covered=255372033 buckets=3989978 raw=134978 (0.1%)` for the thirteen after, at 0.8 → 1.1
|
||||
ns/covered sample (`data` 156.85 → 178.38 ms: the pane holds three more measurements and one more
|
||||
level, and both walk the same fetched bucket list).
|
||||
Note the **final** `measurements:` line and `[measure-trace]`'s last lines may read `--`:
|
||||
`UDPSCOPE_EXIT_AFTER_MS` arms the trigger before it dumps, so the dump can land in `Waiting`
|
||||
with nothing bound and the axis at its default. The race-free reading is an earlier
|
||||
`[measure-trace]` line, which carries its own `view=`; those lines are also where the
|
||||
level-mark fallback is visible (an invalid pass must still print its `level:` marks).
|
||||
|
||||
- `[bandavg-probe]` (stderr, `UDPSCOPE_PROBE_BANDAVG_MS`) — the **band averages**:
|
||||
`above`/`below`/`between`, the plain means over one side of a share and over a band, which are
|
||||
*different numbers* from `top`/`bottom`'s flat levels (see the band-average invariant in
|
||||
[invariants-measurements.md](invariants-measurements.md)). It adds seven measurements, on the three
|
||||
shapes whose band means can be computed by hand, and the four figures to read are:
|
||||
|
||||
* the 1 Hz unit sine at a **2 s window**: `above 50` = **`0.636620`** (2/pi) and `below 50` =
|
||||
**`-0.636620`** — the exact figures the old `top`/`bottom` documented, and the reason this type
|
||||
exists at all; `between 50 75` = `0.255873` (`3(2-sqrt 3)/pi`), an interior mean no side and no
|
||||
whole-window statistic reports;
|
||||
* the mock's band-limited `square` (30 harmonics, its default): `above 50` = **`0.986496`**, the
|
||||
reading the commit that made `top` a flat level recorded as the *old* `top`;
|
||||
* the mock's `ramp`, whose density is uniform: `between 50 75` = `0.250000` and `above 50` =
|
||||
`0.500000` — a band's mean *is* that band's centre, so the shape predicts the number;
|
||||
* and the two nothing-to-measure readings: `above 100` on the `ramp` and `between 50 75` on
|
||||
`--square-harmonics 0` (whose samples are all exactly +/-1) must be `invalid`/`--`, never 0.
|
||||
|
||||
**Run it at a 2 s window** (the probe waits for a full one before it reads anything — the mock
|
||||
delivers about 0.5 MS/s of the 1.3 MS/s it declares, so a freshly enabled ring needs seconds of
|
||||
wall clock), and **not** with `UDPSCOPE_PROBE_MEASURE_MS` or `UDPSCOPE_PROBE_MEASURES_MS`: all
|
||||
three add measurements to one pane. `UDPSCOPE_EXIT_AFTER_MS=17000` covers its sequence. The
|
||||
command, both mock flavours and the lines are in [probe-readings.md](probe-readings.md):
|
||||
`--square-harmonics 30` for the `0.986496` and the `0.302744`, `--square-harmonics 0` for the
|
||||
`1.000000` and the empty band.
|
||||
|
||||
- `[pan-probe] asked=[..] was=[..]`, `[pan-probe] after=[..] held=D asked=[..]`, then
|
||||
`[pan-probe] zoom asked=[..] after=[..] span=S window=W limited=L`
|
||||
(stderr, `UDPSCOPE_PROBE_PAN_MS`) — **`held` must be 1**: a gesture must not move a window
|
||||
@@ -691,8 +723,10 @@ have run. `--spike-every`/`--spike-amp`/
|
||||
|
||||
The three lines and what each must read are in `probe-readings.md`.
|
||||
|
||||
`UDPSCOPE_PROBE_NOISE_MS`, `UDPSCOPE_PROBE_MEASURE_MS` and `UDPSCOPE_PROBE_MATH_MS` must **not** be scheduled
|
||||
in the same run. The noise and measurement probes both push onto `app.measures`, and the later one would read
|
||||
`UDPSCOPE_PROBE_NOISE_MS`, `UDPSCOPE_PROBE_MEASURE_MS`, `UDPSCOPE_PROBE_MEASURES_MS`,
|
||||
`UDPSCOPE_PROBE_BANDAVG_MS` and `UDPSCOPE_PROBE_MATH_MS` must **not** be scheduled
|
||||
in the same run. The noise, measurement, measurements and band-average probes all push onto
|
||||
`app.measures`, and the later one would read
|
||||
the earlier one's rows as its own; the noise and math probes both write `app.math`, so the later one's graph
|
||||
lands on top of the earlier one's and both sets of readings describe a state neither probe set up. The noise probe carries
|
||||
its own measurement legs (see `probe-readings.md`), so nothing is lost by keeping them apart.
|
||||
Reference in new issue
Block a user