feat: the manual is a Mallard set opened in Yelp, not a GResource in the binary
The help was 14 Pango-markup `.txt` bodies in a second GResource, drawn into a paged `GtkWindow` built in `help_window.cpp`. That made F1 a viewer of ours with no search index, no cross-references and no translation, and it kept the manual in the *binary* when every other desktop application's is a document on disk. It is now a real Mallard set: help/C/org.udpscope.app/ index.page + 14 topic pages + figures/*.png and `show_help()` hands `help:org.udpscope.app` to whatever the desktop registered for the `help:` scheme -- Yelp on GNOME -- through a `GtkUriLauncher`, reporting a launch failure with a `GtkAlertDialog` rather than a click that silently does nothing. The paged window's builder, the page table and `help_self_check()`'s markup validation are gone with the resource. The document identifier is the desktop file's own basename (its desktop-file id), derived in CMakeLists from `data/org.udpscope.app.desktop.in`, so the same string is the `.desktop` name, the GApplication id and the install directory at once. The set is installed by a new `install()` rule to `<datadir>/help/C/org.udpscope.app/` -- where the freedesktop Help System Specification's `help:` URI resolves `help:<id>` -- so the manual is read from a datadir by another process even though the binary has no install step. `UDPSCOPE_HELP_NAME` and `UDPSCOPE_HELP_DIR` are compiled in from that one place. The 14 pages were migrated fact for fact, but restructured the Mallard way: a `<desc>` per page (what a search result shows), `<steps>` for procedures, `<terms>` for option lists, `<gui>`/`<key>`/`<keyseq>` for interface words and keys, `<note>` for the asides, and a topic graph via `<link type="guide" xref="index#main"/>`. The figures are annotated screenshots produced by `tools/make-help-shots.sh`, which drives the app headlessly against `tools/mock_udpstreamer.py` and draws the boxes with ImageMagick; the side panel's box is measured from the app's own `side=NNNx` dump reading, the header and status bands come from a documented table, and a crop whose standard deviation is 0 fails the script. `cmake --build build --target test-help` checks the set the way `test-ui` checks the `.ui` files: `yelp-check`'s offline subcommands (links, media, orphans, ids) and a `yelp-build html` render. `yelp-check validate` is deliberately absent -- it fetches the Mallard RelaxNG schema over the network and fails offline for that reason, not for a fault in the pages. The `[help]` probe reading changes with the resource it was written for. `parsed == pages` over in-binary markup cannot exist now, so `help_self_check()` scans `UDPSCOPE_HELP_DIR` and the line reads `topics=14 index=1 figures=3`; `topics=0 index=0` is the manual missing from the tree, which is the finding the old count named. Verified: clean build with the documented three warnings (the help bundle's `-Woverlength-strings` is gone, so the baseline moves from four to three); test-ui 14/14; test-help clean (the four subcommands and 15 pages rendered); a temp-prefix install lands 15 `.page` files and 3 figures at `share/help/C/org.udpscope.app/`; the headless run reads `[help] topics=14 index=1 figures=3 dir=...` with 0 Gtk-CRITICAL. Yelp's own window could not be exercised on this box -- it starts under Xvfb, activates its portals and never maps, the same WebKit/portal limitation that stops a grab -- so its rendering and its search index are unverified here; the content pipeline was (`yelp-build` renders the whole set). Docs: the manual's build/install/open story in `build-and-run.md` and README; the warning baseline in `verification.md`/CLAUDE.md; the help bullets in `invariants-gtk-and-chrome.md`; the `[help]` line in `probe-readings.md`.
This commit is contained in:
1 parent
620473a5be
commit
c1460a6977
47 files changed
+1910
-1056
No files matched your search
@@ -0,0 +1,87 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="cursors">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#analysis"/>
|
||||
<link type="seealso" xref="measurements"/>
|
||||
<desc>The shared vertical cursor pair and the per-row horizontal rulers, their four modes,
|
||||
and how a reading is formatted.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Cursors</title>
|
||||
|
||||
<p>Cursors read values off the traces. There are two kinds: one <em>shared vertical
|
||||
pair</em> that every row carries at the same time, and <em>horizontal rulers</em>, one pair
|
||||
per row, that belong to that row alone.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>Press the cursors button in the toolbar, or <key>c</key>.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>The pane below the plot shows the readings and owns a <gui>Reset</gui> button.
|
||||
Drag a marker to move it.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>The mode dropdown offers four modes: <gui>vertical</gui> (the shared time pair),
|
||||
<gui>horizontal</gui> (the per-row rulers), <gui>both</gui>, and <gui>mix</gui>.</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>The vertical pair is <em>one</em> time for the whole figure, so moving it on
|
||||
any row moves it on all of them. That is what makes <code>dt</code> meaningful across
|
||||
rows.</p></item>
|
||||
<item><p>A ruler belongs to its row and nothing shares it, so two rows' rulers are
|
||||
independent.</p></item>
|
||||
<item><p><gui>mix</gui> is the reading mode: one line per <em>bound</em> trace, with the
|
||||
trace's own values at each marker and their difference. A slot with a name but no
|
||||
channel behind it is not a trace and gets no line.</p></item>
|
||||
<item><p>A value is the <em>nearest stored sample's</em>, never a number between two
|
||||
samples. Zoom in past the sample spacing and the line between two samples is something
|
||||
the plot drew, not data — so a reading off it would be the drawing. The hover crosshair
|
||||
reads the same way.</p></item>
|
||||
<item><p>Every reading carries a <em>metric prefix</em> so the digits sit in
|
||||
<code>1..999</code>: <code>10.52 mV</code>, not <code>0.01 V</code>. Two decimals is all
|
||||
an instrument landing on an <code>f32</code> sample can claim, so without the prefix a
|
||||
small value would read <code>0.00</code> and the reading would be gone. A stream that
|
||||
declares no unit gets the prefix anyway, written tight against the number
|
||||
(<code>585.24m</code>) — the space separates the number from its unit, and a spaced
|
||||
<code>m</code> with nothing after it would read as metres.</p></item>
|
||||
<item><p>A marker past the newest sample reads <code>--</code>. With only one marker
|
||||
over data the row reads <code>partial</code>: a difference between a number and nothing
|
||||
is not a number.</p></item>
|
||||
<item><p>In <gui>mix</gui> each marker also places a crossing annotation per trace,
|
||||
which is what lets you see on the plot which sample a number came from. It is <em>on
|
||||
that sample</em>, so when the marker falls between two it sits a little off its own
|
||||
line: the line is where you put the marker, the dot is the datum being quoted. The hover
|
||||
crosshair's mark snaps the same way, and is drawn larger than a trace's sample dots so
|
||||
it does not read as one more sample.</p></item>
|
||||
<item><p>The time row exists only in the modes that show the vertical pair.
|
||||
<gui>horizontal</gui> has no time row, by design.</p></item>
|
||||
<item><p>A cursor <em>stays where you put it</em> while a live view rolls under it,
|
||||
which is what a bench scope does: its <code>T0</code> is an age, so a marker left at
|
||||
<code>-1.5s</code> keeps reading the sample a second and a half old as the stream
|
||||
advances. A measurement's marks do the opposite — they travel with the sample they
|
||||
measured — because a cursor is a question you asked and a mark is an answer about one
|
||||
particular sample.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>Three traces in <gui>mix</gui> mode with both markers over data: 3 lines and 6 crossing
|
||||
marks, each line reading like <code>sine 0.31 V -0.84 V -1.15 V</code>. Move the second
|
||||
marker past the newest sample and the third column reads <code>--</code>.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,78 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="export">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#output"/>
|
||||
<link type="seealso" xref="sessions"/>
|
||||
<desc>Writing the visible range of every plotted trace to NumPy, CSV or MDF 3.30, and what
|
||||
each format carries.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Saving and export</title>
|
||||
|
||||
<p>Export writes the <em>visible range</em> of every plotted trace to a file: NumPy
|
||||
<file>.npy</file>, <file>.csv</file>, or <gui>MDF 3.30</gui> (<file>.mf3</file> or
|
||||
<file>.mdf</file>), chosen by the extension you type.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>Pick <gui>Export visible range…</gui> in the overflow menu, or press
|
||||
<keyseq><key>Ctrl</key><key>E</key></keyseq>.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>A file chooser asks where; the format follows the path's extension.</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>The table is collected at the moment you click, not when the chooser returns. A
|
||||
live view keeps scrolling while a dialog is up, so a late collection would write a window
|
||||
you never saw.</p></item>
|
||||
<item><p>The rows are the <gui>stored samples</gui>, never the LOD buckets the plot draws
|
||||
from. A row count far below the span times the stored rate means something cut the table
|
||||
from the pyramid.</p></item>
|
||||
<item><p>The stored rate is not always the wire rate. A window long enough to need
|
||||
decimation is stored averaged at a lower rate, and the export carries <em>that</em> —
|
||||
read the status bar's ⓘ popover for the figure before trusting a sample count.</p></item>
|
||||
<item><p>Two time columns. <code>time</code> is the display timeline — negative ages
|
||||
ending at 0 while streaming, offsets from the trigger edge while an event is held — so it
|
||||
is the x you read a value at on screen. <code>abs_seconds</code> is the source's own
|
||||
clock for the same sample, which is what lets two exports taken in different modes line
|
||||
up. Both are strictly increasing and evenly spaced; duplicate or backwards stamps would
|
||||
mean the file held two clocks.</p></item>
|
||||
<item><p>An export taken <em>mid-capture</em> writes the partly filled window that is on
|
||||
screen, which is shorter than the event span. Export outside a capture if you want the
|
||||
whole event.</p></item>
|
||||
<item><p><gui>MDF</gui> exists for handing a window to another tool, and its shape follows
|
||||
the contract that reader uses: one channel group, every channel record-interleaved, the
|
||||
master channel as the x axis and the unit in the channel metadata. So there the
|
||||
<em>master is <code>abs_seconds</code></em>, named <code>time</code> — the clock that is
|
||||
the same in every display mode — and the display axis goes out beside it as an ordinary
|
||||
channel, <code>time_relative</code>. Both are seconds; each signal carries the source's
|
||||
own unit. That is also what <gui>ScryLab</gui> reads: channel name → signal name, master
|
||||
→ x axis, unit → the signal's unit.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>A 2 s window at 10 kS/s over 3 traces writes 20001 rows and 5 columns
|
||||
(<code>time</code>, <code>abs_seconds</code>, three signals). The <file>.npy</file> is
|
||||
<code>64·⌈(10+len(header)+1)/64⌉ + rows·(16 + 4·cols)</code> bytes. Read it back with
|
||||
<code>pd.DataFrame(np.load(path))</code>. The <file>.mf3</file> is bigger — a block per
|
||||
channel plus a comment — <code>272 + comment + rows·record + 28 + channels·274 + 30</code>
|
||||
bytes, with <code>record = 16 + 4·cols</code> and <code>channels = cols + 2</code> for the
|
||||
master and the relative axis. Read that one back with <code>asammdf.MDF(path)</code> or in
|
||||
ScryLab.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
@@ -0,0 +1,67 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="guide" style="task"
|
||||
id="index">
|
||||
|
||||
<info>
|
||||
<desc>udpscope plots a live UDPStreamer source as one figure of up to eight stacked
|
||||
plot rows sharing a single time axis, with a trigger, cursors, measurements, math
|
||||
channels and an export. This manual covers connecting, the window and its panels,
|
||||
navigating and pausing the plot, the trigger and its event cache, analysis, and
|
||||
saving and exporting.</desc>
|
||||
</info>
|
||||
|
||||
<title>udpscope</title>
|
||||
|
||||
<section id="main">
|
||||
<title>Getting started</title>
|
||||
<list>
|
||||
<item><p><link xref="overview">Overview</link> — what udpscope is, how it reaches a
|
||||
source, the window and its panels, and a first five minutes.</p></item>
|
||||
<item><p><link xref="source">Choosing a source</link> — the command line, the
|
||||
chooser, and opening an exported waveform instead of a live stream.</p></item>
|
||||
<item><p><link xref="signals">Signals and calibration</link> — how a signal becomes
|
||||
a channel, what starts acquisition, and how a scale and offset are applied.</p></item>
|
||||
<item><p><link xref="layout">Rows, layout and drag & drop</link> — up to eight
|
||||
rows, four traces each, and the layout editor.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="viewing">
|
||||
<title>Viewing</title>
|
||||
<list>
|
||||
<item><p><link xref="window">Time window</link> — the one control that changes the
|
||||
window, and the decimation a long window causes.</p></item>
|
||||
<item><p><link xref="navigate">Navigating the plot</link> — panning, zooming and
|
||||
fitting with the mouse and the keyboard.</p></item>
|
||||
<item><p><link xref="pause">Pause and play</link> — freezing the display without
|
||||
stopping the stream, and how long you have been looking.</p></item>
|
||||
<item><p><link xref="trigger">Trigger</link> — the four trigger shapes, arming,
|
||||
hold-off, the event cache, and searching the buffer.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="analysis">
|
||||
<title>Analysis</title>
|
||||
<list>
|
||||
<item><p><link xref="cursors">Cursors</link> — the shared vertical pair and the
|
||||
per-row horizontal rulers.</p></item>
|
||||
<item><p><link xref="measurements">Measurements</link> — a statistic of the visible
|
||||
window, its on-plot marks, and its statistics line.</p></item>
|
||||
<item><p><link xref="math">Math channels</link> — building a derived channel from a
|
||||
graph of source nodes, operations and one output.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="output">
|
||||
<title>Output and state</title>
|
||||
<list>
|
||||
<item><p><link xref="export">Saving and export</link> — the visible range to
|
||||
<file>.npy</file>, <file>.csv</file> or <file>.mf3</file>.</p></item>
|
||||
<item><p><link xref="sessions">Sessions and settings</link> — the per-source
|
||||
configuration files and what they carry.</p></item>
|
||||
<item><p><link xref="status">Status bar and diagnostics</link> — the glance line, the
|
||||
ⓘ popover, and the activity badge.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,71 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="layout">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#main"/>
|
||||
<link type="seealso" xref="signals"/>
|
||||
<desc>How the figure's rows and traces are arranged, how to drop a signal onto a row, and
|
||||
how the layout editor adds and removes rows.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Rows, layout and drag & drop</title>
|
||||
|
||||
<p>The figure is a stack of rows, 1 to 8 of them, each holding up to 4 traces. Rows share
|
||||
one time axis and one pair of side margins, so their y labels line up, their data areas
|
||||
start and end on the same two columns, and their windows cannot drift apart. The left
|
||||
margin holds the y labels; the right one is empty, and is there so the newest tick's label
|
||||
and the newest sample's ink have somewhere to land instead of sitting on the frame.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>Drag a signal's chip out of the side panel (<key>s</key>) and drop it on a row to
|
||||
add a trace.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>Press <key>l</key>, or pick <gui>Layout editor</gui> from the overflow menu, to add
|
||||
and remove rows: a <gui>×</gui> appears at the top left of each row and a <gui>+</gui>
|
||||
on each boundary between rows, including above the first and below the last.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>The editor's own button in the top-right corner leaves it.</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>A plot's own <gui>right-click menu</gui> (the same compact rows as the app's
|
||||
menu) holds the y-axis scale, the two fits, and <gui>Remove signal</gui>, which swaps
|
||||
the menu to that plot's traces plus <gui>All traces on this plot</gui> — with a
|
||||
<gui>Back</gui> row to return. That is how a trace comes off a row without going through
|
||||
the layout editor.</p></item>
|
||||
<item><p>A drop <em>adds</em> a trace to the row's next free slot; it does not replace
|
||||
one. With four traces already on a row the drop is refused.</p></item>
|
||||
<item><p>Inserting a row moves the rows below it <em>with their contents</em>. Inserting
|
||||
above row 1 puts what row 1 plotted onto row 2 and leaves the new row empty — the rows
|
||||
are not renumbered under you.</p></item>
|
||||
<item><p>A trace is remembered by the signal's <em>name</em>, not by its channel number,
|
||||
because a new CONFIG or an enable renumbers the channels and an index would silently
|
||||
re-point the trace.</p></item>
|
||||
<item><p>Adding or removing a row rebuilds the figure, which is cheap, but it also
|
||||
re-deals every trace. A frozen trigger event survives the rebuild: the pinned window is
|
||||
restated rather than re-framed on the live edge.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>With three traces on row 1, pressing <key>l</key> shows 3 buttons (one <gui>×</gui>,
|
||||
two <gui>+</gui>). Clicking the <gui>+</gui> above row 1 gives 2 rows and 5 buttons, with
|
||||
the three traces now on row 2 — the layout probe reports exactly this as
|
||||
<code>rows=2 buttons=5 traces=[0:-|1:sine,ramp,noise]</code>.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,109 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="math">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#analysis"/>
|
||||
<link type="seealso" xref="signals"/>
|
||||
<desc>Building a derived channel from a graph of source nodes, operations and one output,
|
||||
and how the graph is evaluated.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Math channels</title>
|
||||
|
||||
<p>A math channel is a signal computed from other signals. You build it as a small graph —
|
||||
source nodes, operations, one output — and udpscope then treats the result as an ordinary
|
||||
channel.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>Press the math button in the toolbar, or <key>g</key>.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>The pane's <gui>+</gui> adds a channel, and opening one shows the graph editor:
|
||||
nodes as boxes, click a box to select it and edit its parameters beside the canvas.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>Inside the editor, <gui>add node</gui> is a menu button whose popover picks the op,
|
||||
so the node arrives as the operation you asked for.</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="gestures">
|
||||
<title>Graph gestures</title>
|
||||
<p><gui>drag</gui> from an output port to an input port to wire them; drag a wired input
|
||||
port off its connection to detach it; click a wire and press <key>Delete</key> to remove
|
||||
it; drag a box to move it. <gui>Wheel</gui> zooms about the pointer. <gui>Drag the
|
||||
background</gui> to pan. <key>f</key> fits all nodes into view;
|
||||
<keyseq><key>Ctrl</key><key>0</key></keyseq> resets the view. <gui>tidy</gui>, the button
|
||||
beside <gui>fit</gui> in the editor, lays the graph out in topological columns and frames
|
||||
it. The orange dot marks the output node; click <gui>set as output</gui> to move it.</p>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>The graph is evaluated as an ordered list, so a node may only read ones
|
||||
<em>above</em> it. A wire drawn right to left re-sorts the list silently so the graph
|
||||
stays valid. A cycle is unrepresentable: the connection is refused rather than created,
|
||||
and the line beneath the form says why — a loop, or a node asked to read itself.</p></item>
|
||||
<item><p>A derived channel <em>is</em> a channel. It appears in the signal panel, can be
|
||||
plotted, measured, exported, and used as a source for another math channel.</p></item>
|
||||
<item><p>The math runs on the <em>stored</em> samples — decimated and averaged — so a
|
||||
cutoff you type in Hz means the rate the filter will actually run at. Change the window
|
||||
across a decimation boundary and the coefficients are rebuilt for the new rate.</p></item>
|
||||
<item><p>A source may only name an <em>acquired</em> channel. The stream's timestamp
|
||||
signal is not one: a graph sourced from it validates and then reads NaN for ever, so the
|
||||
dropdown does not offer it.</p></item>
|
||||
<item><p>The output defaults to the <em>last</em> node, and a node added to a linear
|
||||
graph becomes the output. Choosing an earlier node explicitly is respected.</p></item>
|
||||
<item><p>Every numeric field starts at its own identity value — gain 1, offset 0 — not
|
||||
at the bottom of its range.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="why">
|
||||
<title>Why an operation sometimes does nothing</title>
|
||||
<p>A graph's <em>output</em> is the node whose value becomes the channel, and it is drawn
|
||||
as the orange dot. A new graph is built source, then operation, and the last node added
|
||||
becomes the output — but a graph whose output is left on the source is valid and simply
|
||||
passes its input through, which is the usual reason a gain or a filter appears to have no
|
||||
effect. <gui>Set as output</gui> moves the dot.</p>
|
||||
|
||||
<p><gui>Apply</gui> is insensitive while the graph is incomplete. A line beneath the form
|
||||
says why: an input that is not connected, no output chosen, or a <em>channel</em> node
|
||||
naming a signal the scope is not acquiring. Only the channels being acquired are offered,
|
||||
because a source that is not acquired reads as no data for ever — and a graph that reads
|
||||
NaN is harder to diagnose than one that would not save.</p>
|
||||
|
||||
<p>The filters are real filters. A math channel's samples are produced as the stream
|
||||
arrives, once per stored slot, so a low pass carries the history a filter needs. A cutoff
|
||||
typed in hertz is against the <em>stored</em> rate, which is the wire rate until a wide
|
||||
window makes the buffer decimate — the status line's rate is that number, and the filters
|
||||
are rebuilt whenever it changes.</p>
|
||||
|
||||
<figure>
|
||||
<title>The math channel editor: nodes as boxes on a canvas, the orange dot marking the
|
||||
output, and the selected node's parameters beside it.</title>
|
||||
<media type="image" mime="image/png" src="figures/math-editor.png" width="938" height="420">
|
||||
<p>A sine source wired through a gain into a filter, with the output on the last
|
||||
node.</p>
|
||||
</media>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p><code>[sine] → [gain x2]</code> gives a channel reading <code>y=[-2, 2]</code>. A
|
||||
100 Hz low pass on the same source passes 50 Hz at 0.97 and 2 kHz at 0.002; a 1 kHz band
|
||||
pass with Q=2 passes 200 Hz at 0.05, 1 kHz at 0.95 and 4 kHz at 0.05. Each derived channel
|
||||
must hold exactly as many samples as the shared clock has time slots.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,97 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="measurements">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#analysis"/>
|
||||
<link type="seealso" xref="cursors"/>
|
||||
<desc>A measurement is a statistic of the visible window of one signal, with optional
|
||||
on-plot marks and a per-measurement statistics line.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Measurements</title>
|
||||
|
||||
<p>A measurement is a statistic of the <em>visible window</em> of one signal, recomputed as
|
||||
that window moves. The pane lists them with their values; each can also draw its result on
|
||||
the plot.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>Press the measurements button in the toolbar, or <key>m</key>.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>The pane's <gui>+</gui> adds one; clicking a row opens the dialog where the signal,
|
||||
the type, the threshold share and the on-plot marks are set, with <gui>Save</gui> and
|
||||
<gui>Delete</gui>.</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>The types are average, top, bottom, min, max, peak to peak, amplitude, pulse
|
||||
duration, period and frequency. <em>top</em> and <em>bottom</em> are the means of the
|
||||
upper and lower halves, which is why a unit sine reads 0.637 (2/π) rather than 1.</p></item>
|
||||
<item><p>The on-plot marks come in two kinds and the mix is the reading: a <em>level</em>
|
||||
mark is a ruler at a value, a <em>time</em> mark is a marker at a position. An average
|
||||
draws one level mark; a max draws a level mark <em>and</em> a time mark at the first
|
||||
sample that reached it.</p></item>
|
||||
<item><p>Marks cannot be dragged. A measurement mark says what the measurement measured,
|
||||
so it is created unselectable — as is the trigger marker.</p></item>
|
||||
<item><p>A pass is throttled to a cadence rather than run every frame, so a ruler over a
|
||||
live view is <em>stepped</em>. The number and the ruler come from the same pass, so they
|
||||
always agree.</p></item>
|
||||
<item><p>An invalid pass keeps its <em>level</em> marks and drops its <em>time</em>
|
||||
marks. A level is a property of the signal and outlives the window; a position inside
|
||||
the window does not. That is what stops the rulers blinking off at a mode change.</p></item>
|
||||
<item><p>A crossing is counted with <em>hysteresis</em>, not on a bare level: the
|
||||
thresholds are a tenth of the window's peak to peak either side of the share, and an
|
||||
edge is counted only once the signal has passed the far one. Period, frequency and pulse
|
||||
duration all depend on it. The mid level is exactly where a waveform moves fastest, so
|
||||
any interferer faster than the fundamental recrosses a single level several times per
|
||||
real crossing — a 5 Hz sine with a 500 Hz tone at a tenth of the amplitude reads 39 Hz
|
||||
without the band and 5 Hz with it. The band holds to an interferer a third of the
|
||||
waveform's own size.</p></item>
|
||||
<item><p>The band is clamped to stay inside the data, which is what keeps a threshold set
|
||||
near an extreme usable: a fixed band there would sit above every sample and the
|
||||
measurement would read nothing. A share near an extreme still leaves the band little
|
||||
room, so it rejects less — which is one reason period and frequency offer no share to
|
||||
set.</p></item>
|
||||
<item><p>A window with too few crossings reads <code>invalid</code> / <code>--</code>
|
||||
rather than a plausible invented number. Frequency over a window holding one edge is the
|
||||
usual case.</p></item>
|
||||
<item><p><gui>frequency</gui> is reported in Hz and <gui>period</gui>/<gui>pulse
|
||||
duration</gui> in seconds; every other type carries the channel's own unit. A value is
|
||||
written with a <em>metric prefix</em> so its digits sit in <code>1..999</code> —
|
||||
<code>636.62 mV</code>, not <code>0.64 V</code> — including when the stream declares no
|
||||
unit at all, which is always the case for a math channel: there the prefix goes tight
|
||||
against the number (<code>636.62m</code>), since two decimals of an unprefixed small
|
||||
value is <code>0.00</code>.</p></item>
|
||||
<item><p>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.</p></item>
|
||||
<item><p><gui>Statistics</gui> adds a second line to every measurement showing
|
||||
<code>n</code>, the mean, the standard deviation and the range. Each observation is one
|
||||
<em>recompute</em> of the pane, so <code>n</code> counts windows measured, not samples —
|
||||
a mean frequency over many windows is what that claims to be. <gui>Reset</gui> 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.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>On a 1 Hz ±1 sine: <code>average=0</code>, <code>top=0.636620</code>,
|
||||
<code>bottom=-0.636620</code>, <code>max=1</code>, <code>min=-1</code>,
|
||||
<code>peak to peak=2</code>, <code>amplitude=1</code>, <code>pulse duration=0.5 s</code>
|
||||
(duty 0.5), <code>period=1 s</code>, <code>frequency=1 Hz</code>. Over a 2 M-sample window
|
||||
those two passes read about 0.1% of the samples.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,104 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="navigate">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#viewing"/>
|
||||
<link type="seealso" xref="window"/>
|
||||
<desc>Panning, zooming and fitting the plot with the mouse and the keyboard, and what a
|
||||
pinned view refuses to do.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Navigating the plot</title>
|
||||
|
||||
<p>The plot pans and zooms with the mouse and with the keyboard. Every gesture acts on the
|
||||
row the pointer is over, and because the rows share one time axis, an x change moves all of
|
||||
them.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<terms>
|
||||
<item>
|
||||
<title>Wheel</title>
|
||||
<p><gui>Wheel</gui> zooms both axes · <gui>Shift</gui>+<gui>Wheel</gui> zooms x only ·
|
||||
<gui>Ctrl</gui>+<gui>Wheel</gui> zooms y only.</p>
|
||||
</item>
|
||||
<item>
|
||||
<title>Drag</title>
|
||||
<p><gui>Drag</gui> pans · <gui>Shift</gui>+<gui>Drag</gui> is a box zoom on both axes ·
|
||||
<gui>Ctrl</gui>+<gui>Drag</gui> on x only · <gui>Alt</gui>+<gui>Drag</gui> on values
|
||||
only. The area being chosen is framed while you drag it, so you can see what the
|
||||
release will apply.</p>
|
||||
</item>
|
||||
<item>
|
||||
<title>Keyboard</title>
|
||||
<p><key>+</key> and <key>-</key> zoom both axes; <key>Ctrl</key> with them restricts
|
||||
to x, <key>Alt</key> to y. <gui>Arrow keys</gui> pan, <key>f</key> fits,
|
||||
<key>Escape</key> cancels a gesture in progress.</p>
|
||||
</item>
|
||||
</terms>
|
||||
|
||||
<p>The full table is in the shortcut overview (<keyseq><key>Ctrl</key><key>/</key></keyseq>).</p>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>The keyboard zoom's modifiers are <em>not</em> the wheel's, and that is
|
||||
deliberate: on common layouts <key>+</key> <em>is</em> <keyseq><key>Shift</key><key>=</key></keyseq>,
|
||||
so a bare zoom-in already arrives with Shift set and Shift could not mean anything.
|
||||
Ctrl/Alt mirror the box-zoom drags instead.</p></item>
|
||||
<item><p>A <em>pinned</em> view is navigable within its own data but cannot leave it.
|
||||
While an event is held, a capture is filling, or the view is paused, a zoom <em>in</em>
|
||||
works normally — "not outside the event" is not "not readable" — but a pan or a zoom out
|
||||
is clamped to the data.</p></item>
|
||||
<item><p>Nothing you do on the plot changes the <em>time window</em> — only the header
|
||||
control does. A zoom or a pan moves the view inside the window; <key>f</key> (and
|
||||
<gui>Fit X axis</gui>) is the <em>undo</em> of a zoom, so a 2 s window zoomed into reads
|
||||
2 s again. The window is more than the axis: it is also the trigger's event span and how
|
||||
the samples are stored, so a wheel notch that redefined it would re-plan the buffer and
|
||||
drop your history.</p></item>
|
||||
<item><p>The axis does not scroll — the <em>data</em> does. A live axis is a fixed
|
||||
<code>[-window .. 0]</code> with the newest sample at 0, so the labels stand still and
|
||||
the trace rolls under them. The cursors stand still with the labels (they are positions
|
||||
on screen, so a cursor you placed stays where you put it, over newer data each frame),
|
||||
while a measurement's marks travel with the sample they measured and leave the view when
|
||||
it does.</p></item>
|
||||
<item><p>A live view is clamped to the window you asked for, not to what the ring
|
||||
happens to hold. That is why a buffer that has only just started filling leaves its
|
||||
left-hand side empty instead of squeezing the window down to the first few
|
||||
samples.</p></item>
|
||||
<item><p>Zooming hard into a held capture is exact. The renderer finds the visible
|
||||
samples by binary search over the real timestamps, never over a nominal sample grid, so
|
||||
a capture whose clock wandered does not drift away from its axis as you zoom.</p></item>
|
||||
<item><p>A trace holding far more samples than the plot has pixels is drawn <em>one
|
||||
pixel column at a time</em>: each column is a single vertical line from the lowest to
|
||||
the highest value that falls in it. Drawn as a polyline instead, such a window puts
|
||||
three or four near-vertical lines in every column at slightly different sub-pixel
|
||||
positions, and how they overlap beats against the pixel grid — which reads as bands of
|
||||
light and dark that <em>crawl</em> as you pan. One line per column removes the beat, and
|
||||
it is also much faster: a 1 MS/s view costs a couple of thousand line segments a frame
|
||||
instead of a couple of hundred thousand.</p></item>
|
||||
<item><p>Those columns are <em>graded by brightness</em>, the way a digital-phosphor
|
||||
scope reads: bright where the signal spent its time, faint where it only passed through.
|
||||
A column's highest and lowest value are the same whether the samples in it are spread
|
||||
evenly or piled at its two ends, so the grading is what tells the two apart — the flat
|
||||
parts of a square wave come out solid and the edges between them faint. It is read from
|
||||
a finer division of each column rather than from the line that is drawn, and it costs
|
||||
about a millisecond a frame.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>Pause a live view, then press <keyseq><key>Ctrl</key><key>+</key></keyseq>: x narrows
|
||||
and y does not move. Press <keyseq><key>Alt</key><key>+</key></keyseq>: y narrows and x
|
||||
does not. Eight doublings into a held 2 s capture, the trace still reaches both margins —
|
||||
the zoom probe reports <code>gap_px</code> and <code>end_px</code> within a pixel or two
|
||||
at every step.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,169 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="overview">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#main"/>
|
||||
<desc>What udpscope plots, the four parts of the window, and one pass through the
|
||||
application from connecting to saving.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Overview</title>
|
||||
|
||||
<section id="what">
|
||||
<title>What udpscope is</title>
|
||||
<p>udpscope plots a live UDPStreamer source. One window holds up to eight rows of plots,
|
||||
each with up to four 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
|
||||
<cmd>--buffer-mb</cmd>; the drawing thread reads whatever that ring holds when a frame
|
||||
starts, which is why a trace never makes the window stutter.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>The time axis is <em>relative to now</em>. While streaming it is a fixed
|
||||
<code>[-window .. 0]</code> with the newest sample at 0, so x reads as an age:
|
||||
<code>-1.5s</code> 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.</p>
|
||||
|
||||
<figure>
|
||||
<title>The udpscope window on a live source: the header bar above the plots, the side
|
||||
panel on the right, and the status bar along the bottom.</title>
|
||||
<media type="image" mime="image/png" src="figures/main-window.png" width="1280" height="800">
|
||||
<p>One figure of three rows with four signals enabled, the Measure tab open in the
|
||||
side panel, and the status bar reading <gui>live</gui>.</p>
|
||||
</media>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<p>Start the app with a source
|
||||
(<cmd>udpscope --server 127.0.0.1 --port 8000</cmd>), or start it with no
|
||||
<cmd>--server</cmd> 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 <cmd>--multicast</cmd> with the group and <cmd>--interface</cmd> with the local
|
||||
address.</p>
|
||||
|
||||
<p>The signals arrive <em>disabled</em>. 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.</p>
|
||||
|
||||
<note style="tip">
|
||||
<p>See <link xref="source">Choosing a source</link> for the chooser and the multicast
|
||||
options in full.</p>
|
||||
</note>
|
||||
</section>
|
||||
|
||||
<section id="window">
|
||||
<title>The window</title>
|
||||
<terms>
|
||||
<item>
|
||||
<title>The header bar</title>
|
||||
<p>Left to right: the time window (<gui>2 s</gui>), the trigger and the pause, the
|
||||
events, cursors and side-panel switches, and the menu, which holds
|
||||
<gui>Fit to data</gui>, the <gui>Layout editor</gui>, <gui>Export</gui>,
|
||||
<gui>Keyboard shortcuts</gui>, <gui>Help</gui> and <gui>About</gui>.</p>
|
||||
</item>
|
||||
<item>
|
||||
<title>The side panel</title>
|
||||
<p>Two tabs. <gui>Measure</gui> holds the cursors readout and the measurements
|
||||
derived from it; <gui>Signals</gui> holds the trigger and the channel list, where a
|
||||
math channel is a row of that one list under a <gui>MATH</gui> heading whose
|
||||
<gui>+</gui> 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.</p>
|
||||
</item>
|
||||
<item>
|
||||
<title>The plot</title>
|
||||
<p>Up to eight rows, each with its own y axis and up to four traces, all on one shared
|
||||
time axis. Right-click a row for its own menu (fit, add a signal, remove a trace).</p>
|
||||
</item>
|
||||
<item>
|
||||
<title>The status bar</title>
|
||||
<p>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 below is what a held capture looks like: <gui>event held</gui>, the
|
||||
axis pinned to the event, and the trigger marker at the edge.</p>
|
||||
</item>
|
||||
</terms>
|
||||
|
||||
<figure>
|
||||
<title>A held capture: the trigger has latched, the state reads
|
||||
<gui>event held</gui>, and the axis is pinned to the event rather than to the live
|
||||
edge.</title>
|
||||
<media type="image" mime="image/png" src="figures/trigger-capture.png" width="1280" height="800">
|
||||
<p>The trigger marker sits at x = 0 and the pre-trigger share of the window is drawn
|
||||
to its left.</p>
|
||||
</media>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section id="first">
|
||||
<title>The first five minutes</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p><em>Connect.</em> With no <cmd>--server</cmd>, pick a stored session or type a host
|
||||
and port.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p><em>Enable a signal</em> 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.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p><em>Drag it onto a row.</em> 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.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p><em>Set the window</em> 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.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p><em>Pause</em> (<key>space</key>) to look at a still picture; the producer keeps
|
||||
filling the buffer behind it, and resuming shows what arrived meanwhile rather than a
|
||||
hole.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p><em>Arm the trigger</em> (<key>a</key>) 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.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p><em>Measure it</em> — the measurements pane computes a statistic of the visible
|
||||
window, and <gui>show on plot</gui> marks it as a ruler or a pair of markers.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p><em>Save the data</em> with <gui>Export visible range</gui>, which writes the
|
||||
plotted signals over the window at the stored rate to <file>.npy</file>,
|
||||
<file>.csv</file> or <file>.mf3</file> (MDF 3.30).</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>A signal nobody enables costs nothing — no buffer, no socket work, no
|
||||
measurements.</p></item>
|
||||
<item><p>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.</p></item>
|
||||
<item><p>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.</p></item>
|
||||
<item><p>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.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,61 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="pause">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#viewing"/>
|
||||
<link type="seealso" xref="trigger"/>
|
||||
<desc>Pausing freezes the display without stopping the stream, and the status bar says how
|
||||
long you have been paused.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Pause and play</title>
|
||||
|
||||
<p>Pause freezes the <em>display</em>, not the acquisition. The producer keeps filling the
|
||||
ring at full rate; the view binds an immutable snapshot of the window that was on screen,
|
||||
and the axis is pinned to it.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>Press the pause button in the toolbar, or <key>Space</key>.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>Press it again to resume, which rebinds the live ring and puts the window back on
|
||||
the newest data.</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>Nothing is lost while paused. Because the producer never stops, resuming shows
|
||||
current data with no hole in the history behind it.</p></item>
|
||||
<item><p>The axis is pinned to the <em>snapshot's own stamps</em>, not to the window that
|
||||
was requested. A window reaching back past the oldest retained sample yields a shorter
|
||||
snapshot, and the pinned axis is that shorter span — so you see a narrower view rather
|
||||
than an empty margin.</p></item>
|
||||
<item><p>A paused view is pinned exactly as a held trigger event is: zoom in freely, but
|
||||
a pan or a zoom out is clamped to the snapshot's data.</p></item>
|
||||
<item><p>Pausing is the easiest way to measure a layout or read a cursor precisely,
|
||||
because nothing under the pointer moves between frames.</p></item>
|
||||
<item><p>The status bar says how long you have been paused — wall time, since the stream
|
||||
itself never stopped. It is there so a frozen picture cannot be mistaken for a live one
|
||||
that went quiet.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>Pause with a 2 s window over a ring holding 1.4 s: the frozen axis reads
|
||||
<code>[-1.40 .. 0.00]</code> rather than <code>[-2.00 .. 0.00]</code> with 0.6 s of blank.
|
||||
The pause probe reports <code>ring_produced</code> still growing across the pause and
|
||||
<code>event_size</code> non-zero exactly while paused.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,93 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="sessions">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#output"/>
|
||||
<link type="seealso" xref="source"/>
|
||||
<desc>Per-source configuration files: where they live, what they persist, and how the
|
||||
chooser and the Configurations dialog use them.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Sessions and settings</title>
|
||||
|
||||
<p>udpscope stores named sessions — <em>configurations</em> — per source, so reopening a
|
||||
source gives you the scope you closed, and one source can hold several arrangements for
|
||||
different experiments. There is no global preferences file: the session <em>is</em> the
|
||||
settings.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<p>Nothing to open — the file is written as you work. Its path is in the About dialog, and
|
||||
the directory is <file>$XDG_CONFIG_HOME/udpscope</file>, or <file>~/.config/udpscope</file>
|
||||
when that is unset. Each source has a directory <file><host>_<port>/</file>
|
||||
holding one <file><name>.toml</file> per configuration and an <file>active</file>
|
||||
file naming the one it is on.</p>
|
||||
</section>
|
||||
|
||||
<section id="configs">
|
||||
<title>Configurations</title>
|
||||
<p><gui>Configurations…</gui> in the menu lists the running source's configurations: click
|
||||
a row to open it, <gui>×</gui> to delete it, or type a name and a note and press
|
||||
<gui>Save this session</gui> to keep the current arrangement under a new one. Opening a
|
||||
configuration saves the current session first, so switching never loses what it switches
|
||||
from.</p>
|
||||
|
||||
<p>The startup chooser (start with no <cmd>--server</cmd>) lists one row per configuration,
|
||||
with its note and its settings, so two arrangements of one source are told apart before
|
||||
they open.</p>
|
||||
|
||||
<p>A configuration also carries the settings that can only be decided when the buffers are
|
||||
allocated: the <gui>buffer size</gui> (<cmd>--buffer-mb</cmd>), relative time, and the
|
||||
packet capture's ring size. Opening a configuration from the chooser applies all three;
|
||||
switching inside a running app applies the rest and says in the status line that these take
|
||||
effect on the next start.</p>
|
||||
|
||||
<p>The chooser's form sets the first two for a new host, and its pencil button edits them
|
||||
on an existing configuration — along with the name and the note. Renaming a configuration,
|
||||
or editing the address it points at, moves it: the old file is removed rather than left
|
||||
behind.</p>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>What a configuration persists: the time window, the rows and each row's slot
|
||||
signals, the calibration, the trace colours, which signals were enabled, the math
|
||||
channels with their graphs, the measurements pane — and the startup settings: buffer
|
||||
size, relative time and the packet capture's ring size.</p></item>
|
||||
<item><p>Signal identity is the <em>name</em> throughout, because a new CONFIG renumbers
|
||||
the channels.</p></item>
|
||||
<item><p>A file written by a run against a source whose streamer was down would name no
|
||||
signals at all, so such a run does <em>not</em> write: a configured session is never
|
||||
overwritten by a run that configured nothing. (The chooser hides such a file for the same
|
||||
reason.)</p></item>
|
||||
<item><p>The multicast group and the interface live <em>inside</em> the file, because its
|
||||
name cannot carry them. A file from an older build has neither key and loads as unicast
|
||||
on the default route.</p></item>
|
||||
<item><p>The chooser lists only configurations that name at least one signal, which is
|
||||
what keeps it the sessions you configured rather than every port you have ever tested
|
||||
against.</p></item>
|
||||
<item><p>Deleting a configuration is a supported way to start over; the chooser's
|
||||
<gui>×</gui> and the dialog's do exactly that.</p></item>
|
||||
<item><p>A session from before configurations existed is imported once as the
|
||||
configuration <code>default</code>, and the old file is kept beside the directory as
|
||||
<file><host>_<port>.toml.imported</file>.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>Connect to <code>127.0.0.1:8000</code>, drop three signals on row 1, set a 2 s window,
|
||||
and save the session as <code>bench</code> with a note.
|
||||
<file>~/.config/udpscope/127.0.0.1_8000/bench.toml</file> now holds
|
||||
<code>window_s=2 comment="…"</code> with
|
||||
<code>signals = ["sine", "ramp", "noise", ""]</code>, and the next run reports
|
||||
<code>layout loaded from …/127.0.0.1_8000/bench.toml</code> with those signals already
|
||||
acquired.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,70 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="signals">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#main"/>
|
||||
<link type="seealso" xref="layout"/>
|
||||
<desc>How the source's CONFIG becomes channels, what enabling a signal starts, and how a
|
||||
signal's scale and offset are calibrated.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Signals and calibration</title>
|
||||
|
||||
<p>The source's CONFIG message describes its signals: a name, a unit and a type. udpscope
|
||||
turns each enabled signal into a <em>channel</em> — a ring of values beside the shared time
|
||||
ring — and a channel is what a trace plots. Calibration is a scale and an offset applied at
|
||||
ingest.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>Open the side panel (<key>s</key>), which lists every signal with a switch.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>The <gui>Signal list</gui> item in the overflow menu opens the editor where the
|
||||
unit and the calibration live.</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>Signals start disabled, and enabling one is what starts acquiring it. A trigger
|
||||
on a signal nobody acquires can never fire, so arming a trigger enables its signal for
|
||||
you.</p></item>
|
||||
<item><p><gui>Calibrate</gui> in a signal's row menu opens the gain and offset it is
|
||||
<em>currently</em> drawn through, so a trace already scaled by a gain of 2 shows 2 —
|
||||
pressing <gui>Apply</gui> confirms it rather than resetting it. A signal nobody
|
||||
calibrated shows the identity, 1 and 0.</p></item>
|
||||
<item><p>Calibration is applied <em>at ingest</em>, so changing it re-plans the buffer
|
||||
and drops the history. So does enabling or disabling a signal, and so does a new
|
||||
CONFIG. An empty plot for a second after one of those is the ring refilling, not a
|
||||
fault.</p></item>
|
||||
<item><p>The stream's first signal is usually its <em>timestamp</em>, and that one is
|
||||
never acquired. It cannot be plotted, measured or used as a math source, and udpscope
|
||||
refuses to put it in a row rather than binding a trace that would read nothing for
|
||||
ever.</p></item>
|
||||
<item><p>A signal's colour follows the <em>signal</em>, not the slot, so the same signal
|
||||
on two rows is one colour and the side panel's chip identifies it unambiguously. A
|
||||
signal on no row gets a grey chip rather than none, so the panel's rows keep their
|
||||
height.</p></item>
|
||||
<item><p><em>Click a coloured chip to change that signal's colour.</em> A grey chip
|
||||
names no trace, so it is not clickable. The colour is saved with the session;
|
||||
<gui>Default colour</gui> in the signal's ⋯ menu gives the palette entry back.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>A source declaring <code>time</code>, <code>sine</code>, <code>ramp</code> and
|
||||
<code>noise</code> yields three plottable signals. Enable all three and the exit dump
|
||||
reads <code>3 signals, 3 channels</code>; <code>time</code> is not among them.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,75 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="source">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#main"/>
|
||||
<link type="seealso" xref="sessions"/>
|
||||
<desc>How udpscope is pointed at a source: the command line, the startup chooser, and
|
||||
opening an exported waveform instead of a live stream.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Choosing a source</title>
|
||||
|
||||
<p>A source is a host and a port, optionally reached through a multicast group on a named
|
||||
interface. Pass it on the command line, or let udpscope ask.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>Run with <cmd>--server</cmd> and the app connects straight away.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>Run <em>without</em> <cmd>--server</cmd> and a chooser appears first: one row per
|
||||
stored <em>configuration</em> (<code>host:port · name</code>, its note, and the
|
||||
settings it carries), newest first, plus a <gui>New host…</gui> form that takes the
|
||||
address <em>and</em> the configuration it belongs to — its name and note, the
|
||||
<gui>buffer size</gui>, and whether time is relative or absolute. A row's pencil
|
||||
button opens the same form on that configuration, pre-filled, and changes to its name
|
||||
or address move it (the old file is deleted).</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>The second page, <gui>Waveforms</gui>, opens an <em>exported</em> run instead of a
|
||||
live source: the files opened before (newest first; one that has since been deleted is
|
||||
marked <em>missing</em>) and an <gui>Open…</gui> button for a new one. The same file
|
||||
can be opened straight from the command line with <cmd>--open FILE</cmd>. A waveform
|
||||
is a source that never arrives — no socket, no acquisition, no starvation notice — and
|
||||
every instrument works on it: cursors, measurements, math channels and another export.
|
||||
Only <file>.npy</file> files are read.</p>
|
||||
</item>
|
||||
</steps>
|
||||
|
||||
<p>The chooser runs before any buffer is allocated and before any socket is opened, so
|
||||
declining it costs nothing.</p>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>The list is the sessions that <em>named a signal</em>. A session whose streamer
|
||||
was down stores empty slot names and is filtered out, which is what keeps the list the
|
||||
sources you actually configured rather than every port you have ever typed.</p></item>
|
||||
<item><p>A stored row reconnects the way it was reached. The multicast group and the
|
||||
interface are written inside the session file, because the file's name
|
||||
(<file>host_port.toml</file>) cannot carry them.</p></item>
|
||||
<item><p><cmd>--multicast</cmd> still needs <cmd>--server</cmd>: the CONFIG message is
|
||||
fetched from the server, not from the group.</p></item>
|
||||
<item><p>The <gui>×</gui> beside a row deletes that session file. Nothing else is
|
||||
removed.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p><cmd>udpscope</cmd> with two stored sessions lists
|
||||
<code>127.0.0.1:8000 plots=1 signals=3</code> and
|
||||
<code>10.0.0.5:8001 plots=2 signals=4 multicast=239.1.2.3 interface=10.0.0.9</code>.
|
||||
Activating the first reports <code>[sources] picked 127.0.0.1:8000</code> and continues
|
||||
into the scope with that session's layout already loaded.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,72 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="status">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#output"/>
|
||||
<desc>The status bar's two tiers, the activity badge, the fixed state words, and the ⓘ
|
||||
detail popover.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Status bar and diagnostics</title>
|
||||
|
||||
<p>The status bar is two tiers. The bar itself is one glance line — the state, the rate and
|
||||
the window — and everything else is behind the ⓘ button at its right-hand end.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<p>The bar is always visible under the plot. Click <gui>ⓘ</gui> for the detail popover.</p>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>The badge is one glyph in four colours: green <em>alternating</em> lit and dim
|
||||
means data is arriving, and a steady colour means it is not. A badge that never blinks is
|
||||
not a working badge. Of the two steady colours, <em>grey</em> is no stream at all or a
|
||||
pipeline you stopped (the tooltip says which), and <em>yellow</em> is a source that
|
||||
answered but has sent no configuration.</p></item>
|
||||
<item><p>The state field is one word from a fixed set: <code>connecting</code>,
|
||||
<code>live</code>, <code>paused</code>, <code>armed</code>, <code>capturing</code>,
|
||||
<code>event held</code>, <code>disarmed</code>. Two states add one field each: in trigger
|
||||
mode, how long ago the last event was; while paused, how long you have been paused.</p></item>
|
||||
<item><p><code>connecting</code> means there is no <em>signal list</em> yet — either no
|
||||
session or a session with no configuration behind it. Every other word claims a signal
|
||||
list, so until one arrives there is nothing for them to describe: nothing can be enabled
|
||||
and no data is owed.</p></item>
|
||||
<item><p>The paused figure is <em>wall</em> time, not stream time — the producer never
|
||||
stops while paused, so it answers "how long have I been looking at this picture".</p></item>
|
||||
<item><p>Nothing on the bar may change width with the stream, because the bar sits
|
||||
directly above the plot and a wider label would relayout it. That is why the rate
|
||||
collapses to one figure or a range rather than listing per channel, and why an elapsed
|
||||
time steps to a coarser unit instead of growing digits.</p></item>
|
||||
<item><p><code>gaps</code> counts packets the receiver knows it missed. It is the number
|
||||
that matters: a non-zero count means samples are absent from the buffer, not merely
|
||||
late.</p></item>
|
||||
<item><p><code>reconnects</code> counts sessions re-established with the source. Over
|
||||
unicast a climbing count usually means a second client is taking the source's single
|
||||
session.</p></item>
|
||||
<item><p>The starvation notice — <em>connected, but no data is arriving</em> — is that
|
||||
same situation seen from the other side: the signal list is here, so something took the
|
||||
stream away. A source that has sent <em>no</em> configuration gets a different notice,
|
||||
<em>trying to connect</em>, because nothing was taken away — pointing the app at a port
|
||||
where nothing answers is that case, and the usual cause is a streamer that is not running
|
||||
or is serving another port.</p></item>
|
||||
<item><p><code>stored</code> versus <code>wire</code> in the popover is the decimation.
|
||||
They differ exactly when the window is too long to store at full rate.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>The bar reads <code>● live · 10 kS/s · 2 s window</code>. Behind ⓘ:
|
||||
<code>Stream connected, configured / packets 717 · frames 716 · gaps 0 / 3 signals · 3
|
||||
channels</code>, <code>Buffer 200.0 MB · 1677 s span · time x1 / stored 10 kS/s (wire
|
||||
10 kS/s)</code>, <code>Plot 1 3 traces · 195000 samples</code>,
|
||||
<code>Reconnects 0</code>.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
@@ -0,0 +1,155 @@
|
||||
<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 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>Search the buffer</gui> is at the foot of the same panel.</p>
|
||||
</item>
|
||||
</steps>
|
||||
|
||||
<figure>
|
||||
<title>A held capture: <gui>event held</gui> in the status bar, the axis pinned to the
|
||||
event, and the trigger marker at x = 0.</title>
|
||||
<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="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> 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.</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.</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>
|
||||
@@ -0,0 +1,66 @@
|
||||
<page xmlns="http://projectmallard.org/1.0/"
|
||||
type="topic" style="task"
|
||||
id="window">
|
||||
|
||||
<info>
|
||||
<link type="guide" xref="index#viewing"/>
|
||||
<link type="seealso" xref="navigate"/>
|
||||
<desc>The time window control: how to set it, and why a long window is decimated rather
|
||||
than refused.</desc>
|
||||
<credit type="author copyright">
|
||||
<name>Martino Ferrari</name>
|
||||
</credit>
|
||||
</info>
|
||||
|
||||
<title>Time window</title>
|
||||
|
||||
<p>The window is how much time the plot shows. Every row shares one window: the figure has
|
||||
a single x link group, so the rows cannot drift apart. The trigger's event span follows it,
|
||||
and the pre-trigger share is a share of <em>this</em> window.</p>
|
||||
|
||||
<section id="reach">
|
||||
<title>How to reach it</title>
|
||||
<steps>
|
||||
<item>
|
||||
<p>The header bar's window button opens a popover: type a number, pick a unit under
|
||||
it, or step a decade with the arrows.</p>
|
||||
</item>
|
||||
<item>
|
||||
<p>The button's own label is a reading of the current window in a natural unit,
|
||||
width-locked so a change of window cannot widen the header bar.</p>
|
||||
</item>
|
||||
</steps>
|
||||
</section>
|
||||
|
||||
<section id="knowing">
|
||||
<title>Worth knowing</title>
|
||||
<list>
|
||||
<item><p>A window too long to store at the wire rate is <em>decimated, not refused</em>.
|
||||
The shared clock takes a power-of-two stride, and each group of samples is
|
||||
<em>averaged</em> rather than strided, so a tone above the stored Nyquist is attenuated
|
||||
instead of aliasing to a false low frequency.</p></item>
|
||||
<item><p>The stamp stored for a group is the group's <em>mean</em>, which is where the
|
||||
averaging puts the value. Stamping at the end of a group would shift every trace half a
|
||||
stored sample late.</p></item>
|
||||
<item><p>Moving the window across a decimation boundary rebuilds the buffer and drops
|
||||
the history. Walking it inside one decade is free.</p></item>
|
||||
<item><p><em>This control is the only thing that changes the window.</em> A zoom, a pan
|
||||
or a fit moves the view <em>inside</em> it and leaves it alone — which is the point of
|
||||
the previous bullet: a wheel notch that redefined the window could re-plan the buffer
|
||||
and throw your samples away.</p></item>
|
||||
<item><p>The trigger sees the <em>stored</em> (filtered) signal, not the wire signal,
|
||||
because an edge found on a sample that is not in the buffer could not be cut out of
|
||||
it.</p></item>
|
||||
</list>
|
||||
</section>
|
||||
|
||||
<section id="example">
|
||||
<title>Example</title>
|
||||
<p>1 MS/s with 200 MB of buffer. A 2 s window stores every sample
|
||||
(<code>time x1</code>). Widen it to 600 s and the stride becomes <code>x16</code>: 625 S/s
|
||||
stored, anti-aliased at 312.5 Hz. A 2 kHz sine seen through that window reads
|
||||
<code>y=[-0.059, 0.079]</code> — attenuated to about 6%, not folded down to a
|
||||
full-amplitude 125 Hz one.</p>
|
||||
</section>
|
||||
|
||||
</page>
|
||||
Reference in new issue
Block a user