Skip to content

feat(record-zstack): Hamamatsu fps not settable — report achievable rate, read-only UI#587

Open
hongquanli wants to merge 1 commit into
feat/record-zstack-acquisitionfrom
feat/hamamatsu-fps-not-settable
Open

feat(record-zstack): Hamamatsu fps not settable — report achievable rate, read-only UI#587
hongquanli wants to merge 1 commit into
feat/record-zstack-acquisitionfrom
feat/hamamatsu-fps-not-settable

Conversation

@hongquanli

Copy link
Copy Markdown
Contributor

Summary

On the Hamamatsu ORCA the continuous-mode frame rate is not settableINTERNAL_FRAMERATE and INTERNAL_FRAMEINTERVAL are read-only in every camera state (bench-verified: DCAMERR.NOTWRITABLE). It free-runs at the exposure/readout-limited max, overlapping exposure with readout. This makes the Record + Z-Stack UI and worker reflect that, generalized via a camera capability so it composes cleanly with the ToupTek _continuous_max_framerate work already on this branch (ToupTek can set the rate via PRECISE_FRAMERATE and is untouched).

Supersedes the earlier INTERNAL_FRAMERATE-writing attempt (which produced a NOTWRITABLE ERROR at the bench and silently forced READOUTSPEED=FASTEST).

Changes

  • AbstractCamera: can_set_frame_rate() (default True) + estimate_frame_rate(exposure_ms) (default sequential 1000/(exposure+strobe)).
  • HamamatsuCamera:
    • can_set_frame_rate() → False.
    • set_frame_rate() reports the camera's authoritative free-run rate by reading the read-only INTERNAL_FRAMERATE — which reflects readout mode (fast/standard/ultraquiet), bit depth, ROI and exposure — falling back to the overlap estimate only if the read fails. No writes.
    • estimate_frame_rate() = overlap 1000/max(readout, exposure) (readout = INTERNAL_LINEINTERVAL × rows), for the widget's preview at a hypothetical exposure the camera isn't set to.
  • Record widget: when the camera can't set fps, the fps field is read-only "≈ X fps", recomputed from the (still-editable) recording exposure — including after a YAML preset load.
  • Worker: for not-settable cameras, size/pace the recording to the achievable max instead of honoring params.fps.

Testing

  • camera: base/sim capability + estimate; Hamamatsu authoritative-read vs formula-fallback + overlap math (guarded on the DCAM lib — absent off-instrument, runs where DCAM exists).
  • worker: recording sized to the achievable max for not-settable cameras.
  • widget: fps read-only + live recompute (both max() branches) vs editable for settable cameras.
  • Local run: camera 8 passed / 1 skipped, widget 87 passed, worker 26 passed. black clean.

Review

Independent review: no Critical, no Important; Minor items addressed (docstring accuracy on the capability default; refresh the read-only display after YAML load) or noted (display-only spinbox clamp at multi-second exposures). Reading INTERNAL_FRAMERATE for the actual rate resolves the reviewer's "ROI not reflected in the estimate" note for the worker path.

Off-instrument constraint

HamamatsuCamera can't be imported/run without the DCAM library, so its methods are py_compile + static-review verified here and will first execute on the ORCA. Bench check: confirm set_frame_rate returns a plausible full-frame value (~40–95 fps at short exposures) and the widget's "≈ X fps" tracks exposure.

🤖 Generated with Claude Code

…ate, don't try to set

Hamamatsu ORCA can't command a frame rate in CONTINUOUS mode (INTERNAL_FRAMERATE and
INTERNAL_FRAMEINTERVAL are read-only, bench-verified: DCAMERR.NOTWRITABLE). It free-runs at
the exposure/readout-limited max, overlapping exposure with readout. Make the UI and worker
match, generalized via a camera capability so ToupTek (which CAN set via PRECISE_FRAMERATE)
is unaffected.

- AbstractCamera.can_set_frame_rate() (default True) + estimate_frame_rate(exposure_ms)
  (default sequential 1000/(exposure+strobe)).
- HamamatsuCamera: can_set_frame_rate() -> False. set_frame_rate() reports the camera's
  AUTHORITATIVE free-run rate by reading the read-only INTERNAL_FRAMERATE (which reflects the
  current readout mode fast/standard/ultraquiet, bit depth, ROI and exposure), falling back to
  the overlap estimate only if the read fails. estimate_frame_rate() = overlap
  1000/max(readout, exposure) (readout = line_interval * rows) for the widget's preview at a
  hypothetical exposure. Neither path writes anything (removes the NOTWRITABLE ERROR + the
  READOUTSPEED side-effect of the earlier approach).
- Record widget: when the camera can't set fps, the fps field is read-only and shows
  "~ X fps", recomputed from the (still-editable) recording exposure (incl. after a YAML load).
- Worker: for not-settable cameras, size/pace the recording to the achievable max instead of
  honoring params.fps.

Tests: base/sim capability + estimate; worker sizes to achievable; widget read-only vs editable.
Hamamatsu-specific test (authoritative-read vs formula-fallback + overlap math) guarded on the
DCAM lib (absent off-instrument); the overlap math is also covered by the ToupTek continuous-max
tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant