From 5917ab1bf2b94bb0cfcfcb1443c9491ab7f71464 Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Mon, 17 Aug 2026 01:15:13 +0200 Subject: [PATCH] Restore calibration parity: revert unit-stripping from normaliseCal, fix CSV quoting at export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- Client/udpstreamer/static/app.js | 10 ++++++---- Client/udpstreamer/static/calibration.js | 4 +--- Client/udpstreamer/test/calibration.test.js | 4 ---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Client/udpstreamer/static/app.js b/Client/udpstreamer/static/app.js index 159215e..7695363 100644 --- a/Client/udpstreamer/static/app.js +++ b/Client/udpstreamer/static/app.js @@ -1343,7 +1343,7 @@ function drawTriggerMarker(u, p) { ctx.fillText('T', px + 3, bbox.top + 2); // Horizontal threshold line — only on plots that contain the trigger signal if (p && trig.signal && p.traces.includes(trig.signal)) { - // Normalize the raw threshold to this plot's vscale for the trigger signal. + // Normalise the calibrated threshold to this plot's vscale for the trigger signal. const tvs = p ? sigVScale[p.id + ':' + trig.signal] : null; let threshNorm = trig.threshold; if (tvs) { @@ -2405,7 +2405,7 @@ function updatePlotCursorReadouts() { } /* ─── Hover readout ──────────────────────────────────────────────────────── */ -// Un-normalize a plotted value of trace `key` in plot `p` back to raw units. +// Un-normalize a plotted value of trace `key` in plot `p` back to calibrated units. function rawFromNorm(p, key, vNorm) { const vs = sigVScale[p.id + ':' + key]; if (!vs) return vNorm; @@ -3052,9 +3052,11 @@ async function exportAllCSV() { const displayKeys = keys.map(k => { const name = k.includes(':') ? k.split(':').slice(1).join(':') : k; const u = unitForKey(k); - return u ? name + ' [' + u + ']' : name; + const h = u ? name + ' [' + u + ']' : name; + return '"' + h.replace(/"/g, '""') + '"'; }); - const hdr = [(inTrigMode ? 'time_rel_s' : 'time_s'), ...displayKeys].join(','); + const timeCol = '"' + (inTrigMode ? 'time_rel_s' : 'time_s') + '"'; + const hdr = [timeCol, ...displayKeys].join(','); const rows = sortedT.map(t => [t.toFixed(9), ...lookups.map((lk, i) => lk.has(t) ? Calib.applyCal(lk.get(t), cals[i]) : '')].join(',') diff --git a/Client/udpstreamer/static/calibration.js b/Client/udpstreamer/static/calibration.js index 8756153..1c3c557 100644 --- a/Client/udpstreamer/static/calibration.js +++ b/Client/udpstreamer/static/calibration.js @@ -40,9 +40,7 @@ var offset = obj.offset === undefined ? 0 : obj.offset; if (!isFiniteNum(scale) || scale === 0) return null; if (!isFiniteNum(offset)) return null; - // Commas and quotes would corrupt the CSV export header; drop them here so - // every consumer sees an already-safe unit. - var unit = String(obj.unit == null ? '' : obj.unit).replace(/[",]/g, '').trim(); + var unit = String(obj.unit == null ? '' : obj.unit).trim(); // Cap at MAX_UNIT_LEN UTF-8 bytes, matching both hubs' Normalise() exactly. // TextEncoder/TextDecoder are available natively in all modern browsers and // Node v11+; no build step or bundler is needed. diff --git a/Client/udpstreamer/test/calibration.test.js b/Client/udpstreamer/test/calibration.test.js index c1bed6d..9a108d7 100644 --- a/Client/udpstreamer/test/calibration.test.js +++ b/Client/udpstreamer/test/calibration.test.js @@ -150,7 +150,3 @@ test('CalTable.list is sorted by source then signal', () => { assert.deepStrictEqual(t.list().map(e => e.source + '/' + e.signal), ['a/b', 'a/z', 'z/a']); }); - -test('normaliseCal strips characters that would corrupt a CSV header', () => { - assert.strictEqual(C.normaliseCal({source: 'w', signal: 's', unit: 'k,V"'}).unit, 'kV'); -});