Commit Graph
1194 Commits
Author SHA1 Message Date
x12saandClaude Sonnet 5 bbede883b0 fix(panda): retry arming once after a forced reset instead of hanging in pre_scan
CI for csaxs_bec / test (push) Failing after 2m11s
PandaBoxOMNY inherited on_pre_scan, which waits forever for the PandA READY
event before sending Arm(). After rapid abort/retry cycles the readout thread
can be left parked, so READY never arrives and pre_scan_all_devices() hangs
with no error (flomni_fermat_scan 11998/12000).

on_pre_scan now runs a bounded arm task: wait pre_scan_timeout (default 5 s) for
READY; on failure force-reset the box, restart the readout thread (after the old
loop finished its cleanup) and try once more, printing and logging a warning with
diagnostics. If the second attempt fails too, the status fails with a
RuntimeError so the scan aborts. Arm() is sent from the task, not a status
callback, so the scan is only released after it went out. A stop is not retried.

Also records the outcome and the still-open ophyd_devices race in
AGENTS_mirko.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017szgTwuHG65YhjoBGiK2Gj
2026-09-20 11:47:04 +02:00
x12saandClaude Sonnet 5 bd85d5e673 fix(xrayeye): make DAP sine fit converge by fixing start values and signed amplitude
CI for csaxs_bec / test (push) Failing after 2m13s
The xray eye alignment fit (A*sin(f*x+shift)+intercept, 5 points) relied on
lmfit's SineModel.guess() for the start values. Its FFT-based shift guess is
arbitrary for 5 points and often left the fit stuck at the shift bound (2*pi)
far from the data, so the plotted fit missed the submitted points and
wrong parameters were loaded into flomni.

Override all five parameters so the DAP server skips guess(): signed
amplitude (start 30), shift start 0 within +-pi, intercept start 0,
frequency and slope still fixed. Shared by the x and y waveforms.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017szgTwuHG65YhjoBGiK2Gj
2026-09-20 11:03:06 +02:00
x01dcandClaude Sonnet 5 d01af690a9 fix(flomni,lamni): fovy/piezo-range bounds didn't match scan's strict "<"
CI for csaxs_bec / test (push) Failing after 2m12s
Real hardware failure: flomni.fovy=100.0 was accepted by the property, GUI,
and CLI (all capped at an inclusive 100.0), but FlomniFermatScan's own
ScanArgument requires fovy strictly less than 100 -- so the scan rejected it
at runtime with ScanInputValidationError, only surfacing once actually run.

- flomni.fovy property setter: now raises at >=100 (was >100), matching
  FlomniFermatScan's gt=0/lt=100. GUI/CLI max tightened 100.0 -> 99.9.
- lamni_piezo_range_x/y (become LamniFermatScan's fovx/fovy, gt=0/lt=80):
  found the same bug class at BOTH ends while writing the regression test --
  GUI/CLI min was an inclusive 0.0 (scan requires strictly >0) and max was
  an inclusive 80.0 (scan requires strictly <80). Tightened to 0.1-79.9 in
  both GUI and CLI. Property setter's large_range_scan bypass left
  untouched per earlier explicit instruction -- this fixes the GUI/CLI
  entry points, which is where the bound is actually enforced in practice.
- Added test_fov_bounds_match_scan_args.py: introspects the real
  ScanArgument gt/ge/lt/le from FlomniFermatScan/LamniFermatScan and asserts
  our configured GUI/CLI ranges are strictly inside them, so this class of
  bug (passes every check except the one that actually runs the scan)
  can't silently come back for these or future fields.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 21:30:22 +02:00
x01dcandClaude Sonnet 5 1b802aaad6 docs(flomni,lamni): clarify tomo_countingtime is per-frame, not per-point
CI for csaxs_bec / test (push) Successful in 2m12s
tomo_countingtime is the exposure time of each individual frame within a
frames_per_trigger burst -- total dwell time at a position scales with
frames_per_trigger (see the scan's own timing model, flomni.py:2553:
normal_cadence = ... tomo_countingtime * frames_per_trigger). The GUI label
and both setups' tomo_parameters() CLI prompt/print text previously just
said "Counting time" with no qualifier, which reads as a per-point total.
Relabeled to "Counting time per frame" everywhere it appears.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 21:24:14 +02:00
x01dcandClaude Sonnet 5 f0c5a251d9 docs(omny): note follow-up to tighten tomo-parameter ranges + CLI validation
CI for csaxs_bec / test (push) Successful in 2m13s
Not implemented for omny yet -- records the range tightening and _get_val()
min_/max_ enforcement just done for flomni/lamni (2026-09-19), and confirms
omny's parameter names mirror flomni's exactly. Also confirms tomo_params.py
has no "omny" SETUP_PROFILES entry -- omny doesn't use that shared GUI
widget at all today, so there's nothing to tighten there until its actual
parameter-entry UI (if any) is located.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 21:17:18 +02:00
x01dcandClaude Sonnet 5 d77b5cede9 fix(lamni): tighten circfov/piezo-range GUI limits, enforce ranges in CLI
CI for csaxs_bec / test (push) Successful in 2m18s
- tomo_circfov: GUI upper bound 200.0 -> 500.0 um (floor kept at 0.0 so
  "0 = disabled" -- checked explicitly in lamni_fermat_scan.py:484 -- still
  works)
- lamni_piezo_range_x/y: GUI upper bound 200.0 -> 80.0 um, matching the
  "(max 80)" already printed in the CLI prompt. The property setter's
  large_range_scan bypass is left untouched (uncertain hardware rationale) --
  this only tightens the GUI/CLI entry points, which is where "<80" actually
  becomes enforced in practice, since both now validate before ever calling
  the (still-conditionally-permissive) property setter.
- LamNI._get_val() now supports min_/max_ (mirrors Flomni._get_val()
  exactly, added previously) -- LamNI's tomo_parameters() CLI wizard
  previously had zero range validation at all. Wired up for
  tomo_countingtime (0.001-100.0), tomo_shellstep (0.025-20.0),
  lamni_piezo_range_x/y (0.0-80.0), and tomo_circfov (0.0-500.0).
- tomo_stitch_overlap gets a brand new CLI prompt (0.0-10.0 um) -- it had no
  prompt at all before (property-only, same gap Flomni still has).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 21:16:28 +02:00
x01dcandClaude Sonnet 5 a7e1b5ecf0 docs(flomni): fix stale comment above _TOMO_SCAN_PARAM_NAMES
CI for csaxs_bec / test (push) Successful in 2m31s
manual_shift_y is prompted by tomo_parameters() (has been all along) -- the
comment wrongly grouped it with tomo_stitch_overlap/corridor_size/
at_each_angle_hook as "only set directly as properties". Only those three
are actually property-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 21:05:53 +02:00
x01dcandClaude Sonnet 5 1dd03d16d8 fix(flomni): tighten tomo_shellstep/stitch/golden_ratio_bunch_size ranges
CI for csaxs_bec / test (push) Successful in 2m17s
tomo_shellstep: 0.001-1000.0 -> 0.025-20.0 um
stitch_x/stitch_y (flomni only): 0-50 -> 0-10
golden_ratio_bunch_size: 1-10000 -> 1-100
tomo_stitch_overlap: 0.0-50.0 -> 0.0-10.0 um

Applied in both the GUI (tomo_params.py) and the tomo_parameters() CLI
wizard (flomni.py), matching the ±50 um manual_shift_y precedent. Note:
tomo_shellstep, tomo_stitch_overlap, and golden_ratio_bunch_size are built
by shared (not per-profile) GUI code, so these ranges also apply to LamNI's
Tomo Parameters panel, not just flomni's -- stitch_x/stitch_y are the only
one of the four that's flomni-specific (lamni_stitch_x/y are a separate,
untouched profile entry). tomo_stitch_overlap has no CLI prompt to update
(property-only, not part of the tomo_parameters() wizard). All existing
defaults (tomo_shellstep=1, stitch_x/y=0, tomo_stitch_overlap=0.2,
golden_ratio_bunch_size=20) remain within the new ranges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 21:05:16 +02:00
x01dcandClaude Sonnet 5 158dc800f0 fix(flomni): enforce GUI parameter ranges in tomo_parameters() CLI too
CI for csaxs_bec / test (push) Successful in 2m15s
_get_val() (the wizard's input prompt) previously did no range validation
at all -- only the Tomo Parameters GUI's spinboxes enforced limits, so the
CLI silently accepted any value of the right type (e.g. manual_shift_y=500
despite the GUI's ±50 µm range). _get_val() now takes optional min_/max_
and re-prompts until the value is in range; wired up for every
tomo_parameters() field that already has a GUI-defined range (tomo_countingtime,
tomo_shellstep, fermat_asymmetry, fovx, fovy, stitch_x/y, manual_shift_y,
frames_per_trigger, single_point_random_shift_max, total projections,
golden_ratio_bunch_size, golden_max_number_of_projections, projections/sub-tomo).
fermat_asymmetry reuses FlomniFermatScan's own MIN/MAX constants rather than
duplicating them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 20:21:20 +02:00
x01dcandClaude Sonnet 5 583435ddd6 fix(flomni): tighten manual_shift_y GUI range to ±50 µm
CI for csaxs_bec / test (push) Successful in 2m21s
manual_shift_y (the "additional offset y" that ultimately feeds
tomo_additional_offsety at the scan/RT-controller layer, where it's bounded
by rty's own ±100 hardware travel limit) had no meaningful upper bound of
its own in the Tomo Parameters GUI (-1000 to 1000 µm spinbox range) --
tighten it to ±50 µm, a sane bound at the parameter-entry level, well
inside rty's hardware limit which is left unchanged. GUI-only change
(manual_shift_y itself has no property-level range validation, matching
the existing convention for fovx/fovy/stitch_x/stitch_y). LamNI's own
manual_shift_x/y range is untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 19:34:49 +02:00
x01dcandClaude Sonnet 5 1e553b0004 docs(lamni): document lamni_stitch_x/lamni_stitch_y; note omny follow-up
CI for csaxs_bec / test (push) Successful in 2m25s
Same gap as flomni.md (fixed previous commit): lamni.md had no mention of
stitching. Add the equivalent paragraph using lamni's own parameter names
(lamni_stitch_x/y, lamni_piezo_range_x/y) and cite tomo_scan_projection(),
which passes them straight through to scans.lamni_fermat_scan().

Also add an omny/AI_docs TODO note -- omny.md has the same gap and omny's
mechanism turns out to use identical parameter names to flomni's
(stitch_x/stitch_y/tomo_stitch_overlap), so the flomni.md paragraph should
carry over directly once written.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 18:14:31 +02:00
x01dcandClaude Sonnet 5 5096008286 docs(flomni): document stitch_x/stitch_y tomo parameters
CI for csaxs_bec / test (push) Successful in 2m25s
flomni.md had no mention of stitching at all. Add a paragraph explaining
the tiled-Fermat-scan mechanism (flomni.py:3521-3527) and tomo_stitch_overlap,
matching the existing zero_deg_reference_at_each_subtomo callout's style.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 17:05:51 +02:00
x01dcandClaude Sonnet 5 306b93cc07 docs(omny): note unconditional 0-deg reference projection follow-up
CI for csaxs_bec / test (push) Successful in 2m19s
Not implemented for omny yet -- captures the same feature just added to
Flomni/LamNI so the intent isn't lost. Omny still needs its own retraction-
capability check and tomo_scan() entry point located before this can land.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 10:07:06 +02:00
x01dcandClaude Sonnet 5 0a12d4892f feat(flomni,lamni): unconditional 0-deg reference projection at scan start
CI for csaxs_bec / test (push) Successful in 2m15s
Take one real 0-degree projection at the very start of every new tomogram
(subtomo_number=1, included in reconstruction like any grid projection),
independent of zero_deg_reference_at_each_subtomo /
golden_projections_at_0_deg_for_damage_estimation -- gives an immediate
radiation-damage baseline even when those flags are off. Placed right after
Flomni's existing collect_empty_frames() step; LamNI gets the same 0-deg
shot but no flat-field step, since it has no way to retract the sample out
of the beam. Omny intentionally left as a follow-up (not implemented here).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 10:06:49 +02:00
x01dcandClaude Sonnet 5 22a882fafa docs: note proposed interferometer signal-strength trend on webpage
CI for csaxs_bec / test (push) Successful in 2m14s
Design note only -- not implemented. Captures the idea (low-rate/~hourly
trend, not per-projection), plus a 2026-09-19 survey finding that flomni/
lamni/omny each have incompatible interferometer-read primitives (different
axis counts/semantics, no shared RT-controller base), and that omny has no
working webpage generator to hook into yet (its existing file is dead code,
never imported by the real factory).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 09:56:01 +02:00
x01dcandClaude Sonnet 5 7d21c0e43e docs(panda): note proposed gated-continuous PCAP capture variant
CI for csaxs_bec / test (push) Successful in 2m22s
Record the exposure-active-gated PCAP.gate variant discussed for reducing
idle-time data volume in raw_stream_mode (currently PCAP free-runs via
CLOCK1 regardless of detector state). Documentation only -- not implemented
or tested against hardware.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-19 09:11:31 +02:00
x01dcandClaude Sonnet 5 cd838e4f73 fix(eps): enforce hard byte budget on eps_alarm_history.json uploads
CI for csaxs_bec / test (push) Successful in 2m22s
The 2026-09-11 fix capped alarm-text *count* per event (_ALARM_TEXTS_MAX),
but not text length, and _load() never re-caps an already-oversized event's
texts list -- only new appends are capped. Confirmed on x12sa: the file had
grown to 1,209,271 bytes, still tripping HTTP 413 on every upload cycle
(upload.php itself has no size check -- almost certainly nginx's default
client_max_body_size=1m). HistoryTracker.save() now shrinks a copy of the
payload to a ~250 KB budget (alarm texts, then ring trace, then oldest
events, then field length as a last resort) before writing/uploading, so
the file can no longer exceed the server's limit regardless of how the
underlying data grows. In-memory history is untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-18 13:46:12 +02:00
x01dcandClaude Sonnet 5 bf6f89636a feat(flomni,lamni): clear tomo queue and reset progress on account change
CI for csaxs_bec / test (push) Successful in 2m19s
The account-change check already offered to reset scan parameters to
defaults for a new experiment; extend it to also clear the tomo queue and
reset acquisition progress, so a new account doesn't inherit leftover
queued jobs or in-progress state from the previous account.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
2026-09-18 09:19:41 +02:00
x01dcandClaude Sonnet 5 74996f78e1 feat(flomni,lamni): add on-demand tomo timing report commands
CI for csaxs_bec / test (push) Successful in 2m24s
tomo_scan()'s elapsed/idle timing breakdown only printed on normal
completion, so it was unreachable after a KeyboardInterrupt even though
the underlying data (self.progress, a BEC global var) survives the
interrupt. Factor the report construction into _build_timing_lines() and
expose it via tomo_print_timing_report() (console only) and
tomo_print_timing_report_and_scilog() on both Flomni and LamNI. Also
flags the same pre-existing gap on OMNY in a TODO note.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 08:41:07 +02:00
x01dcandClaude Sonnet 5 fd01806b2f fix(xrayeye): remove self.resize(800, 600) from __init__ -- fights dock manager
CI for csaxs_bec / test (push) Successful in 2m14s
Root cause isolated via a live A/B test (Mirko): the "camera image cut/
collapsed until the window is manually moved" symptom is specific to
OMNY_XRayEye -- flomnigui_show_cameras()'s Image/z_ConsoleButtonsWidget docks
never show it, even swapped in and out of the *same* persistent window
repeatedly (ruling out window-freshness/QtAds-first-dock theories tried and
reverted earlier this session). Further isolated to screen size: reproduces
reliably on a screen too small for the assumed geometry, not on a larger one.

OMNY_XRayEye.__init__() was the only widget in this GUI calling
self.resize(800, 600) on itself. This widget is normally embedded as a dock
(gui_tools.py's flomnigui_show_xeyealign()), where the dock manager (Qt
Advanced Docking System) owns its geometry -- an explicit self-resize call
fights that, and on an undersized screen corrupts the dock's layout until a
manual move/resize forces Qt to reflow within actual available space. The
standalone `python x_ray_eye.py` test harness at the bottom of this file
already does its own win.resize(1000, 800) after construction regardless, so
this call was never actually needed even there -- pure dead weight that only
caused harm once embedded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 17:03:37 +02:00
x01dc 903e465701 Revert "fix(flomni): increase flomni GUI window settle delay after creation (experiment)"
CI for csaxs_bec / test (push) Successful in 2m15s
This reverts commit 631bdedb6c.
2026-09-17 16:02:38 +02:00
x01dcandClaude Sonnet 5 631bdedb6c fix(flomni): increase flomni GUI window settle delay after creation (experiment)
CI for csaxs_bec / test (push) Successful in 2m21s
Root cause finally isolated via a live A/B test: closing the whole "flomni"
window and reopening it (flomnigui_show_gui() takes the gui.new() branch,
creating a fresh top-level window) reproduces the "camera image squeezed/
missing until the window is moved" symptom; closing just the xeyegui dock
and reopening it (window already exists in self.gui.windows, reused as-is)
does not. Same OMNY_XRayEye construction either way -- the only variable is
whether the window itself is freshly created, which rules out the widget's
own layout code (already tried and reverted two fixes there this session).

The existing time.sleep(1) after gui.new() is itself evidence someone
already found the RPC response alone doesn't mean the window has actually
settled on screen. Bumping to 2s is a pragmatic, easily-reversible
experiment, not a confirmed fix -- flagging in case it needs to become a
poll-for-actual-geometry wait instead. LamNI's gui_tools.py has the
identical gui.new() + time.sleep(1) pattern (line 46-47) and is presumably
equally exposed, but untouched here -- not reported/tested there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 15:59:40 +02:00
x01dcandClaude Sonnet 5 3728a24c70 docs: add personal Claude Code notes for mirko
CI for csaxs_bec / test (push) Successful in 2m17s
Personal, machine-syncable notes file (not referenced by any shared
AGENTS.md/CLAUDE.md, so it has no effect on other contributors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GCaaQQSYGCbkAsRnALWC3h
2026-09-17 15:48:39 +02:00
x01dc b43240fbdd Revert "fix(xrayeye): defer control-panel width-fixing until after first show"
This reverts commit 10b02ca2f1.
2026-09-17 15:48:07 +02:00
x01dc 99243931ac Revert "fix(xrayeye): use showEvent() instead of a one-shot timer for control-panel width"
This reverts commit a6dadafe46.
2026-09-17 15:48:07 +02:00
x01dcandClaude Sonnet 5 a6dadafe46 fix(xrayeye): use showEvent() instead of a one-shot timer for control-panel width
CI for csaxs_bec / test (push) Successful in 2m20s
The previous fix (QTimer.singleShot(0, ...) from _init_ui()) was not
reliably late enough in practice -- still reproduced live over a remote
desktop session: window opens, image briefly visible, then the control
panel's wrong fixed width squeezes it down, recoverable only by moving
or resizing the window (which doesn't fix the wrong width itself, just
gives the image enough leftover space to look normal despite it).

showEvent() is the correct Qt idiom for "run once real geometry is
available" -- it fires every time the widget is actually made visible,
not just once at construction. Also more robust than a single deferred
call: it self-corrects on any later re-show too (e.g. a saved-workspace
restore re-applying stale geometry after the fix already ran once).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 15:44:23 +02:00
x01dcandClaude Sonnet 5 10b02ca2f1 fix(xrayeye): defer control-panel width-fixing until after first show
CI for csaxs_bec / test (push) Successful in 2m17s
_init_ui() locked the control panel to a fixed width computed from
sizeHint() called synchronously during construction -- before the
widget had ever been shown/laid out, with no real screen geometry and
fonts not fully resolved yet (worse over a remote desktop session).
Reported live: the widget opens correctly sized for a moment, then the
image area visibly collapses down to a sliver as the (wrong) fixed
width gets applied, recoverable only by manually resizing the window
(which doesn't fix the wrong width itself, just gives the image enough
leftover space to look normal again despite it).

Defer the actual sizeHint()/setFixedWidth() call via
QTimer.singleShot(0, ...) so it runs on the next event-loop iteration,
after the widget has actually been shown/laid out at least once --
same effect a manual resize was achieving, but automatic and correct
the first time.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 15:37:25 +02:00
x01dcandClaude Sonnet 5 3d0686329a fix(sim-cameras): correct exposure_time units from ~10s default to 10ms
CI for csaxs_bec / test (push) Successful in 2m18s
_SimIDSBackend's default exposure_time (10000.0, commented "ms") and its
get_exposure_range() ceiling formula (1_000_000.0 / pixel_clock_MHz) were
both scaled as if the unit were microseconds, inconsistent with the real
IDSCamera's documented exposure_time unit (ms; see its Cpt docstring and
exposure_time_max's own 1000.0 ms placeholder default). Net effect: every
fresh sim connect seeded exposure_time.put(10000), read by the real device
layer as a literal 10 second exposure -- ~50x a 5 Hz live-mode frame period
(200 ms), and get_exposure_range() reported a ~50 s ceiling at the default
20 MHz pixel clock.

New default (10 ms) and range formula (20_000.0 / pixel_clock_MHz, giving
~1000 ms at 20 MHz) keep the same pixel-clock-dependent shape but land in a
believable ms-scale range comfortably under a 5 Hz target.

Noticed live: simulated camera's exposure/gain readout looked nonsensical
after the manual-exposure work made this value load-bearing (previously
just an unused placeholder). Sim frame *content* is independent of
exposure/gain (no brightness modeling), so this alone doesn't explain a
reported "cut" first image, but the seeded value being wildly unrealistic
was worth fixing regardless.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 15:29:33 +02:00
x01dcandClaude Sonnet 5 80ccde83b2 chore(bec_widgets): regenerate client.py RPC stub for lock/unlock_vertical_center
CI for csaxs_bec / test (push) Successful in 2m12s
csaxs_bec/bec_widgets/widgets/client.py is a generated file (bw-generate-cli
--target csaxs_bec) that the BEC client uses to build its dynamic RPC proxy
classes -- it is the actual source of truth for what methods a client-side
widget reference exposes, NOT the live widget class itself. Adding
lock_vertical_center()/unlock_vertical_center() to OMNY_XRayEye without
regenerating this file meant every BEC client session (even a freshly
restarted one) built its xeyegui proxy without those two methods, causing
"AttributeError: 'OMNY_XRayEye' object has no attribute
'unlock_vertical_center'" live on a real session -- traced via
flomni.xeyegui._registry[...].__class__.__module__ == 'client', confirming
the proxy class comes from this file, not csaxs_bec.bec_widgets.widgets.xray_eye.x_ray_eye
directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 15:20:36 +02:00
x01dcandClaude Sonnet 5 5f6520214d fix(flomni): use cached read for sample-name poll in tomo params widget
CI for csaxs_bec / test (push) Successful in 2m19s
The OMNY_TomoParams widget polled flomni_samples.sample_names.sample0
uncached every 2s to refresh a display label. Over a long interlock
lock this produced thousands of RPC calls, overflowing the client's
shared 100-entry request buffer (RequestStorage) and evicting a
pending scan request, which stalled the queue with no error. The
signal is auto-monitored and already live in Redis, so read it from
there instead of round-tripping to the device server.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GCaaQQSYGCbkAsRnALWC3h
2026-09-17 15:18:47 +02:00
x01dcandClaude Sonnet 5 8bdd70eaf4 feat(xrayeye): add separator between shutter/camera-running and smear switches
CI for csaxs_bec / test (push) Successful in 2m28s
Both rows share one QGridLayout (for column alignment across rows of
different label widths) -- insert the separator as a full-width grid
row between them rather than a separate widget, and shift the
smear-integration and ROI-vertical-lock rows down accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 15:08:29 +02:00
x01dcandClaude Sonnet 5 d237357012 fix(omny-shared): filters_out_of_beam tolerates a missing/None dev
CI for csaxs_bec / test (push) Successful in 2m31s
Surfaced live on a simulated flomni session: tomo_scan_projection()
crashed with "DeviceConfigError: Device filter_array_1_x does not
exist" -- the real beamline's four filter_array_*_x devices
(bl_endstation.yaml) aren't part of any endstation simulation config,
by design (out of scope for an isolated endstation sim, same as
frontend/optics-hutch/detector devices).

filters_out_of_beam() already tried to skip a missing device via
getattr(dev, axis_name, None), but BEC's device container raises
DeviceConfigError -- a plain Exception, not AttributeError -- for a
missing device, so getattr's default never applied. Wrap the lookup
in try/except instead, which also preserves the pre-existing (and
apparently relied-upon, per several LamNI tests) behavior of `dev`
itself being None -- the usual state of this codebase's
`dev = builtins.__dict__.get("dev")` idiom before a real session sets
it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 14:22:29 +02:00
x01dcandClaude Sonnet 5 0690fbb391 feat(flomni): auto-lock ROI vertical center to FZP height in x-ray eye alignment
Implements docs/plans/xrayeye-vertical-lock.md: once the height-centering
step fixes the sample's vertical position against the FZP crosshair, every
selection box drawn for the remaining angle-alignment steps (1-5) no longer
needs manual vertical placement -- it snaps to the crosshair's y position on
draw and again on every drag/resize.

- OMNY_XRayEye: new _vertical_lock_enabled state, lock_vertical_center()/
  unlock_vertical_center() RPC methods, _snap_roi_vertical() helper wired
  into _style_new_roi() (both roiAdded and sigRegionChangeFinished). Snap is
  idempotent (no-op once already at the target y) so it can't loop back into
  itself via its own set_position() call.
- Also added a "ROI vertical lock" toggle in the control panel so an
  operator can release/re-engage the lock manually mid-run -- e.g. to freely
  draw/measure an unrelated box without aborting the alignment. Both the
  toggle and the RPC methods stay in sync (blockSignals pattern already used
  elsewhere in this file for on_live_view_enabled).
- x_ray_eye_align.py: lock_vertical_center() right after _height_centered
  flips to True; unlock_vertical_center() at the start of every fresh run
  (so a run never inherits a stale lock) and in align()'s finally block
  (so the lock never outlives a run, including on error/interruption).

FZP box (step 0) and the height-centering box itself remain fully free --
the lock isn't engaged yet at that point. LamNI/OMNY have their own
near-identical scripts but are explicitly out of scope (per the plan); the
widget change is inert by default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 14:22:20 +02:00
x01dcandClaude Sonnet 5 e0cb504c90 fix(flomni): print fermat_asymmetry in tomo_parameters() CLI display
The earlier fermat_asymmetry commit added it to the wrong "Current
settings" printout -- the scilog/permanent-record summary block
further down in the file, not the interactive tomo_parameters()
display operators actually look at before confirming/editing
settings. Caught live: the prompt asked for it but the summary above
never showed it. Add the missing print line, column-aligned with the
existing ones (verified: '=' lands at the same column as every other
line in the block).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 14:22:09 +02:00
x01dcandClaude Sonnet 5 695eac3706 docs(flomni): document fermat_asymmetry's effective x/y spacing
CI for csaxs_bec / test (push) Successful in 2m19s
Add an intuitive spacing table (x spacing stays at step, y spacing
becomes step/fermat_asymmetry) with a worked example, derived from
measuring actual generated positions (area-per-point vs. point count,
averaged over 20 randomized draws per ratio) rather than just stating
the formula. Mirrored into the OMNY port TODO so the same relationship
gets documented there once ported.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 13:46:27 +02:00
x01dcandClaude Sonnet 5 8cd9235e12 feat(flomni): add fermat_asymmetry (x/y spacing ratio) to Fermat scan
CI for csaxs_bec / test (push) Successful in 2m16s
Implements docs/plans/flomni-fermat-asymmetry.md as written: generate the
spiral against an inflated fovy half-width (length_axis2 * fermat_asymmetry),
then compress the accepted y-coordinate back down by the ratio before
storing -- keeps x spacing fixed at step, scales y spacing by the ratio,
preserves the requested FOV. Composes with the already-landed sub-step
randomization: the offset is subtracted at the raw (pre-compression) scale
on both axes, before the inflated-window check, so the offset's phase-shift
stays proportional to the local spacing on both axes.

Wired through the scan class (FlomniFermatScan), Flomni's global var +
queue/CLI plumbing (_TOMO_SCAN_PARAM_NAMES, scan_kwargs, tomo_parameters()
print/prompt, _set_default_tomo_params()), and the tomo_params Qt widget
(new field with a "?" help button, following the widget's existing
offsets-section help-button pattern).

Added tests verifying the inflate/compress math directly (against an
equivalent call with an inflated FOV and fermat_asymmetry=1.0, same seed)
rather than via an approximate spacing-ratio heuristic -- a first attempt
at the latter proved too noisy on a Fermat spiral to assert reliably.
Updated the flOMNI user docs and the OMNY port TODO (out of scope here,
explicitly excludes LamNI per the plan).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 13:07:44 +02:00
x01dcandClaude Sonnet 5 5e26b86d03 feat(scans): randomize Fermat spiral sub-step offset (flOMNI/OMNY/LamNI)
CI for csaxs_bec / test (push) Successful in 2m14s
Restores the legacy spec behavior of drawing a fresh random offset
(uniform in [0, step)) per call and shifting the sampling lattice by
it before the FOV crop check -- decorrelates position-tied
reconstruction errors that otherwise accumulate into ring artifacts
across a ptycho-tomography series. Applied identically to all three
generators (get_flomni_fermat_spiral_pos, get_omny_fermat_spiral_pos,
get_lamni_fermat_spiral_pos), same scalar offset for x and y, matching
the historical spec. New `seed` parameter (default None = fresh
randomization) allows deterministic/reproducible calls.

The four call sites that predict Fermat point counts ahead of a real
scan (flomni.py/lamni.py's _expected_fermat_position_count(),
tomo_params.py's _compute_fermat_positions_flomni/_lamni) now pin
seed=0 so their own estimates stay reproducible across repeated calls;
their docstrings note the real scan's count can differ by a point or
two near the threshold due to the randomization. Updated the affected
tests to compare against a matching seed=0 instead of an independent
random draw.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 12:30:13 +02:00
x01dcandClaude Sonnet 5 f81c4ee531 docs(ids-cameras): mark manual exposure/auto-gain plan as done
Confirmed merged into main (round-4 HW-testing commit 2f6f445 is an
ancestor of main) -- update the plan's status from "pending
real-hardware verification" to done.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 12:30:13 +02:00
x01dcandClaude Sonnet 5 ab1fdd9a19 docs(flomni): add plan for Fermat scan sub-step randomization fix
CI for csaxs_bec / test (push) Successful in 2m15s
Legacy spec code drew a fresh random offset per projection to shift the
Fermat lattice phase before FOV cropping (mitigates ptycho-tomo ring
artifacts). None of the current BEC Fermat spiral generators (flOMNI,
OMNY, LamNI) draw this offset -- all three are fully deterministic.
Plan only, not implemented; applies identically across all three setups.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx3KffiFyyDMKT8vvPENUW
2026-09-17 12:19:37 +02:00
x12sa a73eded409 config(flomni): switch FZP optics config to 120 micron diameter
CI for csaxs_bec / test (push) Successful in 2m25s
Update foptx/fopty/fosax/fosay in/out positions and detector_distance
for the 120 micron FZP (60 nm outermost zone), commenting out the
previous 150 micron settings for reference.
2026-09-17 12:02:37 +02:00
x12saandClaude Sonnet 5 861d2f2ff0 docs(flomni): add plan for Fermat scan x/y spacing asymmetry
CI for csaxs_bec / test (push) Successful in 2m20s
Introduces fermat_asymmetry, a ratio of x spacing to y spacing for the
Flomni Fermat-spiral scan: x spacing stays fixed at step while y spacing
scales by the ratio, with the requested field of view preserved (so the
total position count changes with the ratio). Covers the scan class,
global var/CLI, queuing, and the tomo_params Qt widget. Plan only, not
implemented -- written during a live beamtime experiment.

Also flags this for a future OMNY port (same Fermat-spiral algorithm),
explicitly excluding LamNI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WW873PFzsY4N1r65aQgfFn
2026-09-16 20:08:47 +02:00
x12saandClaude Sonnet 5 df433c88c4 fix(omny): accept p-accounts as valid in TomoIDManager
CI for csaxs_bec / test (push) Successful in 2m12s
bec.active_account is normally a p##### proposal account in
production, but _is_valid_eaccount() only matched e#####, causing
every real beamtime session to be silently rejected and fall back to
tomo ID 0 (skipping OMNY registration and PDF upload). Widen the
regex to accept both e- and p-accounts while still rejecting
test/gac-* accounts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpPtD7tCRwRZZnREhWcJi8
2026-09-16 13:09:45 +02:00
x12saandClaude Sonnet 5 9f922d869f docs(flomni): add plan for auto-locking xrayeye selection box vertical center
CI for csaxs_bec / test (push) Successful in 2m16s
Once height-centering fixes the sample's vertical position during the
flomni alignment procedure, the selection box for the 0-180 deg rotation
steps should auto-snap to the FZP height instead of requiring manual
vertical placement each time. Plan only, not implemented.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpPtD7tCRwRZZnREhWcJi8
2026-09-16 12:28:05 +02:00
x12saandClaude Sonnet 5 43aa84ac0e docs(omny): note that scilog_last_ptycho_scans still needs porting
CI for csaxs_bec / test (push) Successful in 2m18s
flomni and LamNI both have scilog_last_ptycho_scans plus its
projection-timing-log infrastructure; OMNY has neither, only the
older per-subtomo write_to_scilog/_write_subtomo_to_scilog. Flag it
as a TODO (same style as the existing filter/eye-check and
tomo_reconstruct TODOs in this folder) so it isn't lost, and note
that the step-size field just added to flomni/lamni's version should
be included from the start when this gets ported.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpPtD7tCRwRZZnREhWcJi8
2026-09-16 11:40:29 +02:00
x12saandClaude Sonnet 5 066d93f6cf feat(lamni): add step size to scilog_last_ptycho_scans summary line
CI for csaxs_bec / test (push) Successful in 2m22s
Mirrors the same fix just applied to Flomni: tomo_shellstep was already
captured in every projection timing record but never surfaced in the
per-scan SciLog message. Add it next to FOV.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpPtD7tCRwRZZnREhWcJi8
2026-09-16 11:39:42 +02:00
x12saandClaude Sonnet 5 3b332f9239 feat(flomni): add step size to scilog_last_ptycho_scans summary line
CI for csaxs_bec / test (push) Successful in 2m13s
tomo_shellstep was already captured in every projection timing record
but never surfaced in the per-scan SciLog message. Add it next to FOV,
matching the "step=...um" convention already used in
_describe_tomo_job_line.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpPtD7tCRwRZZnREhWcJi8
2026-09-16 11:33:18 +02:00
x12saandClaude Sonnet 5 ce913cfc87 fix(flomni): make fovx=220 inclusive everywhere, default exp_time example to 0.1
CI for csaxs_bec / test (push) Successful in 2m19s
- flomni_fermat_scan.py's fovx bound was lt=220 (exclusive), so 220 itself
  was rejected; change to le=220 to match flomni.py's fovx setter, which
  was already inclusive (only raises for val > 220).
- tomo_params.py's flomni fov_fields spinbox max was still 200.0, an
  independent limit that wasn't updated when fovx's cap was raised
  elsewhere -- bring it to 220.0 for consistency.
- exp_time's real default is now 0.1 instead of 0, so the generated scan
  doc's example (scans.flomni_fermat_scan?) shows a realistic exposure
  time instead of 0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197yD3hTxwvrgmYr7aaYiCD
2026-09-16 11:31:01 +02:00
x12saandClaude Sonnet 5 1fc702b854 fix(scans): correct undersized fermat spiral iteration cap for elongated FOVs
get_flomni_fermat_spiral_pos/get_omny_fermat_spiral_pos/get_lamni_fermat_spiral_pos
sized their spiral loop's iteration cap (n_max) from the FOV's area alone,
but the spiral radius only grows as step*0.57*sqrt(ii). For an elongated
(non-square) FOV -- e.g. flomni's fovx up to 220 vs fovy capped at 100 --
this under-estimated the iterations needed, so the loop ran out before
reaching the long axis's outer region. The two explicitly-appended
bounding-box corner points masked this: they showed up regardless, while
genuine spiral fill points near the true edge silently went missing
(observed as the pattern being "cut" well short of the requested FOV).

Add a shared compute_fermat_spiral_n_max() helper that takes the max of
the old area-based estimate and a corner-radius-based estimate, so
square/near-square FOVs are numerically unaffected (verified
byte-for-byte identical) while elongated ones now reach the requested
edge (verified: flomni fovx=220,fovy=30 now reaches 109.98um vs the
82.78um it reached before, against a 110.0um target).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197yD3hTxwvrgmYr7aaYiCD
2026-09-16 11:30:46 +02:00
x12saandClaude Sonnet 5 82e9e8bee1 feat(flomni): open frontend slit + drop filters on feye_in/feye_out
CI for csaxs_bec / test (push) Successful in 2m16s
feye_in now widens sl1xs/sl1ys by +0.3 mm and sets filter transmission
to 5% before moving the eye in, so there is enough flux/visibility on
the camera. feye_out restores the pre-open slit position (persisted
via a BEC global var so it survives a kernel restart) and fully
removes filters (fil_trans(1)) before checking whether the direct
beam disappeared. If the slit was manually re-adjusted while the eye
was in, feye_out now asks whether to keep the current size instead of
restoring the pre-eye value.

csaxs (for fil_trans) is threaded into Flomni via constructor
injection, gated on the existing "fttrx1 in dev" real-beamline check,
rather than exposing it as a global.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpPtD7tCRwRZZnREhWcJi8
2026-09-16 10:55:35 +02:00
x12saandClaude Sonnet 5 1ef73f31e4 feat(flomni): raise fovx limit from 200 to 220 um
CI for csaxs_bec / test (push) Successful in 2m20s
Raise the flOMNI fovx cap in the flomni.fovx property setter, the
tomo_parameters() summary/prompt text, and the flomni_fermat_scan
ScanArgument bound + docstring, so all four are consistent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197yD3hTxwvrgmYr7aaYiCD
2026-09-16 10:08:41 +02:00