trigger: a phosphor persistence, driven by the plotter's density image
The trigger pane gains the display the user asked for and the library now provides, and the
persistence it already had is renamed to what it is.
**Persistence is one image; the last-N-traces display is "multiple events".** Both live in a new
`Advanced` expander in the trigger panel, together with the settings that are set up once and left
alone. `persistence` (a switch) turns on the plotter's `Plot::set_density()` for every row:
every triggered acquisition is accumulated into a screen-sized intensity grid drawn behind the
traces, brightness being how many acquisitions covered a cell -- no depth to choose, no memory
that grows with the acquisitions (2.2 MB over four 573x412 rows in the probe's run). `fades by`
picks the fade the user asked for: **acquisitions** (each keeps a factor of what was before it,
0.5 by default -- the phosphor afterglow) or **time** (a time constant in stream seconds, so the
display fades while you watch whatever the trigger rate).
- **Trigger mode only, as asked.** `apply_density()` (the tick) states the display on for `Held`
and `Capturing` and off for `Live`, `Waiting`, `Paused` and a file, and turning it off drops the
grid at once rather than at an acquisition a quiet trigger never sends. It is unpickable and
reserves no layout space, exactly as the invalid-span bands are: a density can never be in the
way of a cursor or a drop, and it cannot move the framing.
- **One deposit per acquisition, one frame after the frame that frames it.** `Plot::accumulate_density()`
deposits what the plot holds through the transforms of its *last* layout -- the frame's own is
computed inside the paint that follows the tick -- so `density_pending` is set beside
`record_pending` and consumed by the next tick. In the same tick it was right on a Held-to-Held
transition and wrong on the first latch, whose axis was still the live ring's. Switching the
display on under an acquisition already on screen deposits it too, so the switch does not appear
to do nothing until the next latch (`single` mode may never send one).
- **The session carries it**: `trigger_density`, `trigger_density_decay` ("acquisitions"/"time",
the words the panel shows), `trigger_density_tau` and `trigger_density_factor`, written whether
or not it is on -- and `trigger_persistence` still means the multiple-event depth, so an older
file is unchanged.
- **The library changed twice under this**: the deposit now counts a cell **once per trace per
sweep** (counting the hits put one dense sweep at the ceiling of the brightness scale and hid the
sweeps behind it -- the probe's `on` leg read `peak=18` for a single sweep and reads `peak=1`
now), and a frame's upload is every row's rather than the last row's. Both are in the plotter's
own commit.
`UDPSCOPE_PROBE_DENSITY_MS` reads it all back off the plots -- the plotter's own `ink()`/`peak()` --
with the panel's own widgets driving the switch and the fade, in four legs: **off** (`rows=0
ink=0.0 peak=0.0000`), **on** (`rows=4`, `peak=1.0000`), **step-acquisitions** (`peak=1.0000 ->
1.5000`, the factor then the new sweep) and **step-time** (`ratio=0.367879`, `1/e` for `dt = tau`).
It also reads the frame cost as an A/B **inside one run** (the accumulators at the switch against
the frames after it): `tick 0.74 -> 9.99 ms`, `draw 2.09 -> 20.91 ms` at four rows and 2.2 M cells
on llvmpipe.
Acceptance: clean build 0 errors and exactly the three documented warnings (12352-byte GResource
blob, two `MathNode::source`), `test-ui` 13/13, `test-help` green, a plain run with **0
Gtk-CRITICAL / Gtk-WARNING** and `gtk warnings: 0`, and the panel's own frame reading
`boxes=16 controls_inside=15/15 advanced_widgets=5/5 advanced_inside=5/5` (the block's controls
counted from the expander's child, which is not in the widget-parent chain -- and the search
block's walk now names *its* expander instead of "the first one", which the second expander would
have stolen).
Docs: the trigger/pinning invariants carry the rule and its five consequences, `probe-readings.md`
the four legs and the dump line, `verification.md` the probe's sequence and assertions, and the
manual's trigger page explains both displays to a user.
This commit is contained in:
1 parent
e14e1ee069
commit
974d6aa2d9
15 files changed
+993
-41
No files matched your search
@@ -13,6 +13,19 @@ namespace udpscope {
|
||||
/// How an armed trigger re-arms after it fired.
|
||||
enum class TriggerMode { Normal, Single };
|
||||
|
||||
/// How a trigger-mode **phosphor density** fades (the plotter's `plot::DensityDecay`, restated here
|
||||
/// so the panel's dropdown, the session file and the model's copy of the setting all name one
|
||||
/// thing). The two answer different questions and the user asked for both: see the longer note on
|
||||
/// `TriggerConfig::density_decay`.
|
||||
enum class PersistDecay {
|
||||
/// Every acquisition multiplies the image by `density_factor` before depositing its own sweep,
|
||||
/// so the N-th acquisition back is the factor to the N. Scales with the trigger rate.
|
||||
Acquisitions,
|
||||
/// Every frame fades the image by `exp(-dt / density_tau_s)` of stream time, so the display
|
||||
/// fades while you watch whatever the trigger rate.
|
||||
Time,
|
||||
};
|
||||
|
||||
/// The trigger panel's configuration (spec item 2.2).
|
||||
///
|
||||
/// The trigger names a *signal* (an index into `ScopeModel::signals()`), not a
|
||||
@@ -34,8 +47,34 @@ struct TriggerConfig {
|
||||
/// dimmer (see `RowSet::persist_alpha()`): 1 -- the default -- keeps nothing but the event on
|
||||
/// screen, which is the display without persistence, and there is deliberately no second switch
|
||||
/// for it.
|
||||
///
|
||||
/// This is the **multiple event** display (the trigger pane's own words for it): a *set of
|
||||
/// retained acquisitions*, one trace per (signal, age). It is not the phosphor density below,
|
||||
/// which is one image and no count -- the two are different displays and the panel offers both.
|
||||
int persist_depth = 1;
|
||||
|
||||
/// The **phosphor density** (the plotter's `Plot::set_density()`): off by default, and trigger
|
||||
/// mode only. Where `persist_depth` keeps N whole acquisitions as N traces, this accumulates
|
||||
/// every acquisition into one screen-sized intensity image -- brightness is how often a trace
|
||||
/// passed a cell, so the cost of a display of a thousand acquisitions is the cost of one, and
|
||||
/// there is no depth to get wrong. The image is drawn behind the traces, it is unpickable and it
|
||||
/// reserves no layout space, exactly as the invalid-span bands do.
|
||||
///
|
||||
/// It applies while an acquisition is on screen -- `Held`, and `Capturing` as it fills -- and
|
||||
/// not on a live view, which is what the user asked for: a live view is the stream, and a
|
||||
/// display of the last few sweeps of it is a *trigger* display. Turning the trigger off (or
|
||||
/// pausing, or opening a file) takes the display away, and a fresh presentation starts from an
|
||||
/// empty image rather than a stale one drawn at another scale.
|
||||
bool density = false;
|
||||
/// Which way it fades (see `PersistDecay`) -- the choice the user asked for, and the panel's
|
||||
/// dropdown. Deliberately one field and not two switches: a density fades one way or the other.
|
||||
PersistDecay density_decay = PersistDecay::Acquisitions;
|
||||
/// `PersistDecay::Time`'s constant, in stream seconds. The step the panel offers is 0.5 s.
|
||||
double density_tau_s = 1.0;
|
||||
/// `PersistDecay::Acquisitions`'s factor. 0.5 -- half per acquisition -- is the schedule a
|
||||
/// phosphor's afterglow has, and the panel does not offer another.
|
||||
double density_factor = 0.5;
|
||||
|
||||
/// Seconds of post-trigger data to collect before latching the event, given
|
||||
/// the buffer's span.
|
||||
double post_span_s(double buffer_span_s) const;
|
||||
|
||||
Reference in new issue
Block a user