Captured 2026-09-15 directly from the omny-panda hardware via
GetState() (ophyd_devices/devices/panda_box/utility_scripts.py), one
per Design: omny.ini (Mode A) and omny_freerun.ini (Mode B). Mode B was
speed-tested successfully up to 20 kHz same day, though the saved
omny_freerun.ini itself reflects CLOCK1.PERIOD=0.0001 (10 kHz) -- flagged
in the setup doc as a discrepancy to double-check rather than assumed
away.
Setup doc updated with the exact save/load commands and a diff summary
of the two layouts' mode-specific differences (PCAP.GATE/TRIG source,
CLOCK1.ENABLE, capture types).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TsfqLcPWspjJ6vedMyCxco
Both omny_panda and omny_panda_continuous ended up connected and active
simultaneously against the same physical PandA, even though only
omny_panda was enabled: true -- a device already connected apparently
doesn't disconnect just because a later config reload disables it.
Comment the inactive mode's block out entirely instead of relying on
enabled: false, so BEC never constructs the device object at all. Docs
updated to correct the earlier (wrong) guidance that toggling enabled
alone was sufficient to switch between modes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TsfqLcPWspjJ6vedMyCxco
Mode B (omny_panda_continuous) confirmed working end-to-end against real
PandA hardware on 2026-09-15: correct per-channel raw values, masked
gate_detector_active, and frame_counter-based completion all verified.
Switching back to Mode A (omny_panda) for A/B comparison testing.
Both device entries now stay uncommented in ptycho_flomni.yaml, toggled
via enabled: true/false, rather than commenting/uncommenting blocks --
confirmed a disabled device is an inert placeholder that BEC's device
server never connects to. Speed testing Mode B beyond the initial 100 Hz
is still TODO.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TsfqLcPWspjJ6vedMyCxco
Mode A (per-gate aggregate) and Mode B (continuous raw-sample) need
different PandA hardware layouts and can't run simultaneously, so
omny_panda is now two device config entries in ptycho_flomni.yaml:
omny_panda (legacy, kept commented as the restore reference) and
omny_panda_continuous (raw_stream_mode: true, enabled).
Also fixes on_complete for the continuous case. PCAP.CAPTURED can't be
used to detect end-of-acquisition on this layout: CLOCK1 drives
PCAP.gate/trig continuously, so the captured count never naturally
stops increasing while armed -- the previous settle-based wait would
have just run out its timeout. COUNTER1 (frame_counter) instead counts
completed exposures directly (triggered on the detector-trigger's
falling edge), so PandaBoxOMNY now waits for it to reach the exact
expected num_points * frames_per_trigger, mirroring the legacy
exact-count check instead of guessing when capture has "settled".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TsfqLcPWspjJ6vedMyCxco
TTLIN1 (detector trigger) drives both COUNTER1 (frame_counter) and the
PCAP.BITS0 gate-bit capture, confirmed 2026-09-15 against the omny-panda
hardware via TTLIN1.VAL.CAPTURE_WORD?/OFFSET? (PCAP.BITS0, bit 0).
PCAP.BITS0 is a shared 32-bit word -- other captured bit_out signals may
occupy other bit positions of it, so the raw word is not itself a clean
0/1. PandaBoxOMNY now masks gate_detector_active down to bit 0 before
publishing it, instead of trusting the whole word.
Also records the confirmed free-running setup in the developer docs: all
four encoders plus both FMC_IN channels switched to raw Value capture,
CLOCK1 driving continuous PCAP gate/trig, and the initial 100 Hz test
rate.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TsfqLcPWspjJ6vedMyCxco
Adds an opt-in raw_stream_mode to omny_panda alongside the existing
per-gate averaged burst acquisition, so a burst window can instead be
captured as continuous raw samples plus a detector-gate bit and a
monotonic frame counter for completeness checking. Client-side
coalescing bounds the message rate to the BEC message bus regardless of
PandA's own network-frame batching, and completion uses a settle-based
check since an exact expected sample count isn't predictable for
continuous capture. Defaults to off, preserving today's behavior
exactly; hardware wiring/layout and the raw data schema are documented
under docs/developer/ pending PandA hardware access to configure and
validate against.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a temporary single-camera (ID 41, color) device config for manually
verifying the new exposure/auto-gain controls against real hardware
outside a live beamtime, and update the plan's status accordingly.
Reorders omny.md's parameter table to match the actual signature,
adds the previously undocumented readout_time parameter, and adds an
Examples block to OmnyFermatScan's docstring (mirroring the flomni fix).
Also rewords flomni.md's corridor_size default to read "3 um" for
consistency with omny, while noting it is auto-estimated when omitted.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFeiCHv3qUxxssmP9Qzhom
Reorders parameters to match the actual signature, adds the
undocumented burst_at_each_point parameter, and corrects the
corridor_size default (None/auto-estimated, not a literal 3 um).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFeiCHv3qUxxssmP9Qzhom
Port the flomni doc addition to omny -- both setups share the identical
tomo_alignment_fit mechanism (OMNYAlignmentMixin.get_alignment_offset
mirrors Flomni's), so the same command-line offset nudge applies verbatim.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFeiCHv3qUxxssmP9Qzhom
Add a snippet to the XrayEye alignment section showing how to patch the
constant x/y offset terms of tomo_alignment_fit directly on the command
line, for a quick few-micron correction (e.g. after moving foptz) without
recording a new fit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFeiCHv3qUxxssmP9Qzhom
No code changes -- IDSCamera and the xrayeye widget are both used
during beamtimes. Revises the earlier plain-method design to
Cpt(Signal, kind=Kind.config) (mirroring live_mode_enabled) so the
xrayeye widget can read exposure/auto-exposure/auto-gain state via
its existing device_read_configuration subscription instead of
polling the device, and adds the corresponding GUI control knobs
(auto-exposure/auto-gain toggles, exposure spinbox) to the plan.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s92UHyMfov6Fb934pGqcU
No code changes -- IDSCamera is in production use during beamtimes.
Documents the intended USER_ACCESS additions (get/set_exposure_time,
set_auto_exposure_enabled, set_auto_gain_enabled) for a future session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s92UHyMfov6Fb934pGqcU
find_rotation_center_smear_experimental() rotated 45->0 for its post-shift
verification sweep, needlessly re-treading ground the preceding full smear
sweep already covered and adding a long extra blocking move that could
starve the GUI heartbeat past _gui_call_with_retry's budget, surfacing as
RuntimeError: GUI is not alive. Sweep straight back to 0 instead, and widen
the retry budget (8x1.5s -> 25x2.0s) so update_frame() can ride out long
blocking moves on slower hardware.
Also tidy a stray comment-block artifact in ptycho_lamni.yaml and update
lamni.md docs for the current xrayeye_rotation_center_calibration_* API.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Remove the manual fit-parameter reload docs: fit params already load
automatically, and the fallback path pointed at the retired
~/Data10/specES1 layout.
- Fix stale slits/slit0wh references (slits takes no argument; the
gap monitor is now slit1).
- Stop writing the plain-text xrayeye_alignmentvalues archive in
write_output() -- it was only for external fitting scripts and now
collides with xrayeye_alignmentvalues being a directory used for the
per-run HDF5 archives.
Add docs/developer/lamni_smear_architecture.md covering the three-tier
device/ipython-client/GUI architecture this feature exercises: the
camera's two PreviewSignal channels (image vs smear_preview) and why
composite pushes needed their own channel, XRayEye.set_live_view_signal
and exactly when the caller must switch it back, the sweep algorithm
(non-blocking mv() + ScanReport.status polling + max-projection), and
the bw-generate-cli regeneration step required whenever a plugin
widget's USER_ACCESS changes (the cause of this session's confusing
AttributeError even after a full client/GUI restart).
Also expand the user-facing description in lamni.md with a note on the
live-updating composite display and a cross-reference to the new
developer page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
rotation_center_calibration_start(sample_type="isolated") didn't match the
xrayeye_* naming convention used by the other X-ray eye entry points, and
the sample_type string enum was easy to forget (both the parameter name
and its accepted values). Split into two entry points instead:
lamni.xrayeye_rotation_center_calibration_isolated(keep_shutter_open=False)
lamni.xrayeye_rotation_center_calibration_extended(keep_shutter_open=False)
Both delegate to a shared _xrayeye_rotation_center_calibration() private
helper (single copy of the KeyboardInterrupt cleanup). Updated the user
docs to match; XrayEyeAlign.find_rotation_center()'s own sample_type
parameter is unchanged since it's internal shared implementation, not
what was being objected to.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
lsamx_center/lsamy_center have to be re-measured by hand for nearly every
new sample (the rotation stage is tilted on top of lsamx/lsamy, so the
effective axis position shifts with sample thickness/mounting). Add
XrayEyeAlign.find_rotation_center()/lamni.rotation_center_calibration_start()
to automate this using the existing X-ray eye GUI and lamni_move_to_scan_center
interferometer-feedback move:
- "isolated" sample: click particle centre at 0 and 180 deg, use the
midpoint (angle-tilt-independent) as the rotation axis position.
- "extended" sample: live 0->180->0 sweep for visual identification, single
click, then a verification sweep and an accept/iterate prompt.
Both write the result to lsamx/lsamy's "center" userParameter after
confirmation. Heavily verbose logging throughout since this is fundamentally
an interactive, hardware-in-the-loop procedure that can't be fully exercised
by automated tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a worked example to both lamni.md and flomni.md showing a hook
loaded from a file that temporarily bumps tomo_countingtime and
tomo_shellstep every 5th projection within sub-tomogram 2 (tomo_type 1),
restoring both in a finally block -- demonstrating that a hook can
change any scan parameter, not just move devices like the existing
polarizer example. Notes that progress["subtomo"]/["subtomo_projection"]
work the same way for tomo_types 2/3, just with an open-ended
sub-tomogram count there instead of type 1's fixed 8.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
register_at_each_angle_hook() stores a function object by reference, so
editing a hook's def and forgetting to re-register left the old version
running silently -- easy to miss mid-beamtime. Detect it via
func.__globals__ (a redefined top-level def, or a reloaded module,
mutates the same namespace dict in place) and auto-adopt the new
definition with a printed note instead of failing or staying stale.
Centralizes the shared lookup/error logic (previously duplicated in
lamni.py and flomni.py) into TomoQueueMixin._resolve_at_each_angle_hook().
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The tool is called BEC_ptycho_align now, not SPEC_ptycho_align.m as
the previous docs/comments said -- fixes the references introduced in
the previous commit (12b2538) in lamni.py's docstrings/print message
and the user docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the awkward documented workaround (configure a full tomo_type
1 setup with 96 projections, then launch just sub_tomo_scan(1, 0)) with
a dedicated command, ported from flomni's tomo_alignment_scan(): adjust
tomo_parameters() (FOV/step/counting time), then call
lamni.tomo_alignment_scan() directly -- no tomo_type/sub-tomogram
bookkeeping involved, matching flomni's clean two-step workflow.
Runs 12 points evenly spaced across the full 360 degrees (lamni has no
180-degree symmetry the way flomni does, so unlike flomni's 5-point/
180-degree scan, this covers the full circle -- point count matches
what the old workaround's docs defaulted to, endpoint=False since
360==0 degrees). Aborts if x-ray-eye alignment hasn't been done yet
(tomo_fit_xray_eye unset), mirroring flomni's equivalent guard.
write_alignment_scan_numbers() writes the same 4-line scan-number/
angle/offset log flomni's version does, to
~/data/raw/logs/ptychotomoalign_scannum.txt for SPEC_ptycho_align.m,
also printed at the console (flomni's own console-print equivalent is
dead/commented-out code; lamni's actually prints).
Scope note: flomni's version conditionally skips its eye-out/optics-in
transition when already in measurement condition with feedback
running, to avoid an unneeded interferometer reset -- lamni has no
equivalent helpers for that check, so this calls leye_out()
unconditionally instead. Left as a possible follow-up, not in scope
here.
Item 6 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md. Documented in
docs/user/ptychography/lamni.md's "Fine alignment" section.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add both to the tomo-queue command reference in flomni.md and
lamni.md: tomo_queue_resume() as an alias for tomo_queue_execute(),
and tomo_queue_reacquire(job_index, projection_number) for reopening
a job (including an already-"done" one) at an earlier projection and
cascading later jobs to pending, mirroring the code added in the
previous commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Generalize TomoParamsWidget to lamni via a SETUP_PROFILES mechanism
(setup detection, per-setup field lists/order, a new Offsets section,
a setup-agnostic "Duplicate job" queue button), fixing two latent
TomoQueueDialog bugs that silently mishandled lamni jobs along the way.
While verifying the GUI's projection-count preview against the CLI,
found two real, pre-existing bugs in LamNI.sub_tomo_scan() unrelated
to the GUI itself: a duplicate closing angle every sub-tomogram
(360=0 degrees), and a phase offset computed from the raw stepsize
instead of the achievable one, breaking the equally-spaced-when-
combined guarantee for sub-tomogram pairs/quads/the full set. Both
fixed to mirror Flomni's existing, correct equivalents.
Also fills in lamni's user documentation with the queue/command-job/
at-each-angle-hook system, which it previously lacked entirely,
mirroring flomni.md's coverage.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GUI test checklist: sections 6c/6d/6e (items 37-52) all passed
real-session testing, including the stale-"running" override fixing
an actual stuck job found live -- status line and section 1 items
updated. New section 6f (items 53-55) for the "Add current params to
queue" mid-edit warning, plus section 1 item 13.
Plan doc section 6.1: records the second live-vs-unsaved-edit
confusion (a second window, not just the panel's own button) and why
it's a warning, not a block, unlike Submit.
User manual: new paragraph distinguishing the params panel's "Add to
queue" from the queue window's "Add current params to queue", and
when the warning fires.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User manual: notes that declining the fermat-scan confirmation now
fails the job instead of silently skipping a projection, plus how to
recover a job stuck at "running" (tomo_queue_delete has no status
guard; update_by_id to fix the status directly; the GUI's staleness
override).
AI_docs: plan section 3.4 records the second real incident (declined
confirmation silently returning) and its fix; section 6.3 records the
stale-"running" GUI guard fix. GUI test checklist gets section 6e
(test steps 48-52) and section 1 item 12, status line updated to flag
6c/6d/6e as not yet clicked through.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User manual: new paragraph in the queueing section explaining the
button and that Submit/"Add to queue" still gate the actual write.
AI_docs: plan section 6.8 records the design (not a third write path,
confirm-before-discard, button-eligibility gating) and the
_enter_edit_mode_with() refactor; GUI test checklist gets section 6d
(test steps 42-47) and a new section 1 item 11, with the status line
updated to flag 6c/6d as not yet clicked through.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User manual: notes that the hook (name + source, when registered)
appears in tomo_parameters(), the scilog entry, the PDF report, and
the progress-ring label, and clarifies that unregistering a hook does
not clear at_each_angle_hook -- confirmed expected behavior.
AI_docs test checklist: records that sections 3-6b passed real-session
testing (updating the stale "not yet clicked through" status), adds
section 6c (test steps 37-41) for the fixes found during that testing,
and updates section 1's summary (new item 10).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two additions to the at_each_angle hook writeup: registering a hook
stores the function object at that moment, not a live link to its
name, so editing the function later requires calling
register_at_each_angle_hook() again -- redefining it alone does
nothing. And tomo_scan_projection()/tomo_acquire_at_angle() are not
interchangeable (Fermat vs single-point); the example hook now calls
tomo_scan_projection() plainly (no more confusing _internal=True in
user-facing code) with a clear note on when to use the other one
instead, matching the new CLI/GUI warnings.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User manual: explains the new "At-each-angle hook" dropdown and its
"None (default)"/"(not registered in this session)" states, plus that
an active hook shows on the status webpage.
AI_docs: updates section 3.4 (GUI widget added, webpage display
added, extended sim-validation note for the global-var publishing and
webpage payload) and the plan's top status line. GUI test checklist
gets a new section 6b (test steps 28-35: dropdown population/refresh,
Submit vs Add-to-queue behaviour, the "not registered" label, and the
webpage's queue-detail + Current-measurement rendering) and an updated
section 1 item 9 + top status line noting none of this is click-tested
yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a "Custom behavior at each projection angle" subsection to the
Tomography section of the flOMNI user manual (docs/user/ptychography/flomni.md):
what a hook function looks like, a polarizer-modulation example, and
how to register/activate/deactivate/list/unregister one, with an
explicit warning about resetting at_each_angle_hook before queuing a
normal follow-up job. Also links it from the quick-start queueing
section. AI_docs/TOMO_QUEUE_COMMAND_JOBS_PLAN.md gets a new section
3.4 recording the design decision (session-only registry, not a
hardcoded one; a new _TOMO_SCAN_PARAM_NAMES entry, not a new job kind;
the GUI param-snapshot leak found and fixed while wiring this up; no
GUI widget yet).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a "Command jobs" reference section covering
tomo_queue_add_command(), the move/optimize_idgap action registry, the
mokev/idgap device allow-list, tomo_queue_show() output format, and
the idempotent-vs-prompt crash-resume behaviour, plus a pointer to it
from the quick-start queueing section.
Type-tags queue jobs with a "kind" field (back-compat: missing kind
defaults to "tomo"), and adds "command" jobs that reconfigure the
beamline (currently: absolute moves on an allow-listed device, plus an
optimize_idgap stub) between tomograms instead of running a scan.
Command jobs are added via tomo_queue_add_command() against a curated
named-action registry (_TOMO_QUEUE_ACTIONS) - not arbitrary code - so
every queued action is auditable and safe to persist across a kernel
restart. Validation is two-layer: schema checks at add time, and each
action re-checks its own arguments against live hardware at execution
time, which is the actual safety boundary. Crash-resume for a command
job re-runs the whole step sequence from the top if every step is
idempotent, or prompts the operator otherwise.
flomni_webpage_generator.py is guarded against command jobs, which
have no "params" key.
See AI_docs/TOMO_QUEUE_COMMAND_JOBS_PLAN.md for the full design.