Three pages opened in nothing in Yelp -- overview (twice), math, trigger -- while every check passed. They are exactly the pages whose <figure> had a sentence for a <title> and no <desc>: a Mallard <figure> is title?, desc?, media, and the page is refused without the description. The idiomatic shape, as the shipped sets on this machine write it, moves the sentence into <desc> and leaves the <title> a name. Note trigger-capture.png is referenced from both overview and trigger, which is why those two pages are the ones that were noticed. The gap was in the check, not in its reach: tools/check_help.sh ran links/media/orphans/ids and an HTML render, and all four are silent about a page that is well linked, well parsed and unrenderable. `yelp-check validate` is the one that would have caught it, and it cannot run here: it generates the RelaxNG schema with xsltproc, but the schema includes the base grammar over the network and this machine's libxml cannot fetch. So the structural rules this project has actually needed are checked with xmllint's XPath, offline, each written after a page needed it. One trap, paid for while writing it: a Mallard page declares xmlns="http://projectmallard.org/1.0/", XPath 1.0 has no default namespace, so `//figure` matches nothing and every rule passes -- the first version of this check reported "ok" for the very defect it was written for. Every expression uses local-name() for that reason, and the script says so where the next reader will be. Verified: on the committed pages the rules exit 1 and name the three figures, which is the failing-before case kept as the regression test; on the fixed pages all five rules pass and yelp-build renders all 15; make -C build test-help goes from red to green.
111 lines
5.6 KiB
Plaintext
111 lines
5.6 KiB
Plaintext
<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</title>
|
|
<desc>Nodes as boxes on a canvas, the orange dot marking the output, and the selected
|
|
node's parameters beside it.</desc>
|
|
<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>
|