TCube safety fixes from the 642 rig's facts: fresh reads request twice, two-sided check_lock, calibration reference (0.2.6) - #74
Merged
Conversation
Decision 0033: after X.Y.Z, main carries X.Y.(Z+1)-DEV. TagOnMerge skips a -DEV version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…alibration-reference re-check at the first light_on - (i) request_twice: every request-then-get site (limit, digpot, status, initialize's limit read, W/A read, tcube_get_current, new read_photocurrent_word) sends its request twice, since the TLD001 answers one request behind. - (ii) check_lock reads its own photocurrent and status, and refuses on a high ratio, on 0x400, or on a photocurrent below 1/lock_ratio of the request; returns at code 0. PhotodiodeLoop refuses lock_check_s below LOCK_CHECK_MIN_S = 0.1. - (iii) PhotodiodeLoop, TCubeLaser and SimDiodeLaser take ref_current_mA, ref_photocurrent_A, ref_ratio; the first power-mode light_on after each initialize re-measures the reference in open loop (check_scale!) and refuses on a mismatch, or warns once when there is none. - Small fixes: measured_current accepts -32768; the header's 17.25 mA potentiometer floor gate is removed; the step estimate is the manual's 0.7 mA. - The fake answers one request behind, models the photodiode and the 0x400 bit; tests N1-N21 added. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…enable, latch a reference mismatch, fresh reads in setoutputpower! set_open_loop! confirms open loop before the re-check enables. initialize resets the loop state first (scale_refused added); a mismatch latches until initialize and the re-check dwells REFERENCE_DWELL_S. require_clamp checks the TIA range and returns the fresh status word; check_lock tests the high side before the status read and runs 0x400 at code 0. The pot step estimate returns to 220/255. Deletes output_enabled, initialize's LD_RequestReadings and measured_current's range check. CHANGELOG timings recomputed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…en-loop initialize confirms open loop, latch any re-check failure M1 wraps require_clamp so a refusal found while the output may be on zeroes and disables it; M2 makes open-loop initialize confirm open loop with a fresh status read; M3 deletes setoutputpower!'s dead closed-loop check; M4 corrects the step-estimate docstrings; M5 latches any failure inside the reference re-check. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Sep 30, 2026
Merged
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TCube laser safety fixes for 0.2.6, from the 642 nm rig's measurements of 2026-09-29. Laser files only; independent of #73.
Based on #72 (main + the 0.2.6-DEV bump, 7acee6c), because a PR into a released main must carry a raised version. Until #72 merges, this PR also shows its one commit.
What the rig found, and what changes
The rig found that the controller answers one request behind: the first status request after an enable returned the pre-enable bits. It also found that the readings are signed, and that the photodiode words did not follow a move of the range DIP switch.
Fresh reads request twice.
read_limit_mA,read_status_fresh,read_digpot, the W/A read and initialize's limit read now go request, wait, request, wait, get, through onerequest_twicehelper. Before this,light_on's stored-limit gate could pass a limit read before the potentiometer was raised.check_lockmakes its own reads and is two-sided. It requests the photocurrent twice, then the status word. It zeroes, disables and throws on any of three conditions:lock_ratio× the request (as before);0x400);lock_ratio.The last two catch a loop driven to the clamp, whatever the cause, including a photodiode giving fewer counts per mW than at calibration. The high side is tested before the status read. At
code == 0it waitslock_check_sand runs only the0x400test.lock_check_shas a floor.PhotodiodeLooprefuseslock_check_sbelowLOCK_CHECK_MIN_S = 0.1s; a test pins that to 2 ×POLL_INTERVAL_MS.0used to disable the check.Optional calibration reference.
ref_current_mAandref_photocurrent_A(both or neither) andref_ratio(default 1.5) are added toPhotodiodeLoop,TCubeLaserandSimDiodeLaser.initializenever emits.light_onafter eachinitializeruns a check before it closes the loop:max_currentand withinmax_powerby the calibration's own scale;tia_range;ref_ratioof the reference, either way.REFERENCE_DWELL_S(0.1 s), notlock_check_s.initialize, so the diode is not lit again on every retry.initialize, failed ones too, first clears the clamp and the check's state.tia_rangerelabelled while the words stay put (the rig's DIP-switch observation) fails the check.light_onwarns once and carries on, with the two-sidedcheck_lockas the guard. The CHANGELOG recommends a reference for every power-mode rig;CALIBRATION.mdstep 3b says how to record one.Power-mode
light_onandsetoutputpower!also refuse on a TIA range mismatch. The fresh status word's range must matchtia_range.setoutputpower!now decides on the fresh status word and, with the output on, a fresh photocurrent read, instead of the polled cache.A safety check that refuses while the output may be on zeroes and disables it first. This covers the stored-limit check, and in power mode also a mode, TIA-range or clamp change, or an over-range photodiode. Before, the diode was left lit in the fault. "May be on" means the driver recorded it on, a fresh status read says so, or that read failed.
Open-loop
initializeconfirms open loop with a fresh status read afterLD_SetOpenLoopMode, and refuses if the controller stays in closed loop.Small fixes:
measured_currentaccepts the header-legal raw -32768 (-220 mA) instead of throwing.DIGPOT_MIN_mAfloor gate (17.25 mA, from the header's wrong scale) goes, in open-loopinitializeand in power-mode construction. The controller's limit readback decides.max_currentfrom below. The readback stays the authority.Compatibility (decisions 0033 and 0035)
Nothing is breaking.
lock_check_s < 0.1now throws, andcheck_lockrefuses in cases it used to pass. Both are fixes on already-broken paths: 0 disabled the check, and a loop at its clamp or below its request is not emitting the commanded power.lock_check_s(MicroscopeAdapt, SeqSR and MINFLUX checked by the captain).0.2.6-DEV.Tests
light_ons (the second refuses; a single request would pass);light_ons (trips0x400or the low side);test/tcube_output_order.jl(0.2.4's own cases) passes unchanged.initialize;setoutputpower!;0x400at a zero request;initialize(refuses, never enables).xvfb-run -a): 1916 pass, 3 broken, 0 fail.Rig checks owed (none run; no hardware)
0x400is set, which shows the readback is the clamp.v0.2.4 is not changed.
🤖 Generated with Claude Code