Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions software/control/camera_toupcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,15 @@ def _set_acquisition_mode_imp(self, acquisition_mode: CameraAcquisitionMode):
raise ValueError(f"Do not know how to handle {acquisition_mode=}")
self._camera.put_Option(toupcam.TOUPCAM_OPTION_TRIGGER, trigger_option_value)

if acquisition_mode == CameraAcquisitionMode.SOFTWARE_TRIGGER:
# The SDK moves the trigger source to software along with the option only while the stream runs.
# Switched while stopped, the source stays on the hardware input (GPIO0) and every software
# trigger is ignored, so set it explicitly, as the hardware branch below does for its source.
try:
self._camera.IoControl(1, toupcam.TOUPCAM_IOCONTROLTYPE_SET_TRIGGERSOURCE, 5) # 5 => software
except toupcam.HRESULTException as ex:
self._log.warning("Unable to set trigger source to software: " + hresult_checker(ex))

if acquisition_mode == CameraAcquisitionMode.HARDWARE_TRIGGER:
if HARDWARE_TRIGGER_MODE == HardwareTriggerMode.LEVEL:
try:
Expand Down
61 changes: 61 additions & 0 deletions software/tests/control/test_camera_toupcam_trigger_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Regression test: switching the ToupTek camera to software trigger while its stream is stopped.

Measured on an ITR3CMOS26000KMA: the SDK moves the trigger source (IOCONTROLTYPE TRIGGERSOURCE: 1 = GPIO0,
5 = software) along with OPTION_TRIGGER only while the stream runs. Switched from hardware to software
trigger while stopped, the source stays on GPIO0 and every software trigger is ignored, which aborted
Record + Z-Stack z-stacks started from hardware-trigger live view. The fake SDK below models that.
"""

import squid.logging
from squid.abc import CameraAcquisitionMode
import control.camera_toupcam as camera_toupcam
import control.toupcam as toupcam

_GPIO0, _SOFTWARE = 1, 5


class _FakeSDK:
def __init__(self):
self.streaming = False
self.option_trigger = 1
self.source = _SOFTWARE

def put_Option(self, option, value):
if option == toupcam.TOUPCAM_OPTION_TRIGGER:
self.option_trigger = value
if self.streaming and value == 1:
self.source = _SOFTWARE # the SDK follows the option only while streaming

def IoControl(self, line, kind, value):
if kind == toupcam.TOUPCAM_IOCONTROLTYPE_SET_TRIGGERSOURCE:
self.source = value
return 0


def _make_camera():
cam = object.__new__(camera_toupcam.ToupcamCamera)
cam._camera = _FakeSDK()
cam._log = squid.logging.get_logger("test_toupcam_trigger_source")
cam.get_exposure_time = lambda: 20.0
cam.set_exposure_time = lambda exposure_ms: None
return cam


def test_software_trigger_set_while_stopped_moves_the_source_off_the_hardware_input():
cam = _make_camera()
cam._set_acquisition_mode_imp(CameraAcquisitionMode.HARDWARE_TRIGGER)
assert cam._camera.source == _GPIO0

cam._camera.streaming = False
cam._set_acquisition_mode_imp(CameraAcquisitionMode.SOFTWARE_TRIGGER)

assert cam._camera.option_trigger == 1
assert cam._camera.source == _SOFTWARE


def test_hardware_trigger_still_selects_the_hardware_input():
cam = _make_camera()
cam._set_acquisition_mode_imp(CameraAcquisitionMode.SOFTWARE_TRIGGER)
cam._set_acquisition_mode_imp(CameraAcquisitionMode.HARDWARE_TRIGGER)
assert cam._camera.option_trigger == 2
assert cam._camera.source == _GPIO0
Loading