docs(ids-cameras): add plan for manual exposure/auto-gain control
CI for csaxs_bec / test (push) Failing after 2m5s
CI for csaxs_bec / test (push) Failing after 2m5s
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
This commit is contained in:
1 parent
0cadd8582e
commit
ec3e7c0679
1 file changed
+133
@@ -0,0 +1,133 @@
|
||||
# Plan: Add manual exposure / auto-gain toggle control to IDSCamera
|
||||
|
||||
Status: **planned, not yet implemented** (`IDSCamera` is used in production during
|
||||
beamtimes; implementation should happen in its own change, reviewed and tested outside
|
||||
a live beamtime).
|
||||
|
||||
## Context
|
||||
|
||||
`IDSCamera` (the cSAXS ophyd device wrapping IDS uEye cameras via the vendor's `pyueye`
|
||||
SDK) currently gives operators no way to leave auto-exposure/auto-gain and drive the
|
||||
camera manually. The underlying driver already has almost everything needed — it's just
|
||||
not wired up or exposed to BEC clients:
|
||||
|
||||
- `Camera.exposure_time` (get/set property) — `base_integration/camera.py:220-235`
|
||||
- `Camera.set_auto_shutter(enable)` — `base_integration/camera.py:248-257` (auto-exposure toggle)
|
||||
- `Camera.set_auto_gain(enable)` — `base_integration/camera.py:237-246` (auto-gain toggle)
|
||||
|
||||
None of these three are called anywhere, and none are on `IDSCamera.USER_ACCESS`, so a
|
||||
BEC client can't reach them today. The sibling `AlliedVisionAravisCamera` class already
|
||||
has the equivalent pattern wired up for `exposure_time`/`gain` as plain
|
||||
`get_x`/`set_x` USER_ACCESS methods (`allied_vision_aravis_camera.py:222-238`) — this
|
||||
plan mirrors that same style for IDS, plus adds the auto-exposure/auto-gain enable
|
||||
toggles IDS has (and Allied Vision doesn't).
|
||||
|
||||
Per user decisions (2026-09-11): cover exposure control + the auto-gain enable/disable
|
||||
toggle only (no manual gain *value* — the driver has no SDK call for that yet); expose
|
||||
everything as plain `USER_ACCESS` methods (matching the existing Allied Vision pattern,
|
||||
not a new `Signal`); and leave connect-time default behavior untouched (no new
|
||||
constructor/config kwarg — the camera keeps whatever state `is_ResetToDefault()` leaves
|
||||
it in unless a client explicitly calls the new methods).
|
||||
|
||||
## Changes
|
||||
|
||||
### 1. `csaxs_bec/devices/ids_cameras/ids_camera.py`
|
||||
|
||||
Add a new `############## Exposure / Gain ##############` section (mirroring
|
||||
`allied_vision_aravis_camera.py:222-238`), placed after the live-mode/ROI methods and
|
||||
before `############## User Interface Methods ##############`:
|
||||
|
||||
```python
|
||||
def get_exposure_time(self) -> float:
|
||||
"""Get the current exposure time in milliseconds."""
|
||||
return self.cam.exposure_time
|
||||
|
||||
def set_exposure_time(self, value: float) -> None:
|
||||
"""Set the exposure time in milliseconds. Does not itself disable auto-exposure --
|
||||
call set_auto_exposure_enabled(False) first, or the driver will keep overriding it."""
|
||||
self.cam.exposure_time = value
|
||||
|
||||
def set_auto_exposure_enabled(self, enable: bool) -> None:
|
||||
"""Enable or disable the camera's auto-exposure (auto-shutter)."""
|
||||
self.cam.set_auto_shutter(enable)
|
||||
|
||||
def set_auto_gain_enabled(self, enable: bool) -> None:
|
||||
"""Enable or disable the camera's auto-gain."""
|
||||
self.cam.set_auto_gain(enable)
|
||||
```
|
||||
|
||||
(Confirm the SDK's exposure unit — `is_Exposure`/`IS_EXPOSURE_CMD_*` is documented in
|
||||
milliseconds; adjust the docstring if inspection shows otherwise.)
|
||||
|
||||
Add the four new method names to `USER_ACCESS` (`ids_camera.py:67-76`):
|
||||
`"get_exposure_time"`, `"set_exposure_time"`, `"set_auto_exposure_enabled"`,
|
||||
`"set_auto_gain_enabled"`.
|
||||
|
||||
No changes needed in `base_integration/camera.py` — `exposure_time`, `set_auto_shutter`,
|
||||
`set_auto_gain` already exist there exactly as needed.
|
||||
|
||||
### 2. `csaxs_bec/devices/sim/sim_cameras.py`
|
||||
|
||||
`SimIDSCamera` replaces `self.cam` with `_SimIDSBackend` (line 211), which currently has
|
||||
no `exposure_time` attribute and no `set_auto_shutter`/`set_auto_gain` methods — calling
|
||||
the new `IDSCamera` methods against a sim device would raise `AttributeError`. Add to
|
||||
`_SimIDSBackend` (mirroring `_SimAlliedVisionBackend.exposure_time`, lines 397-403):
|
||||
|
||||
```python
|
||||
def __init__(self, ...):
|
||||
...
|
||||
self._exposure_time = 10000.0 # ms, matches _SimAlliedVisionBackend's default
|
||||
self._auto_exposure = True
|
||||
self._auto_gain = True
|
||||
|
||||
@property
|
||||
def exposure_time(self) -> float:
|
||||
return self._exposure_time
|
||||
|
||||
@exposure_time.setter
|
||||
def exposure_time(self, value: float):
|
||||
self._exposure_time = value
|
||||
|
||||
def set_auto_shutter(self, enable: bool):
|
||||
self._auto_exposure = bool(enable)
|
||||
|
||||
def set_auto_gain(self, enable: bool):
|
||||
self._auto_gain = bool(enable)
|
||||
```
|
||||
|
||||
### 3. Tests — `tests/tests_devices/test_ids_camera.py`
|
||||
|
||||
Mirror `test_allied_vision_aravis_camera.py:124-139`'s pass-through style (that file's
|
||||
fixture mocks the whole `camera.cam`, same as `test_ids_camera.py`'s fixture), e.g.:
|
||||
|
||||
```python
|
||||
def test_get_set_exposure_time(ids_camera):
|
||||
ids_camera.cam.exposure_time = 5000.0
|
||||
assert ids_camera.get_exposure_time() == 5000.0
|
||||
ids_camera.set_exposure_time(1234.5)
|
||||
assert ids_camera.cam.exposure_time == 1234.5
|
||||
|
||||
def test_set_auto_exposure_enabled(ids_camera):
|
||||
ids_camera.set_auto_exposure_enabled(False)
|
||||
ids_camera.cam.set_auto_shutter.assert_called_once_with(False)
|
||||
|
||||
def test_set_auto_gain_enabled(ids_camera):
|
||||
ids_camera.set_auto_gain_enabled(False)
|
||||
ids_camera.cam.set_auto_gain.assert_called_once_with(False)
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
- Run the existing + new unit tests: `pytest tests/tests_devices/test_ids_camera.py -v`
|
||||
(and `test_allied_vision_aravis_camera.py` to confirm no regressions from the shared
|
||||
pattern).
|
||||
- Manual/integration check against real hardware (if available during a beamtime/test
|
||||
session): from a BEC client, on a connected IDS camera device (e.g. `dev.cam_xeye`):
|
||||
```python
|
||||
dev.cam_xeye.set_auto_exposure_enabled(False)
|
||||
dev.cam_xeye.set_exposure_time(20000) # e.g. 20 ms
|
||||
print(dev.cam_xeye.get_exposure_time())
|
||||
dev.cam_xeye.set_auto_gain_enabled(False)
|
||||
```
|
||||
and confirm the live image brightness responds to the manual exposure value instead of
|
||||
drifting on its own.
|
||||
Reference in new issue
Block a user