diff --git a/README.rst b/README.rst index 0750a92..7d0aff9 100644 --- a/README.rst +++ b/README.rst @@ -49,3 +49,9 @@ Viewer2D * **AndorSCMOSPll**: Andor CCD camera using the SDK3 through the pylablib driver (experimental but already in use, subject to changes) + +Legacy +====== +The former plugins replaced by the pylablib based ones (Andor CCD with the SDK2, Shamrock, ShamrockCCD and +ShamrockCCDLegacy) are kept in ``src/pymodaq_plugins_andor/legacy``. They are not loaded by PyMoDAQ and are not runnable +as they are. diff --git a/src/pymodaq_plugins_andor/daq_viewer_plugins/plugins_1D/daq_1Dviewer_ShamrockSCMOS.py b/src/pymodaq_plugins_andor/daq_viewer_plugins/plugins_1D/daq_1Dviewer_ShamrockSCMOS.py index 4d9ec74..1aac178 100644 --- a/src/pymodaq_plugins_andor/daq_viewer_plugins/plugins_1D/daq_1Dviewer_ShamrockSCMOS.py +++ b/src/pymodaq_plugins_andor/daq_viewer_plugins/plugins_1D/daq_1Dviewer_ShamrockSCMOS.py @@ -12,7 +12,7 @@ from pymodaq_plugins_andor.daq_viewer_plugins.plugins_2D.daq_2Dviewer_AndorSCMOS import DAQ_2DViewer_AndorSCMOS -from pymodaq_plugins_andor.daq_move_plugins.daq_move_Shamrock import DAQ_Move_Shamrock +from pymodaq_plugins_andor.daq_move_plugins.daq_move_ShamrockPll import DAQ_Move_ShamrockPll logger = set_logger(get_module_name(__file__)) @@ -27,9 +27,9 @@ class DAQ_1DViewer_ShamrockSCMOS(DAQ_2DViewer_AndorSCMOS): -------- utility_classes.DAQ_Viewer_base """ + supported_platforms = ('win32',) # the Andor SDK3 is loaded with ctypes.windll param_camera = DAQ_2DViewer_AndorSCMOS.params - params_shamrock = DAQ_Move_Shamrock.params - putils.get_param_dict_from_name(params_shamrock, 'andor_lib', pop=True) + params_shamrock = DAQ_Move_ShamrockPll.params d = putils.get_param_dict_from_name(params_shamrock, 'spectro_wl') if d is not None: @@ -46,7 +46,7 @@ class DAQ_1DViewer_ShamrockSCMOS(DAQ_2DViewer_AndorSCMOS): def ini_attributes(self): self.controller: DAQ_2DViewer_AndorSCMOS = None - self.shamrock_controller: DAQ_Move_Shamrock = None + self.shamrock_controller: DAQ_Move_ShamrockPll = None self.x_axis: Axis = None self.is_calibrated = False @@ -81,10 +81,11 @@ def ini_detector(self, controller=None): cam_status, cam_init = super().ini_detector(controller) QtWidgets.QApplication.processEvents() - self.shamrock_controller = DAQ_Move_Shamrock(None, self.settings.child('sham_settings').saveState()) + self.shamrock_controller = DAQ_Move_ShamrockPll(None, self.settings.child('sham_settings').saveState()) self.shamrock_controller.settings = self.settings.child('sham_settings') + self.settings.child('sham_settings', 'controller').hide() self.shamrock_controller.emit_status = self.emit_status - sham_status, sham_init = self.shamrock_controller.ini_stage(controller) + sham_status, sham_init = self.shamrock_controller.ini_stage() QtWidgets.QApplication.processEvents() @@ -97,21 +98,19 @@ def ini_detector(self, controller=None): def setCalibration(self): #setNpixels width, height = self.get_pixel_size() - err = self.shamrock_controller.controller.SetNumberPixelsSR(0, self.get_ROI_size_x()) - err = self.shamrock_controller.controller.SetPixelWidthSR(0, width) + self.shamrock_controller.controller.set_number_pixels(self.get_ROI_size_x()) + self.shamrock_controller.controller.set_pixel_width(width) - self.shamrock_controller.get_wavelength() + self.settings.child('sham_settings', 'spectro_settings', + 'spectro_wl').setValue(self.shamrock_controller.controller.get_wavelength() * 1e9) self.x_axis = self.get_xaxis() def getCalibration(self): if self.shamrock_controller is not None and self.shamrock_controller.controller is not None: - (err, calib) = self.shamrock_controller.controller.GetCalibrationSR(0, self.get_ROI_size_x()) - if err != "SHAMROCK_SUCCESS": - raise Exception(err) - - calib = np.array(calib) + self.shamrock_controller.controller.set_number_pixels(self.get_ROI_size_x()) # the ROI may have changed + calib = np.array(self.shamrock_controller.controller.get_calibration()) * 1e9 # m to nm else: raise(Exception('controller not defined')) self.is_calibrated = True @@ -158,7 +157,7 @@ def stop(self): if self.controller is not None: super().stop() if self.shamrock_controller is not None: - self.shamrock_controller.stop() + self.shamrock_controller.stop_motion() def close(self): self.stop() diff --git a/src/pymodaq_plugins_andor/daq_viewer_plugins/plugins_1D/daq_1Dviewer_ShamrockSCMOSLegacy.py b/src/pymodaq_plugins_andor/daq_viewer_plugins/plugins_1D/daq_1Dviewer_ShamrockSCMOSLegacy.py index bd6b5c1..9b9f2c5 100644 --- a/src/pymodaq_plugins_andor/daq_viewer_plugins/plugins_1D/daq_1Dviewer_ShamrockSCMOSLegacy.py +++ b/src/pymodaq_plugins_andor/daq_viewer_plugins/plugins_1D/daq_1Dviewer_ShamrockSCMOSLegacy.py @@ -12,7 +12,7 @@ from pymodaq.utils.data import Axis, DataFromPlugins from ..plugins_2D.daq_2Dviewer_AndorSCMOS import DAQ_2DViewer_AndorSCMOS -from ...daq_move_plugins.daq_move_Shamrock import DAQ_Move_Shamrock +from pymodaq_plugins_andor.legacy.daq_move_Shamrock import DAQ_Move_Shamrock logger = set_logger(get_module_name(__file__)) @@ -27,6 +27,7 @@ class DAQ_1DViewer_ShamrockSCMOSLegacy(DAQ_2DViewer_AndorSCMOS, DAQ_Move_Shamroc -------- utility_classes.DAQ_Viewer_base """ + supported_platforms = ('win32',) # the Shamrock SDK is loaded with ctypes.windll param_camera = DAQ_2DViewer_AndorSCMOS.params params_shamrock = DAQ_Move_Shamrock.params diff --git a/src/pymodaq_plugins_andor/legacy/README.md b/src/pymodaq_plugins_andor/legacy/README.md new file mode 100644 index 0000000..a2f8af0 --- /dev/null +++ b/src/pymodaq_plugins_andor/legacy/README.md @@ -0,0 +1,7 @@ +# Legacy plugins + +Former instrument plugins of this package, kept for reference: the Andor SDK2 camera (`daq_2Dviewer_AndorCCD`), the Shamrock spectrometer (`daq_move_Shamrock`) and the Shamrock CCD detectors (`daq_1Dviewer_ShamrockCCD`, `daq_1Dviewer_ShamrockCCDLegacy`). + +They were replaced by the pylablib based plugins (`daq_2Dviewer_AndorCCDPll`, `daq_move_ShamrockPll`, `daq_1Dviewer_ShamrockCCDPll`). + +This folder is outside `daq_move_plugins` and `daq_viewer_plugins`, so PyMoDAQ does not load these modules: they are not runnable as they are. To use one again, move it back to the matching plugin folder and fix its imports (the imports between the modules of this folder point here). diff --git a/src/pymodaq_plugins_andor/legacy/daq_1Dviewer_ShamrockCCD.py b/src/pymodaq_plugins_andor/legacy/daq_1Dviewer_ShamrockCCD.py new file mode 100644 index 0000000..43b688c --- /dev/null +++ b/src/pymodaq_plugins_andor/legacy/daq_1Dviewer_ShamrockCCD.py @@ -0,0 +1,197 @@ +import numpy as np +from qtpy import QtWidgets + +from pymodaq_utils.logger import set_logger, get_module_name +from pymodaq_utils.utils import ThreadCommand, find_dict_in_list_from_key_val + +from pymodaq_gui.parameter import utils as putils + +from pymodaq.utils.data import Axis, DataFromPlugins, DataToExport +from pymodaq.control_modules.viewer_utility_classes import main, DAQ_Viewer_base, comon_parameters + +from pymodaq_plugins_andor.legacy.daq_2Dviewer_AndorCCD import DAQ_2DViewer_AndorCCD +from pymodaq_plugins_andor.legacy.daq_move_Shamrock import DAQ_Move_Shamrock + +logger = set_logger(get_module_name(__file__)) + + +class DAQ_1DViewer_ShamrockCCD(DAQ_2DViewer_AndorCCD): + """ + =============== ================== + + =============== ================== + + See Also + -------- + utility_classes.DAQ_Viewer_base + """ + param_camera = DAQ_2DViewer_AndorCCD.params + params_shamrock = DAQ_Move_Shamrock.params + putils.get_param_dict_from_name(params_shamrock, 'andor_lib', pop=True) + + d = putils.get_param_dict_from_name(params_shamrock, 'spectro_wl') + if d is not None: + d['readonly'] = False + d = putils.get_param_dict_from_name(params_shamrock, 'flip_wavelength') + if d is not None: + d['visible'] = True + + params = [{'title': 'Get Calibration:', 'name': 'get_calib', 'type': 'bool_push', 'value': False, + 'label': 'Update!'},] + param_camera + [ + {'title': 'Shamrock Settings:', 'name': 'sham_settings', 'type': 'group', 'children': params_shamrock}, + ] + + + def ini_attributes(self): + self.controller: DAQ_2DViewer_AndorCCD = None + self.shamrock_controller: DAQ_Move_Shamrock = None + + self.x_axis: Axis = None + self.is_calibrated = False + + super().ini_attributes() + + def commit_settings(self, param): + + if param.name() == 'flip_wavelength': + self.get_xaxis() + elif 'camera_settings' in putils.get_param_path(param): + super().commit_settings(param) + elif 'spectro_settings' in putils.get_param_path(param): + self.shamrock_controller.commit_settings(param) + QtWidgets.QApplication.processEvents() + if param.name() == 'spectro_wl': + self.is_calibrated = False + self.get_xaxis() + elif param.name() == 'zero_order': + self.is_calibrated = False + elif param.name() == 'flip_wavelength': + self.get_xaxis() + elif param.name() == 'readout' or param.name() in \ + putils.iter_children(self.settings.child('camera_settings', 'readout_settings')): + self.get_xaxis() + elif param.name() == 'get_calib': + if param.value(): + self.get_xaxis() + param.setValue(False) + + def ini_detector(self, controller=None): + cam_status, cam_init = super().ini_detector(controller) + QtWidgets.QApplication.processEvents() + + self.shamrock_controller = DAQ_Move_Shamrock(None, self.settings.child('sham_settings').saveState()) + self.shamrock_controller.settings = self.settings.child('sham_settings') + self.shamrock_controller.emit_status = self.emit_status + sham_status, sham_init = self.shamrock_controller.ini_stage(controller) + + QtWidgets.QApplication.processEvents() + + + initialized = sham_init and cam_init + + self.setCalibration() + return sham_status + cam_status, initialized + + def setCalibration(self): + #setNpixels + width, height = self.get_pixel_size() + err = self.shamrock_controller.controller.SetNumberPixelsSR(0, self.get_ROI_size_x()) + err = self.shamrock_controller.controller.SetPixelWidthSR(0, width) + + self.shamrock_controller.get_wavelength() + self.x_axis = self.get_xaxis() + + + def getCalibration(self): + + if self.shamrock_controller is not None and self.shamrock_controller.controller is not None: + (err, calib) = self.shamrock_controller.controller.GetCalibrationSR(0, self.get_ROI_size_x()) + if err != "SHAMROCK_SUCCESS": + raise Exception(err) + + calib = np.array(calib) + else: + raise(Exception('controller not defined')) + self.is_calibrated = True + return calib + + def get_xaxis(self): + """ + Obtain the horizontal axis of the image. + + Returns + ------- + 1D numpy array + Contains a vector of integer corresponding to the horizontal camera pixels. + """ + + if self.shamrock_controller is None or np.abs(self.settings.child('sham_settings', 'spectro_settings', 'spectro_wl').value()) < 1e-3: + nx = self.get_ROI_size_x() + calib = np.linspace(0, nx, nx-1) + self.x_axis = Axis(data=calib, label='Wavelength (nm)') + else: + calib = self.getCalibration() + + if (calib.astype('int') != 0).all(): # check if calib values are equal to zero + if self.settings.child('sham_settings', 'spectro_settings', 'flip_wavelength').value(): + calib = calib[::-1] + + else: + self.settings.child('sham_settings', 'spectro_settings', 'flip_wavelength').setValue(False) + #self.emit_status(ThreadCommand('Update_Status', ['Impossible to flip wavelength', "log"])) + + self.x_axis = Axis(data=calib, label='Wavelength (nm)') + return self.x_axis + + def get_exposure_ms(self): + #for compatibility with PyMoDAQ Spectro module + self.emit_status(ThreadCommand('exposure_ms', [self.settings.child('camera_settings', 'exposure').value()])) + + def set_exposure_ms(self, exposure): + self.settings.child('camera_settings', 'exposure').setValue(exposure) + QtWidgets.QApplication.processEvents() + self.emit_status(ThreadCommand('exposure_ms', [self.settings.child('camera_settings', 'exposure').value()])) + + def stop(self): + if self.controller is not None: + super().stop() + if self.shamrock_controller is not None: + self.shamrock_controller.stop() + + def close(self): + self.stop() + if self.shamrock_controller is not None: + self.shamrock_controller.close() + super().close() + + def grab_data(self, Naverage=1, **kwargs): + if not self.is_calibrated: + self.get_xaxis() + super().grab_data( Naverage, **kwargs) + + def emit_data(self): + """ + overloadded function from DAQ_2DViewer_AndorCCD + """ + try: + self.ind_grabbed += 1 + sizey = self.settings.child('camera_settings', 'image_size', 'Ny').value() + sizex = self.settings.child('camera_settings', 'image_size', 'Nx').value() + self.controller.GetAcquiredDataNumpy(self.data_pointer, sizex * sizey) + self.dte_signal.emit( + DataToExport('Spectro', + data=[ + DataFromPlugins(name='Camera', + data=[np.atleast_1d(np.squeeze(self.data.reshape( + (sizey, sizex)))).astype(float)], + dim=self.data_shape, + axes=[self.x_axis]), + ])) + QtWidgets.QApplication.processEvents() # here to be sure the timeevents are executed even if in continuous grab mode + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) + + +if __name__ == '__main__': + main(__file__, True) diff --git a/src/pymodaq_plugins_andor/legacy/daq_1Dviewer_ShamrockCCDLegacy.py b/src/pymodaq_plugins_andor/legacy/daq_1Dviewer_ShamrockCCDLegacy.py new file mode 100644 index 0000000..17d7513 --- /dev/null +++ b/src/pymodaq_plugins_andor/legacy/daq_1Dviewer_ShamrockCCDLegacy.py @@ -0,0 +1,186 @@ +import numpy as np +from qtpy import QtWidgets + +from pymodaq_utils.logger import set_logger, get_module_name +from pymodaq_utils.utils import ThreadCommand, find_dict_in_list_from_key_val + +from pymodaq_gui.parameter import utils as putils + +from pymodaq.utils.data import Axis, DataFromPlugins +from pymodaq.control_modules.viewer_utility_classes import main + +from pymodaq_plugins_andor.legacy.daq_2Dviewer_AndorCCD import DAQ_2DViewer_AndorCCD +from pymodaq_plugins_andor.legacy.daq_move_Shamrock import DAQ_Move_Shamrock + + +logger = set_logger(get_module_name(__file__)) + + +class DAQ_1DViewer_ShamrockCCDLegacy(DAQ_2DViewer_AndorCCD, DAQ_Move_Shamrock): + """ + =============== ================== + + =============== ================== + + See Also + -------- + utility_classes.DAQ_Viewer_base + """ + param_camera = DAQ_2DViewer_AndorCCD.params + params_shamrock = DAQ_Move_Shamrock.params + putils.get_param_dict_from_name(params_shamrock, 'andor_lib', pop=True) + + d = putils.get_param_dict_from_name(params_shamrock, 'spectro_wl') + if d is not None: + d['readonly'] = False + d = putils.get_param_dict_from_name(params_shamrock, 'flip_wavelength') + if d is not None: + d['visible'] = True + + params = [{'title': 'Get Calibration:', 'name': 'get_calib', 'type': 'bool_push', 'value': False, + 'label': 'Update!'}] + param_camera + params_shamrock + + def __init__(self, parent=None, params_state=None): + DAQ_Move_Shamrock.__init__(self, parent, params_state) + DAQ_2DViewer_AndorCCD.__init__(self, parent, params_state) + + self.camera_controller = None # this will be the controller attribute of the DAQ_2DViewer_AndorCCD instance + self.shamrock_controller = None # this will be the controller attribute of the DAQ_Move_Shamrock instance + # both plugins don't have the generic controller name 'controller' but specific one for this reason + + self.x_axis: Axis = None + self.is_calibrated = False + + def commit_settings(self, param): + + if param.name() == 'flip_wavelength': + self.get_xaxis() + elif 'camera_settings' in putils.get_param_path(param): + DAQ_2DViewer_AndorCCD.commit_settings(self, param) + elif 'spectro_settings' in putils.get_param_path(param): + DAQ_Move_Shamrock.commit_settings(self, param) + QtWidgets.QApplication.processEvents() + if param.name() == 'spectro_wl': + self.is_calibrated = False + self.get_xaxis() + elif param.name() == 'zero_order': + self.is_calibrated = False + elif param.name() == 'flip_wavelength': + self.get_xaxis() + elif param.name() == 'readout' or param.name() in \ + putils.iter_children(self.settings.child('camera_settings', 'readout_settings')): + self.get_xaxis() + elif param.name() == 'get_calib': + if param.value(): + self.get_xaxis() + param.setValue(False) + + def ini_detector(self, controller=None): + _, shamrock_initialized = DAQ_Move_Shamrock.ini_stage(self, controller) + QtWidgets.QApplication.processEvents() + # if status_shamrock.initialized: + # self.move_Home() + + _, camera_initialized = DAQ_2DViewer_AndorCCD.ini_detector(self, controller) + QtWidgets.QApplication.processEvents() + + initialized = shamrock_initialized and camera_initialized + + self.setCalibration() + #self.emit_status(ThreadCommand('close_splash')) + return '', initialized + + def setCalibration(self): + #setNpixels + width, height = self.get_pixel_size() + err = self.shamrock_controller.SetNumberPixelsSR(0, self.get_ROI_size_x()) + err = self.shamrock_controller.SetPixelWidthSR(0, width) + + self.get_wavelength() + self.x_axis = self.get_xaxis() + + + def getCalibration(self): + + if self.shamrock_controller is not None: + (err, calib) = self.shamrock_controller.GetCalibrationSR(0, self.get_ROI_size_x()) + if err != "SHAMROCK_SUCCESS": + raise Exception(err) + + calib = np.array(calib) + else: + raise(Exception('controller not defined')) + self.is_calibrated = True + return calib + + def get_xaxis(self): + """ + Obtain the horizontal axis of the image. + + Returns + ------- + 1D numpy array + Contains a vector of integer corresponding to the horizontal camera pixels. + """ + + if np.abs(self.settings.child('spectro_settings', 'spectro_wl').value()) < 1e-3: + DAQ_2DViewer_AndorCCD.get_xaxis(self) + else: + calib = self.getCalibration() + + if (calib.astype('int') != 0).all(): # check if calib values are equal to zero + if self.settings.child('spectro_settings', 'flip_wavelength').value(): + calib = calib[::-1] + + else: + self.settings.child('spectro_settings', 'flip_wavelength').setValue(False) + #self.emit_status(ThreadCommand('Update_Status', ['Impossible to flip wavelength', "log"])) + + self.x_axis = Axis(data=calib, label='Wavelength (nm)') + return self.x_axis + + def get_exposure_ms(self): + #for compatibility with PyMoDAQ Spectro module + self.emit_status(ThreadCommand('exposure_ms', [self.settings.child('camera_settings', 'exposure').value()])) + + def set_exposure_ms(self, exposure): + self.settings.child('camera_settings', 'exposure').setValue(exposure) + QtWidgets.QApplication.processEvents() + self.emit_status(ThreadCommand('exposure_ms', [self.settings.child('camera_settings', 'exposure').value()])) + + def stop(self): + DAQ_2DViewer_AndorCCD.stop(self) + + def close(self): + if self.camera_controller is not None: + DAQ_2DViewer_AndorCCD.stop(self) + if self.shamrock_controller is not None: + DAQ_Move_Shamrock.stop(self) + + def grab_data(self, Naverage=1, **kwargs): + if not self.is_calibrated: + self.get_xaxis() + DAQ_2DViewer_AndorCCD.grab_data(self, Naverage, **kwargs) + + def emit_data(self): + """ + overloadded function from DAQ_2DViewer_AndorCCD + """ + try: + self.ind_grabbed += 1 + sizey = self.settings.child('camera_settings', 'image_size', 'Ny').value() + sizex = self.settings.child('camera_settings', 'image_size', 'Nx').value() + self.camera_controller.GetAcquiredDataNumpy(self.data_pointer, sizex * sizey) + self.data_grabed_signal.emit([DataFromPlugins(name='Camera', + data=[np.squeeze( + self.data.reshape((sizey, sizex)).astype(float))], + dim=self.data_shape, + axes=[self.x_axis]),]) + QtWidgets.QApplication.processEvents() # here to be sure the timeevents are executed even if in continuous grab mode + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) + + +if __name__ == '__main__': + main(__file__, True) diff --git a/src/pymodaq_plugins_andor/legacy/daq_2Dviewer_AndorCCD.py b/src/pymodaq_plugins_andor/legacy/daq_2Dviewer_AndorCCD.py new file mode 100644 index 0000000..af516a2 --- /dev/null +++ b/src/pymodaq_plugins_andor/legacy/daq_2Dviewer_AndorCCD.py @@ -0,0 +1,604 @@ +import numpy as np +from enum import IntEnum +import ctypes +from ctypes.util import find_library +import platform +from qtpy import QtWidgets, QtCore +from easydict import EasyDict as edict + +from pymodaq_utils.utils import ThreadCommand, find_dict_in_list_from_key_val +from pymodaq_gui.parameter.utils import iter_children + +from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main +from pymodaq.utils.data import DataFromPlugins, Axis, DataToExport + +from pymodaq_plugins_andor.hardware.andor_sdk2 import sdk2 + +libpath = sdk2.dllpath +camera_list = sdk2.AndorSDK.GetCamerasInfo() + + +class Andor_Camera_ReadOut(IntEnum): + """ + Enum class of readout modes. + + =============== ======================= + **Attributes** **Type** + *names* string list of members + =============== ======================= + """ + + FullVertBinning = 0 + SingleTrack = 3 + MultiTrack = 1 + RandomTrack = 2 + Image = 4 + Cropped = 5 + + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + +class Andor_Camera_AcqMode(IntEnum): + """ + Enum class of AcqModes modes. + + =============== ======================= + **Attributes** **Type** + *names* string list of members + =============== ======================= + """ + Single_Scan = 1 + Accumulate = 2 + Kinetics = 3 + Fast_Kinetics = 4 + Run_till_abort = 5 + + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class DAQ_2DViewer_AndorCCD(DAQ_Viewer_base): + """ + Base class for Andor CCD camera and Shamrock spectrometer + + + =============== ================== + **Attributes** **Type** + + =============== ================== + + See Also + -------- + utility_classes.DAQ_Viewer_base + """ + callback_signal = QtCore.Signal(int) + hardware_averaging = True #will use the accumulate acquisition mode if averaging is neccessary + params = comon_parameters+[ + {'title': 'Dll library:', 'name': 'andor_lib', 'type': 'browsepath', 'value': str(libpath)}, + + {'title': 'Camera Settings:', 'name': 'camera_settings', 'type': 'group', 'expanded': True, 'children': [ + {'title': 'Camera Models:', 'name': 'camera_model', 'type': 'list', + 'limits': [f"{cam['model']}-{cam['serial']}" for cam in camera_list]}, + + {'title': 'Readout Modes:', 'name': 'readout', 'type': 'list', 'limits': Andor_Camera_ReadOut.names()[0:-1], + 'value': 'FullVertBinning'}, + {'title': 'Readout Speed:', 'name': 'readout_speed', 'type': 'list', 'limits': [], + 'value': '0.05MHz'}, + {'title': 'Readout Settings:', 'name': 'readout_settings', 'type': 'group', 'children':[ + + {'title': 'Single Track Settings:', 'name': 'st_settings', 'type': 'group', 'visible': False, 'children':[ + {'title': 'Center pixel:', 'name': 'st_center', 'type': 'int', 'value': 1 , 'default':1, 'min':1}, + {'title': 'Height:', 'name': 'st_height', 'type': 'int', 'value': 1 , 'default':1, 'min':1}, + ]}, + {'title': 'Multi Track Settings:', 'name': 'mt_settings', 'type': 'group', 'visible': False, 'children':[ + {'title': 'Ntrack:', 'name': 'mt_N', 'type': 'int', 'value': 1 , 'default':1, 'min':1}, + {'title': 'Height:', 'name': 'mt_height', 'type': 'int', 'value': 1 , 'default':1, 'min':1}, + {'title': 'Offset:', 'name': 'mt_offset', 'type': 'int', 'value': 0 , 'default':0, 'min':0}, + {'title': 'Bottom:', 'name': 'mt_bottom', 'type': 'int', 'value': 0 , 'default':0, 'min':0, 'readonly': True}, + {'title': 'Gap:', 'name': 'mt_gap', 'type': 'int', 'value': 1 , 'default':1, 'min':0, 'readonly': True}, + ]}, + {'title': 'Image Settings:', 'name': 'image_settings', 'type': 'group', 'visible': False, 'children':[ + {'title': 'Binning along x:', 'name': 'bin_x', 'type': 'int', 'value': 1, 'default': 1, 'min': 1}, + {'title': 'Binning along y:', 'name': 'bin_y', 'type': 'int', 'value': 1, 'default': 1, 'min': 1}, + {'title': 'Start x:', 'name': 'im_startx', 'type': 'int', 'value': 1 , 'default':1, 'min':0}, + {'title': 'End x:', 'name': 'im_endx', 'type': 'int', 'value': 1024 , 'default':1024, 'min':0}, + {'title': 'Start y:', 'name': 'im_starty', 'type': 'int', 'value': 1 , 'default':1, 'min':1}, + {'title': 'End y:', 'name': 'im_endy', 'type': 'int', 'value': 256, 'default':256, 'min':1,}, + ]}, + ]}, + {'title': 'Exposure (ms):', 'name': 'exposure', 'type': 'float', 'value': 0.01 , 'default':0.01, 'min': 0}, + + {'title': 'Image size:', 'name': 'image_size', 'type': 'group', 'children':[ + {'title': 'Nx:', 'name': 'Nx', 'type': 'int', 'value': 0, 'default':0 , 'readonly': True}, + {'title': 'Ny:', 'name': 'Ny', 'type': 'int', 'value': 0 , 'default':0 , 'readonly': True}, + ]}, + + {'title': 'Shutter Settings:', 'name': 'shutter', 'type': 'group', 'children':[ + {'title': 'Open Shutter on:', 'name': 'shutter_type', 'type': 'list', 'value': 'high', 'limits': ['low', 'high']}, + {'title': 'Shutter mode:', 'name': 'shutter_mode', 'type': 'list', 'value': 'Auto', 'limits': ['Auto', 'Always Opened', 'Always Closed', ]}, + {'title': 'Closing time (ms):', 'name': 'shutter_closing_time', 'type': 'int', 'value': 0, 'tip': 'millisecs it takes to close'}, + {'title': 'Opening time (ms):', 'name': 'shutter_opening_time', 'type': 'int', 'value': 10, 'tip': 'millisecs it takes to open'}, + ]}, + {'title': 'Temperature Settings:', 'name': 'temperature_settings', 'type': 'group', 'children': [ + {'title': 'Set Point:', 'name': 'set_point', 'type': 'float', 'value': -60, 'default': -60}, + {'title': 'Current value:', 'name': 'current_value', 'type': 'float', 'value': 0, 'default': 0, + 'readonly': True}, + {'title': 'Locked:', 'name': 'locked', 'type': 'led', 'value': False, 'default': False, + 'readonly': True}, + ]}, + ]}, + ] + + def ini_attributes(self): + + self.camera_controller: sdk2.AndorSDK() = None + + self.x_axis = None + self.y_axis = None + self.camera_controller = None + self.data = None + self.CCDSIZEX, self.CCDSIZEY = (None, None) + self.data_pointer = None + self.camera_done = False + self.acquirred_image = None + self.callback_thread = None + self.Naverage = None + self.data_shape = None # 'Data2D' if sizey != 1 else 'Data1D' + self.timer = QtCore.QTimer() + self.timer.timeout.connect(self.updated_timer) + + def commit_settings(self, param): + """ + | Activate parameters changes on the hardware from parameter's name. + | + + =============== ================================ ========================= + **Parameters** **Type** **Description** + *param* instance of pyqtgraph parameter The parameter to activate + =============== ================================ ========================= + + Three profile of parameter : + * **bin_x** : set binning camera from bin_x parameter's value + * **bin_y** : set binning camera from bin_y parameter's value + * **set_point** : Set the camera's temperature from parameter's value. + + """ + try: + if param.name() == 'set_point': + self.camera_controller.SetTemperature(param.value()) + + elif param.name() == 'readout' or param.name() in iter_children(self.settings.child('camera_settings', 'readout_settings')): + self.update_read_mode() + + elif param.name() == 'readout_speed': + self.update_read_speed() + + elif param.name() == 'exposure': + self.camera_controller.SetExposureTime(param.value() / 1000) #temp should be in s + (err, timings) = self.camera_controller.GetAcquisitionTimings() + self.settings.child('camera_settings', 'exposure').setValue(timings['exposure']*1000) + QtWidgets.QApplication.processEvents() + #self.get_exposure_ms() + + elif param.name() in iter_children(self.settings.child('camera_settings', 'shutter'), []): + self.set_shutter() + + elif param.name() in iter_children(self.settings.child('camera_settings', 'readout_settings', 'image_settings')): + if self.settings.child('camera_settings', 'readout').value() == 'Image': + self.set_image_area() + pass + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) + + def emit_data(self): + """ + Fonction used to emit data obtained by callback. + + See Also + -------- + daq_utils.ThreadCommand + """ + try: + self.ind_grabbed += 1 + + sizey = self.settings.child('camera_settings', 'image_size', 'Ny').value() + sizex = self.settings.child('camera_settings', 'image_size', 'Nx').value() + self.camera_controller.GetAcquiredDataNumpy(self.data_pointer, sizex * sizey) + self.dte_signal.emit(DataToExport('Camera', + data=[DataFromPlugins(name='Camera', + data=[np.squeeze( + self.data.reshape((sizey, sizex)).astype(float))], + dim=self.data_shape)])) + QtWidgets.QApplication.processEvents() # here to be sure the timeevents are executed even if in continuous grab mode + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) + + + def update_read_mode(self): + read_mode = Andor_Camera_ReadOut[self.settings.child('camera_settings', 'readout').value()].value + err = self.camera_controller.SetReadMode(read_mode) + if err != 'DRV_SUCCESS': + self.emit_status(ThreadCommand('Update_Status',[err,'log'])) + else: + self.settings.child('camera_settings', 'readout_settings').show() + if read_mode == 0:#FVB: + self.settings.child('camera_settings', 'readout_settings').hide() + self.settings.child('camera_settings','image_size','Nx').setValue(self.CCDSIZEX) + self.settings.child('camera_settings','image_size','Ny').setValue(1) + + + elif read_mode == 3: #single track + self.settings.child('camera_settings', 'readout_settings','mt_settings').hide() + self.settings.child('camera_settings', 'readout_settings','st_settings').show() + self.settings.child('camera_settings', 'readout_settings','image_settings').hide() + + err = self.set_single_track_area() + + elif read_mode == 1: #multitrack + self.settings.child('camera_settings', 'readout_settings','mt_settings').show() + self.settings.child('camera_settings', 'readout_settings','st_settings').hide() + self.settings.child('camera_settings', 'readout_settings','image_settings').hide() + + err = self.set_multi_track_area() + + + + elif read_mode == 2: #random + err = 'Random mode not implemented yet' + + elif read_mode == 4: #image + self.settings.child('camera_settings', 'readout_settings','mt_settings').hide() + self.settings.child('camera_settings', 'readout_settings','st_settings').hide() + self.settings.child('camera_settings', 'readout_settings','image_settings').show() + + self.set_image_area() + + + elif read_mode == 5: #croped + err = 'Croped mode not implemented yet' + self.emit_status(ThreadCommand('Update_Status',[err,'log'])) + + (err, timings) = self.camera_controller.GetAcquisitionTimings() + self.settings.child('camera_settings', 'exposure').setValue(timings['exposure']*1000) + + self.x_axis = self.get_xaxis() + self.y_axis = self.get_yaxis() + + def set_multi_track_area(self): + + N = self.settings.child('camera_settings', 'readout_settings', 'mt_settings', 'mt_N').value() + height = self.settings.child('camera_settings', 'readout_settings', 'mt_settings', 'mt_height').value() + offset = self.settings.child('camera_settings', 'readout_settings', 'mt_settings', 'mt_offset').value() + (err, bottom, gap) = self.camera_controller.SetMultiTrack(N, height, offset) + self.settings.child('camera_settings', 'readout_settings', 'mt_settings', 'mt_bottom').setValue(bottom) + self.settings.child('camera_settings', 'readout_settings', 'mt_settings', 'mt_gap').setValue(gap) + if err == 'DRV_SUCCESS': + self.settings.child('camera_settings', 'image_size', 'Nx').setValue(self.CCDSIZEX) + self.settings.child('camera_settings', 'image_size', 'Ny').setValue(N) + return err + + def set_single_track_area(self): + center = self.settings.child('camera_settings', 'readout_settings', 'st_settings', 'st_center').value() + height = self.settings.child('camera_settings', 'readout_settings', 'st_settings', 'st_height').value() + err = self.camera_controller.SetSingleTrack(center, height) + if err == 'DRV_SUCCESS': + self.settings.child('camera_settings', 'image_size', 'Nx').setValue(self.CCDSIZEX) + self.settings.child('camera_settings', 'image_size', 'Ny').setValue(1) + + return err + + + def set_image_area(self): + + binx = self.settings.child('camera_settings', 'readout_settings', 'image_settings', 'bin_x').value() + biny = self.settings.child('camera_settings', 'readout_settings','image_settings', 'bin_y').value() + startx = self.settings.child('camera_settings', 'readout_settings', 'image_settings', 'im_startx').value() + endx = self.settings.child('camera_settings', 'readout_settings', 'image_settings', 'im_endx').value() + starty = self.settings.child('camera_settings', 'readout_settings', 'image_settings', 'im_starty').value() + endy = self.settings.child('camera_settings', 'readout_settings', 'image_settings', 'im_endy').value() + err = self.camera_controller.SetImage(binx, biny, startx, endx, starty, endy) + if err == 'DRV_SUCCESS': + self.settings.child('camera_settings', 'image_size', 'Nx').setValue(int((endx-startx+1)/binx)) + self.settings.child('camera_settings', 'image_size', 'Ny').setValue(int((endy-starty+1)/biny)) + + return err + + def get_speeds_string(self): + + get_speeds = self.camera_controller.GetHSSpeed(0, 0) + speeds_str_arr = [] + + for speed in get_speeds: + speed_str = str("{:.2f}".format(speed)) + speeds_str_arr.append(speed_str + "MHz") + + return speeds_str_arr + + def set_speed_index(self, value): + + speeds_arr = self.get_speeds_string() + return speeds_arr.index(value) + + def update_read_speed(self): + read_speed_val = self.settings.child('camera_settings', 'readout_speed').value() + + err = self.camera_controller.SetHSSpeed(self.set_speed_index(read_speed_val)) + if err != 'DRV_SUCCESS': + self.emit_status(ThreadCommand('Update_Status', [err, 'log'])) + + + def ini_detector(self, controller=None): + """ + Initialisation procedure of the detector in four steps : + * Register callback to get data from camera + * Get image size and current binning + * Set and Get temperature from camera + * Init axes from image + + Returns + ------- + string list ??? + The initialized status. + + See Also + -------- + daq_utils.ThreadCommand, hardware1D.DAQ_1DViewer_Picoscope.update_pico_settings + """ + + self.camera_controller = self.ini_detector_init(old_controller=controller, + new_controller=sdk2.AndorSDK()) + + self.emit_status(ThreadCommand('show_splash', "Set/Get Camera's settings")) + self.ini_camera() + + # %%%%%%% init axes from image + self.x_axis = self.get_xaxis() + self.y_axis = self.get_yaxis() + + self.emit_status(ThreadCommand('close_splash')) + + info = "" + initialized = True + return info, initialized + + def get_ROI_size_x(self): + self.CCDSIZEX, self.CCDSIZEY = self.camera_controller.GetDetector() + return self.CCDSIZEX + + def get_pixel_size(self): + err, (width, height) = self.camera_controller.GetPixelSize() + if err == 'DRV_SUCCESS': + return width, height + else: + pass + return 0., 0. + + def ini_camera(self): + # %%%%%% Get image size and current binning + # get info from camera + model_param = self.settings.child('camera_settings', 'camera_model') + cam_index = model_param.opts['limits'].index(model_param.value()) + self.camera_controller.SetCurrentCamera(camera_list[cam_index]['handle']) + + self.CCDSIZEX, self.CCDSIZEY = self.camera_controller.GetDetector() + self.settings.child('camera_settings', 'readout_settings', + 'st_settings', 'st_center').setLimits((1, self.CCDSIZEY)) + self.settings.child('camera_settings', 'readout_settings', + 'st_settings', 'st_height').setLimits((1, self.CCDSIZEY)) + self.settings.child('camera_settings', 'readout_settings', 'image_settings', 'im_endy').setValue(self.CCDSIZEY) + self.settings.child('camera_settings', 'readout_settings', 'image_settings', + 'im_endy').setOpts(max=self.CCDSIZEY, default=self.CCDSIZEY) + self.settings.child('camera_settings', 'readout_settings', 'image_settings', 'im_endx').setValue(self.CCDSIZEX) + self.settings.child('camera_settings', 'readout_settings', 'image_settings', + 'im_endx').setOpts(max=self.CCDSIZEX, default=self.CCDSIZEX) + + # get available readout speeds + self.settings.child('camera_settings', 'readout_speed').setLimits(self.get_speeds_string()) + + # get max exposure range + err, maxexpo = self.camera_controller.GetMaximumExposure() + if err == 'DRV_SUCCESS': + self.settings.child('camera_settings', 'exposure').setLimits((0, maxexpo * 1000)) + + # set default read mode (full vertical binning) + self.update_read_mode() + + # %%%%%%% Set and Get temperature from camera + # get temperature range + (err, temp_range) = self.camera_controller.GetTemperatureRange() + if err == "DRV_SUCCESS": + self.settings.child('camera_settings', 'temperature_settings', 'set_point').setLimits( + (temp_range[0], temp_range[1])) + + + self.set_shutter() + + if not self.camera_controller.IsCoolerOn(): # gets 0 or 1 + self.camera_controller.CoolerON() + + self.camera_controller.SetTemperature( + self.settings.child('camera_settings', 'temperature_settings', 'set_point').value()) + locked_status, temp = self.camera_controller.GetTemperature() + self.settings.child('camera_settings', 'temperature_settings', 'current_value').setValue(temp) + self.settings.child('camera_settings', 'temperature_settings', 'locked').setValue( + locked_status == 'DRV_TEMP_STABILIZED') + # set timer to update temperature info from controller + self.timer.start(2000) + + callback = AndorCallback(self.camera_controller.WaitForAcquisition) + self.callback_thread = QtCore.QThread() + callback.moveToThread(self.callback_thread) + callback.data_sig.connect(self.emit_data) # when the wait for acquisition returns (with data taken), emit_data will be fired + + self.callback_signal.connect(callback.wait_for_acquisition) + self.callback_thread.callback = callback + self.callback_thread.start() + + + def set_shutter(self): + typ = self.settings.child('camera_settings', 'shutter', 'shutter_type').opts['limits'].index( + self.settings.child('camera_settings', 'shutter', 'shutter_type').value()) + mode = self.settings.child('camera_settings', 'shutter', 'shutter_mode').opts['limits'].index( + self.settings.child('camera_settings', 'shutter', 'shutter_mode').value()) + + self.camera_controller.SetShutter(typ, mode, self.settings.child('camera_settings', 'shutter', 'shutter_closing_time').value(), + self.settings.child('camera_settings', 'shutter', 'shutter_opening_time').value()) + + def updated_timer(self): + """ + + """ + locked_status, temp = self.camera_controller.GetTemperature() + self.settings.child('camera_settings', 'temperature_settings', 'current_value').setValue(temp) + self.settings.child('camera_settings', 'temperature_settings', 'locked').setValue( + locked_status == 'DRV_TEMP_STABILIZED') + + def close(self): + """ + + """ + if self.camera_controller is not None: + err, temp = self.camera_controller.GetTemperature() + print(temp) + if temp < -20.: + print( + "Camera temperature is still at {:d}°C. Closing it now may damage it! The cooling will be maintained " + "while shutting down camera. Keep it power plugged!!!".format( + temp)) + self.camera_controller.SetCoolerMode(1) + self.timer.stop() + self.camera_controller.close() + + def get_xaxis(self): + """ + Obtain the horizontal axis of the image. + + Returns + ------- + 1D numpy array + Contains a vector of integer corresponding to the horizontal camera pixels. + """ + if self.camera_controller is not None: + # if self.control_type == "camera": + Nx = self.settings.child('camera_settings', 'image_size', 'Nx').value() + self.x_axis = Axis(data=np.linspace(0, Nx - 1, Nx, dtype=int), label='Pixels', index=1) + + else: + raise(Exception('controller not defined')) + return self.x_axis + + def get_yaxis(self): + """ + Obtain the vertical axis of the image. + + Returns + ------- + 1D numpy array + Contains a vector of integer corresponding to the vertical camera pixels. + """ + if self.camera_controller is not None: + + Ny = self.settings.child('camera_settings', 'image_size', 'Ny').value() + self.y_axis = Axis(data=np.linspace(0, Ny - 1, Ny, dtype=int), label='Pixels', index=0) + else: + raise (Exception('Camera not defined')) + return self.y_axis + + def prepare_data(self): + sizex = self.settings.child('camera_settings', 'image_size', 'Nx').value() + sizey = self.settings.child('camera_settings', 'image_size', 'Ny').value() + + # %%%%%% Initialize data: self.data for the memory to store new data and self.data_average to store the average data + image_size = sizex * sizey + self.data = np.zeros((image_size,), dtype=np.uint32) + self.data_pointer = self.data.ctypes.data_as(ctypes.c_void_p) + + data_shape = 'Data2D' if sizey != 1 else 'Data1D' + if data_shape != self.data_shape: + self.data_shape = data_shape + # init the viewers + self.dte_signal_temp.emit(DataToExport('Camera', + data=[DataFromPlugins(name='Camera ', + data=[np.squeeze( + self.data.reshape((sizey, sizex)).astype(float))], + dim=self.data_shape)])) + + def grab_data(self, Naverage=1, **kwargs): + """ + Start new acquisition in two steps : + * Initialize data: self.data for the memory to store new data and self.data_average to store the average data + * Start acquisition with the given exposure in ms, in "1d" or "2d" mode + + =============== =========== ============================= + **Parameters** **Type** **Description** + Naverage int Number of images to average + =============== =========== ============================= + + See Also + -------- + daq_utils.ThreadCommand + """ + try: + self.camera_done = False + + self.ind_grabbed = 0 # to keep track of the current image in the average + self.Naverage = Naverage # + + self.prepare_data() + if Naverage == 1: + self.camera_controller.SetAcquisitionMode(1) + else: + self.camera_controller.SetAcquisitionMode(2) + self.camera_controller.SetNumberAccumulations(Naverage) + + self.camera_controller.SetExposureTime( + self.settings.child('camera_settings', 'exposure').value() / 1000) # temp should be in s + (err, timings) = self.camera_controller.GetAcquisitionTimings() + self.settings.child('camera_settings', 'exposure').setValue(timings['exposure'] * 1000) + # %%%%% Start acquisition with the given exposure in ms, in "1d" or "2d" mode + self.camera_controller.StartAcquisition() + self.callback_signal.emit(self.Naverage) # will trigger the waitfor acquisition + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [str(e), "log"])) + + def stop(self): + """ + stop the camera's actions. + """ + try: + self.camera_controller.CancelWait() # first cancel the waitacquistion (if any) + QtWidgets.QApplication.processEvents() + self.camera_controller.AbortAcquisition() # abort the camera actions + + except: + pass + return "" + + +class AndorCallback(QtCore.QObject): + """ + + """ + data_sig = QtCore.Signal() + + def __init__(self, wait_fn): + super(AndorCallback, self).__init__() + self.wait_fn = wait_fn + + def wait_for_acquisition(self, n_average: int): + for ind in range(n_average): + err = self.wait_fn() + + if err != 'DRV_NO_NEW_DATA': # will be returned if the main thread called CancelWait + self.data_sig.emit() + + +if __name__ == '__main__': + main(__file__, init=True) + diff --git a/src/pymodaq_plugins_andor/legacy/daq_move_Shamrock.py b/src/pymodaq_plugins_andor/legacy/daq_move_Shamrock.py new file mode 100644 index 0000000..d14a93d --- /dev/null +++ b/src/pymodaq_plugins_andor/legacy/daq_move_Shamrock.py @@ -0,0 +1,354 @@ + +from easydict import EasyDict as edict +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters_fun, main +from pymodaq_utils.utils import ThreadCommand +from pymodaq_plugins_andor.hardware import shamrock_sdk + + +libpath = shamrock_sdk.dllpath + + +class DAQ_Move_Shamrock(DAQ_Move_base): + + _controller_units = 'nm' + is_multiaxes = False + axes_names = [] # "list of strings of the multiaxes + _epsilon = 0.1 + + params = [ + {'title': 'Dll library:', 'name': 'andor_lib', 'type': 'browsepath', 'value': str(libpath), 'readonly': True}, + {'title': 'Spectro Settings:', 'name': 'spectro_settings', 'type': 'group', 'expanded': True, + 'children': [ + {'title': 'Spectro SN:', 'name': 'spectro_serialnumber', 'type': 'str', 'value': '', + 'readonly': True}, + {'title': 'Wavelength (nm):', 'name': 'spectro_wl', 'type': 'float', 'value': 600, 'min': 0, + 'readonly': True}, + + {'title': 'Home Wavelength (nm):', 'name': 'spectro_wl_home', 'type': 'float', 'value': 600, 'min': 0, + 'readonly': False}, + {'title': 'Slit Width (um):', 'name': 'slit_width', 'type': 'int', 'value': 100, 'min': 0, + 'readonly': False}, + {'title': 'Input Port:', 'name': 'input_port', 'type': 'list'}, + {'title': 'Output Port:', 'name': 'output_port', 'type': 'list'}, + {'title': 'Grating Settings:', 'name': 'grating_settings', 'type': 'group', 'expanded': True, + 'children': [ + {'title': 'Grating:', 'name': 'grating', 'type': 'list'}, + {'title': 'Lines (/mm):', 'name': 'lines', 'type': 'int', 'readonly': True}, + {'title': 'Blaze WL (nm):', 'name': 'blaze', 'type': 'str', 'readonly': True}, + {'title': 'Offset (steps):', 'name': 'grating_offset', 'type': 'int'}, + ]}, + {'title': 'Flip wavelength axis:', 'name': 'flip_wavelength', 'type': 'bool', 'value': False, + 'visible': False}, + {'title': 'Go to zero order:', 'name': 'zero_order', 'type': 'bool'}, + ]}, + ] + comon_parameters_fun(is_multiaxes, axes_names, epsilon=_epsilon) + + def commit_settings(self, param): + """ + | Activate parameters changes on the hardware from parameter's name. + | + + =============== ================================ ========================= + **Parameters** **Type** **Description** + *param* instance of pyqtgraph parameter The parameter to activate + =============== ================================ ========================= + + Three profile of parameter : + * **bin_x** : set binning camera from bin_x parameter's value + * **bin_y** : set binning camera from bin_y parameter's value + * **set_point** : Set the camera's temperature from parameter's value. + + """ + try: + if param.name() == 'grating': + index_grating = self.grating_list.index(param.value()) + self.get_set_grating(index_grating) + self.set_wavelength(self.settings.child('spectro_settings', 'spectro_wl').value()) + + elif param.name() == 'grating_offset': + err, ind_grating = self.shamrock_controller.GetGratingSR(0) + offset = int(self.settings.child('spectro_settings', 'grating_settings', 'grating_offset').value()) + err = self.shamrock_controller.SetGratingOffset(0, ind_grating, offset) + + elif param.name() == 'spectro_wl': + self.set_wavelength(param.value()) + + elif param.name() == 'zero_order': + if param.value(): + param.setValue(False) + self.emit_status(ThreadCommand('show_splash', "Moving to zero order, please wait!")) + err = self.shamrock_controller.GotoZeroOrderSR(0) + if err != 'SHAMROCK_SUCCESS': + raise Exception(err) + self.emit_status(ThreadCommand('close_splash')) + + elif param.name() == 'slit_width': + self.set_slitwidth(self.shamrock_controller.get_input_port(0)[1], param.value()) + + elif param.name() == 'input_port': + index_input_port = self.inputport_list.index(param.value()) + self.set_inputport(index_input_port) + + elif param.name() == 'output_port': + index_output_port = self.outputport_list.index(param.value()) + self.set_outputport(index_output_port) + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) + + def ini_stage(self, controller=None): + """Actuator communication initialization + + Parameters + ---------- + controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) + + Returns + ------- + self.status (edict): with initialization status: three fields: + * info (str) + * controller (object) initialized controller + *initialized: (bool): False if initialization failed otherwise True + """ + self.shamrock_controller = self.ini_stage_init(old_controller=controller, + new_controller=shamrock_sdk.ShamrockSDK()) + + self.emit_status(ThreadCommand('show_splash', "Set/Get Shamrock's settings")) + self.ini_spectro() + + initialized = True + self.emit_status(ThreadCommand('close_splash')) + return '', initialized + + def get_actuator_value(self): + """Get the current position from the hardware with scaling conversion. + + Returns + ------- + float: The position obtained after scaling conversion. + """ + pos = self.get_wavelength() + ## + + pos = self.get_position_with_scaling(pos) + return pos + + def move_abs(self, position): + """ Move the actuator to the absolute target defined by position + + Parameters + ---------- + position: (flaot) value of the absolute target positioning + """ + + position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here + position = self.set_position_with_scaling(position) # apply scaling if the user specified one + + self.set_wavelength(position) + + ############################## + + self.target_position = position + self.poll_moving() # start a loop to poll the current actuator value and compare it with target position + + def move_rel(self, position): + """ Move the actuator to the relative target actuator value defined by position + + Parameters + ---------- + position: (flaot) value of the relative target positioning + """ + position = self.check_bound(self.current_position + position) - self.current_position + self.target_position = position + self.current_position + + self.set_wavelength(self.target_position) + ############################## + + self.poll_moving() + + def move_home(self): + """ + + """ + self.move_abs(self.settings.child('spectro_settings', 'spectro_wl_home').value()) + + def stop_motion(self): + """ + Call the specific move_done function (depending on the hardware). + + See Also + -------- + move_done + """ + + self.move_done() # to let the interface know the actuator stopped. Direct call as the setwavelength call is + # blocking anyway + + def close(self): + """ + + """ + if self.shamrock_controller is not None: + self.shamrock_controller.close() + + def set_wavelength(self, wavelength): + self.emit_status(ThreadCommand('show_splash', "Setting wavelength, please wait!")) + err = self.shamrock_controller.SetWavelengthSR(0, wavelength) + self.emit_status(ThreadCommand('close_splash')) + + if err != 'SHAMROCK_SUCCESS': + raise IOError(err) + + self.get_wavelength() + + def get_wavelength(self): + err, wl = self.shamrock_controller.GetWavelengthSR(0) + if err == "SHAMROCK_SUCCESS": + self.settings.child('spectro_settings', 'spectro_wl').setValue(wl) + return float(wl) + + def set_slitwidth(self, index, slitwidth): + self.emit_status(ThreadCommand('show_splash', "Setting slit width, please wait!")) + err = self.shamrock_controller.SetAutoSlitWidthSR(0, index, int(slitwidth)) + if err != "SHAMROCK_SUCCESS": + raise IOError(err) + self.emit_status(ThreadCommand('close_splash')) + + if err != 'SHAMROCK_SUCCESS': + raise IOError(err) + + self.get_slitwidth(index) + + def get_slitwidth(self,index): + err, sw = self.shamrock_controller.GetAutoSlitWidthSR(0,index) + if err == "SHAMROCK_SUCCESS": + self.settings.child('spectro_settings', 'slit_width').setValue(sw) + return float(sw) + + + def ini_spectro(self): + self.settings.child('spectro_settings', 'spectro_serialnumber').setValue( + self.shamrock_controller.GetSerialNumberSR(0)[1].decode()) + + # get grating info + (err, Ngratings) = self.shamrock_controller.GetNumberGratingsSR(0) + self.grating_list = [] + for ind_grating in range(1, Ngratings + 1): + (err, lines, blaze, home, offset) = self.shamrock_controller.GetGratingInfoSR(0, ind_grating) + self.grating_list.append(str(int(lines))) + + self.settings.child('spectro_settings', 'grating_settings', 'grating').setLimits(self.grating_list) + err, ind_grating = self.shamrock_controller.GetGratingSR(0) + self.settings.child('spectro_settings', 'grating_settings', 'grating').setValue( + self.grating_list[ind_grating - 1]) + + self.get_set_grating(ind_grating - 1) + + # idem pour les ports (PV/L2C/) :###### + if self.shamrock_controller.FlipperMirrorIsPresent(0, 1)[1] == 1: + self.inputport_list = ["INPUT_FRONT", "INPUT_SIDE"] + self.settings.child('spectro_settings', 'input_port').setLimits(self.inputport_list) + err, inputport_index = self.shamrock_controller.get_input_port(0) + self.settings.child('spectro_settings', 'input_port').setValue(self.inputport_list[inputport_index]) + self.set_inputport(inputport_index) + else : + self.inputport_list = ["SINGLE_INPUT_PORT"] + self.settings.child('spectro_settings', 'input_port').setLimits(self.inputport_list) + + if self.shamrock_controller.FlipperMirrorIsPresent(0, 2)[1] == 1: + self.outputport_list = ["OUTPUT_FRONT", "OUTPUT_SIDE"] + self.settings.child('spectro_settings', 'output_port').setLimits(self.outputport_list) + err, outputport_index = self.shamrock_controller.get_output_port(0) + self.settings.child('spectro_settings', 'output_port').setValue(self.outputport_list[outputport_index]) + self.set_outputport(outputport_index) + else : + self.outputport_list = ["SINGLE_OUTPUT_PORT"] + self.settings.child('spectro_settings', 'output_port').setLimits(self.outputport_list) + + # check if auto slit width setting is available + if self.shamrock_controller.AutoSlitIsPresent(0, 1)[1] == 0: + self.settings.child('spectro_settings', 'slit_width').hide() + +####################################### + + def get_set_grating(self, ind_grating): + """ + set the current grating to ind_grating+1. ind_grating corresponds to the index in the GUI grating list while the SDK index starts at 1... + + """ + self.emit_status(ThreadCommand('show_splash', "Moving grating please wait")) + err = self.shamrock_controller.SetGratingSR(0, ind_grating + 1) + err, ind_grating = self.shamrock_controller.GetGratingSR(0) + + (err, lines, blaze, home, offset) = self.shamrock_controller.GetGratingInfoSR(0, ind_grating) + self.settings.child('spectro_settings', 'grating_settings', 'grating').setValue( + self.grating_list[ind_grating - 1]) + self.settings.child('spectro_settings', 'grating_settings', 'lines').setValue(lines) + self.settings.child('spectro_settings', 'grating_settings', 'blaze').setValue(blaze) + self.settings.child('spectro_settings', 'grating_settings', 'grating_offset').setValue(offset) + + (err, wl_min, wl_max) = self.shamrock_controller.GetWavelengthLimitsSR(0, ind_grating) + + if err == "SHAMROCK_SUCCESS": + self.settings.child('spectro_settings', + 'spectro_wl').setOpts(limits=(wl_min, wl_max), + tip=f'Possible values are within {wl_min} and {wl_max} for' + f' the selected grating') + self.settings.child('spectro_settings', + 'spectro_wl_home').setOpts(limits=(wl_min, wl_max), + tip=f'Possible values are within {wl_min} and {wl_max} for' + f' the selected grating') + + self.emit_status(ThreadCommand('close_splash')) + + def set_inputport(self, index_inputport): + self.emit_status(ThreadCommand('show_splash', "Setting input port, please wait!")) + if index_inputport == 0 : + strinputport = "INPUT_FRONT" + elif index_inputport == 1 : + strinputport = "INPUT_SIDE" + err = self.shamrock_controller.set_input_port(0, strinputport) + self.emit_status(ThreadCommand('close_splash')) + + if err != 'SHAMROCK_SUCCESS': + raise IOError(err) + + self.get_inputport() + + def get_inputport(self): + err, inputport = self.shamrock_controller.get_input_port(0) + + if err == "SHAMROCK_SUCCESS": + self.settings.child('spectro_settings', 'input_port').setValue(self.inputport_list[inputport]) + + return self.inputport_list[inputport] + + + def set_outputport(self, index_outputport): + self.emit_status(ThreadCommand('show_splash', "Setting output port, please wait!")) + if index_outputport == 0: + stroutputport = "OUTPUT_FRONT" + elif index_outputport == 1: + stroutputport = "OUTPUT_SIDE" + err = self.shamrock_controller.set_output_port(0, stroutputport) + self.emit_status(ThreadCommand('close_splash')) + + if err != 'SHAMROCK_SUCCESS': + raise IOError(err) + + self.get_outputport() + + def get_outputport(self): + err, outputport = self.shamrock_controller.get_output_port(0) + + if err == "SHAMROCK_SUCCESS": + self.settings.child('spectro_settings', 'output_port').setValue(self.outputport_list[outputport]) + + return self.outputport_list[outputport] + + def stop(self): + pass + + +if __name__ == '__main__': + main(__file__, True)