From 0493f3fe6ee5d21f68a9fb252fd3126195030129 Mon Sep 17 00:00:00 2001 From: Bernhard Trinnes Date: Thu, 27 Aug 2026 13:30:41 +0200 Subject: [PATCH 1/2] Prune the finished work and the settled questions from the docs The tasklist kept a growing record of everything already delivered, and the requirements kept four open questions of which three were struck through and answered in place. Both are history that git already holds, so drop them and leave the tasklist saying only what is next: M8, packaging and signing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01F5XUZVcyUoQrG2UgksjhcS --- docs/requirements.md | 17 ---------------- tasklist.md | 47 -------------------------------------------- 2 files changed, 64 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index c9f5603..8197834 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -363,20 +363,3 @@ The core feature is a two-phase scan: | Quasi-peak dwell makes runs long | Poor UX | Bounded peak counts, pause/resume, progress with time estimate | | GPL-3.0 vs proprietary in-house drivers | Adoption friction | Plugin API headers dual-licensed (CON-3) | -**Open questions** -1. Which instruments constitute the v1 supported set (proposal: Siglent SSA3000X/SVA1000X, Rigol - DSA800/DSA700, plus the simulated driver)? -2. ~~Plot backend: Qt Graphs 2D via a `QQuickWidget` host, or a QPainter-based widget?~~ - **Settled: QPainter.** The plot decimates a trace to a min/max envelope per pixel - column, so redraw cost follows the widget width rather than the trace length, and a - 40,001-point trace stays interactive without a QML runtime in the process. The backend - stays behind `IPlotBackend` (ADR-5), so this can be revisited without touching - application code. -3. ~~Should conducted-emission LISN control (relay switching of line/neutral) be in v1?~~ - **Settled: no.** PeakEmi drives no relays. Instead a run may carry user-supplied - commands sent at its start and at its end (FR-RUN-9), which is enough to switch a LISN, - a relay box or an antenna mast that already speaks SCPI, without PeakEmi modelling any - of them. -4. ~~Minimum Qt version: hold at 6.5 LTS or require 6.8+?~~ **Settled: 6.8 minimum.** CI - builds against it, so code that needs something newer fails there rather than in a - user's build. diff --git a/tasklist.md b/tasklist.md index 6654583..a76a696 100644 --- a/tasklist.md +++ b/tasklist.md @@ -1,55 +1,8 @@ # PeakEmi tasklist -## Done - -* **Build system** — CMakeLists (modern C++23, Qt 6), `CMakePresets.json`, - `.clang-format`, `.clang-tidy`, `.editorconfig`. -* **Core domain (`peakemi_core`)** — strong units, traces, capabilities, limit lines and - the built-in CISPR/FCC catalogue, correction chain, CISPR band table, peak detection, - run configuration with segment planning, session model and versioned JSON container, - atomic file writes, categorised + rotating logging. -* **Ports** — `ITransport` and `AbstractAnalyzerDriver`, error values via `std::expected`, - `CancelToken`. -* **HAL (`peakemi_hal`)** — SCPI parsing helpers, TCP and serial transports, driver - registry with scored `*IDN?` matching, LAN sweep and serial port enumeration. -* **Drivers (`peakemi_drivers`)** — deterministic `SimulatedDriver`, and a SCPI driver - with per-model profiles for the Siglent SSA3000X and SVA1000X and the Rigol DSA700 and - DSA800 families: each model's frequency range, point count, bandwidths and dialect, - narrowed from the family defaults as soon as the instrument identifies itself. -* **Engine** — two-phase scan → detect → verify state machine on a worker thread, with - pause/resume/abort, bounded retries, multi-pass max-hold and autosave. -* **Transports (M5)** — VXI-11 over ONC-RPC including the portmapper lookup, USBTMC over - libusb with USB hotplug discovery, and an optional VISA path resolved at run time. The - optional buses compile in or out without touching the rest of the tree. -* **Reporting (`peakemi_reporting`)** — CSV and JSON export of traces and results, PDF - report, and a report template (company, address, logo, free text) that is editable in - the UI, importable, exportable and stored as the default for new sessions. -* **User interface (`peakemi_ui`)** — main window with instrument, configuration, results - and log/SCPI-console docks, decimating spectrum plot behind `IPlotBackend`, run - controller owning the acquisition thread, session and export actions. -* **Tests** — 11 Qt Test executables: unit tests for every non-UI algorithm, component - tests against a scripted transport, UI smoke tests and a headless integration test of - the full two-phase run. -* **CI** — GitHub Actions matrix (Windows/MSVC, Ubuntu/GCC, macOS/Clang) building with - warnings as errors and running the suite headless, plus a format/clang-tidy job. -* **Release** — tag-triggered workflow that verifies the tag sits on `main` and matches - `project(VERSION ...)`, then builds and publishes an AppImage, a macOS `.dmg` and a - Windows zip. -* **Python bridge (M7)** — embedded CPython through pybind11, the `peakemi_plugin` - bindings, discovery with a SHA-256 trust store that requires explicit per-file - approval, a driver proxy turning plugin exceptions into errors with their traceback, - the plugin manager UI, a worked example driver and the versioned - [plugin API specification](docs/plugin-api.md). -* **Documentation** — README screenshots generated headlessly from the real main window - by `tools/screenshots` (`PEAKEMI_BUILD_TOOLS=ON`). - ## Next * **M8** — a notarised macOS release (the disk image, its icon, the bundle layout and ad-hoc signing are done; a Developer ID identity and notarisation credentials are not something the build can supply for itself), code signing for the Windows artifacts, and a Windows installer. -All three open questions are settled and recorded in -[requirements.md §6](docs/requirements.md#6-assumptions-risks--open-questions): the -QPainter plot backend stays, PeakEmi drives no relays and sends operator-supplied commands -around a run instead, and the minimum Qt version is 6.8. From dd274fd36743ddd978e82c031efeeb6161410fbb Mon Sep 17 00:00:00 2001 From: Bernhard Trinnes Date: Thu, 27 Aug 2026 13:30:41 +0200 Subject: [PATCH 2/2] Polish the README and correct the plugin bridge status The README still said the embedded Python plugin bridge was not implemented, which contradicted both its own status line and 8ddc94c. Say instead what is actually left, packaging, and give the plugin bridge an entry of its own that names the build option it needs and the per-file approval it enforces. Add a "Why PeakEmi" section stating what the project offers, one source tree across three platforms, no tie to an instrument vendor, visible SCPI and plain file formats, a simulator to try before buying hardware, no licence server, and close it by saying plainly where the scope stops. Show the log and console screenshot, which tools/screenshots already generated but nothing displayed, and drop the sentences the new section now duplicates. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01F5XUZVcyUoQrG2UgksjhcS --- README.md | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index aa522b5..bd2be05 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,29 @@ evaluates traces against CISPR/FCC limit lines and produces reproducible reports | Platforms | Windows 10/11, Linux (Ubuntu 22.04+), macOS 13+ | | Docs | [Contributing](CONTRIBUTING.md) · [Requirements](docs/requirements.md) · [Architecture](docs/architecture.md) · [Plugin API](docs/plugin-api.md) · [Tasklist](tasklist.md) | +## Why PeakEmi + +* **Runs where you work.** One source tree, one feature set, on Windows, Linux and macOS. +* **Not tied to one instrument vendor.** The buses are the standard ones and the driver is + chosen by scoring the instrument's `*IDN?` reply. A model nobody has written a driver for + can be added as a single Python file rather than as a patch to the application. Supported + models declare their own frequency range, point count and command dialect, so a sweep the + instrument cannot make is refused with a reason rather than sent and rejected. +* **Nothing is hidden.** Every SCPI exchange is visible in the console and in the + `--verbose` transcript. Limit lines and correction tables are documented CSV/JSON you can + write by hand, and sessions are versioned JSON — your measurements stay diffable, + scriptable and readable without PeakEmi. +* **Nothing to plug in to start.** The simulated analyzer ships with the application, so the + full loop, including the report, can be tried before any hardware is on the bench. +* **Results keep their provenance.** Every verified point carries the settings that produced + it, and the session file is the record of the run rather than a screenshot of it. +* **No licence server, no dongle, no seat count.** GPL-3.0-or-later; build it yourself and + run it offline. + +The instrument list is short and the scope is deliberately narrow — pre-compliance +measurement, not lab automation. Turntables, masts and LISNs are switched through the +[commands sent around a run](#what-works-today), not driven by PeakEmi. + ## Screenshots A completed run against the built-in simulated analyzer: a CISPR 32 class B radiated scan with @@ -40,6 +63,11 @@ bandwidth, dwell time, limit, margin and timestamp — coloured by verdict. ![Phase 2 result table](docs/images/results-table.png) +The log dock keeps the run narrative and the raw SCPI transcript side by side, so what the +application asked the instrument is always inspectable. + +![Application log and SCPI console](docs/images/log-console.png) + The screenshots are generated by driving the real main window against the simulated analyzer, so they cannot drift from the application: @@ -61,9 +89,15 @@ QT_QPA_PLATFORM=offscreen ./build/debug/bin/peakemi_screenshots docs/images selection, bounded and opt-in LAN sweep, live USB hotplug detection, serial port enumeration, an optional VISA path, and a raw SCPI console. * **Supported analyzers** — Siglent SSA3021X/3032X/3075X and SVA1015X/1032X/1075X, Rigol - DSA705/710 and DSA815/832/875. Each model carries its own frequency range, point count - and command dialect, so a sweep the instrument cannot make is refused with a reason - instead of being sent and rejected. + DSA705/710 and DSA815/832/875, each with its own frequency range, point count and + command dialect. +* **Python driver plugins** — in a build configured with `PEAKEMI_WITH_PYTHON=ON`, a + driver can be a single Python file loaded into the embedded interpreter, and the + measurement engine cannot tell it from one written in C++. A plugin is + imported only after you approve it, and the approval is recorded as a hash of the file's + contents, so an edited plugin is untrusted again until you say otherwise. See the + [plugin API](docs/plugin-api.md) and the worked + [example driver](plugins/drivers/example_sweeper.py). * **Commands around a run** — a run can send operator-supplied commands when it starts and when it ends, which is how a LISN, a relay box or a mast that speaks SCPI is switched. The closing commands are sent whether the run finished, was aborted or failed. PeakEmi @@ -77,7 +111,8 @@ QT_QPA_PLATFORM=offscreen ./build/debug/bin/peakemi_screenshots docs/images a report template that can be edited in the app, shared as a file and kept as the default. -Not yet implemented: the embedded Python plugin bridge — see [tasklist.md](tasklist.md). +What is left is packaging: a notarised macOS disk image, signed Windows artifacts and a +Windows installer — see [tasklist.md](tasklist.md). ### Instrument buses