`UDPSCOPE_PROBE_SIDE_TAB_MS` is the one reading for the design's §5 rule -- the statistics accumulator keeps running on the hidden page -- which nothing tested before. It switches to Measure, forces one pass so there is a baseline, switches away to Signals and reads again two seconds later: `advanced=4` is the assertion, and `advanced=0` is the defect where the accumulation is gated on the visible tab, which would leave a gap in min/max/avg for exactly as long as the trigger was on screen. Two things the probe has to do for itself, and getting either wrong makes `advanced=0` a lie about the tab: - it **enables its own signal**, because a signal starts disabled (enabling is what starts acquisition) and a probe that waited for one to be on would be reading another probe's side effects -- and `UDPSCOPE_ENABLE_ALL_MS` is not part of this run; - it **assigns that channel to a plot**, because a channel with no trace leaves the live window *still*, and a still window is precisely the state `update_measures()` is supposed to skip. Measured the hard way: without the assignment the probe read `advanced=0 min_n=0` with the view pinned and only four passes in twelve seconds, none of them the tab's doing. Docs: `probe-readings.md` gains the probe's entry and the signals panel's new shape (`rows == signals + 1 + math`, `divider=0` a list that lost its `+`), loses the whole `math panel:` entry, and follows `pane_sensitive=`, the gone `revealed=` field, the six toolbar controls and the tab-reading `m`/`g` key pairs. `verification.md` gains the probe's bullet and the re-measured warning figures. The invariants record the sidebar as a switcher above a stack with a scroller per page, the hidden-page trap that replaces the unrevealed-revealer one for it, that a feature's on/off is sensitivity rather than visibility, that a math channel is a row of the signal list, and that a cursors-off readout blanks to `--` with the panel left on screen. `shortcuts.ui` and the help page's window and sidebar bullets stop describing five revealers. The README records the deviation from `SPECS.md` that names the panes as separate collapsible sections. Verified: `[side-tab-probe] page=signals rows=4 accums=4 advanced=4 min_n=16`; `signals panel: labels=2 list=1 rows=9 signals=4 math=4 divider=1`; `panels: … (page_current=1) (sensitive=0) switcher=1`; `toolbar icons:` six controls with every name `(ok)`, with the Preferences dialog opened in the same run; zero `Gtk-CRITICAL` and no missing icon.
80 lines
5.0 KiB
Plaintext
80 lines
5.0 KiB
Plaintext
<b>What udpscope is</b>
|
|
|
|
udpscope plots a live UDPStreamer source. One window holds up to 8 rows of plots, each with up
|
|
to 4 traces, and every row shares one time axis, so the whole figure scrolls together.
|
|
Acquisition runs on its own thread and writes a ring buffer sized from <tt>--buffer-mb</tt>;
|
|
the drawing thread reads whatever that ring holds when a frame starts, which is why a trace
|
|
never makes the window stutter.
|
|
|
|
The stream is the clock. Every interval you read on screen is measured between timestamps the
|
|
source sent, never your machine's, so a stalled source freezes the view instead of scrolling it
|
|
-- and a source whose clock steps still gets the window you asked for.
|
|
|
|
The time axis is <i>relative to now</i>. While streaming it is a fixed <tt>[-window .. 0]</tt>
|
|
with the newest sample at 0, so x reads as an age: <tt>-1.5s</tt> is a sample a second and a half
|
|
old. In trigger mode 0 is the event's edge instead, and a paused view keeps the ages it was
|
|
frozen with. The source's own clock is still there when you need it -- an export writes it as a
|
|
second column.
|
|
|
|
<b>How to reach it</b>
|
|
|
|
Start the app with a source (<tt>udpscope --server 127.0.0.1 --port 8000</tt>), or start it with
|
|
no <tt>--server</tt> and pick one from the chooser, which lists the sessions you have opened
|
|
before. A unicast source serves one client at a time: if a second viewer connects, they take
|
|
turns and the status bar says so. To watch one source from several instances, use
|
|
<tt>--multicast</tt> with the group and <tt>--interface</tt> with the local address.
|
|
|
|
The signals arrive <i>disabled</i>. Nothing is acquired until you turn one on in the signal
|
|
list, and enabling is what starts acquisition -- so a freshly opened scope with an idle side
|
|
panel is working correctly, not failing to connect.
|
|
|
|
<b>The window</b>
|
|
|
|
• <b>The header bar</b> -- left to right: the time window (<tt>2 s</tt>), the trigger and the
|
|
pause, the events, cursors and side-panel switches, and the menu, which holds Fit to data, the
|
|
layout editor, Export, Keyboard shortcuts, Help and About.
|
|
|
|
• <b>The side panel</b> -- two tabs. <b>Measure</b> holds the cursors readout and the
|
|
measurements derived from it; <b>Signals</b> holds the trigger and the channel list, where a
|
|
math channel is a row of that one list under a <tt>MATH</tt> heading whose <tt>+</tt> creates
|
|
one. One row per channel: its colour, an enable switch, its name and its unit. Channels are
|
|
dragged from here onto a plot row.
|
|
|
|
• <b>The plot</b> -- up to 8 rows, each with its own y axis and up to 4 traces, all on one
|
|
shared time axis. Right-click a row for its own menu (fit, add a signal, remove a trace).
|
|
|
|
• <b>The status bar</b>, directly above the plot's bottom edge -- the state word, the stored
|
|
sample rate and the window, and the ⓘ beside it for packets, gaps and the buffer's own
|
|
figures. The second screenshot above is what a held capture looks like: <tt>event held</tt>,
|
|
the axis pinned to the event, and the trigger marker at the edge.
|
|
|
|
<b>The first five minutes</b>
|
|
|
|
1. <b>Connect.</b> With no <tt>--server</tt>, pick a stored session or type a host and port.
|
|
2. <b>Enable a signal</b> in the signal list. The plot stays empty for the first fraction of a
|
|
second while the first window fills -- the buffer holds only what has arrived.
|
|
3. <b>Drag it onto a row.</b> Drop a second signal on another row, or the same signal again for
|
|
a second view of it. The colour is per signal, so it is the same one wherever it appears.
|
|
4. <b>Set the window</b> with the control in the header bar: type a number, pick a unit, or step
|
|
a decade. The trigger's event window is a share of this span, so a window change moves it.
|
|
5. <b>Pause</b> (<tt>space</tt>) to look at a still picture; the producer keeps filling the
|
|
buffer behind it, and resuming shows what arrived meanwhile rather than a hole.
|
|
6. <b>Arm the trigger</b> (<tt>a</tt>) and wait for a latch: the view freezes on one event, and
|
|
you can zoom in on it. Disarming mid-capture keeps what the capture got.
|
|
7. <b>Measure it</b> -- the measurements pane computes a statistic of the visible window, and
|
|
<b>show on plot</b> marks it as a ruler or a pair of markers.
|
|
8. <b>Save the data</b> with Export visible range, which writes the plotted signals over the
|
|
window at the stored rate to <tt>.npy</tt> or <tt>.csv</tt>.
|
|
|
|
<b>Worth knowing</b>
|
|
|
|
• A signal nobody enables costs nothing -- no buffer, no socket work, no measurements.
|
|
• The window remembers itself. Rows, traces, the time window, calibration, which signals were
|
|
enabled, the math channels with their graphs, and the measurements are written per host and
|
|
port, and reloaded the next time you open that source.
|
|
• A very wide window on a fast stream is drawn from averaged groups of samples, so the rate in
|
|
the status bar falls: that is the anti-alias filter, not a fault -- striding would fold a fast
|
|
signal back as a slow one, which looks entirely plausible on screen.
|
|
• Every instrument the app computed -- the trigger marker, the measurement marks -- is locked to
|
|
the pointer's reach. The cursors are yours to drag; those are not.
|