Add synthetic array (waveform) DSP support + UX improvements
Adds full array/waveform support through the synthetic DSP engine: a dsp.Sample value model (scalar or []float64), array ops (index, slice, sum, mean, min, max, length, fft) with an in-tree radix-2 FFT, and static type propagation (OpOutputType) that the editor mirrors to colour wires by data type and flag invalid wirings. Stateful filters and lua stay scalar-only. Adds a waveform plot mode (x-vs-index trace). Also: errored-node hover reasons, S/N add-signal/add-node HUD shortcuts in the synthetic editor, and view-mode widgets that blend with the canvas background (chrome kept in edit mode). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+26
-11
@@ -41,7 +41,7 @@
|
||||
| ---------------- | --------------------------------------------------------------- |
|
||||
| `preact` 10 | Virtual DOM UI framework |
|
||||
| `uPlot` | Extremely fast time-series/line plot (canvas-based, < 40 kB) |
|
||||
| `Apache ECharts` | FFT, waterfall, histogram, bar, logic analyser plots |
|
||||
| `Apache ECharts` | FFT, waterfall, histogram, bar, logic analyser, waveform plots |
|
||||
| `uplot.css` | uPlot default stylesheet |
|
||||
|
||||
**Intentionally excluded:** React, Vue, Svelte, Konva, WebGPU, jQuery, npm at runtime.
|
||||
@@ -234,15 +234,30 @@ endpoints and for any change to a panel's `<logic>` block.
|
||||
|
||||
**Built-in node types:**
|
||||
|
||||
| Node type | Parameters | Description |
|
||||
| ------------ | ----------------------------------- | ------------------------------------------------ |
|
||||
| `source` | `ds`, `name` | Reads a signal from any data source |
|
||||
| `gain` | `factor` | Multiplies by a constant |
|
||||
| `offset` | `value` | Adds a constant |
|
||||
| `moving_avg` | `window` (samples) | Rolling mean |
|
||||
| `lowpass` | `freq` (Hz), `order` (1–8) | Cascaded IIR Butterworth-style low-pass filter |
|
||||
| `formula` | `expr` | Inline math expression (variables: `a`, `b`, …) |
|
||||
| `lua` | `script` | Arbitrary Lua 5.1 code with persistent state |
|
||||
| Node type | Parameters | In→Out | Description |
|
||||
| ---------------- | --------------------------- | -------------- | ----------------------------------------------- |
|
||||
| `source` | `ds`, `name` | — | Reads a signal from any data source |
|
||||
| `gain` | `gain` | elementwise | Multiplies by a constant |
|
||||
| `offset` | `offset` | elementwise | Adds a constant |
|
||||
| `add`/`subtract` | — | elementwise | Sum of inputs / `a − b` |
|
||||
| `multiply`/`divide` | — | elementwise | Product of inputs / `a ÷ b` |
|
||||
| `clamp` | `min`, `max` | elementwise | Constrains to a range |
|
||||
| `threshold` | `threshold`, `high`, `low` | elementwise | Comparator output |
|
||||
| `moving_average` | `window` (samples) | scalar-only | Rolling mean |
|
||||
| `rms` | `window` (samples) | scalar-only | Rolling RMS |
|
||||
| `derivative` | — | scalar-only | Time derivative (per-sample `dt`) |
|
||||
| `integrate` | — | scalar-only | Trapezoidal integral |
|
||||
| `lowpass` | `freq` (Hz), `order` (1–8) | scalar-only | Cascaded IIR Butterworth-style low-pass filter |
|
||||
| `expr` | `expr`, `vars` | elementwise | Inline math expression (named inputs) |
|
||||
| `lua` | `script`, `vars` | scalar-only | Arbitrary Lua 5.1 code with persistent state |
|
||||
| `index` | `i` | array→scalar | Element `i` of a waveform (bounds-checked) |
|
||||
| `slice` | `start`, `end` | array→array | Sub-range of a waveform (clamped) |
|
||||
| `sum`/`mean` | — | array→scalar | Σ / average of a waveform |
|
||||
| `min`/`max` | — | array→scalar | Reduction of a waveform |
|
||||
| `length` | — | array→scalar | Element count of a waveform |
|
||||
| `fft` | — | array→array | Magnitude spectrum (zero-padded to next pow-2) |
|
||||
|
||||
**Scalar vs waveform values:** A value flowing through the graph is a `dsp.Sample` — either a scalar `float64` or a `[]float64` waveform (the array-aware counterpart of EPICS `TypeFloat64Array`). *Elementwise* ops broadcast over arrays (scalar inputs act as constants; array inputs must share a length). *Reduction*/*producer* ops (`index`/`slice`/`sum`/…/`fft`) operate natively on waveforms. *Scalar-only* ops (stateful filters + `lua`) reject array inputs, since their per-evaluation state cannot be split across array lanes. `OpOutputType` (`internal/dsp/types.go`) propagates types statically at compile time to reject invalid wirings and to report the synthetic's metadata type; the editor mirrors these rules in `web/src/lib/synthTypes.ts` to colour wires by data type (scalar vs array) and flag type-incompatible links. Runtime `Sample` typing is authoritative.
|
||||
|
||||
**Low-pass filter implementation:** Cascaded first-order IIR sections. Each stage computes `y = y_prev + α·(x − y_prev)` where `α = dt / (RC + dt)` and `RC = 1/(2π·fc)`. `dt` is computed per sample from source timestamps so the filter is correct for event-driven (non-uniform) data.
|
||||
|
||||
@@ -353,7 +368,7 @@ The edit canvas is a free-form HTML div with absolutely positioned widget compon
|
||||
View mode renders widgets as absolutely positioned Preact components on a scrollable canvas div:
|
||||
|
||||
- Each widget subscribes to its signal store(s) in a `useEffect` and re-renders only when values change.
|
||||
- uPlot (time series) and ECharts (histogram, bar, FFT, waterfall, logic analyser) manage their own canvas elements inside their widget component.
|
||||
- uPlot (time series) and ECharts (histogram, bar, FFT, waterfall, logic analyser, waveform) manage their own canvas elements inside their widget component. The `waveform` plot renders a waveform (array) signal's latest `[]float64` as an x-vs-index trace, replacing the trace on each update.
|
||||
- Plot widgets maintain a rolling ring buffer of 200,000 samples per signal for smooth long-window display.
|
||||
- Step-hold interpolation: when multiple signals at different update rates share a plot, the most recent value is carried forward to fill the shared time axis correctly.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user