udpscope: an oscilloscope view for a UDPStreamer source

GTK4 and libadwaita. It receives one UDP stream through the vendored
udpsclient, buffers it in a large ring, and draws it with the vendored
plotter: a trigger with a pre/post event window, a pause that freezes the
window, cursors, measurements, math channels in a node editor, an event cache,
named configurations, preferences, and an export of the visible window to
.npy/.csv. Waveform files can be opened and analysed in place.

The interface is two threads -- a producer that owns the socket and the GTK
main thread that owns every widget and the GL context -- sharing one model
under one mutex, with samples crossing through lock-free rings.

deps/plotter is a submodule pinned to the commit this tree builds against;
deps/udpsclient is vendored in place. docs/claude/ documents the rules that
cost a defect to learn, and SPECS.md is the requirement document.
This commit is contained in:
Martino Ferrari committed 2026-10-11 15:21:40 +02:00
1 parent 8f54841c2c
commit 0e256764d5
41 files changed
+2081 -154

No files matched your search

@@ -75,3 +75,9 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
tells a real gesture from its own echo, so writing the group alone is reverted to the zoomed
range on the next frame. `fit_all()` is the y fit plus this, shared rather than duplicated so
the two cannot drift apart.
- **Relative time is decided by the newest statement, not by the default.** `Config::relative_time_specified`
is set by `--relative-time` and by the preferences — which the source chooser can reach before the run
is planned, so that is the one place a preference still changes *this* run. Both the list's own copy
(`apply_source()`) and a source's stored configuration (`apply_stored_settings()`) only fill in when
nothing newer was said. Without the marker, clicking a source row after applying the preference
silently reverted it — `[sources] picked ... relative=0`, with the dialog having just written `1`.