Module matisse_controller.shamrock_ple.ccd
Source code
import time
from ctypes import *
import numpy as np
import matisse_controller.config as cfg
from matisse_controller.shamrock_ple.constants import *
from matisse_controller.shamrock_ple.utils import load_lib
# TODO: Add action to give live feed from CCD
class CCD:
LIBRARY_NAME = 'atmcd64d.dll'
WIDTH = 1024
HEIGHT = 256
MIN_TEMP = -120
MAX_TEMP = -10
def __init__(self):
try:
self.lib = load_lib(CCD.LIBRARY_NAME)
self.lib.Initialize()
self.lib.SetTemperature(c_int(cfg.get(cfg.PLE_TARGET_TEMPERATURE)))
self.lib.CoolerON()
self.temperature_ok = False
self.exit_flag = False
num_cameras = c_long()
self.lib.GetAvailableCameras(pointer(num_cameras))
assert num_cameras.value > 0, 'No CCD camera found.'
except OSError as err:
raise RuntimeError('Unable to initialize Andor CCD API.') from err
def __del__(self):
self.shutdown()
def setup(self, exposure_time: float, acquisition_mode=ACQ_MODE_SINGLE, readout_mode=READ_MODE_FVB,
temperature=-70, cool_down=True):
"""
Perform setup procedures on CCD, like cooling down to a given temperature and setting acquisition parameters.
Parameters
----------
exposure_time
the desired exposure time at which to configure the CCD
acquisition_mode
the desired acquisition mode at which to configure the CCD (default is accumulate)
readout_mode
the desired readout mode at which to configure the CCD (default is FVB)
temperature
the desired temperature in degrees centigrade at which to configure the CCD (default is -70)
cool_down
whether to cool down the CCD at all (sometimes we don't care, like when taking a single acquisition)
"""
self.exit_flag = False
if cool_down:
min_temp, max_temp = c_int(), c_int()
self.lib.GetTemperatureRange(pointer(min_temp), pointer(max_temp))
min_temp, max_temp = min_temp.value, max_temp.value
assert min_temp < temperature < max_temp, f"Temperature must be set between {min_temp} and {max_temp}"
self.lib.SetTemperature(c_int(temperature))
self.lib.CoolerON()
# Cooler stops when temp is within 3 degrees of target, so wait until it's close
# CCD normally takes a few minutes to fully cool down
while not self.temperature_ok:
if self.exit_flag:
return
current_temp = self.get_temperature()
print(f"Cooling CCD. Current temperature is {round(current_temp, 2)} °C")
self.temperature_ok = current_temp < temperature + cfg.get(cfg.PLE_TEMPERATURE_TOLERANCE)
time.sleep(10)
print('Configuring acquisition parameters.')
self.lib.SetAcquisitionMode(c_int(acquisition_mode))
self.lib.SetReadMode(c_int(readout_mode))
self.lib.SetVSSpeed(c_int(1))
self.lib.SetTriggerMode(c_int(TRIGGER_MODE_INTERNAL))
self.lib.SetExposureTime(c_float(exposure_time))
print('CCD ready for acquisition.')
def get_temperature(self) -> float:
"""
Returns
-------
float
the current temperature of the CCD camera
"""
temperature = c_float()
self.lib.GetTemperatureF(pointer(temperature))
return temperature.value
def take_acquisition(self, num_points=1024) -> np.ndarray:
"""
Parameters
----------
num_points
the number of pixels to read from the CCD camera - in FVB mode, this is the width of the screen.
Returns
-------
ndarray
an array of counts for each pixel on the CCD screen
"""
self.exit_flag = False
self.lib.StartAcquisition()
acquisition_array_type = c_int32 * num_points
data = acquisition_array_type()
# self.lib.WaitForAcquisition() does not work, so use a loop instead and check the status.
while True:
if self.exit_flag:
break
status = c_int()
self.lib.GetStatus(pointer(status))
if status.value == CCDErrorCode.DRV_IDLE.value:
break
else:
time.sleep(1)
self.lib.GetAcquiredData(data, c_int(num_points))
data = np.flip(np.array(data, dtype=np.int32)) # Data comes out backwards!
return data
def shutdown(self):
"""Run CCD-related cleanup and shutdown procedures."""
self.lib.CoolerOFF()
# TODO: Before shutting it down, we should wait for temp to hit -20 °C, otherwise it rises too fast
# In practice, of course, we don't do this :)
Classes
class CCD
-
Source code
class CCD: LIBRARY_NAME = 'atmcd64d.dll' WIDTH = 1024 HEIGHT = 256 MIN_TEMP = -120 MAX_TEMP = -10 def __init__(self): try: self.lib = load_lib(CCD.LIBRARY_NAME) self.lib.Initialize() self.lib.SetTemperature(c_int(cfg.get(cfg.PLE_TARGET_TEMPERATURE))) self.lib.CoolerON() self.temperature_ok = False self.exit_flag = False num_cameras = c_long() self.lib.GetAvailableCameras(pointer(num_cameras)) assert num_cameras.value > 0, 'No CCD camera found.' except OSError as err: raise RuntimeError('Unable to initialize Andor CCD API.') from err def __del__(self): self.shutdown() def setup(self, exposure_time: float, acquisition_mode=ACQ_MODE_SINGLE, readout_mode=READ_MODE_FVB, temperature=-70, cool_down=True): """ Perform setup procedures on CCD, like cooling down to a given temperature and setting acquisition parameters. Parameters ---------- exposure_time the desired exposure time at which to configure the CCD acquisition_mode the desired acquisition mode at which to configure the CCD (default is accumulate) readout_mode the desired readout mode at which to configure the CCD (default is FVB) temperature the desired temperature in degrees centigrade at which to configure the CCD (default is -70) cool_down whether to cool down the CCD at all (sometimes we don't care, like when taking a single acquisition) """ self.exit_flag = False if cool_down: min_temp, max_temp = c_int(), c_int() self.lib.GetTemperatureRange(pointer(min_temp), pointer(max_temp)) min_temp, max_temp = min_temp.value, max_temp.value assert min_temp < temperature < max_temp, f"Temperature must be set between {min_temp} and {max_temp}" self.lib.SetTemperature(c_int(temperature)) self.lib.CoolerON() # Cooler stops when temp is within 3 degrees of target, so wait until it's close # CCD normally takes a few minutes to fully cool down while not self.temperature_ok: if self.exit_flag: return current_temp = self.get_temperature() print(f"Cooling CCD. Current temperature is {round(current_temp, 2)} °C") self.temperature_ok = current_temp < temperature + cfg.get(cfg.PLE_TEMPERATURE_TOLERANCE) time.sleep(10) print('Configuring acquisition parameters.') self.lib.SetAcquisitionMode(c_int(acquisition_mode)) self.lib.SetReadMode(c_int(readout_mode)) self.lib.SetVSSpeed(c_int(1)) self.lib.SetTriggerMode(c_int(TRIGGER_MODE_INTERNAL)) self.lib.SetExposureTime(c_float(exposure_time)) print('CCD ready for acquisition.') def get_temperature(self) -> float: """ Returns ------- float the current temperature of the CCD camera """ temperature = c_float() self.lib.GetTemperatureF(pointer(temperature)) return temperature.value def take_acquisition(self, num_points=1024) -> np.ndarray: """ Parameters ---------- num_points the number of pixels to read from the CCD camera - in FVB mode, this is the width of the screen. Returns ------- ndarray an array of counts for each pixel on the CCD screen """ self.exit_flag = False self.lib.StartAcquisition() acquisition_array_type = c_int32 * num_points data = acquisition_array_type() # self.lib.WaitForAcquisition() does not work, so use a loop instead and check the status. while True: if self.exit_flag: break status = c_int() self.lib.GetStatus(pointer(status)) if status.value == CCDErrorCode.DRV_IDLE.value: break else: time.sleep(1) self.lib.GetAcquiredData(data, c_int(num_points)) data = np.flip(np.array(data, dtype=np.int32)) # Data comes out backwards! return data def shutdown(self): """Run CCD-related cleanup and shutdown procedures.""" self.lib.CoolerOFF()
Class variables
var HEIGHT
var LIBRARY_NAME
var MAX_TEMP
var MIN_TEMP
var WIDTH
Methods
def get_temperature(self)
-
Returns
float
- the current temperature of the CCD camera
Source code
def get_temperature(self) -> float: """ Returns ------- float the current temperature of the CCD camera """ temperature = c_float() self.lib.GetTemperatureF(pointer(temperature)) return temperature.value
def setup(self, exposure_time, acquisition_mode=1, readout_mode=0, temperature=-70, cool_down=True)
-
Perform setup procedures on CCD, like cooling down to a given temperature and setting acquisition parameters.
Parameters
exposure_time
- the desired exposure time at which to configure the CCD
acquisition_mode
- the desired acquisition mode at which to configure the CCD (default is accumulate)
readout_mode
- the desired readout mode at which to configure the CCD (default is FVB)
temperature
- the desired temperature in degrees centigrade at which to configure the CCD (default is -70)
cool_down
- whether to cool down the CCD at all (sometimes we don't care, like when taking a single acquisition)
Source code
def setup(self, exposure_time: float, acquisition_mode=ACQ_MODE_SINGLE, readout_mode=READ_MODE_FVB, temperature=-70, cool_down=True): """ Perform setup procedures on CCD, like cooling down to a given temperature and setting acquisition parameters. Parameters ---------- exposure_time the desired exposure time at which to configure the CCD acquisition_mode the desired acquisition mode at which to configure the CCD (default is accumulate) readout_mode the desired readout mode at which to configure the CCD (default is FVB) temperature the desired temperature in degrees centigrade at which to configure the CCD (default is -70) cool_down whether to cool down the CCD at all (sometimes we don't care, like when taking a single acquisition) """ self.exit_flag = False if cool_down: min_temp, max_temp = c_int(), c_int() self.lib.GetTemperatureRange(pointer(min_temp), pointer(max_temp)) min_temp, max_temp = min_temp.value, max_temp.value assert min_temp < temperature < max_temp, f"Temperature must be set between {min_temp} and {max_temp}" self.lib.SetTemperature(c_int(temperature)) self.lib.CoolerON() # Cooler stops when temp is within 3 degrees of target, so wait until it's close # CCD normally takes a few minutes to fully cool down while not self.temperature_ok: if self.exit_flag: return current_temp = self.get_temperature() print(f"Cooling CCD. Current temperature is {round(current_temp, 2)} °C") self.temperature_ok = current_temp < temperature + cfg.get(cfg.PLE_TEMPERATURE_TOLERANCE) time.sleep(10) print('Configuring acquisition parameters.') self.lib.SetAcquisitionMode(c_int(acquisition_mode)) self.lib.SetReadMode(c_int(readout_mode)) self.lib.SetVSSpeed(c_int(1)) self.lib.SetTriggerMode(c_int(TRIGGER_MODE_INTERNAL)) self.lib.SetExposureTime(c_float(exposure_time)) print('CCD ready for acquisition.')
def shutdown(self)
-
Run CCD-related cleanup and shutdown procedures.
Source code
def shutdown(self): """Run CCD-related cleanup and shutdown procedures.""" self.lib.CoolerOFF()
def take_acquisition(self, num_points=1024)
-
Parameters
num_points
- the number of pixels to read from the CCD camera - in FVB mode, this is the width of the screen.
Returns
ndarray
- an array of counts for each pixel on the CCD screen
Source code
def take_acquisition(self, num_points=1024) -> np.ndarray: """ Parameters ---------- num_points the number of pixels to read from the CCD camera - in FVB mode, this is the width of the screen. Returns ------- ndarray an array of counts for each pixel on the CCD screen """ self.exit_flag = False self.lib.StartAcquisition() acquisition_array_type = c_int32 * num_points data = acquisition_array_type() # self.lib.WaitForAcquisition() does not work, so use a loop instead and check the status. while True: if self.exit_flag: break status = c_int() self.lib.GetStatus(pointer(status)) if status.value == CCDErrorCode.DRV_IDLE.value: break else: time.sleep(1) self.lib.GetAcquiredData(data, c_int(num_points)) data = np.flip(np.array(data, dtype=np.int32)) # Data comes out backwards! return data