20"""AndorSDK3 camera device.
22This class provides a wrapper for PYME's SDK3 interface that allows
23a camera and all its settings to be exposed over Pyro.
46_logger = logging.getLogger(__name__)
50DPTR_TYPE = SDK3.POINTER(SDK3.AT_U8)
54SDK3_STRING_TO_TRIGGER = {
56 microscope.TriggerType.RISING_EDGE,
57 microscope.TriggerMode.ONCE,
59 "external exposure": (
60 microscope.TriggerType.RISING_EDGE,
61 microscope.TriggerMode.BULB,
63 "software": (microscope.TriggerType.SOFTWARE, microscope.TriggerMode.ONCE),
67 "_accumulate_count":
"AccumulateCount",
68 "_acquisition_start":
"AcquisitionStart",
69 "_acquisition_stop":
"AcquisitionStop",
70 "_alternating_readout_direction":
"AlternatingReadoutDirection",
71 "_aoi_binning":
"AOIBinning",
72 "_aoi_height":
"AOIHeight",
73 "_aoi_left":
"AOILeft",
75 "_aoi_width":
"AOIWidth",
76 "_aoi_stride":
"AOIStride",
77 "_auxiliary_out_source":
"AuxiliaryOutSource",
78 "_aux_out_source_two":
"AuxOutSourceTwo",
79 "_baseline_level":
"BaselineLevel",
80 "_bit_depth":
"BitDepth",
81 "_buffer_overflow_event":
"BufferOverflowEvent",
82 "_bytes_per_pixel":
"BytesPerPixel",
83 "_camera_acquiring":
"CameraAcquiring",
84 "_camera_dump":
"CameraDump",
85 "_camera_model":
"CameraModel",
86 "_camera_name":
"CameraName",
87 "_camera_present":
"CameraPresent",
88 "_controller_id":
"ControllerId",
89 "_frame_count":
"FrameCount",
90 "_cycle_mode":
"CycleMode",
91 "_electronic_shuttering_mode":
"ElectronicShutteringMode",
92 "_event_enable":
"EventEnable",
93 "_events_missed_event":
"EventsMissedEvent",
94 "_event_selector":
"EventSelector",
95 "_exposed_pixel_height":
"ExposedPixelHeight",
96 "_exposure_time":
"ExposureTime",
97 "_exposure_end_event":
"ExposureEndEvent",
98 "_exposure_start_event":
"ExposureStartEvent",
99 "_external_trigger_delay":
"ExternalTriggerDelay",
100 "_fan_speed":
"FanSpeed",
101 "_firmware_version":
"FirmwareVersion",
102 "_frame_rate":
"FrameRate",
103 "_full_aoi_control":
"FullAOIControl",
104 "_image_size_bytes":
"ImageSizeBytes",
105 "_interface_type":
"InterfaceType",
106 "_io_invert":
"IoInvert",
107 "_io_selector":
"IoSelector",
108 "_line_scan_speed":
"LineScanSpeed",
109 "_lut_index":
"LutIndex",
110 "_lut_value":
"LutValue",
111 "_max_interface_transfer_rate":
"MaxInterfaceTransferRate",
112 "_metadata_enable":
"MetadataEnable",
113 "_metadata_timestamp":
"MetadataTimestamp",
114 "_metadata_frame":
"MetadataFrame",
115 "_overlap":
"Overlap",
116 "_pixel_encoding":
"PixelEncoding",
117 "_pixel_readout_rate":
"PixelReadoutRate",
118 "_pre_amp_gain_control":
"PreAmpGainControl",
119 "_readout_time":
"ReadoutTime",
120 "_rolling_shutter_global_clear":
"RollingShutterGlobalClear",
121 "_row_n_exposure_end_event":
"RowNExposureEndEvent",
122 "_row_n_exposure_start_event":
"RowNExposureStartEvent",
123 "_row_read_time":
"RowReadTime",
124 "_scan_speed_control_enable":
"ScanSpeedControlEnable",
125 "_sensor_cooling":
"SensorCooling",
126 "_sensor_height":
"SensorHeight",
127 "_sensor_readout_mode":
"SensorReadoutMode",
128 "_sensor_temperature":
"SensorTemperature",
129 "_sensor_width":
"SensorWidth",
130 "_serial_number":
"SerialNumber",
131 "_simple_pre_amp_gain_control":
"SimplePreAmpGainControl",
132 "_software_trigger":
"SoftwareTrigger",
133 "_static_blemish_correction":
"StaticBlemishCorrection",
134 "_spurious_noise_filter":
"SpuriousNoiseFilter",
135 "_target_sensor_temperature":
"TargetSensorTemperature",
136 "_temperature_control":
"TemperatureControl",
137 "_temperature_status":
"TemperatureStatus",
138 "_timestamp_clock":
"TimestampClock",
139 "_timestamp_clock_frequency":
"TimestampClockFrequency",
140 "_timestamp_clock_reset":
"TimestampClockReset",
141 "_trigger_mode":
"TriggerMode",
142 "_vertically_centre_aoi":
"VerticallyCentreAOI",
146def readable_wrapper(func):
147 def wrapper(self, *args, **kwargs):
148 if SDK3.IsReadable(self.handle, self.propertyName):
149 return func(self, *args, **kwargs)
157def writable_wrapper(func):
158 def wrapper(self, *args, **kwargs):
159 if SDK3.IsWritable(self.handle, self.propertyName):
160 return func(self, *args, **kwargs)
168ATInt.get_value = readable_wrapper(ATInt.getValue)
169ATInt.set_value = writable_wrapper(ATInt.setValue)
170ATInt.min = readable_wrapper(ATInt.min)
171ATInt.max = readable_wrapper(ATInt.max)
172ATBool.get_value = readable_wrapper(ATBool.getValue)
173ATBool.set_value = writable_wrapper(ATBool.setValue)
174ATFloat.get_value = readable_wrapper(ATFloat.getValue)
175ATFloat.set_value = writable_wrapper(ATFloat.setValue)
176ATString.get_value = readable_wrapper(ATString.getValue)
177ATString.set_value = writable_wrapper(ATString.setValue)
178ATString.max_length = readable_wrapper(ATString.maxLength)
179ATEnum.get_index = readable_wrapper(ATEnum.getIndex)
180ATEnum.set_index = writable_wrapper(ATEnum.setIndex)
181ATEnum.get_string = readable_wrapper(ATEnum.getString)
182ATEnum.set_string = writable_wrapper(ATEnum.setString)
183ATEnum.get_available_values = readable_wrapper(ATEnum.getAvailableValueMap)
184ATProperty.is_readonly =
lambda self:
not SDK3.IsWritable(
185 self.handle, self.propertyName
197INVALIDATES_BUFFERS = [
198 "_simple_pre_amp_gain_control",
199 "_pre_amp_gain_control",
212 SDK_INITIALIZED =
False
214 def __init__(self, index=0, **kwargs):
215 super().__init__(index=index, **kwargs)
216 if not AndorSDK3.SDK_INITIALIZED:
217 SDK3.InitialiseLibrary()
331 def _acquiring(self):
334 @microscope.abc.keep_acquiring
335 def _enable_callback(self, use=False):
338 SDK3.RegisterFeatureCallback(
345 SDK3.UnregisterFeatureCallback(
353 def _acquiring(self, value):
359 def set_num_buffers(self, num):
360 self.num_buffers = num
361 self._buffers_valid =
False
363 def _purge_buffers(self):
364 """Purge buffers on both camera and PC."""
365 _logger.debug(
"Purging buffers.")
366 self._buffers_valid =
False
369 "Can not modify buffers while camera acquiring."
371 SDK3.Flush(self.handle)
374 self.buffers.get(block=
False)
378 def _create_buffers(self, num=None):
379 """Create buffers and store values needed to remove padding later."""
380 if self._buffers_valid:
383 num = self.num_buffers
384 self._purge_buffers()
385 _logger.debug(
"Creating %d buffers.", num)
386 self._img_stride = self._aoi_stride.get_value()
387 self._img_width = self._aoi_width.get_value()
388 self._img_height = self._aoi_height.get_value()
389 self._img_encoding = self._pixel_encoding.get_string()
390 img_size = self._image_size_bytes.get_value()
391 self._buffer_size = img_size
396 requirements=[
"C_CONTIGUOUS",
"ALIGNED",
"OWNDATA"],
398 self.buffers.put(buf)
400 self.handle, buf.ctypes.data_as(DPTR_TYPE), img_size
402 self._buffers_valid =
True
405 """Wrap functions that invalidate buffers so buffers are recreated."""
408 def wrapper(self, *args, **kwargs):
409 func(self, *args, **kwargs)
410 outerself._buffers_valid =
False
414 def _fetch_data(self, timeout=5, debug=False):
415 """Fetch data and recycle buffers."""
417 ptr, length = SDK3.WaitBuffer(self.
handle, timeout)
418 except SDK3.TimeoutError
as e:
427 data = np.empty((height, width), dtype=
"uint16")
430 data.ctypes.data_as(DPTR_TYPE),
440 SDK3.QueueBuffer(self.
handle, ptr, length)
447 """Abort acquisition."""
448 _logger.debug(
"Disabling acquisition.")
452 def initialize(self):
453 """Initialise the camera.
455 Open the connection, connect properties and populate settings
461 except Exception
as e:
465 for name, var
in sorted(self.__dict__.items()):
466 if isinstance(var, ATProperty):
467 sdk_name = SDK_NAMES[name]
468 if not SDK3.IsImplemented(self.
handle, sdk_name):
471 var.connect(self.
handle, sdk_name)
473 if type(var)
is ATCommand:
476 is_readonly_func = var.is_readonly
477 if type(var)
is ATEnum:
478 set_func = var.set_index
479 get_func = var.get_index
480 vals_func = var.get_available_values
482 set_func = var.set_value
483 get_func = var.get_value
484 if type(var)
is ATString:
485 vals_func = var.max_length
486 elif type(var)
in (ATFloat, ATInt):
487 vals_func =
lambda v=var: (v.min(), v.max())
491 if name
in INVALIDATES_BUFFERS:
496 PROPERTY_TYPES[type(var)],
508 _logger.warn(
"No hardware found - using SIMCAM")
512 timestamp = time.time()
514 self.
_put(data, timestamp)
521 def set_cooling(self, value):
524 except AttributeError:
530 def _do_shutdown(self) -> None:
534 def _do_disable(self):
538 def _do_enable(self):
539 _logger.debug(
"Preparing for acquisition.")
547 @microscope.abc.keep_acquiring
549 bounded_value = sorted(
555 "Set exposure time to %f, resulting framerate %f.",
566 def _get_sensor_shape(self):
572 def soft_trigger(self):
577 def trigger_mode(self) -> microscope.TriggerMode:
579 return SDK3_STRING_TO_TRIGGER[sdk3_string][1]
582 def trigger_type(self) -> microscope.TriggerType:
584 return SDK3_STRING_TO_TRIGGER[sdk3_string][0]
589 for available_mode
in self.
_trigger_mode.get_available_values():
590 trigger = SDK3_STRING_TO_TRIGGER[available_mode.lower()]
591 if trigger == (ttype, tmode):
596 "no SDK3 mode for %s and %s" % (ttype, tmode)
599 def _do_trigger(self) -> None:
602 def _get_binning(self):
604 return tuple(int(t)
for t
in as_text)
606 @microscope.abc.keep_acquiring
607 def _set_binning(self, binning):
609 as_text =
"%dx%d" % (binning.h, binning.v)
625 @microscope.abc.keep_acquiring
626 def _set_roi(self, roi):
639 self.
_aoi_top.set_value(current.top)
644 if hasattr(self,
"_preampgain"):
645 return self._preampgain.get_value()
microscope.ROI get_roi(self)
None _put(self, data, timestamp)
None add_setting(self, name, dtype, get_func, set_func, values, typing.Optional[typing.Callable[[], bool]] readonly=None)
_electronic_shuttering_mode
_target_sensor_temperature
_max_interface_transfer_rate
_simple_pre_amp_gain_control
_rolling_shutter_global_clear
_row_n_exposure_start_event
_row_n_exposure_end_event
def invalidate_buffers(self, func)
_static_blemish_correction
None set_trigger(self, microscope.TriggerType ttype, microscope.TriggerMode tmode)
def get_exposure_time(self)
def set_cooling(self, value)
def _fetch_data(self, timeout=5, debug=False)
def _acquiring(self, value)
_timestamp_clock_frequency
_scan_speed_control_enable
def _create_buffers(self, num=None)
def _enable_callback(self, use=False)
def set_num_buffers(self, num)
_alternating_readout_direction
def set_exposure_time(self, value)