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.
281 lines
16 KiB
Plaintext
281 lines
16 KiB
Plaintext
<page xmlns="http://projectmallard.org/1.0/"
|
|
type="topic" style="task"
|
|
id="trigger">
|
|
|
|
<info>
|
|
<link type="guide" xref="index#viewing"/>
|
|
<link type="seealso" xref="pause"/>
|
|
<desc>The trigger's four shapes, arming, forcing an acquisition and hold-off, the event cache with its replay,
|
|
compare and accumulate, and searching the buffer for a condition.</desc>
|
|
<credit type="author copyright">
|
|
<name>Martino Ferrari</name>
|
|
</credit>
|
|
</info>
|
|
|
|
<title>Trigger</title>
|
|
|
|
<p>The trigger watches one signal for a condition and captures a window around it. The
|
|
<gui>Shape</gui> 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.</p>
|
|
|
|
<section id="reach">
|
|
<title>How to reach it</title>
|
|
<steps>
|
|
<item>
|
|
<p>The trigger button in the toolbar (<key>t</key>) reveals the trigger panel: the
|
|
signal, the level, the shape, the edge (rising, falling, either), the mode (Normal or
|
|
Single) and the pre-trigger share.</p>
|
|
</item>
|
|
<item>
|
|
<p><gui>Arm</gui> starts watching; <key>a</key> arms and disarms from the keyboard.
|
|
When the pane is locked with its <gui>[edit]</gui> button — so that it states what the
|
|
trigger is set to instead of showing the controls — <gui>Arm/Disarm</gui> is in its
|
|
heading, and the trigger can be armed or disarmed without unfolding the panel. It is
|
|
offered only while trigger mode is on, because a trigger that is off has no engine to
|
|
arm.</p>
|
|
</item>
|
|
<item>
|
|
<p><gui>Force</gui> takes one acquisition <em>now</em>, with the event window above,
|
|
without waiting for the condition to hold — for when the event you are waiting for is not
|
|
coming and you want to see what the signal is doing this instant. The capture runs out its
|
|
post-trigger share and then freezes, exactly like a latch: the marker sits at the instant
|
|
you pressed. In <gui>Normal</gui> mode the trigger goes back to waiting afterwards; in
|
|
<gui>Single</gui> mode it disarms, because one acquisition is the whole of what that mode
|
|
means either way. It needs a signal to be acquired — the trigger cannot watch one that is
|
|
switched off — and pressing it while a capture is already filling leaves that capture
|
|
alone, since it is the acquisition you asked for.</p>
|
|
</item>
|
|
<item>
|
|
<p><gui>Search the buffer</gui> is at the foot of the same panel.</p>
|
|
</item>
|
|
</steps>
|
|
|
|
<figure>
|
|
<title>A held capture</title>
|
|
<desc><gui>event held</gui> in the status bar, the axis pinned to the event, and the
|
|
trigger marker at x = 0.</desc>
|
|
<media type="image" mime="image/png" src="figures/trigger-capture.png" width="1280" height="800">
|
|
<p>The pre-trigger share is drawn to the left of the marker.</p>
|
|
</media>
|
|
</figure>
|
|
</section>
|
|
|
|
<section id="shapes">
|
|
<title>The four shapes</title>
|
|
<terms>
|
|
<item>
|
|
<title>Edge</title>
|
|
<p>A crossing of the level in the chosen direction, the classic behaviour.</p>
|
|
</item>
|
|
<item>
|
|
<title>Pulse width</title>
|
|
<p>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.</p>
|
|
</item>
|
|
<item>
|
|
<title>Window</title>
|
|
<p>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 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.</p>
|
|
</item>
|
|
<item>
|
|
<title>Dropout</title>
|
|
<p>The absence of a crossing for longer than a timeout; one event per outage, re-arms
|
|
when a crossing returns.</p>
|
|
</item>
|
|
</terms>
|
|
|
|
<p>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.</p>
|
|
</section>
|
|
|
|
<section id="multiple-events">
|
|
<title>Multiple events: keeping the last few acquisitions</title>
|
|
<p><gui>multiple events</gui> decides how many of the most recent triggered acquisitions stay on
|
|
screen at once. It is <em>1</em> by default, which is the event alone — the display you get
|
|
when you ask for no persistence — and it can be raised to 8. Each older acquisition is drawn
|
|
at half the brightness of the one before it (1, 0.5, 0.25, 0.125, …), and they are all aligned
|
|
on their own trigger edge, so the pre-trigger part of the event lines up from acquisition to
|
|
acquisition and whatever changes between them — jitter, ringing, the occasional glitch — shows
|
|
as a faint ghost behind the current trace. That is what a phosphor display does with
|
|
successive sweeps.</p>
|
|
|
|
<p>The older acquisitions are drawn <em>behind</em> the current one and nothing else changes
|
|
because of them: the axis still frames the event on screen, the trigger marker still marks
|
|
<em>its</em> edge, and the measurements still describe the current frame. Lowering
|
|
<gui>multiple events</gui> back to 1 remembers nothing and frees the retained acquisitions
|
|
immediately.</p>
|
|
|
|
<p>Two things bound how many are drawn, and the panel tells you when one of them bites.</p>
|
|
<list>
|
|
<item><p><em>Room in the plot.</em> Each older acquisition is an extra trace in the row that
|
|
plots its signal, so it needs a free trace slot there. A row holding four signals — the
|
|
layout the app starts with — has room for one older acquisition of each of them; a row per
|
|
signal has room for all seven.</p></item>
|
|
<item><p><em>Memory.</em> A retained acquisition is a whole window copied out of the ring,
|
|
so persistence may hold at most as much again as the buffer (<gui>--buffer-mb</gui>) does.
|
|
On a long window at a high sample rate that bound is reached before the eighth layer.</p></item>
|
|
</list>
|
|
<section id="persistence">
|
|
<title>Persistence: how often the trace has been here</title>
|
|
<p><gui>persistence</gui> — the switch in the trigger panel's <gui>Advanced</gui> block — keeps
|
|
one <em>image</em> of the last acquisitions instead of their traces. Every acquisition is
|
|
accumulated into it, so a cell's brightness means how many of them passed through it: the
|
|
sweep on screen is the brightest, the ones behind it add on top, and where several agree the
|
|
ink is brighter still. It is off by default.</p>
|
|
|
|
<p>Two things make it different from <gui>multiple events</gui> beside it, and the panel offers
|
|
both because they answer different questions. <em>It never runs out of room</em>: the image is
|
|
the size of the plot on screen, so a display of a thousand acquisitions costs what a display of
|
|
one costs, and there is no count to choose. <em>And it fades</em>, in one of two ways —
|
|
<gui>fades by</gui> picks them:</p>
|
|
<list>
|
|
<item><p><gui>acquisitions</gui>: every acquisition keeps
|
|
<gui>per acquisition</gui> of what was there before it (0.5, the default, is the schedule a
|
|
phosphor's afterglow has: 1, 0.5, 0.25, …). The display then shows the last few sweeps
|
|
whatever the trigger rate.</p></item>
|
|
<item><p><gui>time</gui>: the image fades by <gui>time constant s</gui> as the display runs —
|
|
one constant is the time for it to fall to a third of its ink — so a slow trigger leaves
|
|
faint sweeps and a fast one leaves bright ones, and the picture is the same whichever the
|
|
stream is doing.</p></item>
|
|
</list>
|
|
|
|
<p>Persistence is a <em>trigger mode</em> display, like the trigger itself: it is drawn while a
|
|
captured acquisition is on screen and goes away with the trigger, on a pause, and when you open
|
|
a stored waveform. Nothing about it moves the framing — the axis is still the current event's,
|
|
the older sweeps only add ink — and it reserves no space in the plot and cannot be clicked, so
|
|
it is never in the way of a cursor or a drop.</p>
|
|
|
|
<p>Both displays live in the trigger panel's <gui>Advanced</gui> block, together with the
|
|
trigger's other settings that are set up once and left alone.</p>
|
|
</section>
|
|
|
|
|
|
<p>If a depth cannot be shown in full, a notice says how many layers fit, and the panel's
|
|
tooltip carries the same rule. Persistence is a display of what has already been captured: it
|
|
changes neither what is acquired nor what is measured, and it does nothing while the trigger
|
|
is off.</p>
|
|
</section>
|
|
|
|
<section id="cache">
|
|
<title>The event cache, replay and compare</title>
|
|
<p><em>Every triggered acquisition is kept.</em> Latches are written to the event cache
|
|
(<file>~/.cache/udpscope/events/<source>/</file>) as a <file>.npy</file> of the
|
|
event, a <file>.txt</file> of what it was, and a <file>.bmp</file> picture drawn from the
|
|
samples. The <gui>Events</gui> pane on the left — opened from the toolbar's
|
|
event-history button, which appears while recording is enabled, and collapsed until you
|
|
ask for it — lists the last 32 newest first (time, duration, signals, rate), each row with
|
|
its own picture beside the numbers.</p>
|
|
|
|
<p>Clicking a row <em>replays</em> it: the stored event is loaded exactly like an exported
|
|
file, <em>without changing the layout</em> — the rows, the colours, the window, the
|
|
cursors and the measurements stay where they are and read the stored data instead. A
|
|
replay leaves trigger mode, because a file has no edge to wait for, and the pane stays
|
|
open. Drag the pane's edge to resize it. <gui>Live</gui> goes back to the stream; the
|
|
status bar names the file until you do.</p>
|
|
|
|
<p>Select two or more events in the list and the two buttons under the header act on them:
|
|
<gui>Compare</gui> puts two events in two plots side by side, keeping the layout you have
|
|
— one plot per event, the axes synchronised, so zooming a row in one zooms the same row in
|
|
the other — and <gui>Accumulate</gui> overlays up to four of them in the same traces,
|
|
translucent, so where they agree the trace is brighter. Your own traces stay where they
|
|
are in both. They need events with the same signals and the same length. A single click
|
|
selects, a <em>double</em> click replays.</p>
|
|
|
|
<p><gui>Record events</gui>, in the trigger panel with the trigger's other options, is
|
|
what decides whether anything is kept. It is <em>off by default</em> — nothing is written
|
|
to disk until you ask — and with it off the history button goes with it.</p>
|
|
</section>
|
|
|
|
<section id="search">
|
|
<title>Search the buffer</title>
|
|
<p><gui>Search the buffer</gui>, at the foot of the trigger panel and collapsed until you open
|
|
it, answers the question the trigger answers — <em>where does this condition hold?</em> — over
|
|
the <em>whole</em> retained buffer rather than at the live edge. It runs the trigger's own
|
|
evaluator over the ring in stream order, so a <gui>hit</gui> is not a level you point at: it is
|
|
an <em>instant</em> the trigger would have fired on, timed the way an acquisition would have
|
|
timed it. A pulse-width finding, for instance, is the closing edge of a qualifying excursion,
|
|
not the excursion itself.</p>
|
|
|
|
<p>The condition is the one the rest of the panel is set to — the same signal, shape, level and
|
|
options — so there is no second condition to keep in step: change the panel and the next search
|
|
asks the new question. Nothing is searched until you ask, and the status line says
|
|
<gui>not searched</gui>.</p>
|
|
|
|
<steps>
|
|
<item>
|
|
<p>Set the condition above, then press <gui>Search</gui>. Covering the whole ring takes a
|
|
moment, and the scan runs in chunks so the stream keeps arriving while it does; the status
|
|
line counts the ground covered — <gui>scanning 1.2 s of 8.0 s, 40 found</gui>.
|
|
<gui>Stop</gui> ends a scan early and keeps the hits found so far.</p>
|
|
</item>
|
|
<item>
|
|
<p>Step through the hits with the arrows either side of the position readout
|
|
(<gui>3 of 40</gui>). The first <gui>next</gui> goes to the first hit, and the ends do not
|
|
wrap — a wrap from the last hit to the first would look exactly like the view not having
|
|
moved. Each step <em>pauses</em> on its hit, centred, exactly as the Pause button does, so
|
|
what you read is the frozen window cut around that instant; press the pause button again to
|
|
go back to the stream.</p>
|
|
</item>
|
|
<item>
|
|
<p>Every plot row marks the hits that fall in the window, up to sixteen of them. When more
|
|
are in view the pane says so and suggests zooming in — <gui>16 of 38 marks shown — zoom in
|
|
to see the rest</gui> — and when the selected hit has scrolled off the window it says that
|
|
instead. A hit the live stream has since overwritten reads <gui>no longer in the buffer</gui>
|
|
beside its position.</p>
|
|
</item>
|
|
</steps>
|
|
|
|
<p>How a scan ended is part of the answer: <gui>N found in 8.0 s</gui> when it covered the whole
|
|
ring; <gui>N found; the stream overtook the oldest samples</gui> when the producer overwrote
|
|
samples before the scan reached them, so the count is of what was left; and
|
|
<gui>N found (the limit) in the newest …</gui> when a condition that holds almost everywhere —
|
|
an edge on a noisy signal — reached the ten-thousand-hit ceiling. The last is a stop, not a
|
|
crash: a hit list nobody could navigate is not worth building.</p>
|
|
|
|
<p>A result describes the ring it was taken from, so it is <em>dropped</em> when that ring is
|
|
replaced: a new <gui>CONFIG</gui> from the source, or a change to the signal set, re-describes
|
|
the stream and clears the hits. The condition above is untouched — only the answer to it
|
|
is.</p>
|
|
</section>
|
|
|
|
<section id="knowing">
|
|
<title>Worth knowing</title>
|
|
<list>
|
|
<item><p>Arming <em>enables</em> the signal if it was off, because a trigger on a signal
|
|
nobody acquires can never fire.</p></item>
|
|
<item><p>The pre-trigger share is a share of the <em>display window</em>, not of the
|
|
ring. A 2 s window at 50% captures 1 s before the edge and 1 s after.</p></item>
|
|
<item><p>There are four display states, and the status bar names each: <gui>armed</gui>
|
|
(watching, nothing drawn yet), <gui>capturing</gui> (the edge was accepted and the post
|
|
window is filling <em>in view</em>, with the marker at x = 0), <gui>event held</gui>
|
|
(the frozen capture) and <gui>disarmed</gui>. A held capture is never quietly replaced
|
|
by live data.</p></item>
|
|
<item><p>The first edge after arming is only accepted once the history reaches back a
|
|
full pre-trigger window, so a capture may legitimately be shorter than you asked for.
|
|
The axis is pinned to what the capture actually got. <gui>Force</gui> is the one
|
|
acquisition that skips that gate — it captures the history that exists, because "now"
|
|
is the whole of what you asked for — so on a ring that has just started filling its
|
|
left side is short even though the axis still shows only what it got.</p></item>
|
|
<item><p>Disarming <em>during</em> a capture publishes what the capture got to, so the
|
|
picture on screen at that moment stays on screen. No event fired, so the event count
|
|
does not move.</p></item>
|
|
<item><p>The trigger reads the <em>stored</em> 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.</p></item>
|
|
</list>
|
|
</section>
|
|
|
|
<section id="example">
|
|
<title>Example</title>
|
|
<p>A 1 Hz sine, level 0, rising, 2 s window at 50%, Single. The capture spans
|
|
<code>[-1.000 .. 1.000]</code> with <code>n=20001</code> samples at 10 kS/s — the span
|
|
times the real rate, plus one — and the marker sits at x = 0 throughout.</p>
|
|
</section>
|
|
|
|
</page>
|