Lamp testbench

Measurement station and tools around the HPCS 6500 spectrophotometer / integrating sphere, for the buildfor.life lamp comparison and for calibrating B4L's tunable-white LED light. Everything is Rust; one binary, lampbench.

Part Role Where
HPCS 6500 spectrum, photometry, CRI, electrical, built-in AC/DC supply driver: submodule HPCS6500/ (B4L/HPCS6500)
Brother QL-820NWB lamp ID labels, DK-11221 23x23 mm crates/brother-ql, crates/lamp-label
TM-30-18 Rf, Rg and the 16 hue bins from the spectrum crates/tm30
comparison-data the published lamp bundles --data-repo (git checkout)

Same layout as mppt-testbench: the instrument driver is its own repository, pulled in as a submodule.

Install

git clone --recurse-submodules https://git.b4l.co.th/B4L/lamp-testbench.git
cd lamp-testbench
cargo build --release          # target/release/lampbench(.exe)
cargo install --path .         # or put lampbench on the PATH

The instrument is found by its USB IDs (STM32 virtual COM port, VID 0483, PID 5741); --port COM42 picks one by hand. hpcs6500 (the driver's CLI, cargo install --path HPCS6500) reads and controls the instrument directly.

Lamp station

lampbench station                              # scan -> measure -> commit -> push -> labels
lampbench station --no-push                    # commit locally only
lampbench station --no-commit                  # just write the bundle
lampbench station --settle 0 --readings 1      # quick smoke test
lampbench station --simulate --no-commit --data-repo D:/scratch-data   # no instrument needed

Every lamp gets an internal ID (L0001, L0002, ...) as its primary key; the EAN is optional metadata, so one barcode can cover daylight and warm white variants, and unbranded lamps work too.

  1. Every run opens with a fresh ID; it only becomes real once a bundle is written, so aborting never burns a number.
  2. Scan the box's EAN (Enter skips it) or type an existing ID like L0004 to re-measure that lamp with everything pre-filled (this also rewrites its bundle in the current format). EANs are checksum-validated and resolved to manufacturer / model from previous lamps, the local cache (~/.cache/hpcs6500/ean-cache.json), then upcitemdb.com; GS1-restricted 20-29 barcodes are flagged as retailer-internal. Lamps already measured under the same EAN are offered for re-measurement.
  3. Confirm the identification and type the advertised values from the packaging (flux, CCT, power, CRI, lifetime, equivalent W). They are stored under rated in metrics.json for claimed-vs-measured comparison.
  4. Insert the lamp and press Enter. The procedure: 230 V / 50 Hz from the built-in supply, 60 s settle, 5 readings averaged (vendor single-test cycle per reading).
  5. lamps/<ID>/ is written into the comparison-data checkout, committed and pushed; the website picks it up automatically (data-driven device list, auto-bump deploy). An .md page in the web repo is only needed for photos, buy links or notes.
  6. Two ID labels (QR code of the ID plus the ID, DK-11221) print on the Brother QL-820NWB: one for the box, one for the lamp. Scanning a label at the station types the ID and lands in the re-measure flow. Set HPCS_LABEL_PRINTER=tcp://192.168.1.50 or pass --printer.

Ctrl+C during a measurement switches the supply off and skips the lamp; at the scan prompt, q or Ctrl+C quits.

Defaults assume C:\dev\buildfor_life_web\apps\web\comparison-data with the comparison-data submodule initialized; override with --data-repo.

Lamp bundle export

lampbench export --name philips-a60-8w --manufacturer Philips --model "A60 8W 927"
lampbench export --name philips-a60-8w --readings 5
lampbench export --name philips-a60-8w --voltage 230 --frequency 50 --settle 60   # power from the built-in supply
lampbench export --name some-lamp --parse captures/run.pcap                       # from a USB capture
lampbench export --name x --passive                                               # vendor software drives the instrument

Output lands in lamps/<name>/ (--out):

File
spd.csv wavelength_nm,value, the full 380-1050 nm spectrum
tm30.csv TM-30-18 per hue bin: reference/test a' b' (raw and normalized), Rcs, Rhs, Rf,h
metrics.json name, EAN, type, rated values, TM-30 Rf/Rg, CRI Ra + R1-R15, supply, photometric, chromaticity, radiometric, electrical, sensor

The files are byte for byte what the former Python lamp_export.py wrote (tested against its output), so re-measured lamps only change where the measurement changed.

Labels

lampbench label L0006                    # 2 labels on $HPCS_LABEL_PRINTER
lampbench label L0006 --copies 1 --printer tcp://192.168.1.50
lampbench label L0006 --preview l6.png   # render only

TM-30

lampbench tm30 lamps/L0011/spd.csv            # Rf 83.2  Rg 109.3  CCT 4008 K  Duv -0.0370
lampbench tm30 lamps/L0011/spd.csv --bins     # the tm30.csv rows

crates/tm30 implements ANSI/IES TM-30-18 (CIE 2017 colour fidelity, 99 CES, CAM02-UCS, Ohno 2013 CCT) in plain Rust. It reproduces colour-science 0.4.7, which the Python tools used, on 52 golden cases (the published lamps, CIE illuminants, blackbodies, odd grids): Rf and Rg agree to 1e-10, every output to 1e-9 relative or 1e-11 absolute. The spectrum is relative; TM-30 is invariant to its scale.

Known issue: the spectrum's wavelength axis. The CCT/Duv that TM-30 derives from the spectrum (4008 K / -0.037 for L0011) disagree with the instrument's own values (4049 K / +0.0026), because the spectrum's chromaticity on the assumed linear 380-1050 nm axis is 0.09-0.13 (xy) off the instrument's x, y. The published Rf/Rg inherit this. Details and how to confirm the axis: HPCS6500/PROTOCOL.md, "Spectral Data".

LED light calibration

Calibrates the mixing table of the B4L tunable-white light (2700 K + 5000 K LEDs) over its UART, with the HPCS 6500 as the meter:

lampbench calibrate COM3                 # y-data, then up to 10 mixing-ratio passes until all 45 points are within 5 %
lampbench calibrate COM3 --skip-y        # mixing ratios only
lampbench calibrate COM3 --y-only        # LED curves only
lampbench calibrate COM3 --read          # show the table in the light
lampbench calibrate COM3 --clear         # back to firmware defaults
lampbench calibrate COM3 --generate-only # table from calibration_table.json
lampbench calibrate COM3 --write         # ... and write it to the light

Progress and results go to calibration_table.json (--output), in the format of the former calibration.py.

Development

cargo test --workspace        # everything, including the driver submodule
cargo clippy --workspace --all-targets -- -D warnings
  • tests/golden/bundles/ are bundles written by the Python lamp_export.py from real readings (the driver's tests/fixtures/); bundle.rs must reproduce them byte for byte.
  • tests/station.rs runs the whole station with scripted answers against the simulated instrument and a throwaway comparison-data repository.
  • The calibration is tested against a simulated light whose CCT follows its mixing ratio.
  • crates/tm30/tests, crates/brother-ql/tests, crates/lamp-label/tests compare against the Python libraries (colour-science, brother_ql, qrcode); tools/ in each crate regenerates the golden data.

The Python tools these replace are in HPCS6500 at tag python-final.

S
Description
No description provided
Readme
1 MiB
0 Stars 8 Watchers 0 Forks
Languages
Rust 89.6%
Python 10.4%