<b>Math channels</b>

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.

<b>How to reach it</b>

The math button in the toolbar, or <tt>g</tt>. The pane's <tt>+</tt> adds a channel — a picker
chooses the op — and opening one shows the graph editor: nodes as boxes, click a box to select it
and edit its parameters beside the canvas.

<b>Graph gestures:</b> <b>drag</b> 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 <tt>Delete</tt> to remove
it; drag a box to move it. <b>Wheel</b> zooms about the pointer. <b>Drag the background</b> to
pan. <tt>f</tt> fits all nodes into view; <tt>Ctrl+0</tt> resets the view. <tt>Tidy</tt> in the
toolbar lays the graph out in topological columns. The orange dot marks the output node; click
<i>set as output</i> to move it.

<b>Worth knowing</b>

• The graph is evaluated as an ordered list, so a node may only read ones <i>above</i> 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.
• A derived channel <i>is</i> a channel. It appears in the signal panel, can be plotted,
  measured, exported, and used as a source for another math channel.
• The math runs on the <b>stored</b> 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.
• A source may only name an <i>acquired</i> 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.
• The output defaults to the <i>last</i> node, and a node added to a linear graph becomes the
  output. Choosing an earlier node explicitly is respected.
• Every numeric field starts at its own identity value — gain 1, offset 0 — not at the bottom
  of its range.

<b>Example</b>

<tt>[sine] → [gain x2]</tt> gives a channel reading <tt>y=[-2, 2]</tt>. 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.

<b>Why an operation sometimes does nothing</b>. A graph's <i>output</i> 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. <i>Set as output</i> moves the dot.

<b>Apply is insensitive while the graph is incomplete.</b> A line beneath the form says why:
an input that is not connected, no output chosen, or a <i>channel</i> 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.

<b>The filters are real filters.</b> 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 <i>stored</i> 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.
