From 8f667dc6cf9713cfc7b996c4f1959c72d71b03e1 Mon Sep 17 00:00:00 2001 From: grabowski Date: Thu, 22 Oct 2026 17:25:09 +0700 Subject: [PATCH] Flag the unverified spectrum wavelength axis; keep the fixture generator The linear 380-1050 nm axis gives spectrum chromaticities 0.09-0.13 (xy) away from the instrument's own x, y, so TM-30 and spd.csv inherit an axis error until it is confirmed. tools/make_fixtures.py regenerates the golden data from the python-final driver. The release profile moves to the workspace that builds the binary. --- Cargo.toml | 2 - PROTOCOL.md | 13 ++++ tools/make_fixtures.py | 142 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 tools/make_fixtures.py diff --git a/Cargo.toml b/Cargo.toml index fc13dd9..d48266d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,5 +34,3 @@ ctrlc = { version = "3.4", optional = true } [dev-dependencies] serde_json = { version = "1", features = ["preserve_order"] } -[profile.release] -strip = true diff --git a/PROTOCOL.md b/PROTOCOL.md index 39f7778..001bff3 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -190,6 +190,19 @@ The 350 floats cover **380 nm to 1050 nm** at ~1.92 nm steps ((1050-380) / (350-1) = 1.917 nm/step). Values are absolute spectral irradiance; the vendor software normalizes by dividing by the peak value. +> **Unverified: the wavelength axis.** The linear 380-1050 nm mapping above +> is an assumption. Integrating the published lamp spectra with the CIE 1931 +> observer on this axis gives chromaticities 0.09-0.13 (xy) away from the +> x, y the instrument itself reports in the same block (offsets 52/56); for +> L0011 the spectrum says x,y = 0.358, 0.295 against the instrument's 0.380, +> 0.382. No other linear range fits all lamps (380-780 nm comes closer but +> moves the blue LED peak to ~419 nm). The 350 points are probably on a +> non-linear (spectrometer pixel) axis. Everything computed from the +> spectrum (TM-30 Rf/Rg, the spd.csv files) inherits this until the axis is +> confirmed, e.g. against a spectrum exported by the vendor software with +> its wavelengths, or by fitting the axis so the spectrum reproduces the +> instrument's own XYZ. + ### Unmapped Regions Offsets 176-223, 256-271, and 1832-3903 contain additional data not yet diff --git a/tools/make_fixtures.py b/tools/make_fixtures.py new file mode 100644 index 0000000..ba8a9ae --- /dev/null +++ b/tools/make_fixtures.py @@ -0,0 +1,142 @@ +"""Golden fixtures for the Rust hpcs6500 driver, produced by the legacy Python driver. + +Run it next to hpcs6500.py from tag python-final, with the USB captures in +C:\dev\HPCS\captures (not in git): + git show python-final:hpcs6500.py > /tmp/py/hpcs6500.py; cp tools/make_fixtures.py /tmp/py/ + python /tmp/py/make_fixtures.py # then copy /fixtures and /golden to tests/ +pyfmt.json was trimmed to ~2000 cases afterwards. + +Writes into /: + fixtures/rNN_meas.bin, rNN_elec.bin raw blocks from real USB captures + golden/readings.json Python dict per fixture reading (floats as repr strings) + golden/format_*.txt format_reading / format_quick output + golden/csv_row.txt CSV row (without timestamp) as DictWriter writes it + golden/pyfmt.json repr / .6g / .2f / .0f / .1f of many floats + golden/parse_.txt `hpcs6500.py --parse` output, full and --quick --spectrum +""" +import contextlib +import csv +import io +import json +import math +import random +import struct +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent)) +import hpcs6500 as h # noqa: E402 + +out = Path(sys.argv[1]) +captures = Path(r"C:\dev\HPCS\captures") +(out / "fixtures").mkdir(parents=True, exist_ok=True) +(out / "golden").mkdir(parents=True, exist_ok=True) + +pairs = [] # (name, meas, elec) +for f in sorted(captures.glob("*.pcap")): + if f.stat().st_size < 100: + continue + m = h.parse_pcap_messages(str(f)) + b = [x for x in m if x["dir"] == "RX" and len(x["data"]) == 3904 and x["data"][:8] == b"HPCS6500"] + e = [x for x in m if x["dir"] == "RX" and len(x["data"]) == 1584] + for i, blk in enumerate(b): + el = e[i]["data"] if i < len(e) else None + harm = el is not None and abs(struct.unpack_from("