Files
scanengine-3/KNOWN_ISSUES.md
T
Thomas Ales c7eb33891c Helios diode current: stop the status poll overwriting the setpoint
The current spin box could not hold a typed value: the 1 Hz status poll
read LDS and wrote it straight into the spin box, so the operator's number
was replaced by the laser's within a second — before Set could be pressed.
The spin box is now seeded once on connect and belongs to the operator
after that; the poll's reading goes to a read-back label beside the Set
button, so the value about to be sent and the value the laser holds are
separate readouts.

The write itself was also unverified. Section 6 of the operator's manual:
"Commands or set values can be discarded by the controller unintentionally.
It is recommended to query the set value after the command is entered to
confirm the actual value." set_current_ma() wrote LDS and returned True
regardless, so a discarded write looked exactly like a good one.
_write_verified() now writes, reads back, and retries up to three times;
set_current_ma() and set_frequency_hz() use it, and HeliosWorker reports a
refusal on the status line instead of echoing the requested value.

Also from the manual, recorded but not acted on: LDS accepts 0-7000 mA
(the driver's 2000 mA ceiling is this rig's, not the protocol's), LDF has
to be re-sent after LDG changes, and the power-monitor mnemonic is HMP —
which this laser does not implement, per the operator.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 07:37:10 -05:00

118 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Known issues requiring on-rig verification
Questions that cannot be answered from the code alone. Check these the next
time the hardware is available; each one gates a small code change.
## uC480 camera: gain/exposure during active capture
The driver used to carry an (unused) `_capture_paused` context manager whose
docstring claimed many IDS cameras return `IS_CANT_COMMUNICATE_WITH_DRIVER`
(17) or `IS_NO_SUCCESS` (-1) when gain/exposure commands are issued during
active capture. `set_exposure()` and `set_gain()` never used it, and the
helper was deleted in the Phase-1 cleanup.
**Bench check:** with live streaming running, move the exposure and gain
sliders in `camera_test_app.py` and watch the log for those error codes.
If they appear, the setters need a stop-live/apply/restart sequence
(re-create the helper around the two call sites in
[uc480_camera.py](hardware/uc480_camera.py)).
## Helios: no output-power query
`docs/hardware/HELIOS_DRIVER_README.md` documents `driver.get_power_mw()`,
but `HeliosLaser` has no such method and no output-power mnemonic appears
anywhere in this repo's protocol notes. `helios_test_app.py` called it
anyway and raised `AttributeError` into a popup; the button is now disabled
and the handler reports the gap instead.
**Answered (2026-09-08):** the mnemonic is `HMP` (Table 6-3, "Laser Power
Monitor", read-only, 0-5000 mW) — but the table adds "not available on all
models", and the operator confirms this rig's laser has no power meter. So
the button stays disabled; what remains is to delete the Power Monitoring
group from `helios_test_app.py` and drop the power-monitoring claims from
`docs/hardware/HELIOS_DRIVER_README.md`.
## Genesis laser: forked protocol implementations disagree
`hardware/genesis_core.py` and the reference implementation
`tools/genesis_laser_gui.py` disagree on ADC command bytes, LDD enable
polarity, shutter semantics, filtering, and scaling. Do not modify either
until the checklist in [docs/genesis_verification.md](docs/genesis_verification.md)
has been run on the bench.
## `lib/ueye_loader.so` — still needed?
`lib/ueye_loader.c` is an `LD_PRELOAD` shim that dlopens
`/usr/lib/libueye_api.so` — yet nothing in the repo references it, and the
vendored SDK copy is `lib/libueye_api64.so.3.82` (a different file). On the
rig, check whether the camera apps run without the shim; if they do, delete
`lib/ueye_loader.{c,so}`. Either way, record in SETUP.md where
`libueye_api64.so.3.82` came from (IDS SDK version) and how the loader is
meant to be used.
## Per-angle background: trigger round trip mid-scan
Every angle now captures its own background, so the scope switches from the
scan-time logic-AND trigger back to the single-record edge trigger and
returns to it once per angle (`core/scope_sras.py`:
`configure_background_trigger` → `capture_background` →
`configure_scan_trigger`). Before this, that transition happened once per
scan, with the stage idle and nothing depending on how long it took.
**Bench check:** run a multi-angle scan and watch the first row after each
background. If frames go missing at the start of an angle, the 0.2 s settle
in `configure_scan_trigger` is not enough for FastFrame to re-arm after an
AVERAGE-mode sequence, and the row-packing warning ("N frames acquired, M
expected") will say so in the log. Raise the settle rather than the ramp
buffer — the stage geometry is not what changed.
## Auto-align: constants that are guesses until the rig confirms them
`core/auto_align.py` closes a loop over hardware whose gain nobody has
measured. Three numbers in `AlignSettings`/`TAxisSettings` are reasoned
defaults, not readings:
- `probe_steps = 200` — the first move of every search, made only to learn how
many millivolts a microstep is worth. Too small and each search wastes
iterations doubling it (the status line says so: "moved N microsteps and the
DC difference did not change"); too large and the first move overshoots by
more than the platform should be asked to travel in one go.
- `max_excursion_steps = 20000` — the per-axis safety limit, measured from
wherever the axis started. It exists to stop a runaway before the actuator
reaches its end stop, so it has to be smaller than the real travel.
- `hold_current_ma = 300` — the run current (600 mA) and microstepping (32)
are specified; the standstill current is half the run current by analogy
with the GR axis, and has not been checked against the platform's weight.
**Bench check:** run one auto-align and read the log. The first search's
iteration count is the probe verdict — 3 or 4 steps means the probe is about
right, and a "did not change by 2 mV" line means it is too small. Convert the
applied corrections into actuator travel and compare against the T-axis
travel to set the excursion limit. Watch whether the platform holds its tilt
between the two phases; if it sags, raise the hold current.
## Auto-align: does the X phase's piston matter, and where is the pivot?
The X phase moves T1 alone, as specified. T1 is the only axis lying along X,
so it does tilt the platform along X — but moving one leg of three also lifts
the platform by a third of the move (`tilt_response(X_TILT)` returns a piston
of 1/3 alongside the 2/3 tilt). The search nulls the split-detector
difference, which a piston should not move, so the assumption is that the
piston is harmless. The piston-free alternative is T1 +1 with T0 and T2 at
−0.5 each.
Separately, the procedure assumes the tilt pivot is under the beam: if it is
not, applying the correction shifts the DC levels at the reference point
itself, and the Y phase then chases levels that no longer describe the rig.
The code reports this rather than compensating for it — `AxisResult`'s
"back at the reference" reading after the X phase is exactly that
measurement.
**Bench check:** during an X search, watch DC1 + DC2 (the sum, not the
difference) on the scope. If the sum moves as T1 moves, the piston is
changing the amount of collected light and `X_TILT` should become the
piston-free triple. Then read the X phase's reference residual out of the
log: more than a few millivolts means the pivot is not under the beam, and
the Y phase's reference should be re-measured after the X correction instead
of reusing the operator's original numbers.