Commit Graph
28 Commits
Author SHA1 Message Date
Martino FerrariandClaude Sonnet 4.6 5a8479cda9 feat(udpscope): per-element timestamp reconstruction from UDPS frames
Implements FrameDecoder with five timing rules that mirror
UDPSourceSession.cpp: FullArray (per-element time signal), FirstSample
and LastSample (rate-spread from anchor), accumulated scalar with
declared rate (forward-chain anchoring, immune to arrival jitter), and
PACKET burst (backward-span from previous arrival). 9 new tests, 38
pass total.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-27 20:01:30 +02:00
Martino FerrariandClaude Opus 4.6 c89decef8e docs: say that HrtRateFit::toSeconds returns producer-epoch, not wall, seconds
The fit keeps the slope and discards the intercept, so the result counts from
the producer's boot. Tasks 4 and 7 compose it with ClockOffset::map, which is
correct, but the bare name invites passing it straight to a plot axis.

Also unwrapped the stalled-clock assertion from behind `if (fit.ready())` —
that branch never runs, so the test confirmed nothing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-27 19:55:24 +02:00
Martino FerrariandClaude Opus 4.6 e4817dd284 fix(udpscope): keep the clock-offset recalibration threshold symmetric
The jitter test fed a receive timestamp that went backwards (1001.02 then
1000.97), putting the second reading 1.03 s from the prediction — twice the
threshold, so not jitter under any reading. That is a digit slip for 1001.97.

It had been worked around by making the threshold one-sided, which passes the
test but never fires when the producer's clock steps forward: the prediction
stays ahead of the wall clock, the error stays negative, and the trace sits in
the future for the rest of the run. Restored std::fabs, corrected the test data,
and added the forward-jump case that the one-sided version silently failed.

Plan amended so the bad data does not come back.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-27 19:52:41 +02:00
Martino FerrariandClaude Sonnet 4.6 41ab151a2f feat(udpscope): producer-clock calibration and hrt tick-rate fit
Adds TimeBase.h/cpp with ClockOffset (latched wall-clock offset with
one-sided recalibration on positive drift only, so early-arriving packets
do not wobble the trace) and HrtRateFit (sliding-window OLS that recovers
an unknown hrt tick rate from receive timestamps). Also adds
TimeSignalScale() which maps UDPS type codes to seconds-per-count.
9 new tests, all 28 pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-27 19:50:24 +02:00
Martino FerrariandClaude Opus 4.6 ea9689591d test: add coverage for closeLeaf's two untested branches
Gap 1: closeLeaf was only tested closing the first sibling; added test
closing the second sibling to cover the else branch of parent->a.get()==leaf.
The test verifies the correct sibling survives with its signal intact.

Gap 2: closeLeaf was only tested at depth 1 (root's direct children);
added test with depth-2 leaf (in right subtree) to exercise findParent's
recursive search in both subtrees. Tests that the correct leaf is promoted
and remaining signals are preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-27 19:46:29 +02:00
Martino FerrariandClaude Sonnet 4.6 0e5d103e73 feat(udpscope): BSP pane tree with split, close and hit-testing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-27 19:42:09 +02:00
Martino FerrariandClaude Opus 4.6 c1029a25df fix(udpscope): make the time-order test actually exercise the swap, guard the font copy
The ramp data in EmitsPointsInTimeOrder never produced a bucket whose maximum
preceded its minimum, so an implementation ordering the emitted pair by value
instead of by time would have passed. Replaced with an explicit two-bucket case
whose second bucket reverses the order.

file(COPY) is a hard configure error on a missing source, so a checkout without
the sibling StreamHub resources failed to configure despite the ASCII-icon
fallback the block above had just selected.

Plan amended to match on both points.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-27 19:39:14 +02:00
Martino FerrariandClaude Sonnet 4.6 fba4360c80 feat(udpscope): build scaffold and min/max envelope decimation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-27 17:27:41 +02:00
Martino Ferrari e03c60db25 Implemented and fixed many issues 2026-08-21 23:24:48 +02:00
Martino FerrariandClaude Sonnet 4.6 1f8592f854 fix: address all 9 findings from final calibration code review
- calibration.js: fix baseSignalName('[0]') parity with Go/C++ (>= 0 not > 0)
- calibration.test.js: add assertions for '[0]' edge case in two existing tests
- app.js: remove stale typeof guard around refreshVScaleMenu (always defined)
- app.js: call refreshTrigThresholdField on trig-signal change (both assignment sites)
- index.html: drop maxlength='16' on unit input; normaliseCal is the sole enforcer
- configcheck/main.go: delete dead nextOneOf function (no callers)
- hub_calibration_test.go: delete orphaned waitBroadcast comment (function never existed)
- calibration.go: correct arrayIndexSuffix comment to document known Go/C++ difference
- Docs/StreamHub-API.md: add calibration entry count and unit byte limits to §5 table
- spec: fix configReloaded missing path field, '16 chars'→'16 UTF-8 bytes', StreamString→char[], chain scenario→configcheck program, four→five new frames

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 07:57:28 +02:00
Martino FerrariandClaude Sonnet 4.6 e37eec0276 webui: add the Sources & Config sidebar section
Renames the "Add Source" section to "Sources & Config" and replaces the
fire-and-forget "Save list" button with Save and Reload, plus a one-line
status area that renders the hub's configSaved/configReloaded ack.

onConfigAck replaces the no-op stub Task 7 left behind. It branches on the
frame type because configSaved carries a path and configReloaded does not,
and surfaces the hub's error text on failure rather than failing silently.
The status is kept outside the DOM because buildSidebar() recreates this
section on every sources broadcast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 07:23:08 +02:00
Martino FerrariandClaude Sonnet 4.6 5917ab1bf2 Restore calibration parity: revert unit-stripping from normaliseCal, fix CSV quoting at export
Finding 1: revert the comma/quote strip added in 3cb998c from
normaliseCal() in calibration.js. The strip broke byte-identical parity
with Go CalConfig.Normalise and C++ StreamHub::SetCalibrationEntry, both
of which only trim whitespace and cap at 16 UTF-8 bytes. Delete the
companion test that asserted the now-removed behaviour (suite returns to
18 tests).

Finding 2: fix the actual CSV-safety problem at the point of use in
exportAllCSV() in app.js. Header cells (time column and signal columns)
are now RFC 4180-quoted: wrapped in double quotes with any embedded
double quote doubled. This safely handles units or signal names that
contain commas or quotes without touching normaliseCal.

Finding 3: update two stale comments in app.js that called the trigger
threshold or rawFromNorm result 'raw' — Task 9 moved trig.threshold into
calibrated units throughout, so the comments now say 'calibrated'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 01:15:13 +02:00
Martino FerrariandClaude Sonnet 4.6 3cb998c5da webui: calibrate CSV export, trigger threshold and unit display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 00:55:07 +02:00
Martino FerrariandClaude Sonnet 4.6 4908c039a5 fix(webui): keep cal-invalid styling on focused field during hub broadcast
refreshVScaleMenu() was unconditionally removing the cal-invalid class
from all three calibration inputs, which silently cleared the red-border
error indicator on a field the user was editing whenever a hub
calibration broadcast arrived. Apply the same focused-element guard
already used for .value writes so a rejected value's error styling
persists until the user corrects it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 00:52:04 +02:00
Martino FerrariandClaude Sonnet 4.6 4e3a90b2c6 webui: add calibration editor to the V-Scale toolbar
Adds the Cal row (Scale / Offset / Unit / Reset) to #vscale-menu, its
CSS, and the refreshVScaleMenu() / commitCal() logic that wires it to
calTable and the hub via setCalibrationWS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 00:48:39 +02:00
Martino Ferrari 0e9ec61226 webui: apply per-signal calibration to the whole display path 2026-08-17 00:43:18 +02:00
Martino FerrariandClaude Opus 4.6 7312dd0ca0 docs(calibration.js): explain why one UTF-8 repair pass suffices in JS
TextEncoder always emits well-formed UTF-8, so truncation can strand at
most a lead byte plus three continuations — one repair pass covers it.
The C++ hub needs a loop because its input is raw bytes off the wire.
Also drops a dead variable from the byte-boundary test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-17 00:41:00 +02:00
Martino FerrariandClaude Opus 4.6 48d62c1f80 fix(calibration.js): cap unit at 16 UTF-8 bytes, matching both hubs
normaliseCal was using String.length/.slice() (UTF-16 code units), so
multi-byte characters like °, Ω, µ could slip through oversized. Now uses
TextEncoder to slice at 16 bytes, then repairs any incomplete trailing
UTF-8 sequence by walking back over continuation bytes to find the lead
byte and dropping the incomplete rune — exactly mirroring Go's
utf8.DecodeLastRuneInString loop and the C++ walk-back in
StreamHub::SetCalibrationEntry. Adds 4 new test cases covering the
non-ASCII/boundary scenarios, and corrects the canonical test command in
the task-6 report to 'cd Client/udpstreamer && node --test'.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-17 00:36:01 +02:00
Martino FerrariandClaude Sonnet 4.6 21d084d2ea webui: add pure calibration module with unit tests
Implements Calib JS module (calibration.js) with affine transform primitives,
CalTable, and normaliseCal matching Go CalConfig.Normalise semantics; 14 node
--test cases all pass. Loads before app.js in index.html.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 00:29:53 +02:00
Martino Ferrari ff5ad22447 included jitter correction on client 2026-08-13 10:28:43 +02:00
Martino Ferrari f0f83110a4 fix(debugger): extract MarteController into an importable controller package
Client/debugger was entirely package main, which Go forbids importing from
another module ("is a program, not an importable package") -- discovered
while wiring the new debugclient E2E tool against NewHeadlessMarteController.
Move martecontrol.go and its test into a new marte2debugger/controller
subpackage (package controller) and update Client/debugger/main.go to call
controller.NewMarteController/controller.DangerousCommandsEnabled. No
behavioral change to the browser-facing server.
2026-07-01 19:18:08 +02:00
Martino Ferrari 269b2c4d97 refactor(debugger): extract MarteController sink so it can run headless
Add a sink func(v any) field so MarteController's event stream can be
routed somewhere other than the browser WebSocket hub. NewMarteController
now sets sink to broadcast through the hub as before; a new
NewHeadlessMarteController(sink) constructor builds an instance with
hub == nil for the upcoming debugclient E2E tool. Direct m.hub.* calls
(SetSourceState/UpdateConfigForSource/PushDataForSource) are now guarded
with nil checks so a headless controller doesn't panic.
2026-07-01 19:12:41 +02:00
Martino Ferrari 0bea41f866 Implemented better testing and fixed skipepd frames 2026-07-01 16:39:34 +02:00
Martino Ferrari 4702d0a217 Implemented qt port + e2e 2026-06-26 09:11:10 +02:00
Martino Ferrari 0412c20edd Implemented client datasource 2026-06-25 00:45:45 +02:00
Martino Ferrari dd7dd22cb0 Implemented history writer 2026-06-14 14:09:51 +02:00
Martino Ferrari f25bd7f08e Implemented new C++ logic 2026-06-12 15:25:13 +02:00
Martino Ferrari 617b5bd712 Initial release 2026-05-29 13:29:59 +02:00