From 8a8b15d7cfd909531aa8970baeb70e8dd54fd3bb Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 21 Mar 2023 19:11:53 +0100 Subject: [PATCH 01/61] Add Thorlabs Kinesis C API drivers Thorlabs APT is marked as "legacy": (https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=10285) To Do: - add actual functionality to the drivers. - add documentation --- .../Thorlabs/Thorlabs_K10CR1/__init__.py | 0 .../{ => Thorlabs_K10CR1/apt}/K10CR1.py | 12 +- .../Thorlabs/Thorlabs_K10CR1/apt/__init__.py | 1 + .../kinesis/Thorlabs_K10CR1.py | 13 + .../Thorlabs_K10CR1/kinesis/__init__.py | 1 + .../Thorlabs/Thorlabs_MFF10x/__init__.py | 0 .../{ => Thorlabs_MFF10x/apt}/MFF10x.py | 23 +- .../Thorlabs/Thorlabs_MFF10x/apt/__init__.py | 1 + .../kinesis/Thorlabs_MFF10x.py | 33 +++ .../Thorlabs_MFF10x/kinesis/__init__.py | 1 + .../Thorlabs/Thorlabs_PRM1Z8/__init__.py | 0 .../{ => Thorlabs_PRM1Z8/apt}/PRM1Z8.py | 2 +- .../Thorlabs/Thorlabs_PRM1Z8/apt/__init__.py | 0 .../drivers/Thorlabs/__init__.py | 3 + .../drivers/Thorlabs/_kinesis/__init__.py | 0 .../drivers/Thorlabs/_kinesis/core.py | 239 ++++++++++++++++++ .../drivers/Thorlabs/_kinesis/isc.py | 28 ++ 17 files changed, 348 insertions(+), 9 deletions(-) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py rename qcodes_contrib_drivers/drivers/Thorlabs/{ => Thorlabs_K10CR1/apt}/K10CR1.py (95%) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py rename qcodes_contrib_drivers/drivers/Thorlabs/{ => Thorlabs_MFF10x/apt}/MFF10x.py (74%) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py rename qcodes_contrib_drivers/drivers/Thorlabs/{ => Thorlabs_PRM1Z8/apt}/PRM1Z8.py (95%) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/__init__.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/__init__.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/K10CR1.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/K10CR1.py similarity index 95% rename from qcodes_contrib_drivers/drivers/Thorlabs/K10CR1.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/K10CR1.py index 5f8ad701d..0d7286b2f 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/K10CR1.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/K10CR1.py @@ -1,10 +1,10 @@ import enum -from typing import Tuple, Optional +from typing import Tuple import qcodes.utils.validators as vals from qcodes import Instrument -from .APT import Thorlabs_APT, ThorlabsHWType +from qcodes_contrib_drivers.drivers.Thorlabs.APT import Thorlabs_APT, ThorlabsHWType class RotationDirection(enum.Enum): @@ -19,7 +19,7 @@ class HomeLimitSwitch(enum.Enum): FORWARD = "fwd" -class Thorlabs_K10CR1(Instrument): +class K10CR1(Instrument): """ Instrument driver for the Thorlabs K10CR1 rotator. @@ -169,7 +169,7 @@ def _get_velocity_parameters(self) -> Tuple[float, float, float]: return self.apt.mot_get_velocity_parameters(self.serial_number) def _set_velocity_parameters(self, - min_vel: Optional[float] = None, accn: Optional[float] = None, max_vel: Optional[float] = None): + min_vel: float = None, accn: float = None, max_vel: float = None): if min_vel is None or accn is None or max_vel is None: old_min_vel, old_accn, old_max_vel = self._get_velocity_parameters() if min_vel is None: @@ -204,8 +204,8 @@ def _set_velocity_max(self, max_vel: float): def _get_home_parameters(self) -> Tuple[int, int, float, float]: return self.apt.mot_get_home_parameters(self.serial_number) - def _set_home_parameters(self, direction: Optional[int] = None, lim_switch: Optional[int] = None, - velocity: Optional[float] = None, zero_offset: Optional[float] = None): + def _set_home_parameters(self, direction: int = None, lim_switch: int = None, + velocity: float = None, zero_offset:float = None): if direction is None or lim_switch is None or velocity is None or zero_offset is None: old_direction, old_lim_switch, old_velocity, old_zero_offset = self._get_home_parameters() if direction is None: diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py new file mode 100644 index 000000000..1b6624a0b --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py @@ -0,0 +1 @@ +from K10CR1 import K10CR1 \ No newline at end of file diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py new file mode 100644 index 000000000..0850030cc --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py @@ -0,0 +1,13 @@ +from qcodes_contrib_drivers.drivers.Thorlabs._kinesis.core import ( + KinesisHWType +) +from qcodes_contrib_drivers.drivers.Thorlabs._kinesis.isc import ( + KinesisIntegratedStepperMotor +) + + +class ThorlabsK10CR1(KinesisIntegratedStepperMotor): + @classmethod + @property + def hardware_type(cls) -> KinesisHWType: + return KinesisHWType.CageRotator diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py new file mode 100644 index 000000000..ac9d9e0c3 --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py @@ -0,0 +1 @@ +from .Thorlabs_K10CR1 import ThorlabsK10CR1 diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/MFF10x.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/MFF10x.py similarity index 74% rename from qcodes_contrib_drivers/drivers/Thorlabs/MFF10x.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/MFF10x.py index 3cfb268d0..d09a3b972 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/MFF10x.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/MFF10x.py @@ -1,5 +1,23 @@ from qcodes import Instrument -from .APT import Thorlabs_APT, ThorlabsHWType +from qcodes_contrib_drivers.drivers.Thorlabs.APT import Thorlabs_APT, ThorlabsHWType + + +def _position_get_parser(val) -> str: + val = int(val) + if val == 0: + return 'open' + elif val == 1: + return 'close' + raise ValueError('Invalid return code', val) + + +def _position_set_parser(val) -> int: + if val == 'open': + return 0 + elif val == 'close': + return 1 + else: + return int(val) class Thorlabs_MFF10x(Instrument): @@ -34,7 +52,8 @@ def __init__(self, name: str, device_id: int, apt: Thorlabs_APT, **kwargs): self.add_parameter('position', get_cmd=self._get_position, set_cmd=self._set_position, - get_parser=int, + get_parser=_position_get_parser, + set_parser=_position_set_parser, label='Position') # print connect message diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py new file mode 100644 index 000000000..b3d70a9e3 --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py @@ -0,0 +1 @@ +from .MFF10x import Thorlabs_MFF10x diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py new file mode 100644 index 000000000..4e569cc0d --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +import pathlib +import warnings +from typing import Mapping, Any + +from qcodes_contrib_drivers.drivers.Thorlabs._kinesis.core import ( + ThorlabsKinesis, + KinesisHWType, + KinesisInstrument +) + + +class ThorlabsMFF10x(KinesisInstrument): + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + metadata: Mapping[Any, Any] | None = None, + label: str | None = None): + self.kinesis = ThorlabsKinesis('FilterFlipper', dll_dir) + + super().__init__(name, dll_dir, metadata, label) + + @classmethod + @property + def hardware_type(cls) -> KinesisHWType: + return KinesisHWType.FilterFlipper + + def open_device(self, serial: int): + super().open_device(serial) + self.kinesis.error_check(self.kinesis.lib.FF_Open(self._c_serial)) + + def close_device(self): + super().close_device() + self.kinesis.lib.FF_Close(self._c_serial) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py new file mode 100644 index 000000000..0aa57120d --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py @@ -0,0 +1 @@ +from .Thorlabs_MFF10x import ThorlabsMFF10x diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/PRM1Z8.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/PRM1Z8.py similarity index 95% rename from qcodes_contrib_drivers/drivers/Thorlabs/PRM1Z8.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/PRM1Z8.py index f856233ae..de980bf1d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/PRM1Z8.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/PRM1Z8.py @@ -1,5 +1,5 @@ from qcodes import Instrument -from .APT import Thorlabs_APT, ThorlabsHWType +from qcodes_contrib_drivers.drivers.Thorlabs.APT import Thorlabs_APT, ThorlabsHWType class Thorlabs_PRM1Z8(Instrument): diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py index e69de29bb..27b165b65 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py @@ -0,0 +1,3 @@ +from Thorlabs_K10CR1.apt import K10CR1 +from Thorlabs_MFF10x.apt import MFF10x +from Thorlabs_PRM1Z8.apt import PRM1Z8 diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py new file mode 100644 index 000000000..30d75a85a --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py @@ -0,0 +1,239 @@ +from __future__ import annotations + +import abc +import ctypes +import enum +import os +import pathlib +import warnings +from typing import Mapping, Any, List, Tuple, Iterable + +import numpy as np + +from qcodes import Instrument + +_DLL_DIR = r"C:\Program Files\Thorlabs\Kinesis" + + +class KinesisHWType(enum.Enum): + CageRotator = 55 + FilterFlipper = 37 + + +class ThorlabsKinesis: + _ERROR_CODES = { + # Errors generated from the FTDI communications module or + # supporting code + 0: 'FT_OK', + 1: 'FT_InvalidHandle', + 2: 'FT_DeviceNotFound', + 3: 'FT_DeviceNotOpened', + 4: 'FT_IOError', + 5: 'FT_InsufficientResources', + 6: 'FT_InvalidParameter', + 7: 'FT_DeviceNotPresent', + 8: 'FT_IncorrectDevice', + # Errors generated by the device libraries + 16: 'FT_NoDLLLoaded', + 17: 'FT_NoFunctionsAvailable', + 18: 'FT_FunctionNotAvailable', + 19: 'FT_BadFunctionPointer', + 20: 'FT_GenericFunctionFail', + 21: 'FT_SpecificFunctionFail', + # General errors generated by all DLLs + 0x20: 'TL_ALREADY_OPEN', + 0x21: 'TL_NO_RESPONSE', + 0x22: 'TL_NOT_IMPLEMENTED', + 0x23: 'TL_FAULT_REPORTED', + 0x24: 'TL_INVALID_OPERATION', + 0x28: 'TL_DISCONNECTING', + 0x29: 'TL_FIRMWARE_BUG', + 0x2A: 'TL_INITIALIZATION_FAILURE', + 0x2B: 'TL_INVALID_CHANNEL', + # Motor-specific errors generated by the Motor DLLs + 0x25: 'TL_UNHOMED', + 0x26: 'TL_INVALID_POSITION', + 0x27: 'TL_INVALID_VELOCITY_PARAMETER', + 0x2C: 'TL_CANNOT_HOME_DEVICE', + 0x2D: 'TL_JOG_CONTINUOUS_MODE', + 0x2E: 'TL_NO_MOTOR_INFO', + 0x2F: 'TL_CMD_TEMP_UNAVAILABLE' + } + + _ERROR_MESSAGES = { + 'FT_OK': 'Success', + 'FT_InvalidHandle': 'The FTDI functions have not been initialized.', + 'FT_DeviceNotFound': 'The Device could not be found. This can be ' + 'generated if the function TLI_BuildDeviceList() ' + 'has not been called.', + 'FT_DeviceNotOpened': 'The Device must be opened before it can be ' + 'accessed. See the appropriate Open function ' + 'for your device.', + 'FT_IOError': 'An I/O Error has occured in the FTDI chip.', + 'FT_InsufficientResources': 'There are Insufficient resources to run ' + 'this application.', + 'FT_InvalidParameter': 'An invalid parameter has been supplied to the ' + 'device.', + 'FT_DeviceNotPresent': 'The Device is no longer present. The device ' + 'may have been disconnected since the last ' + 'TLI_BuildDeviceList() call.', + 'FT_IncorrectDevice': 'The device detected does not match that ' + 'expected.', + 'FT_NoDLLLoaded': 'The library for this device could not be found.', + 'FT_NoFunctionsAvailable': 'No functions available for this device.', + 'FT_FunctionNotAvailable': 'The function is not available for this ' + 'device.', + 'FT_BadFunctionPointer': 'Bad function pointer detected.', + 'FT_GenericFunctionFail': 'The function failed to complete ' + 'succesfully.', + 'FT_SpecificFunctionFail': 'The function failed to complete ' + 'succesfully', + 'TL_ALREADY_OPEN': 'Attempt to open a device that was already open.', + 'TL_NO_RESPONSE': 'The device has stopped responding.', + 'TL_NOT_IMPLEMENTED': 'This function has not been implemented.', + 'TL_FAULT_REPORTED': 'The device has reported a fault.', + 'TL_INVALID_OPERATION': 'The function could not be completed at this ' + 'time.', + 'TL_DISCONNECTING': 'The function could not be completed because the ' + 'device is disconnected.', + 'TL_FIRMWARE_BUG': 'The firmware has thrown an error.', + 'TL_INITIALIZATION_FAILURE': 'The device has failed to initialize.', + 'TL_INVALID_CHANNEL': 'An Invalid channel address was supplied.', + 'TL_UNHOMED': 'The device cannot perform this function until it has ' + 'been Homed.', + 'TL_INVALID_POSITION': 'The function cannot be performed as it would ' + 'result in an illegal position.', + 'TL_INVALID_VELOCITY_PARAMETER': 'An invalid velocity parameter was ' + 'supplied. The velocity must be ' + 'greater than zero.', + 'TL_CANNOT_HOME_DEVICE': 'This device does not support Homing. Check ' + 'the Limit switch parameters are correct.', + 'TL_JOG_CONTINOUS_MODE': 'An invalid jog mode was supplied for the ' + 'jog function.', + 'TL_NO_MOTOR_INFO': 'There is no Motor Parameters available to ' + 'convert Real World Units.', + 'TL_CMD_TEMP_UNAVAILABLE': 'Command temporarily unavailable, Device ' + 'may be busy.' + } + + def __init__(self, lib: str, dll_dir: str | os.PathLike | None = None): + self.dll_dir = os.add_dll_directory(dll_dir or _DLL_DIR) + + if not lib.startswith("Thorlabs.MotionControl"): + lib = "Thorlabs.MotionControl." + lib + if not lib.endswith(".dll"): + lib = lib + ".dll" + lib = pathlib.Path(lib) + if not (dll := (self.dll_dir.path / lib)).exists(): + raise FileNotFoundError(f'Did not find DLL {dll}') + + self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary(str(lib)) + self.error_check(self.lib.TLI_BuildDeviceList()) + + def __del__(self): + self.dll_dir.close() + + @classmethod + def error_check(cls, code: int): + if (status := cls._ERROR_CODES.get(code)) != 'FT_OK': + raise KinesisError(f'{status}: {cls._ERROR_MESSAGES[status]}') + + +class KinesisInstrument(Instrument): + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + metadata: Mapping[Any, Any] | None = None, + label: str | None = None): + try: + self.kinesis = ThorlabsKinesis(self.hardware_type.name, dll_dir) + except FileNotFoundError: + # Subclass needs to handle irregular dll name + pass + + self.serial: int | None = None + + super().__init__(name, metadata, label) + + @classmethod + @property + @abc.abstractmethod + def hardware_type(cls) -> KinesisHWType: + pass + + @abc.abstractmethod + def open_device(self, serial: int): + if self.serial is not None: + warnings.warn(f'Already opened device with serial {self.serial}. ' + 'Closing.', UserWarning, stacklevel=2) + self.close_device() + self.serial = serial + + @abc.abstractmethod + def close_device(self): + self.serial = None + + @property + def _c_serial(self) -> ctypes.c_char_p: + return ctypes.c_char_p(str(self.serial).encode()) + + def list_available_devices(self) -> List[int]: + return [serial for _, serial in + list_available_devices(self.kinesis.lib, self.hardware_type)] + + def close(self): + self.close_device() + super().close() + + +class KinesisError(Exception): + """An error raised by a Kinesis DLL.""" + + +def list_available_devices( + lib: str | pathlib.Path | ctypes.CDLL | None = None, + hardware_type: Iterable[KinesisHWType] | KinesisHWType | None = None +) -> List[Tuple[KinesisHWType, int]]: + if not isinstance(lib, ctypes.CDLL): + # Open base directory + if lib is None: + lib = _DLL_DIR + lib = pathlib.Path(lib) + if lib.is_file(): + lib = lib.parent + lib = os.add_dll_directory(str(lib)) + lib = ctypes.cdll.LoadLibrary(str(pathlib.Path( + lib.path, + 'Thorlabs.MotionControl.DeviceManager.dll' + ))) + + ThorlabsKinesis.error_check(lib.TLI_BuildDeviceList()) + n: int = lib.TLI_GetDeviceListSize() + + devices = [] + hw_type_list = [] + if hardware_type is not None: + # Only search for devices of the passed hardware type (model) + if not np.iterable(hardware_type): + hardware_type = [hardware_type] + for hw in hardware_type: + hw_type_list.append(hw.value) + else: + # Search for all models + hw_type_list = list(range(1, 101)) + + for hw_type_id in hw_type_list: + # char array, 8 bytes for serial number, 1 for delimiter, plus 1 + # surplus needed apparently + serialNo = (ctypes.c_char * (9 + 1))() + + ThorlabsKinesis.error_check(lib.TLI_GetDeviceListByTypeExt( + ctypes.byref(serialNo), + ctypes.wintypes.DWORD(9 + 1), + hw_type_id + )) + if serialNo.value: + devices.append((KinesisHWType(hw_type_id), int(serialNo.value.rstrip(b',')))) + if len(devices) == n: + # Found all devices already + break + + return devices diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py new file mode 100644 index 000000000..5b217a8aa --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +import pathlib +from typing import Mapping, Any + +from .core import KinesisInstrument, ThorlabsKinesis + + +class KinesisIntegratedStepperMotor(KinesisInstrument): + """Devices which are controlled from the IntegratedStepperMotor dll. + """ + + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + metadata: Mapping[Any, Any] | None = None, + label: str | None = None): + super().__init__(name, dll_dir, metadata, label) + self.kinesis = ThorlabsKinesis( + 'Thorlabs.MotionControl.IntegratedStepperMotors.dll', + dll_dir + ) + + def open_device(self, serial: int): + super().open_device(serial) + self.kinesis.error_check(self.kinesis.lib.ISC_Open(self._c_serial)) + + def close_device(self): + super().close_device() + self.kinesis.lib.ISC_Open(self._c_serial) From 0f9c1657bd0a1867f9c4f3944633c505d0560ee8 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 24 Mar 2023 16:04:37 +0100 Subject: [PATCH 02/61] Fix relative imports --- .../drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py | 2 +- qcodes_contrib_drivers/drivers/Thorlabs/__init__.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py index 1b6624a0b..76fc5ca2a 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py @@ -1 +1 @@ -from K10CR1 import K10CR1 \ No newline at end of file +from .K10CR1 import K10CR1 diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py index 27b165b65..6669cc14f 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py @@ -1,3 +1,3 @@ -from Thorlabs_K10CR1.apt import K10CR1 -from Thorlabs_MFF10x.apt import MFF10x -from Thorlabs_PRM1Z8.apt import PRM1Z8 +from .Thorlabs_K10CR1.apt import K10CR1 +from .Thorlabs_MFF10x.apt import MFF10x +from .Thorlabs_PRM1Z8.apt import PRM1Z8 From 6a6be07e345775069d0051328bfe5718c387fa91 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 24 Mar 2023 16:06:13 +0100 Subject: [PATCH 03/61] Move more functionality to base classes A lot of dll functions of different devices have the same signature but just a different prefix. --- .../kinesis/Thorlabs_K10CR1.py | 5 + .../kinesis/Thorlabs_MFF10x.py | 39 +++-- .../drivers/Thorlabs/_kinesis/core.py | 134 +++++++++++++++--- .../drivers/Thorlabs/_kinesis/isc.py | 11 +- 4 files changed, 155 insertions(+), 34 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py index 0850030cc..440eeb388 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py @@ -7,6 +7,11 @@ class ThorlabsK10CR1(KinesisIntegratedStepperMotor): + @classmethod + @property + def _prefix(self): + return 'ISC' + @classmethod @property def hardware_type(cls) -> KinesisHWType: diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py index 4e569cc0d..31cf14ee6 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py @@ -1,5 +1,6 @@ from __future__ import annotations +import ctypes import pathlib import warnings from typing import Mapping, Any @@ -11,6 +12,24 @@ ) +def _position_get_parser(val) -> str: + val = int(val) + if val == 1: + return 'open' + elif val == 2: + return 'close' + raise ValueError('Invalid return code', val) + + +def _position_set_parser(val) -> int: + if val == 'open': + return 1 + elif val == 'close': + return 2 + else: + return int(val) + + class ThorlabsMFF10x(KinesisInstrument): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, metadata: Mapping[Any, Any] | None = None, @@ -19,15 +38,19 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, super().__init__(name, dll_dir, metadata, label) + self.add_parameter('position', + get_cmd=self.kinesis.get_position, + set_cmd=self.kinesis.set_position, + get_parser=_position_get_parser, + set_parser=_position_set_parser, + label='Position') + + @classmethod + @property + def _prefix(cls): + return 'FF' + @classmethod @property def hardware_type(cls) -> KinesisHWType: return KinesisHWType.FilterFlipper - - def open_device(self, serial: int): - super().open_device(serial) - self.kinesis.error_check(self.kinesis.lib.FF_Open(self._c_serial)) - - def close_device(self): - super().close_device() - self.kinesis.lib.FF_Close(self._c_serial) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py index 30d75a85a..0c2aa50df 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py @@ -5,6 +5,7 @@ import enum import os import pathlib +import time import warnings from typing import Mapping, Any, List, Tuple, Iterable @@ -116,7 +117,8 @@ class ThorlabsKinesis: 'may be busy.' } - def __init__(self, lib: str, dll_dir: str | os.PathLike | None = None): + def __init__(self, lib: str, prefix: str, + dll_dir: str | os.PathLike | None = None): self.dll_dir = os.add_dll_directory(dll_dir or _DLL_DIR) if not lib.startswith("Thorlabs.MotionControl"): @@ -130,6 +132,9 @@ def __init__(self, lib: str, dll_dir: str | os.PathLike | None = None): self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary(str(lib)) self.error_check(self.lib.TLI_BuildDeviceList()) + self.prefix = prefix + self.serialNo = ctypes.c_char_p() + def __del__(self): self.dll_dir.close() @@ -138,51 +143,142 @@ def error_check(cls, code: int): if (status := cls._ERROR_CODES.get(code)) != 'FT_OK': raise KinesisError(f'{status}: {cls._ERROR_MESSAGES[status]}') + @staticmethod + def parse_fw_version(fw: int) -> str: + parts = [f'{i:02d}' for i in fw.to_bytes(length=4, byteorder='big')] + return '.'.join(parts).lstrip('0.') + + def request_status(self): + self.error_check( + getattr(self.lib, f'{self.prefix}_RequestStatus')(self.serialNo) + ) + + def get_position(self) -> int | float | str: + self.request_status() + time.sleep(self.get_polling_duration() * 1e-3) + return getattr(self.lib, f'{self.prefix}_GetPosition')(self.serialNo) + + def set_position(self, val: int | str): + self.error_check(getattr(self.lib, f'{self.prefix}_MoveToPosition')( + self.serialNo, val + )) + + def start_polling(self, duration: int): + success = getattr(self.lib, f'{self.prefix}_StartPolling')( + self.serialNo, duration + ) + if not success: + raise KinesisError('Failed') + + def stop_polling(self): + getattr(self.lib, f'{self.prefix}_StopPolling')(self.serialNo) + + def get_polling_duration(self) -> int: + return getattr(self.lib, f'{self.prefix}_PollingDuration')(self.serialNo) + + def set_polling_duration(self, duration: int): + self.stop_polling() + self.start_polling(duration) + + def connect(self, polling_duration: int = 100): + self.error_check( + getattr(self.lib, f'{self.prefix}_Open')(self.serialNo) + ) + + def disconnect(self): + getattr(self.lib, f'{self.prefix}_Close')(self.serialNo) + + def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: + modelNo = ctypes.create_string_buffer(64) + type = ctypes.wintypes.WORD() + numChannels = ctypes.wintypes.WORD() + notes = ctypes.create_string_buffer(64) + firmwareVersion = ctypes.wintypes.DWORD() + hardwareVersion = ctypes.wintypes.WORD() + modificationState = ctypes.wintypes.WORD() + self.error_check( + getattr(self.lib, f'{self.prefix}_GetHardwareInfo')( + self.serialNo, + modelNo, 64, + ctypes.byref(type), + ctypes.byref(numChannels), + notes, 64, + ctypes.byref(firmwareVersion), + ctypes.byref(hardwareVersion), + ctypes.byref(modificationState) + ) + ) + return (modelNo.value.decode('utf-8'), + type.value, + numChannels.value, + notes.value.decode('utf-8'), + self.parse_fw_version(firmwareVersion.value), + hardwareVersion.value, + modificationState.value) + class KinesisInstrument(Instrument): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): try: - self.kinesis = ThorlabsKinesis(self.hardware_type.name, dll_dir) + self.kinesis = ThorlabsKinesis(self.hardware_type.name, self._prefix, dll_dir) except FileNotFoundError: # Subclass needs to handle irregular dll name pass - self.serial: int | None = None - super().__init__(name, metadata, label) + self.add_parameter('polling_duration', + get_cmd=self.kinesis.get_polling_duration, + set_cmd=self.kinesis.set_polling_duration, + unit='ms') + @classmethod @property @abc.abstractmethod - def hardware_type(cls) -> KinesisHWType: + def _prefix(cls) -> str: pass + @classmethod + @property @abc.abstractmethod - def open_device(self, serial: int): - if self.serial is not None: - warnings.warn(f'Already opened device with serial {self.serial}. ' - 'Closing.', UserWarning, stacklevel=2) - self.close_device() - self.serial = serial - - @abc.abstractmethod - def close_device(self): - self.serial = None + def hardware_type(cls) -> KinesisHWType: + pass @property - def _c_serial(self) -> ctypes.c_char_p: - return ctypes.c_char_p(str(self.serial).encode()) + def serial(self) -> int | None: + sn = self.kinesis.serialNo.value + if sn is not None: + return int(sn.decode()) + return None def list_available_devices(self) -> List[int]: return [serial for _, serial in list_available_devices(self.kinesis.lib, self.hardware_type)] - def close(self): - self.close_device() + def connect(self, serial: int, polling_duration: int = 100): + if self.serial is not None: + warnings.warn('Already connected to device with serial ' + f'{self.serial}. Disconnecting.', + UserWarning, stacklevel=2) + self.disconnect() + + self.kinesis.serialNo.value = str(serial).encode() + self.kinesis.connect() + self.kinesis.start_polling(polling_duration) + + def disconnect(self): + self.kinesis.stop_polling() + self.kinesis.disconnect() super().close() + def get_idn(self) -> dict[str, str]: + model, type, num_channels, notes, firmware, hardware, state = \ + self.kinesis.get_hw_info() + return {'vendor': 'Thorlabs', 'model': model, 'firmware': firmware, + 'serial': self.serial} + class KinesisError(Exception): """An error raised by a Kinesis DLL.""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py index 5b217a8aa..153f5cc80 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py @@ -19,10 +19,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, dll_dir ) - def open_device(self, serial: int): - super().open_device(serial) - self.kinesis.error_check(self.kinesis.lib.ISC_Open(self._c_serial)) - - def close_device(self): - super().close_device() - self.kinesis.lib.ISC_Open(self._c_serial) + @classmethod + @property + def _prefix(cls) -> str: + return 'ISC' From f39f98c8f18a689c2eb31ebf78744ed8307973b8 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 17 Apr 2023 12:32:09 +0200 Subject: [PATCH 04/61] Add toggle_position() function --- .../Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py index 31cf14ee6..1f680bd38 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py @@ -5,6 +5,7 @@ import warnings from typing import Mapping, Any +from qcodes.validators import validators from qcodes_contrib_drivers.drivers.Thorlabs._kinesis.core import ( ThorlabsKinesis, KinesisHWType, @@ -54,3 +55,10 @@ def _prefix(cls): @property def hardware_type(cls) -> KinesisHWType: return KinesisHWType.FilterFlipper + + def toggle_position(self): + """Toggle the position of the flipper.""" + if self.position() == 'open': + self.position('close') + else: + self.position('open') From c536b48fabfa03618942c45fed63971f08d76a62 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Thu, 20 Apr 2023 17:26:30 +0200 Subject: [PATCH 05/61] Fix issues with connecting and disconnecting --- .../drivers/Thorlabs/_kinesis/core.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py index 0c2aa50df..b7efdd48e 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py @@ -264,14 +264,20 @@ def connect(self, serial: int, polling_duration: int = 100): UserWarning, stacklevel=2) self.disconnect() - self.kinesis.serialNo.value = str(serial).encode() - self.kinesis.connect() + try: + old_serial_value = self.kinesis.serialNo.value + self.kinesis.serialNo.value = str(serial).encode() + self.kinesis.connect() + except KinesisError: + self.kinesis.serialNo.value = old_serial_value + raise self.kinesis.start_polling(polling_duration) + self.connect_message() def disconnect(self): self.kinesis.stop_polling() self.kinesis.disconnect() - super().close() + self.kinesis.serialNo.value = None def get_idn(self) -> dict[str, str]: model, type, num_channels, notes, firmware, hardware, state = \ @@ -279,6 +285,10 @@ def get_idn(self) -> dict[str, str]: return {'vendor': 'Thorlabs', 'model': model, 'firmware': firmware, 'serial': self.serial} + def close(self): + self.disconnect() + super().close() + class KinesisError(Exception): """An error raised by a Kinesis DLL.""" From ee52c4b4c5455508e6d7e49500f90bc3503d0d2b Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 28 Apr 2023 18:52:56 +0200 Subject: [PATCH 06/61] Add enums and structs and move to separate modules --- .../kinesis/Thorlabs_MFF10x.py | 6 +- .../drivers/Thorlabs/_kinesis/core.py | 17 +- .../drivers/Thorlabs/_kinesis/enums.py | 49 ++++++ .../drivers/Thorlabs/_kinesis/isc.py | 8 +- .../drivers/Thorlabs/_kinesis/structs.py | 159 ++++++++++++++++++ 5 files changed, 223 insertions(+), 16 deletions(-) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/enums.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/structs.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py index 1f680bd38..8e31281b8 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py @@ -6,9 +6,9 @@ from typing import Mapping, Any from qcodes.validators import validators +from qcodes_contrib_drivers.drivers.Thorlabs._kinesis import enums from qcodes_contrib_drivers.drivers.Thorlabs._kinesis.core import ( ThorlabsKinesis, - KinesisHWType, KinesisInstrument ) @@ -53,8 +53,8 @@ def _prefix(cls): @classmethod @property - def hardware_type(cls) -> KinesisHWType: - return KinesisHWType.FilterFlipper + def hardware_type(cls) -> enums.KinesisHWType: + return enums.KinesisHWType.FilterFlipper def toggle_position(self): """Toggle the position of the flipper.""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py index b7efdd48e..0a03b543d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py @@ -2,7 +2,6 @@ import abc import ctypes -import enum import os import pathlib import time @@ -12,15 +11,11 @@ import numpy as np from qcodes import Instrument +from . import enums _DLL_DIR = r"C:\Program Files\Thorlabs\Kinesis" -class KinesisHWType(enum.Enum): - CageRotator = 55 - FilterFlipper = 37 - - class ThorlabsKinesis: _ERROR_CODES = { # Errors generated from the FTDI communications module or @@ -217,7 +212,7 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: modificationState.value) -class KinesisInstrument(Instrument): +class KinesisInstrument(Instrument, metaclass=abc.ABCMeta): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): @@ -243,7 +238,7 @@ def _prefix(cls) -> str: @classmethod @property @abc.abstractmethod - def hardware_type(cls) -> KinesisHWType: + def hardware_type(cls) -> enums.KinesisHWType: pass @property @@ -296,8 +291,8 @@ class KinesisError(Exception): def list_available_devices( lib: str | pathlib.Path | ctypes.CDLL | None = None, - hardware_type: Iterable[KinesisHWType] | KinesisHWType | None = None -) -> List[Tuple[KinesisHWType, int]]: + hardware_type: Iterable[enums.KinesisHWType] | enums.KinesisHWType | None = None +) -> List[Tuple[enums.KinesisHWType, int]]: if not isinstance(lib, ctypes.CDLL): # Open base directory if lib is None: @@ -337,7 +332,7 @@ def list_available_devices( hw_type_id )) if serialNo.value: - devices.append((KinesisHWType(hw_type_id), int(serialNo.value.rstrip(b',')))) + devices.append((enums.KinesisHWType(hw_type_id), int(serialNo.value.rstrip(b',')))) if len(devices) == n: # Found all devices already break diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/enums.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/enums.py new file mode 100644 index 000000000..e8bfefee6 --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/enums.py @@ -0,0 +1,49 @@ +import enum + + +class KinesisHWType(enum.Enum): + CageRotator = 55 + FilterFlipper = 37 + + +class ISCUnitType(enum.Enum): + Distance = 0 + Velocity = 1 + Acceleration = 2 + + +class JogModes(enum.Enum): + JogModeUndefined = 0 + """Undefined.""" + Continuous = 1 + """Continuous jogging.""" + SingleStep = 2 + """Jog 1 step at a time.""" + + +class StopModes(enum.Enum): + StopModeUndefined = 0 + """Undefined.""" + Immediate = 1 + """Stops immediate.""" + Profiled = 2 + """Stops using a velocity profile.""" + + +class TravelDirection(enum.Enum): + TravelDirectionDisabled = 0 + """Disabled or Undefined.""" + Forwards = 1 + """Move in a Forward direction.""" + Reverse = 2 + """Move in a Backward / Reverse direction.""" + + +class HomeLimitSwitchDirection(enum.Enum): + """Values that represent Limit Switch Directions.""" + LimitSwitchDirectionUndefined = 0 + """Undefined.""" + ReverseLimitSwitch = 1 + """Limit switch in forward direction.""" + ForwardLimitSwitch = 1 + """Limit switch in reverse direction.""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py index 153f5cc80..9c9d01982 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py @@ -1,9 +1,11 @@ from __future__ import annotations +import ctypes import pathlib from typing import Mapping, Any -from .core import KinesisInstrument, ThorlabsKinesis +from . import enums +from .core import KinesisInstrument, ThorlabsKinesis, KinesisError class KinesisIntegratedStepperMotor(KinesisInstrument): @@ -13,11 +15,13 @@ class KinesisIntegratedStepperMotor(KinesisInstrument): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, metadata, label) + self.kinesis = ThorlabsKinesis( 'Thorlabs.MotionControl.IntegratedStepperMotors.dll', + self._prefix, dll_dir ) + super().__init__(name, dll_dir, metadata, label) @classmethod @property diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/structs.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/structs.py new file mode 100644 index 000000000..d708407ed --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/structs.py @@ -0,0 +1,159 @@ +import ctypes + +from . import enums + + +class StructureWithEnums(ctypes.Structure): + """Add missing enum feature to ctypes Structures. + + Taken from https://gist.github.com/christoph2/9c390e5c094796903097 + """ + _map = {} + + def __getattribute__(self, name): + _map = ctypes.Structure.__getattribute__(self, '_map') + value = ctypes.Structure.__getattribute__(self, name) + if name in _map: + EnumClass = _map[name] + if isinstance(value, ctypes.Array): + return [EnumClass(x) for x in value] + else: + return EnumClass(value) + else: + return value + + def __str__(self): + result = ["struct {0} {{".format(self.__class__.__name__)] + for field in self._fields_: + attr, attrType = field + if attr in self._map: + attrType = self._map[attr] + value = getattr(self, attr) + result.append(" {0} [{1}] = {2!r};".format(attr, attrType.__name__, value)) + result.append("};") + return '\n'.join(result) + + __repr__ = __str__ + + +class VelocityParameters(ctypes.Structure): + """Structure containing the velocity parameters. + + Moves are performed using a velocity profile. The move starts at the + Minimum Velocity (always 0 at present) and accelerated to the + Maximum Velocity using the defined Acceleration. The move is usually + completed using a similar deceleration. + + Fields + ------ + acceleration : int + The acceleration in Device Units. + maxVelocity : int + The maximum velocity in Device Units. + minVelocity : int + The minimum velocity in Device Units usually 0. + """ + _fields_ = [('acceleration', ctypes.c_int), + ('maxVelocity', ctypes.c_int), + ('minVelocity', ctypes.c_int)] + + +class JogParameters(StructureWithEnums): + """Structure containing the jog parameters. + + Jogs are performed using a velocity profile over small fixed + distances. The move starts at the Minimum Velocity (always 0 at + present) and accelerated to the Maximum Velocity using the defined + Acceleration. The move is usually completed using a similar + deceleration. + + Fields + ------ + mode : class:`enums.JogModes` + The jogging mode. + + The mode can be one of the following: + + +---+-------------------------------------------------------+ + | | Continuous Jogging | + | 1 | The device will continue moving until the end stop is | + | | reached or the device button is raised. | + +---+-------------------------------------------------------+ + | | Step Jog | + | 2 | The device will move by a fixed amount as defined in | + | | this structure. | + +---+-------------------------------------------------------+ + + stepSize : uint + The step size in Device Units. + stopMode : :class:`enums.StopModes` + The Stop Mode. + + The Stop Mode determines how the jog should stop. + + +---+-----------+ + | 1 | Immediate | + +---+-----------+ + | 2 | Profiled. | + +---+-----------+ + + velParams : VelocityParameters + The VelocityParameters for the jog. + """ + _fields_ = [('mode', ctypes.c_int), + ('stepSize', ctypes.c_uint), + ('stopMode', ctypes.c_int), + ('velParams', VelocityParameters)] + + _map = {'mode': enums.JogModes, 'stopMode': enums.StopModes} + + +class HomingParameters(StructureWithEnums): + """Structure containing the homing parameters. + + Homing is performed using a constant velocity. The home starts + moving the motor in the defined direction until the limit switch is + detected. The device will then back off from the limit switch by the + defined offset distance. + + Fields + ------ + direction : int + The Homing direction sense + + The Homing Operation will always move in a decreasing position + sense, but the actuator gearing may change the actual physical + sense. Therefore the homing direction can correct the physical + sense. + + +---+------------+ + | 1 | Forwards | + +---+------------+ + | 2 | Backwards. | + +---+------------+ + + limitSwitch : int + The limit switch direction. + + The limit switch which will be hit when homing completes. + + +---+-----------------------+ + | 1 | Forward Limit Switch | + +---+-----------------------+ + | 2 | Reverse Limit Switch. | + +---+-----------------------+ + + offsetDistance : uint + Distance of home from limit in small indivisible units. + velocity : uint + The velocity in small indivisible units. + + As the homing operation is performed at a much lower velocity, + to achieve accuracy, a profile is not required. + """ + _fields_ = [('direction', ctypes.c_int), + ('limitSwitch', ctypes.c_int), + ('offsetDistance', ctypes.c_uint), + ('velocity', ctypes.c_uint)] + + _map = {'direction': enums.TravelDirection, 'limitSwitch': enums.HomeLimitSwitchDirection} From 66f20685ea0a014b57d2dab08deb13aca0b44ea5 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 14:11:05 +0200 Subject: [PATCH 07/61] Move _kinesis to private/kinesis --- .../drivers/Thorlabs/_kinesis/isc.py | 29 --------------- .../{_kinesis => private}/__init__.py | 0 .../Thorlabs/private/kinesis/__init__.py | 0 .../{_kinesis => private/kinesis}/core.py | 35 ++++++++++++++----- .../{_kinesis => private/kinesis}/enums.py | 0 .../drivers/Thorlabs/private/kinesis/isc.py | 21 +++++++++++ .../{_kinesis => private/kinesis}/structs.py | 0 7 files changed, 48 insertions(+), 37 deletions(-) delete mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py rename qcodes_contrib_drivers/drivers/Thorlabs/{_kinesis => private}/__init__.py (100%) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py rename qcodes_contrib_drivers/drivers/Thorlabs/{_kinesis => private/kinesis}/core.py (92%) rename qcodes_contrib_drivers/drivers/Thorlabs/{_kinesis => private/kinesis}/enums.py (100%) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py rename qcodes_contrib_drivers/drivers/Thorlabs/{_kinesis => private/kinesis}/structs.py (100%) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py deleted file mode 100644 index 9c9d01982..000000000 --- a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/isc.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -import ctypes -import pathlib -from typing import Mapping, Any - -from . import enums -from .core import KinesisInstrument, ThorlabsKinesis, KinesisError - - -class KinesisIntegratedStepperMotor(KinesisInstrument): - """Devices which are controlled from the IntegratedStepperMotor dll. - """ - - def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, - metadata: Mapping[Any, Any] | None = None, - label: str | None = None): - - self.kinesis = ThorlabsKinesis( - 'Thorlabs.MotionControl.IntegratedStepperMotors.dll', - self._prefix, - dll_dir - ) - super().__init__(name, dll_dir, metadata, label) - - @classmethod - @property - def _prefix(cls) -> str: - return 'ISC' diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/__init__.py rename to qcodes_contrib_drivers/drivers/Thorlabs/private/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py similarity index 92% rename from qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py rename to qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 0a03b543d..878b9ea7b 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -169,7 +169,9 @@ def stop_polling(self): getattr(self.lib, f'{self.prefix}_StopPolling')(self.serialNo) def get_polling_duration(self) -> int: - return getattr(self.lib, f'{self.prefix}_PollingDuration')(self.serialNo) + return getattr(self.lib, f'{self.prefix}_PollingDuration')( + self.serialNo + ) def set_polling_duration(self, duration: int): self.stop_polling() @@ -216,11 +218,13 @@ class KinesisInstrument(Instrument, metaclass=abc.ABCMeta): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): + self._initialized: bool = False try: - self.kinesis = ThorlabsKinesis(self.hardware_type.name, self._prefix, dll_dir) + self.kinesis = ThorlabsKinesis(self.hardware_type.name, + self._prefix, dll_dir) except FileNotFoundError: # Subclass needs to handle irregular dll name - pass + self.kinesis = self._init_kinesis(dll_dir) super().__init__(name, metadata, label) @@ -229,6 +233,12 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, set_cmd=self.kinesis.set_polling_duration, unit='ms') + def _init_kinesis(self, + dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: + raise NotImplementedError(f'The subclass {type(self)} should override ' + 'the _init_kinesis() method for irregular ' + 'dll name.') + @classmethod @property @abc.abstractmethod @@ -249,25 +259,34 @@ def serial(self) -> int | None: return None def list_available_devices(self) -> List[int]: - return [serial for _, serial in - list_available_devices(self.kinesis.lib, self.hardware_type)] + self._initialized = True + try: + return [serial for _, serial in + list_available_devices(self.kinesis.lib, + self.hardware_type)] + except KinesisError: + self._initialized = False + raise def connect(self, serial: int, polling_duration: int = 100): + begin_time = time.time() + if not self._initialized: + self.list_available_devices() if self.serial is not None: - warnings.warn('Already connected to device with serial ' + warnings.warn('Already connected to device with serial ' f'{self.serial}. Disconnecting.', UserWarning, stacklevel=2) self.disconnect() + old_serial_value = self.kinesis.serialNo.value try: - old_serial_value = self.kinesis.serialNo.value self.kinesis.serialNo.value = str(serial).encode() self.kinesis.connect() except KinesisError: self.kinesis.serialNo.value = old_serial_value raise self.kinesis.start_polling(polling_duration) - self.connect_message() + self.connect_message(begin_time=begin_time) def disconnect(self): self.kinesis.stop_polling() diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/enums.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/enums.py rename to qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py new file mode 100644 index 000000000..3d700d0cf --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import ctypes +import pathlib + +from . import enums +from .core import KinesisInstrument, ThorlabsKinesis, KinesisError + + +class KinesisIntegratedStepperMotor(KinesisInstrument): + """Devices which are controlled from the IntegratedStepperMotor dll. + """ + + def _init_kinesis(self, dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: + return ThorlabsKinesis('Thorlabs.MotionControl.IntegratedStepperMotors.dll', self._prefix, + dll_dir) + + @classmethod + @property + def _prefix(cls) -> str: + return 'ISC' diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/structs.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/_kinesis/structs.py rename to qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py From 6f7065fcc1f8a00331babd93074a4ad33b0d80b1 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 14:36:41 +0200 Subject: [PATCH 08/61] Reorganize directory structure. Where two different drivers are available, there is now a file for each named after the driver type (tlpm, visa, kinesis, apt) which contains the instrument class. --- .../drivers/Thorlabs/Thorlabs_K10CR1/__init__.py | 8 ++++++++ .../Thorlabs_K10CR1/{apt/K10CR1.py => apt.py} | 11 +++++++++-- .../drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py | 1 - .../{kinesis/Thorlabs_K10CR1.py => kinesis.py} | 4 ++-- .../Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py | 1 - .../drivers/Thorlabs/Thorlabs_MFF10x/__init__.py | 8 ++++++++ .../Thorlabs_MFF10x/{apt/MFF10x.py => apt.py} | 12 ++++++++++-- .../drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py | 1 - .../{kinesis/Thorlabs_MFF10x.py => kinesis.py} | 11 +++-------- .../Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py | 1 - .../apt/__init__.py => Thorlabs_PM100D/visa.py} | 0 .../Thorlabs_PRM1Z8/{apt/PRM1Z8.py => apt.py} | 12 ++++++++++-- qcodes_contrib_drivers/drivers/Thorlabs/__init__.py | 3 --- .../drivers/Thorlabs/{ => private}/APT.py | 0 .../drivers/Thorlabs/private/kinesis/__init__.py | 1 + 15 files changed, 51 insertions(+), 23 deletions(-) rename qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/{apt/K10CR1.py => apt.py} (96%) delete mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py rename qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/{kinesis/Thorlabs_K10CR1.py => kinesis.py} (66%) delete mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py rename qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/{apt/MFF10x.py => apt.py} (85%) delete mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py rename qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/{kinesis/Thorlabs_MFF10x.py => kinesis.py} (83%) delete mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py rename qcodes_contrib_drivers/drivers/Thorlabs/{Thorlabs_PRM1Z8/apt/__init__.py => Thorlabs_PM100D/visa.py} (100%) rename qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/{apt/PRM1Z8.py => apt.py} (81%) rename qcodes_contrib_drivers/drivers/Thorlabs/{ => private}/APT.py (100%) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py index e69de29bb..7ce9911d8 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py @@ -0,0 +1,8 @@ +"""Drivers for Thorlabs K10CR1. + +There are two driver versions: + - Kinesis + - APT + +The APT platform has been deprecated by Thorlabs. +""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/K10CR1.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py similarity index 96% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/K10CR1.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py index 0d7286b2f..9bf10de2b 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/K10CR1.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py @@ -1,10 +1,11 @@ import enum from typing import Tuple +import warnings import qcodes.utils.validators as vals from qcodes import Instrument -from qcodes_contrib_drivers.drivers.Thorlabs.APT import Thorlabs_APT, ThorlabsHWType +from qcodes_contrib_drivers.drivers.Thorlabs.private.APT import Thorlabs_APT, ThorlabsHWType class RotationDirection(enum.Enum): @@ -19,7 +20,7 @@ class HomeLimitSwitch(enum.Enum): FORWARD = "fwd" -class K10CR1(Instrument): +class ThorlabsK10CR1(Instrument): """ Instrument driver for the Thorlabs K10CR1 rotator. @@ -265,3 +266,9 @@ def _move_home(self): def _move_home_async(self): self.apt.mot_move_home(self.serial_number, False) + + +class K10CR1(ThorlabsK10CR1): + def __post_init__(self): + warnings.warn('This class name is deprecated. Please use the ThorlabsK10CR1 class instead', + DeprecationWarning) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py deleted file mode 100644 index 76fc5ca2a..000000000 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .K10CR1 import K10CR1 diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py similarity index 66% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py index 440eeb388..df1a82193 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/Thorlabs_K10CR1.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -1,7 +1,7 @@ -from qcodes_contrib_drivers.drivers.Thorlabs._kinesis.core import ( +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.enums import ( KinesisHWType ) -from qcodes_contrib_drivers.drivers.Thorlabs._kinesis.isc import ( +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.isc import ( KinesisIntegratedStepperMotor ) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py deleted file mode 100644 index ac9d9e0c3..000000000 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .Thorlabs_K10CR1 import ThorlabsK10CR1 diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py index e69de29bb..58f5968bc 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py @@ -0,0 +1,8 @@ +"""Drivers for Thorlabs MFF10x. + +There are two driver versions: + - Kinesis + - APT + +The APT platform has been deprecated by Thorlabs. +""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/MFF10x.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py similarity index 85% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/MFF10x.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py index d09a3b972..7235ed0f2 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/MFF10x.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py @@ -1,5 +1,7 @@ +import warnings + from qcodes import Instrument -from qcodes_contrib_drivers.drivers.Thorlabs.APT import Thorlabs_APT, ThorlabsHWType +from qcodes_contrib_drivers.drivers.Thorlabs.private.APT import Thorlabs_APT, ThorlabsHWType def _position_get_parser(val) -> str: @@ -20,7 +22,7 @@ def _position_set_parser(val) -> int: return int(val) -class Thorlabs_MFF10x(Instrument): +class ThorlabsMFF10x(Instrument): """ Instrument driver for the Thorlabs MFF10x mirror flipper. @@ -71,3 +73,9 @@ def _get_position(self): # set methods def _set_position(self, position): self.apt.mot_move_jog(self.serial_number, position+1, False) + + +class MFF10x(ThorlabsMFF10x): + def __post_init__(self): + warnings.warn('This class name is deprecated. Please use the ThorlabsMFF10x class instead', + DeprecationWarning) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py deleted file mode 100644 index b3d70a9e3..000000000 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .MFF10x import Thorlabs_MFF10x diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py similarity index 83% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index 8e31281b8..c09936321 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/Thorlabs_MFF10x.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -1,14 +1,10 @@ from __future__ import annotations -import ctypes import pathlib -import warnings from typing import Mapping, Any -from qcodes.validators import validators -from qcodes_contrib_drivers.drivers.Thorlabs._kinesis import enums -from qcodes_contrib_drivers.drivers.Thorlabs._kinesis.core import ( - ThorlabsKinesis, +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import ( KinesisInstrument ) @@ -35,10 +31,9 @@ class ThorlabsMFF10x(KinesisInstrument): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - self.kinesis = ThorlabsKinesis('FilterFlipper', dll_dir) - super().__init__(name, dll_dir, metadata, label) + # TODO: The positions might be different elsewhere. self.add_parameter('position', get_cmd=self.kinesis.get_position, set_cmd=self.kinesis.set_position, diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py deleted file mode 100644 index 0aa57120d..000000000 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .Thorlabs_MFF10x import ThorlabsMFF10x diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/__init__.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/PRM1Z8.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py similarity index 81% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/PRM1Z8.py rename to qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py index de980bf1d..297b12af6 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt/PRM1Z8.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py @@ -1,8 +1,10 @@ +import warnings + from qcodes import Instrument -from qcodes_contrib_drivers.drivers.Thorlabs.APT import Thorlabs_APT, ThorlabsHWType +from qcodes_contrib_drivers.drivers.Thorlabs.private.APT import Thorlabs_APT, ThorlabsHWType -class Thorlabs_PRM1Z8(Instrument): +class ThorlabsPRM1Z8(Instrument): """ Instrument driver for the Thorlabs PRMZ1Z8 polarizer wheel. @@ -51,3 +53,9 @@ def _get_position(self): # set methods def _set_position(self, position): self.apt.mot_move_absolute_ex(self.serial_number, position, True) + + +class Thorlabs_PRM1Z8(ThorlabsPRM1Z8): + def __post_init__(self): + warnings.warn('This class name is deprecated. Please use the ThorlabsPRM1Z8 class instead', + DeprecationWarning) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py index 6669cc14f..e69de29bb 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py @@ -1,3 +0,0 @@ -from .Thorlabs_K10CR1.apt import K10CR1 -from .Thorlabs_MFF10x.apt import MFF10x -from .Thorlabs_PRM1Z8.apt import PRM1Z8 diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/APT.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/APT.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/APT.py rename to qcodes_contrib_drivers/drivers/Thorlabs/private/APT.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py index e69de29bb..b2af46055 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py @@ -0,0 +1 @@ +from .core import KinesisInstrument \ No newline at end of file From eaa358de7264330d5e4105685af5b740e40c6b92 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 15:44:53 +0200 Subject: [PATCH 09/61] Refactor KinesisInstrument to connect in constructor --- .../Thorlabs/private/kinesis/__init__.py | 2 +- .../drivers/Thorlabs/private/kinesis/core.py | 29 ++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py index b2af46055..24bcc1dfa 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py @@ -1 +1 @@ -from .core import KinesisInstrument \ No newline at end of file +from .core import KinesisInstrument, list_available_devices diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 878b9ea7b..976a4fa63 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -216,6 +216,7 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: class KinesisInstrument(Instrument, metaclass=abc.ABCMeta): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + serial: int | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): self._initialized: bool = False @@ -233,6 +234,8 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, set_cmd=self.kinesis.set_polling_duration, unit='ms') + self.connect(serial) + def _init_kinesis(self, dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: raise NotImplementedError(f'The subclass {type(self)} should override ' @@ -261,30 +264,30 @@ def serial(self) -> int | None: def list_available_devices(self) -> List[int]: self._initialized = True try: - return [serial for _, serial in - list_available_devices(self.kinesis.lib, - self.hardware_type)] + return [ + serial for _, serial in + list_available_devices(self.kinesis.lib, self.hardware_type) + ] except KinesisError: self._initialized = False raise - def connect(self, serial: int, polling_duration: int = 100): + def connect(self, serial: int | None, polling_duration: int = 100): begin_time = time.time() - if not self._initialized: - self.list_available_devices() + if serial is None or not self._initialized: + serials = self.list_available_devices() + if serial is None: + if not len(serials): + raise RuntimeError(f'No {self.prefix} devices found!') + serial = serials[0] if self.serial is not None: warnings.warn('Already connected to device with serial ' f'{self.serial}. Disconnecting.', UserWarning, stacklevel=2) self.disconnect() - old_serial_value = self.kinesis.serialNo.value - try: - self.kinesis.serialNo.value = str(serial).encode() - self.kinesis.connect() - except KinesisError: - self.kinesis.serialNo.value = old_serial_value - raise + self.kinesis.serialNo.value = str(serial).encode() + self.kinesis.connect() self.kinesis.start_polling(polling_duration) self.connect_message(begin_time=begin_time) From d9e1f615ee9bff27f280dd5ee400439d7728ebeb Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 16:18:30 +0200 Subject: [PATCH 10/61] __post_init__ is only available to dataclasses... --- qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py | 3 ++- qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py | 3 ++- qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py index 9bf10de2b..8791a88cd 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py @@ -269,6 +269,7 @@ def _move_home_async(self): class K10CR1(ThorlabsK10CR1): - def __post_init__(self): + def __init__(self, name: str, device_id: int, apt: Thorlabs_APT, **kwargs): warnings.warn('This class name is deprecated. Please use the ThorlabsK10CR1 class instead', DeprecationWarning) + super().__init__(name, device_id, apt, **kwargs) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py index 7235ed0f2..c8fa48f99 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py @@ -76,6 +76,7 @@ def _set_position(self, position): class MFF10x(ThorlabsMFF10x): - def __post_init__(self): + def __init__(self, name: str, device_id: int, apt: Thorlabs_APT, **kwargs): warnings.warn('This class name is deprecated. Please use the ThorlabsMFF10x class instead', DeprecationWarning) + super().__init__(name, device_id, apt, **kwargs) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py index 297b12af6..17c2ece73 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py @@ -56,6 +56,7 @@ def _set_position(self, position): class Thorlabs_PRM1Z8(ThorlabsPRM1Z8): - def __post_init__(self): + def __init__(self, name: str, device_id: int, apt: Thorlabs_APT, **kwargs): warnings.warn('This class name is deprecated. Please use the ThorlabsPRM1Z8 class instead', DeprecationWarning) + super().__init__(name, device_id, apt, **kwargs) From 8bf1e93aeb5c6d5e1a3b096868c0ac662c6ec674 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 16:28:40 +0200 Subject: [PATCH 11/61] Make position val_mapping dynamic --- .../Thorlabs/Thorlabs_MFF10x/kinesis.py | 36 ++++++------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index c09936321..a1c69cbc7 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -1,44 +1,26 @@ from __future__ import annotations import pathlib -from typing import Mapping, Any +from typing import Mapping, Any, Literal -from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import ( KinesisInstrument ) - - -def _position_get_parser(val) -> str: - val = int(val) - if val == 1: - return 'open' - elif val == 2: - return 'close' - raise ValueError('Invalid return code', val) - - -def _position_set_parser(val) -> int: - if val == 'open': - return 1 - elif val == 'close': - return 2 - else: - return int(val) +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums class ThorlabsMFF10x(KinesisInstrument): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + position_mapping: Mapping[str, Literal[1, 2]] | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): super().__init__(name, dll_dir, metadata, label) - # TODO: The positions might be different elsewhere. + position_mapping = position_mapping or {'open': 1, 'close': 2} self.add_parameter('position', get_cmd=self.kinesis.get_position, set_cmd=self.kinesis.set_position, - get_parser=_position_get_parser, - set_parser=_position_set_parser, + val_mapping=position_mapping, label='Position') @classmethod @@ -53,7 +35,9 @@ def hardware_type(cls) -> enums.KinesisHWType: def toggle_position(self): """Toggle the position of the flipper.""" - if self.position() == 'open': - self.position('close') + # val_mapping is dynamic, so use inverse_val_mapping together + # with the hardware values + if self.position() == self.position.inverse_val_mapping[1]: + self.position(self.position.inverse_val_mapping[2]) else: - self.position('open') + self.position(self.position.inverse_val_mapping[1]) From a11e88a9b4847bfaf178b93cbf065b2919e886f9 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 16:29:27 +0200 Subject: [PATCH 12/61] Fix close if not connected to anything --- .../drivers/Thorlabs/private/kinesis/core.py | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 976a4fa63..5027cd2ad 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -215,11 +215,11 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: class KinesisInstrument(Instrument, metaclass=abc.ABCMeta): + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, serial: int | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - self._initialized: bool = False try: self.kinesis = ThorlabsKinesis(self.hardware_type.name, self._prefix, dll_dir) @@ -227,6 +227,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, # Subclass needs to handle irregular dll name self.kinesis = self._init_kinesis(dll_dir) + self._initialized: bool = False + self.kinesis.serialNo.value = serial + super().__init__(name, metadata, label) self.add_parameter('polling_duration', @@ -234,7 +237,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, set_cmd=self.kinesis.set_polling_duration, unit='ms') - self.connect(serial) + self.connect(self.serial) def _init_kinesis(self, dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: @@ -261,6 +264,10 @@ def serial(self) -> int | None: return int(sn.decode()) return None + @property + def connected(self) -> bool: + return self.serial is not None + def list_available_devices(self) -> List[int]: self._initialized = True try: @@ -275,12 +282,12 @@ def list_available_devices(self) -> List[int]: def connect(self, serial: int | None, polling_duration: int = 100): begin_time = time.time() if serial is None or not self._initialized: - serials = self.list_available_devices() + available_devices = self.list_available_devices() if serial is None: - if not len(serials): + if not len(available_devices): raise RuntimeError(f'No {self.prefix} devices found!') - serial = serials[0] - if self.serial is not None: + serial = available_devices[0] + if self.connected: warnings.warn('Already connected to device with serial ' f'{self.serial}. Disconnecting.', UserWarning, stacklevel=2) @@ -292,9 +299,10 @@ def connect(self, serial: int | None, polling_duration: int = 100): self.connect_message(begin_time=begin_time) def disconnect(self): - self.kinesis.stop_polling() - self.kinesis.disconnect() - self.kinesis.serialNo.value = None + if self.connected: + self.kinesis.stop_polling() + self.kinesis.disconnect() + self.kinesis.serialNo.value = None def get_idn(self) -> dict[str, str]: model, type, num_channels, notes, firmware, hardware, state = \ From e5c86a5f6ab0cbdf274702ff38bfacee5e7103cd Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 16:34:48 +0200 Subject: [PATCH 13/61] Add PM100D driver. --- .../Thorlabs/Thorlabs_PM100D/__init__.py | 10 ++ .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 104 ++++++++++++++++++ .../drivers/Thorlabs/Thorlabs_PM100D/visa.py | 20 ++++ 3 files changed, 134 insertions(+) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/__init__.py create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/__init__.py new file mode 100644 index 000000000..e1dd0a5a1 --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/__init__.py @@ -0,0 +1,10 @@ +"""Drivers for Thorlabs PM100D. + +There are two driver versions: + - TLPM (libusb) + - VISA + +Newer versions of the Thorlabs Power Meter software use the TLPM driver +and include a utility to switch the device between the two driver +versions. +""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py new file mode 100644 index 000000000..b6755d892 --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +import ctypes +import os +import pathlib +import platform +import sys +from typing import Dict, Mapping, Any + +from qcodes import Instrument + +_bitness = platform.architecture()[0][:2] +try: + vxi_32 = os.environ['VXIPNPPATH'] + vxi_64 = os.environ['VXIPNPPATH64'] +except KeyError: + vxi_32 = None + vxi_64 = None + raise RuntimeError('IVI VXIPNP path not detected.') + +if vxi_32 is not None: + if _bitness == '64': + dll_path = pathlib.Path(vxi_64, 'Win64', 'Bin', 'TLPM_64.dll') + else: + dll_path = pathlib.Path(vxi_32, 'WinNT', 'Bin', 'TLPM_32.dll') + + os.add_dll_directory(str(dll_path.parent)) + sys.path.append(str(pathlib.Path(vxi_32, 'WinNT', 'TLPM', 'Examples', + 'Python'))) + + from TLPM import TLPM + + +class ThorlabsPM100D(Instrument): + def __init__(self, name: str, addr: str = '', reset: bool = False, + thorlabs_tlpm: TLPM | None = None, + metadata: Mapping[Any, Any] | None = None, + label: str | None = None): + if vxi_32 is None: + raise FileNotFoundError('IVI VXIPNP path not detected.') + + self.tlpm = thorlabs_tlpm or TLPM() + # NEED to call with IDQuery==True, otherwise the following error is + # raised: NameError: The given session or object reference does not + # support this operation. + self.tlpm.open(addr.encode() or self._search_for_device(), True, + reset) + + super().__init__(name, metadata, label) + + self.add_parameter('power', + get_cmd=self._get_power, + label='Power', + unit='Watt') + self.add_parameter('wavelength', + get_cmd=self._get_wavelength, + set_cmd=self._set_wavelength, + label='Wavelength', + unit='nm') + + self.connect_message() + + def _search_for_device(self) -> ctypes.Array[ctypes.c_char]: + deviceCount = ctypes.c_uint32() + resourceName = ctypes.create_string_buffer(1024) + + self.tlpm.findRsrc(ctypes.byref(deviceCount)) + + for i in range(0, deviceCount.value): + self.tlpm.getRsrcName(ctypes.c_int(i), resourceName) + break + + return resourceName + + def _get_power(self) -> float: + power = ctypes.c_double() + self.tlpm.measPower(ctypes.byref(power)) + return power.value + + def _get_wavelength(self) -> float: + wavelength = ctypes.c_double() + self.tlpm.getWavelength(0, ctypes.byref(wavelength)) + return wavelength.value + + def _set_wavelength(self, wavelength: float): + self.tlpm.setWavelength(ctypes.c_double(wavelength)) + + def get_idn(self) -> Dict[str, str]: + manufacturerName = ctypes.create_string_buffer(1024) + deviceName = ctypes.create_string_buffer(1024) + serialNumber = ctypes.create_string_buffer(1024) + firmwareRevision = ctypes.create_string_buffer(1024) + + self.tlpm.identificationQuery(manufacturerName, deviceName, + serialNumber, firmwareRevision) + + return {'vendor': manufacturerName.value.decode(), + 'model': deviceName.value.decode(), + 'serial': serialNumber.value.decode(), + 'firmware': firmwareRevision.value.decode()} + + def close(self): + self.tlpm.close() + super().close() diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py index e69de29bb..25c721b18 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py @@ -0,0 +1,20 @@ +from qcodes import VisaInstrument + + +class ThorlabsPM100D(VisaInstrument): + + def __init__(self, name, address, **kwargs): + super().__init__(name, address, terminator='\n', **kwargs) + + self.add_parameter('wav', + get_cmd='CORR:WAV?', + set_cmd='CORR:WAV {}', + get_parser=float, + set_parser=lambda value: '{:.8E}'.format(value), + label='wavelength', + unit='nm') + self.add_parameter('pow', + get_cmd='READ?', + get_parser=float, + label='power', + unit='Watt') From ec202835e88d028b019b45ff934ec4526ed81054 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 17:14:10 +0200 Subject: [PATCH 14/61] Add documentation. --- .../Thorlabs/Thorlabs_K10CR1/kinesis.py | 19 +++++++++++++ .../Thorlabs/Thorlabs_MFF10x/kinesis.py | 24 ++++++++++++++++ .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 28 +++++++++++++++++++ .../drivers/Thorlabs/private/kinesis/core.py | 21 +++++++++++++- 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py index df1a82193..1fc4df2b8 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -7,6 +7,25 @@ class ThorlabsK10CR1(KinesisIntegratedStepperMotor): + """Kinesis driver for Thorlabs K10CR1 cage rotator. + + Args: + name: + An identifier for this instrument. + dll_dir (optional): + The directory where the kinesis dlls reside. + serial (optional): + The serial number of the device to connect to. If omitted, + the first available device found will be used. For a list + of all available devices, use + :meth:`list_available_devices` on an existing instance or + :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. + metadata (optional): + Additional static metadata. + label (optional): + Nicely formatted name of the instrument. + + """ @classmethod @property def _prefix(self): diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index a1c69cbc7..23edb9fd0 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -10,7 +10,31 @@ class ThorlabsMFF10x(KinesisInstrument): + """Kinesis driver for Thorlabs MFF10x filter flipper. + + Args: + name: + An identifier for this instrument. + dll_dir (optional): + The directory where the kinesis dlls reside. + serial (optional): + The serial number of the device to connect to. If omitted, + the first available device found will be used. For a list + of all available devices, use + :meth:`list_available_devices` on an existing instance or + :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. + position_mapping (optional): + A val mapping for a more human-readable position mapping + than the internally used 1 and 2. Defaults to + ``{'open': 1, 'close': 2}``. + metadata (optional): + Additional static metadata. + label (optional): + Nicely formatted name of the instrument. + + """ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + serial: int | None = None, position_mapping: Mapping[str, Literal[1, 2]] | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index b6755d892..a2b1d6f97 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -32,6 +32,34 @@ class ThorlabsPM100D(Instrument): + """TLPM driver for Thorlabs PM100D power meter. + + This driver wraps the example Python driver bundled with the + Optical Power Monitor software which can be downloaded `here`_. + + .. _here: https://www.thorlabs.com/software_pages/ViewSoftwarePage.cfm?Code=OPM + + Args: + name: + An identifier for this instrument. + addr (optional): + The USB address for the device. If omitted, the first + available device found will be used. + reset (optional): + Reset the instrument on connection. Defaults to False. + thorlabs_tlpm (optional): + An instance of the :class:`TLPM` class from the power + monitor software examples. + metadata (optional): + Additional static metadata. + label (optional): + Nicely formatted name of the instrument. + + Raises: + FileNotFoundError: + If the dll is not found. + + """ def __init__(self, name: str, addr: str = '', reset: bool = False, thorlabs_tlpm: TLPM | None = None, metadata: Mapping[Any, Any] | None = None, diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 5027cd2ad..6ce34eb50 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -214,7 +214,26 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: modificationState.value) -class KinesisInstrument(Instrument, metaclass=abc.ABCMeta): +class KinesisInstrument(Instrument, abc.ABC): + """Qcodes Instrument subclass for Kinesis instruments. + + Args: + name: + An identifier for this instrument. + dll_dir (optional): + The directory where the kinesis dlls reside. + serial (optional): + The serial number of the device to connect to. If omitted, + the first available device found will be used. For a list + of all available devices, use + :meth:`list_available_devices` on an existing instance or + :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. + metadata (optional): + Additional static metadata. + label (optional): + Nicely formatted name of the instrument. + + """ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, serial: int | None = None, From e3fa172b95596ffe75ca42a84c6e6906e7123a7b Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 19:45:03 +0200 Subject: [PATCH 15/61] Fix super() call --- .../drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index 23edb9fd0..670544c13 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -33,12 +33,13 @@ class ThorlabsMFF10x(KinesisInstrument): Nicely formatted name of the instrument. """ + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, serial: int | None = None, position_mapping: Mapping[str, Literal[1, 2]] | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, metadata, label) + super().__init__(name, dll_dir, serial, metadata, label) position_mapping = position_mapping or {'open': 1, 'close': 2} self.add_parameter('position', From 72b1fe4f9abe32b64ea266ce781fcaa3eee09278 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 19:45:36 +0200 Subject: [PATCH 16/61] Format lines to 72/79 characters --- .../Thorlabs/private/kinesis/structs.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py index d708407ed..3621abdb3 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py @@ -29,7 +29,9 @@ def __str__(self): if attr in self._map: attrType = self._map[attr] value = getattr(self, attr) - result.append(" {0} [{1}] = {2!r};".format(attr, attrType.__name__, value)) + result.append(" {0} [{1}] = {2!r};".format( + attr, attrType.__name__, value) + ) result.append("};") return '\n'.join(result) @@ -39,10 +41,10 @@ def __str__(self): class VelocityParameters(ctypes.Structure): """Structure containing the velocity parameters. - Moves are performed using a velocity profile. The move starts at the - Minimum Velocity (always 0 at present) and accelerated to the - Maximum Velocity using the defined Acceleration. The move is usually - completed using a similar deceleration. + Moves are performed using a velocity profile. The move starts at + the Minimum Velocity (always 0 at present) and accelerated to the + Maximum Velocity using the defined Acceleration. The move is + usually completed using a similar deceleration. Fields ------ @@ -113,8 +115,8 @@ class HomingParameters(StructureWithEnums): Homing is performed using a constant velocity. The home starts moving the motor in the defined direction until the limit switch is - detected. The device will then back off from the limit switch by the - defined offset distance. + detected. The device will then back off from the limit switch by + the defined offset distance. Fields ------ @@ -156,4 +158,5 @@ class HomingParameters(StructureWithEnums): ('offsetDistance', ctypes.c_uint), ('velocity', ctypes.c_uint)] - _map = {'direction': enums.TravelDirection, 'limitSwitch': enums.HomeLimitSwitchDirection} + _map = {'direction': enums.TravelDirection, + 'limitSwitch': enums.HomeLimitSwitchDirection} From 4eec1b1933de7df70adaae1849f5ca706738dea8 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 19:47:30 +0200 Subject: [PATCH 17/61] Refactor ThorlabsKinesis to include more basic functionality --- .../drivers/Thorlabs/private/kinesis/core.py | 335 ++++++++++-------- 1 file changed, 196 insertions(+), 139 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 6ce34eb50..f7dee7fb5 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -6,6 +6,7 @@ import pathlib import time import warnings +from functools import wraps, partial from typing import Mapping, Any, List, Tuple, Iterable import numpy as np @@ -13,108 +14,130 @@ from qcodes import Instrument from . import enums -_DLL_DIR = r"C:\Program Files\Thorlabs\Kinesis" +DLL_DIR = r"C:\Program Files\Thorlabs\Kinesis" + +ERROR_CODES = { + # Errors generated from the FTDI communications module or + # supporting code + 0: 'FT_OK', + 1: 'FT_InvalidHandle', + 2: 'FT_DeviceNotFound', + 3: 'FT_DeviceNotOpened', + 4: 'FT_IOError', + 5: 'FT_InsufficientResources', + 6: 'FT_InvalidParameter', + 7: 'FT_DeviceNotPresent', + 8: 'FT_IncorrectDevice', + # Errors generated by the device libraries + 16: 'FT_NoDLLLoaded', + 17: 'FT_NoFunctionsAvailable', + 18: 'FT_FunctionNotAvailable', + 19: 'FT_BadFunctionPointer', + 20: 'FT_GenericFunctionFail', + 21: 'FT_SpecificFunctionFail', + # General errors generated by all DLLs + 0x20: 'TL_ALREADY_OPEN', + 0x21: 'TL_NO_RESPONSE', + 0x22: 'TL_NOT_IMPLEMENTED', + 0x23: 'TL_FAULT_REPORTED', + 0x24: 'TL_INVALID_OPERATION', + 0x28: 'TL_DISCONNECTING', + 0x29: 'TL_FIRMWARE_BUG', + 0x2A: 'TL_INITIALIZATION_FAILURE', + 0x2B: 'TL_INVALID_CHANNEL', + # Motor-specific errors generated by the Motor DLLs + 0x25: 'TL_UNHOMED', + 0x26: 'TL_INVALID_POSITION', + 0x27: 'TL_INVALID_VELOCITY_PARAMETER', + 0x2C: 'TL_CANNOT_HOME_DEVICE', + 0x2D: 'TL_JOG_CONTINUOUS_MODE', + 0x2E: 'TL_NO_MOTOR_INFO', + 0x2F: 'TL_CMD_TEMP_UNAVAILABLE' +} + +ERROR_MESSAGES = { + 'FT_OK': 'Success', + 'FT_InvalidHandle': 'The FTDI functions have not been initialized.', + 'FT_DeviceNotFound': 'The Device could not be found. This can be ' + 'generated if the function TLI_BuildDeviceList() ' + 'has not been called.', + 'FT_DeviceNotOpened': 'The Device must be opened before it can be ' + 'accessed. See the appropriate Open function ' + 'for your device.', + 'FT_IOError': 'An I/O Error has occured in the FTDI chip.', + 'FT_InsufficientResources': 'There are Insufficient resources to run ' + 'this application.', + 'FT_InvalidParameter': 'An invalid parameter has been supplied to the ' + 'device.', + 'FT_DeviceNotPresent': 'The Device is no longer present. The device ' + 'may have been disconnected since the last ' + 'TLI_BuildDeviceList() call.', + 'FT_IncorrectDevice': 'The device detected does not match that ' + 'expected.', + 'FT_NoDLLLoaded': 'The library for this device could not be found.', + 'FT_NoFunctionsAvailable': 'No functions available for this device.', + 'FT_FunctionNotAvailable': 'The function is not available for this ' + 'device.', + 'FT_BadFunctionPointer': 'Bad function pointer detected.', + 'FT_GenericFunctionFail': 'The function failed to complete ' + 'succesfully.', + 'FT_SpecificFunctionFail': 'The function failed to complete ' + 'succesfully', + 'TL_ALREADY_OPEN': 'Attempt to open a device that was already open.', + 'TL_NO_RESPONSE': 'The device has stopped responding.', + 'TL_NOT_IMPLEMENTED': 'This function has not been implemented.', + 'TL_FAULT_REPORTED': 'The device has reported a fault.', + 'TL_INVALID_OPERATION': 'The function could not be completed at this ' + 'time.', + 'TL_DISCONNECTING': 'The function could not be completed because the ' + 'device is disconnected.', + 'TL_FIRMWARE_BUG': 'The firmware has thrown an error.', + 'TL_INITIALIZATION_FAILURE': 'The device has failed to initialize.', + 'TL_INVALID_CHANNEL': 'An Invalid channel address was supplied.', + 'TL_UNHOMED': 'The device cannot perform this function until it has ' + 'been Homed.', + 'TL_INVALID_POSITION': 'The function cannot be performed as it would ' + 'result in an illegal position.', + 'TL_INVALID_VELOCITY_PARAMETER': 'An invalid velocity parameter was ' + 'supplied. The velocity must be ' + 'greater than zero.', + 'TL_CANNOT_HOME_DEVICE': 'This device does not support Homing. Check ' + 'the Limit switch parameters are correct.', + 'TL_JOG_CONTINOUS_MODE': 'An invalid jog mode was supplied for the ' + 'jog function.', + 'TL_NO_MOTOR_INFO': 'There is no Motor Parameters available to ' + 'convert Real World Units.', + 'TL_CMD_TEMP_UNAVAILABLE': 'Command temporarily unavailable, Device ' + 'may be busy.' +} + + +def success_check(func): + """Wraps functions that return a boolean success code. + + 0 means success, 1 means failure.""" + @wraps(func) + def wrapped(*args, **kwargs): + if func(*args, **kwargs): + raise KinesisError('Unspecified failure.') + return wrapped + + +def error_check(func): + """Wraps functions that return an integer error code.""" + @wraps(func) + def wrapped(*args, **kwargs): + code = func(*args, **kwargs) + if (status := ERROR_CODES.get(code)) != 'FT_OK': + raise KinesisError(f'{status}: {ERROR_MESSAGES[status]}') + return wrapped class ThorlabsKinesis: - _ERROR_CODES = { - # Errors generated from the FTDI communications module or - # supporting code - 0: 'FT_OK', - 1: 'FT_InvalidHandle', - 2: 'FT_DeviceNotFound', - 3: 'FT_DeviceNotOpened', - 4: 'FT_IOError', - 5: 'FT_InsufficientResources', - 6: 'FT_InvalidParameter', - 7: 'FT_DeviceNotPresent', - 8: 'FT_IncorrectDevice', - # Errors generated by the device libraries - 16: 'FT_NoDLLLoaded', - 17: 'FT_NoFunctionsAvailable', - 18: 'FT_FunctionNotAvailable', - 19: 'FT_BadFunctionPointer', - 20: 'FT_GenericFunctionFail', - 21: 'FT_SpecificFunctionFail', - # General errors generated by all DLLs - 0x20: 'TL_ALREADY_OPEN', - 0x21: 'TL_NO_RESPONSE', - 0x22: 'TL_NOT_IMPLEMENTED', - 0x23: 'TL_FAULT_REPORTED', - 0x24: 'TL_INVALID_OPERATION', - 0x28: 'TL_DISCONNECTING', - 0x29: 'TL_FIRMWARE_BUG', - 0x2A: 'TL_INITIALIZATION_FAILURE', - 0x2B: 'TL_INVALID_CHANNEL', - # Motor-specific errors generated by the Motor DLLs - 0x25: 'TL_UNHOMED', - 0x26: 'TL_INVALID_POSITION', - 0x27: 'TL_INVALID_VELOCITY_PARAMETER', - 0x2C: 'TL_CANNOT_HOME_DEVICE', - 0x2D: 'TL_JOG_CONTINUOUS_MODE', - 0x2E: 'TL_NO_MOTOR_INFO', - 0x2F: 'TL_CMD_TEMP_UNAVAILABLE' - } - - _ERROR_MESSAGES = { - 'FT_OK': 'Success', - 'FT_InvalidHandle': 'The FTDI functions have not been initialized.', - 'FT_DeviceNotFound': 'The Device could not be found. This can be ' - 'generated if the function TLI_BuildDeviceList() ' - 'has not been called.', - 'FT_DeviceNotOpened': 'The Device must be opened before it can be ' - 'accessed. See the appropriate Open function ' - 'for your device.', - 'FT_IOError': 'An I/O Error has occured in the FTDI chip.', - 'FT_InsufficientResources': 'There are Insufficient resources to run ' - 'this application.', - 'FT_InvalidParameter': 'An invalid parameter has been supplied to the ' - 'device.', - 'FT_DeviceNotPresent': 'The Device is no longer present. The device ' - 'may have been disconnected since the last ' - 'TLI_BuildDeviceList() call.', - 'FT_IncorrectDevice': 'The device detected does not match that ' - 'expected.', - 'FT_NoDLLLoaded': 'The library for this device could not be found.', - 'FT_NoFunctionsAvailable': 'No functions available for this device.', - 'FT_FunctionNotAvailable': 'The function is not available for this ' - 'device.', - 'FT_BadFunctionPointer': 'Bad function pointer detected.', - 'FT_GenericFunctionFail': 'The function failed to complete ' - 'succesfully.', - 'FT_SpecificFunctionFail': 'The function failed to complete ' - 'succesfully', - 'TL_ALREADY_OPEN': 'Attempt to open a device that was already open.', - 'TL_NO_RESPONSE': 'The device has stopped responding.', - 'TL_NOT_IMPLEMENTED': 'This function has not been implemented.', - 'TL_FAULT_REPORTED': 'The device has reported a fault.', - 'TL_INVALID_OPERATION': 'The function could not be completed at this ' - 'time.', - 'TL_DISCONNECTING': 'The function could not be completed because the ' - 'device is disconnected.', - 'TL_FIRMWARE_BUG': 'The firmware has thrown an error.', - 'TL_INITIALIZATION_FAILURE': 'The device has failed to initialize.', - 'TL_INVALID_CHANNEL': 'An Invalid channel address was supplied.', - 'TL_UNHOMED': 'The device cannot perform this function until it has ' - 'been Homed.', - 'TL_INVALID_POSITION': 'The function cannot be performed as it would ' - 'result in an illegal position.', - 'TL_INVALID_VELOCITY_PARAMETER': 'An invalid velocity parameter was ' - 'supplied. The velocity must be ' - 'greater than zero.', - 'TL_CANNOT_HOME_DEVICE': 'This device does not support Homing. Check ' - 'the Limit switch parameters are correct.', - 'TL_JOG_CONTINOUS_MODE': 'An invalid jog mode was supplied for the ' - 'jog function.', - 'TL_NO_MOTOR_INFO': 'There is no Motor Parameters available to ' - 'convert Real World Units.', - 'TL_CMD_TEMP_UNAVAILABLE': 'Command temporarily unavailable, Device ' - 'may be busy.' - } def __init__(self, lib: str, prefix: str, dll_dir: str | os.PathLike | None = None): - self.dll_dir = os.add_dll_directory(dll_dir or _DLL_DIR) + self.dll_dir = os.add_dll_directory(dll_dir or DLL_DIR) if not lib.startswith("Thorlabs.MotionControl"): lib = "Thorlabs.MotionControl." + lib @@ -125,7 +148,7 @@ def __init__(self, lib: str, prefix: str, raise FileNotFoundError(f'Did not find DLL {dll}') self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary(str(lib)) - self.error_check(self.lib.TLI_BuildDeviceList()) + self.build_device_list() self.prefix = prefix self.serialNo = ctypes.c_char_p() @@ -133,57 +156,56 @@ def __init__(self, lib: str, prefix: str, def __del__(self): self.dll_dir.close() - @classmethod - def error_check(cls, code: int): - if (status := cls._ERROR_CODES.get(code)) != 'FT_OK': - raise KinesisError(f'{status}: {cls._ERROR_MESSAGES[status]}') - @staticmethod def parse_fw_version(fw: int) -> str: parts = [f'{i:02d}' for i in fw.to_bytes(length=4, byteorder='big')] return '.'.join(parts).lstrip('0.') + def get_function(self, name: str): + return partial(getattr(self.lib, f'{self.prefix}_{name}'), + self.serialNo) + + @error_check + def build_device_list(self): + return self.lib.TLI_BuildDeviceList() + + @success_check + def load_settings(self): + return self.get_function('LoadSettings')() + + @error_check def request_status(self): - self.error_check( - getattr(self.lib, f'{self.prefix}_RequestStatus')(self.serialNo) - ) + return self.get_function('RequestStatus')() def get_position(self) -> int | float | str: self.request_status() time.sleep(self.get_polling_duration() * 1e-3) - return getattr(self.lib, f'{self.prefix}_GetPosition')(self.serialNo) + return self.get_function('GetPosition')() + @error_check def set_position(self, val: int | str): - self.error_check(getattr(self.lib, f'{self.prefix}_MoveToPosition')( - self.serialNo, val - )) + return self.get_function('MoveToPosition')(val) + @success_check def start_polling(self, duration: int): - success = getattr(self.lib, f'{self.prefix}_StartPolling')( - self.serialNo, duration - ) - if not success: - raise KinesisError('Failed') + return self.get_function('StartPolling')(duration) def stop_polling(self): - getattr(self.lib, f'{self.prefix}_StopPolling')(self.serialNo) + return self.get_function('StopPolling')() def get_polling_duration(self) -> int: - return getattr(self.lib, f'{self.prefix}_PollingDuration')( - self.serialNo - ) + return self.get_function('PollingDuration')() def set_polling_duration(self, duration: int): self.stop_polling() self.start_polling(duration) + @error_check def connect(self, polling_duration: int = 100): - self.error_check( - getattr(self.lib, f'{self.prefix}_Open')(self.serialNo) - ) + return self.get_function('Open')() def disconnect(self): - getattr(self.lib, f'{self.prefix}_Close')(self.serialNo) + self.get_function('Close')() def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: modelNo = ctypes.create_string_buffer(64) @@ -193,17 +215,14 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: firmwareVersion = ctypes.wintypes.DWORD() hardwareVersion = ctypes.wintypes.WORD() modificationState = ctypes.wintypes.WORD() - self.error_check( - getattr(self.lib, f'{self.prefix}_GetHardwareInfo')( - self.serialNo, - modelNo, 64, - ctypes.byref(type), - ctypes.byref(numChannels), - notes, 64, - ctypes.byref(firmwareVersion), - ctypes.byref(hardwareVersion), - ctypes.byref(modificationState) - ) + error_check(self.get_function('GetHardwareInfo'))( + modelNo, 64, + ctypes.byref(type), + ctypes.byref(numChannels), + notes, 64, + ctypes.byref(firmwareVersion), + ctypes.byref(hardwareVersion), + ctypes.byref(modificationState) ) return (modelNo.value.decode('utf-8'), type.value, @@ -213,6 +232,45 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: hardwareVersion.value, modificationState.value) + def device_unit_from_real_value( + self, + real_unit: float, + unit_type: enums.ISCUnitType + ) -> ctypes.c_int: + if isinstance(unit_type, int): + unit_type = enums.ISCUnitType(unit_type) + elif isinstance(unit_type, str): + unit_type = getattr(enums.ISCUnitType, unit_type) + elif not isinstance(unit_type, enums.ISCUnitType): + raise TypeError('unit_type should be int, str, or ISCUnitType, ' + f'not {type(unit_type)}') + + device_unit = ctypes.c_int() + success_check(self.get_function('GetDeviceUnitFromRealValue'))( + ctypes.c_double(real_unit), + ctypes.byref(device_unit), + unit_type.value, + ) + return device_unit + + def real_value_from_device_unit(self, device_unit: ctypes.c_int, + unit_type: enums.ISCUnitType) -> float: + if isinstance(unit_type, int): + unit_type = enums.ISCUnitType(unit_type) + elif isinstance(unit_type, str): + unit_type = getattr(enums.ISCUnitType, unit_type) + elif not isinstance(unit_type, enums.ISCUnitType): + raise TypeError('unit_type should be int, str, or ISCUnitType, ' + f'not {type(unit_type)}') + + real_unit = ctypes.c_double() + error_check(self.get_function('GetRealValueFromDeviceUnit'))( + device_unit, + ctypes.byref(real_unit), + unit_type.value + ) + return real_unit.value + class KinesisInstrument(Instrument, abc.ABC): """Qcodes Instrument subclass for Kinesis instruments. @@ -247,7 +305,6 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, self.kinesis = self._init_kinesis(dll_dir) self._initialized: bool = False - self.kinesis.serialNo.value = serial super().__init__(name, metadata, label) @@ -256,7 +313,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, set_cmd=self.kinesis.set_polling_duration, unit='ms') - self.connect(self.serial) + self.connect(serial) def _init_kinesis(self, dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: @@ -345,7 +402,7 @@ def list_available_devices( if not isinstance(lib, ctypes.CDLL): # Open base directory if lib is None: - lib = _DLL_DIR + lib = DLL_DIR lib = pathlib.Path(lib) if lib.is_file(): lib = lib.parent @@ -355,7 +412,7 @@ def list_available_devices( 'Thorlabs.MotionControl.DeviceManager.dll' ))) - ThorlabsKinesis.error_check(lib.TLI_BuildDeviceList()) + error_check(lib.TLI_BuildDeviceList()) n: int = lib.TLI_GetDeviceListSize() devices = [] @@ -375,11 +432,11 @@ def list_available_devices( # surplus needed apparently serialNo = (ctypes.c_char * (9 + 1))() - ThorlabsKinesis.error_check(lib.TLI_GetDeviceListByTypeExt( + error_check(lib.TLI_GetDeviceListByTypeExt)( ctypes.byref(serialNo), ctypes.wintypes.DWORD(9 + 1), hw_type_id - )) + ) if serialNo.value: devices.append((enums.KinesisHWType(hw_type_id), int(serialNo.value.rstrip(b',')))) if len(devices) == n: From de816746e8c7de5b61109b558f5652b9ed5cda3a Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 22 Sep 2023 19:48:58 +0200 Subject: [PATCH 18/61] Add position parameter to K10CR1 --- .../Thorlabs/Thorlabs_K10CR1/kinesis.py | 35 +++++++++++++++---- .../drivers/Thorlabs/private/kinesis/isc.py | 28 +++++++++++---- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py index 1fc4df2b8..06287bcbc 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -1,12 +1,20 @@ +from __future__ import annotations + +import pathlib +from functools import partial +from typing import Mapping, Any + +from qcodes import validators as vals +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.enums import ( KinesisHWType ) from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.isc import ( - KinesisIntegratedStepperMotor + KinesisISCIntrument ) -class ThorlabsK10CR1(KinesisIntegratedStepperMotor): +class ThorlabsK10CR1(KinesisISCIntrument): """Kinesis driver for Thorlabs K10CR1 cage rotator. Args: @@ -26,10 +34,25 @@ class ThorlabsK10CR1(KinesisIntegratedStepperMotor): Nicely formatted name of the instrument. """ - @classmethod - @property - def _prefix(self): - return 'ISC' + + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + serial: int | None = None, + metadata: Mapping[Any, Any] | None = None, + label: str | None = None): + super().__init__(name, dll_dir, serial, metadata, label) + + self.add_parameter( + "position", + get_cmd=self.kinesis.get_position, + set_cmd=self.kinesis.set_position, + get_parser=partial(self.kinesis.real_value_from_device_unit, + unit_type=enums.ISCUnitType.Distance), + set_parser=partial(self.kinesis.device_unit_from_real_value, + unit_type=enums.ISCUnitType.Distance), + vals=vals.Numbers(0, 360), + unit=u"\u00b0", + label="Position" + ) @classmethod @property diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 3d700d0cf..fbf5162f7 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -1,19 +1,33 @@ from __future__ import annotations -import ctypes import pathlib +from abc import ABC +from typing import Mapping, Any -from . import enums -from .core import KinesisInstrument, ThorlabsKinesis, KinesisError +from .core import KinesisInstrument, ThorlabsKinesis -class KinesisIntegratedStepperMotor(KinesisInstrument): +class KinesisISCIntrument(KinesisInstrument, ABC): """Devices which are controlled from the IntegratedStepperMotor dll. """ - def _init_kinesis(self, dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: - return ThorlabsKinesis('Thorlabs.MotionControl.IntegratedStepperMotors.dll', self._prefix, - dll_dir) + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + serial: int | None = None, metadata: Mapping[Any, Any] | None = None, + label: str | None = None): + super().__init__(name, dll_dir, serial, metadata, label) + + # Update the device with stored settings. This is necessary to be able + # to convert units since there are specific formulae for each motor + # taking into account Gearing, Pitch, Steps Per Revolution etc. + self.kinesis.load_settings() + + def _init_kinesis(self, + dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: + return ThorlabsKinesis( + 'Thorlabs.MotionControl.IntegratedStepperMotors.dll', + self._prefix, + dll_dir + ) @classmethod @property From e523b3173a0ebc18d181808f52216e7803e79801 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 26 Sep 2023 17:16:44 +0200 Subject: [PATCH 19/61] Fix success definition --- .../drivers/Thorlabs/private/kinesis/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index f7dee7fb5..27cd585cd 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -115,10 +115,10 @@ def success_check(func): """Wraps functions that return a boolean success code. - 0 means success, 1 means failure.""" + 1 means success, 0 means failure.""" @wraps(func) def wrapped(*args, **kwargs): - if func(*args, **kwargs): + if not func(*args, **kwargs): raise KinesisError('Unspecified failure.') return wrapped From 6898fe86d4f90671065e6a71ad420d8732be6677 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 26 Sep 2023 17:23:34 +0200 Subject: [PATCH 20/61] Add simulation. Doesn't work for a K10CR1 on my end (yet). The LoadSettings function returns an unspecified error and so do setting/getting the position. --- .../Thorlabs/Thorlabs_K10CR1/kinesis.py | 9 ++++- .../Thorlabs/Thorlabs_MFF10x/kinesis.py | 9 ++++- .../drivers/Thorlabs/private/kinesis/core.py | 39 +++++++++++++++---- .../drivers/Thorlabs/private/kinesis/isc.py | 15 ++++--- 4 files changed, 55 insertions(+), 17 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py index 06287bcbc..20a964370 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -28,6 +28,11 @@ class ThorlabsK10CR1(KinesisISCIntrument): of all available devices, use :meth:`list_available_devices` on an existing instance or :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. + simulation (optional): + Enable the Kinesis simulator mode. Note that the serial + number assigned to the simulated device should be given + since otherwise the first available device will be + connected (which might not be a simulated but a real one). metadata (optional): Additional static metadata. label (optional): @@ -36,10 +41,10 @@ class ThorlabsK10CR1(KinesisISCIntrument): """ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, - serial: int | None = None, + serial: int | None = None, simulation: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, serial, metadata, label) + super().__init__(name, dll_dir, serial, simulation, metadata, label) self.add_parameter( "position", diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index 670544c13..48fcea526 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -23,6 +23,11 @@ class ThorlabsMFF10x(KinesisInstrument): of all available devices, use :meth:`list_available_devices` on an existing instance or :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. + simulation (optional): + Enable the Kinesis simulator mode. Note that the serial + number assigned to the simulated device should be given + since otherwise the first available device will be + connected (which might not be a simulated but a real one). position_mapping (optional): A val mapping for a more human-readable position mapping than the internally used 1 and 2. Defaults to @@ -35,11 +40,11 @@ class ThorlabsMFF10x(KinesisInstrument): """ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, - serial: int | None = None, + serial: int | None = None, simulation: bool = False, position_mapping: Mapping[str, Literal[1, 2]] | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, serial, metadata, label) + super().__init__(name, dll_dir, serial, simulation, metadata, label) position_mapping = position_mapping or {'open': 1, 'close': 2} self.add_parameter('position', diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 27cd585cd..03d68a56c 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -136,8 +136,13 @@ def wrapped(*args, **kwargs): class ThorlabsKinesis: def __init__(self, lib: str, prefix: str, - dll_dir: str | os.PathLike | None = None): + dll_dir: str | os.PathLike | None = None, + simulation: bool = False): + + self.prefix = prefix self.dll_dir = os.add_dll_directory(dll_dir or DLL_DIR) + self.serialNo = ctypes.c_char_p() + self.simulation = simulation if not lib.startswith("Thorlabs.MotionControl"): lib = "Thorlabs.MotionControl." + lib @@ -148,10 +153,10 @@ def __init__(self, lib: str, prefix: str, raise FileNotFoundError(f'Did not find DLL {dll}') self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary(str(lib)) - self.build_device_list() + if simulation: + self.enable_simulation() - self.prefix = prefix - self.serialNo = ctypes.c_char_p() + self.build_device_list() def __del__(self): self.dll_dir.close() @@ -165,6 +170,16 @@ def get_function(self, name: str): return partial(getattr(self.lib, f'{self.prefix}_{name}'), self.serialNo) + def enable_simulation(self) -> None: + """Initialise a connection to the simulation manager, which must + already be running.""" + self.lib.TLI_InitializeSimulations() + + def disable_simulation(self) -> None: + """Uninitialize a connection to the simulation manager, which + must be running.""" + self.lib.TLI_UninitializeSimulations() + @error_check def build_device_list(self): return self.lib.TLI_BuildDeviceList() @@ -286,6 +301,11 @@ class KinesisInstrument(Instrument, abc.ABC): of all available devices, use :meth:`list_available_devices` on an existing instance or :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. + simulation (optional): + Enable the Kinesis simulator mode. Note that the serial + number assigned to the simulated device should be given + since otherwise the first available device will be + connected (which might not be a simulated but a real one). metadata (optional): Additional static metadata. label (optional): @@ -294,15 +314,15 @@ class KinesisInstrument(Instrument, abc.ABC): """ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, - serial: int | None = None, + serial: int | None = None, simulation: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): try: self.kinesis = ThorlabsKinesis(self.hardware_type.name, - self._prefix, dll_dir) + self._prefix, dll_dir, simulation) except FileNotFoundError: # Subclass needs to handle irregular dll name - self.kinesis = self._init_kinesis(dll_dir) + self.kinesis = self._init_kinesis(dll_dir, simulation) self._initialized: bool = False @@ -316,7 +336,8 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, self.connect(serial) def _init_kinesis(self, - dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: + dll_dir: str | pathlib.Path | None, + simulation: bool) -> ThorlabsKinesis: raise NotImplementedError(f'The subclass {type(self)} should override ' 'the _init_kinesis() method for irregular ' 'dll name.') @@ -375,6 +396,8 @@ def connect(self, serial: int | None, polling_duration: int = 100): self.connect_message(begin_time=begin_time) def disconnect(self): + if self.kinesis.simulation: + self.kinesis.disable_simulation() if self.connected: self.kinesis.stop_polling() self.kinesis.disconnect() diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index fbf5162f7..8b49d9b04 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -12,21 +12,26 @@ class KinesisISCIntrument(KinesisInstrument, ABC): """ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, - serial: int | None = None, metadata: Mapping[Any, Any] | None = None, + serial: int | None = None, simulation: bool = False, + metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, serial, metadata, label) + super().__init__(name, dll_dir, serial, simulation, metadata, label) # Update the device with stored settings. This is necessary to be able # to convert units since there are specific formulae for each motor # taking into account Gearing, Pitch, Steps Per Revolution etc. self.kinesis.load_settings() - def _init_kinesis(self, - dll_dir: str | pathlib.Path | None) -> ThorlabsKinesis: + def _init_kinesis( + self, + dll_dir: str | pathlib.Path | None, + simulation: bool + ) -> ThorlabsKinesis: return ThorlabsKinesis( 'Thorlabs.MotionControl.IntegratedStepperMotors.dll', self._prefix, - dll_dir + dll_dir, + simulation ) @classmethod From f2daeb5d9a5c72bc969de9a8b2a5731e47aa2c4c Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 26 Sep 2023 17:24:14 +0200 Subject: [PATCH 21/61] Fix list_available_devices() for n!=1 devices --- .../drivers/Thorlabs/private/kinesis/core.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 03d68a56c..afc4670ec 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -438,6 +438,9 @@ def list_available_devices( error_check(lib.TLI_BuildDeviceList()) n: int = lib.TLI_GetDeviceListSize() + if not n: + return [] + devices = [] hw_type_list = [] if hardware_type is not None: @@ -452,16 +455,18 @@ def list_available_devices( for hw_type_id in hw_type_list: # char array, 8 bytes for serial number, 1 for delimiter, plus 1 - # surplus needed apparently - serialNo = (ctypes.c_char * (9 + 1))() + # surplus needed, apparently. Since the function returns all serials + # of a given hardware type, the char buffer needs to be large enough + # to accomodate the worst case (all devices are of this hardware type) + serialNo = (ctypes.c_char * (8 * n + 1 + 1))() error_check(lib.TLI_GetDeviceListByTypeExt)( ctypes.byref(serialNo), - ctypes.wintypes.DWORD(9 + 1), + ctypes.wintypes.DWORD(8 * n + 1 + 1), hw_type_id ) if serialNo.value: - devices.append((enums.KinesisHWType(hw_type_id), int(serialNo.value.rstrip(b',')))) + devices.append((enums.KinesisHWType(hw_type_id), int(serialNo.value.split(b',')[0]))) if len(devices) == n: # Found all devices already break From d53c9fea13fff258f5534e4bfecebab4a4587bc2 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 26 Sep 2023 17:26:28 +0200 Subject: [PATCH 22/61] Move error and success checks into get_function() --- .../drivers/Thorlabs/private/kinesis/core.py | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index afc4670ec..e6b729552 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -166,9 +166,24 @@ def parse_fw_version(fw: int) -> str: parts = [f'{i:02d}' for i in fw.to_bytes(length=4, byteorder='big')] return '.'.join(parts).lstrip('0.') - def get_function(self, name: str): - return partial(getattr(self.lib, f'{self.prefix}_{name}'), - self.serialNo) + def get_function(self, name: str, check_errors: bool = False, + check_success: bool = False) -> callable: + """Convenience method for getting a function from the dll. + + If check_errors or check_success is True, the return value of + the function will be checked for the respective codes. + """ + try: + func = partial(getattr(self.lib, f'{self.prefix}_{name}'), + self.serialNo) + except AttributeError as err: + raise AttributeError(f'Function {self.prefix}_{name} not found in ' + f'dll {self.lib}') from err + if check_errors: + func = error_check(func) + if check_success: + func = success_check(func) + return func def enable_simulation(self) -> None: """Initialise a connection to the simulation manager, which must @@ -184,26 +199,22 @@ def disable_simulation(self) -> None: def build_device_list(self): return self.lib.TLI_BuildDeviceList() - @success_check def load_settings(self): - return self.get_function('LoadSettings')() + return self.get_function('LoadSettings', check_success=True)() - @error_check def request_status(self): - return self.get_function('RequestStatus')() + return self.get_function('RequestStatus', check_errors=True)() def get_position(self) -> int | float | str: self.request_status() time.sleep(self.get_polling_duration() * 1e-3) return self.get_function('GetPosition')() - @error_check def set_position(self, val: int | str): - return self.get_function('MoveToPosition')(val) + return self.get_function('MoveToPosition', check_errors=True)(val) - @success_check def start_polling(self, duration: int): - return self.get_function('StartPolling')(duration) + return self.get_function('StartPolling', check_success=True)(duration) def stop_polling(self): return self.get_function('StopPolling')() @@ -215,9 +226,8 @@ def set_polling_duration(self, duration: int): self.stop_polling() self.start_polling(duration) - @error_check def connect(self, polling_duration: int = 100): - return self.get_function('Open')() + return self.get_function('Open', check_errors=True)() def disconnect(self): self.get_function('Close')() @@ -230,7 +240,7 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: firmwareVersion = ctypes.wintypes.DWORD() hardwareVersion = ctypes.wintypes.WORD() modificationState = ctypes.wintypes.WORD() - error_check(self.get_function('GetHardwareInfo'))( + self.get_function('GetHardwareInfo', check_errors=True)( modelNo, 64, ctypes.byref(type), ctypes.byref(numChannels), @@ -261,7 +271,8 @@ def device_unit_from_real_value( f'not {type(unit_type)}') device_unit = ctypes.c_int() - success_check(self.get_function('GetDeviceUnitFromRealValue'))( + # Documentation says success is returned, but actually the error code + self.get_function('GetDeviceUnitFromRealValue', check_errors=True)( ctypes.c_double(real_unit), ctypes.byref(device_unit), unit_type.value, From 810221d0126e6613bd7f398cd574268e84285e68 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 26 Sep 2023 17:27:52 +0200 Subject: [PATCH 23/61] Rename functions to mirror DLL names --- .../drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py | 2 +- .../drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py | 2 +- .../drivers/Thorlabs/private/kinesis/core.py | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py index 20a964370..b922ed027 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -49,7 +49,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, self.add_parameter( "position", get_cmd=self.kinesis.get_position, - set_cmd=self.kinesis.set_position, + set_cmd=self.kinesis.move_to_position, get_parser=partial(self.kinesis.real_value_from_device_unit, unit_type=enums.ISCUnitType.Distance), set_parser=partial(self.kinesis.device_unit_from_real_value, diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index 48fcea526..8f30e36a2 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -49,7 +49,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, position_mapping = position_mapping or {'open': 1, 'close': 2} self.add_parameter('position', get_cmd=self.kinesis.get_position, - set_cmd=self.kinesis.set_position, + set_cmd=self.kinesis.move_to_position, val_mapping=position_mapping, label='Position') diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index e6b729552..9d11b0ebd 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -210,7 +210,7 @@ def get_position(self) -> int | float | str: time.sleep(self.get_polling_duration() * 1e-3) return self.get_function('GetPosition')() - def set_position(self, val: int | str): + def move_to_position(self, val: int | str): return self.get_function('MoveToPosition', check_errors=True)(val) def start_polling(self, duration: int): @@ -226,10 +226,10 @@ def set_polling_duration(self, duration: int): self.stop_polling() self.start_polling(duration) - def connect(self, polling_duration: int = 100): + def open(self): return self.get_function('Open', check_errors=True)() - def disconnect(self): + def close(self): self.get_function('Close')() def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: @@ -402,7 +402,7 @@ def connect(self, serial: int | None, polling_duration: int = 100): self.disconnect() self.kinesis.serialNo.value = str(serial).encode() - self.kinesis.connect() + self.kinesis.open() self.kinesis.start_polling(polling_duration) self.connect_message(begin_time=begin_time) @@ -411,7 +411,7 @@ def disconnect(self): self.kinesis.disable_simulation() if self.connected: self.kinesis.stop_polling() - self.kinesis.disconnect() + self.kinesis.close() self.kinesis.serialNo.value = None def get_idn(self) -> dict[str, str]: From ace54ef1e3bd856d678b025ff297a7d18fa4a59f Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 26 Sep 2023 17:28:17 +0200 Subject: [PATCH 24/61] Add get/set_motor_params_ext and documentation --- .../drivers/Thorlabs/private/kinesis/core.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 9d11b0ebd..bae24bd59 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -213,6 +213,25 @@ def get_position(self) -> int | float | str: def move_to_position(self, val: int | str): return self.get_function('MoveToPosition', check_errors=True)(val) + def get_motor_params_ext(self) -> Tuple[float, float, float]: + stepsPerRev = ctypes.c_double() + gearBoxRatio = ctypes.c_double() + pitch = ctypes.c_double() + self.get_function('GetMotorParamsExt', check_errors=True)( + ctypes.byref(stepsPerRev), + ctypes.byref(gearBoxRatio), + ctypes.byref(pitch) + ) + return stepsPerRev.value, gearBoxRatio.value, pitch.value + + def set_motor_params_ext(self, steps_per_rev: float, gearbox_ratio: float, + pitch: float): + self.get_function('SetMotorParamsExt', check_errors=True)( + ctypes.c_double(steps_per_rev), + ctypes.c_double(gearbox_ratio), + ctypes.c_double(pitch) + ) + def start_polling(self, duration: int): return self.get_function('StartPolling', check_success=True)(duration) @@ -262,6 +281,11 @@ def device_unit_from_real_value( real_unit: float, unit_type: enums.ISCUnitType ) -> ctypes.c_int: + """Convert real values to device units. + + In order to do this, the device settings must be loaded using + :meth:`load_settings` + """ if isinstance(unit_type, int): unit_type = enums.ISCUnitType(unit_type) elif isinstance(unit_type, str): @@ -281,6 +305,11 @@ def device_unit_from_real_value( def real_value_from_device_unit(self, device_unit: ctypes.c_int, unit_type: enums.ISCUnitType) -> float: + """Convert device units to real values. + + In order to do this, the device settings must be loaded using + :meth:`load_settings` + """ if isinstance(unit_type, int): unit_type = enums.ISCUnitType(unit_type) elif isinstance(unit_type, str): From 4bf761b6b7c98bcf61eedc80d41e10f11aa991ce Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 19 Oct 2023 10:25:41 +0200 Subject: [PATCH 25/61] Make hardware_type and _prefix classmethods Stacking classmethod and property is deprecated in CPython --- .../drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py | 1 - .../drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py | 4 +--- .../drivers/Thorlabs/private/kinesis/core.py | 8 +++----- .../drivers/Thorlabs/private/kinesis/isc.py | 3 +-- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py index b922ed027..f4368d024 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -60,6 +60,5 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, ) @classmethod - @property def hardware_type(cls) -> KinesisHWType: return KinesisHWType.CageRotator diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index 8f30e36a2..29878b3dc 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -54,12 +54,10 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, label='Position') @classmethod - @property - def _prefix(cls): + def _prefix(cls) -> str: return 'FF' @classmethod - @property def hardware_type(cls) -> enums.KinesisHWType: return enums.KinesisHWType.FilterFlipper diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index bae24bd59..e35253146 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -358,8 +358,8 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): try: - self.kinesis = ThorlabsKinesis(self.hardware_type.name, - self._prefix, dll_dir, simulation) + self.kinesis = ThorlabsKinesis(self.hardware_type().name, + self._prefix(), dll_dir, simulation) except FileNotFoundError: # Subclass needs to handle irregular dll name self.kinesis = self._init_kinesis(dll_dir, simulation) @@ -383,13 +383,11 @@ def _init_kinesis(self, 'dll name.') @classmethod - @property @abc.abstractmethod def _prefix(cls) -> str: pass @classmethod - @property @abc.abstractmethod def hardware_type(cls) -> enums.KinesisHWType: pass @@ -410,7 +408,7 @@ def list_available_devices(self) -> List[int]: try: return [ serial for _, serial in - list_available_devices(self.kinesis.lib, self.hardware_type) + list_available_devices(self.kinesis.lib, self.hardware_type()) ] except KinesisError: self._initialized = False diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 8b49d9b04..52d929979 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -29,12 +29,11 @@ def _init_kinesis( ) -> ThorlabsKinesis: return ThorlabsKinesis( 'Thorlabs.MotionControl.IntegratedStepperMotors.dll', - self._prefix, + self._prefix(), dll_dir, simulation ) @classmethod - @property def _prefix(cls) -> str: return 'ISC' From 31eccc657dffd5632e510e8b8df8f3ade01e2e55 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 19 Oct 2023 11:00:18 +0200 Subject: [PATCH 26/61] Fix MyPy errors --- pyproject.toml | 3 +- .../drivers/Thorlabs/Thorlabs_K10CR1/apt.py | 9 ++- .../Thorlabs/Thorlabs_K10CR1/kinesis.py | 1 + .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 14 +--- .../drivers/Thorlabs/private/kinesis/core.py | 77 ++++++++++--------- .../Thorlabs/private/kinesis/structs.py | 3 +- 6 files changed, 56 insertions(+), 51 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4b6cc8137..6361cbe64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,8 @@ module = [ "pyspcm", "spirack", "zhinst.*", - "ruamel.*" + "ruamel.*", + "TLPM" ] ignore_missing_imports = true diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py index 8791a88cd..7e911ea26 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import enum from typing import Tuple import warnings @@ -170,7 +172,8 @@ def _get_velocity_parameters(self) -> Tuple[float, float, float]: return self.apt.mot_get_velocity_parameters(self.serial_number) def _set_velocity_parameters(self, - min_vel: float = None, accn: float = None, max_vel: float = None): + min_vel: float | None = None, accn: float | None = None, + max_vel: float | None = None): if min_vel is None or accn is None or max_vel is None: old_min_vel, old_accn, old_max_vel = self._get_velocity_parameters() if min_vel is None: @@ -205,8 +208,8 @@ def _set_velocity_max(self, max_vel: float): def _get_home_parameters(self) -> Tuple[int, int, float, float]: return self.apt.mot_get_home_parameters(self.serial_number) - def _set_home_parameters(self, direction: int = None, lim_switch: int = None, - velocity: float = None, zero_offset:float = None): + def _set_home_parameters(self, direction: int | None = None, lim_switch: int | None = None, + velocity: float | None = None, zero_offset: float | None = None): if direction is None or lim_switch is None or velocity is None or zero_offset is None: old_direction, old_lim_switch, old_velocity, old_zero_offset = self._get_home_parameters() if direction is None: diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py index f4368d024..ac0ff11df 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -5,6 +5,7 @@ from typing import Mapping, Any from qcodes import validators as vals + from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.enums import ( KinesisHWType diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index a2b1d6f97..9f25ed0e7 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -9,17 +9,11 @@ from qcodes import Instrument -_bitness = platform.architecture()[0][:2] -try: - vxi_32 = os.environ['VXIPNPPATH'] - vxi_64 = os.environ['VXIPNPPATH64'] -except KeyError: - vxi_32 = None - vxi_64 = None - raise RuntimeError('IVI VXIPNP path not detected.') +vxi_32 = os.environ.get('VXIPNPPATH') +vxi_64 = os.environ.get('VXIPNPPATH64') if vxi_32 is not None: - if _bitness == '64': + if platform.architecture()[0][:2] == '64' and vxi_64 is not None: dll_path = pathlib.Path(vxi_64, 'Win64', 'Bin', 'TLPM_64.dll') else: dll_path = pathlib.Path(vxi_32, 'WinNT', 'Bin', 'TLPM_32.dll') @@ -113,7 +107,7 @@ def _get_wavelength(self) -> float: def _set_wavelength(self, wavelength: float): self.tlpm.setWavelength(ctypes.c_double(wavelength)) - def get_idn(self) -> Dict[str, str]: + def get_idn(self) -> Dict[str, str | None]: manufacturerName = ctypes.create_string_buffer(1024) deviceName = ctypes.create_string_buffer(1024) serialNumber = ctypes.create_string_buffer(1024) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index e35253146..6a8b0e6de 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -1,19 +1,27 @@ from __future__ import annotations import abc -import ctypes import os import pathlib import time import warnings from functools import wraps, partial -from typing import Mapping, Any, List, Tuple, Iterable - -import numpy as np +from typing import Mapping, Any, List, Tuple, Iterable, Callable from qcodes import Instrument + from . import enums +try: + import ctypes.wintypes +except ImportError: + import ctypes + from types import ModuleType + + ctypes.wintypes = ModuleType('wintypes') + ctypes.wintypes.WORD = ctypes.c_ushort + ctypes.wintypes.DWORD = ctypes.c_ulong + DLL_DIR = r"C:\Program Files\Thorlabs\Kinesis" ERROR_CODES = { @@ -140,7 +148,7 @@ def __init__(self, lib: str, prefix: str, simulation: bool = False): self.prefix = prefix - self.dll_dir = os.add_dll_directory(dll_dir or DLL_DIR) + self.dll_dir = pathlib.Path(dll_dir or DLL_DIR) self.serialNo = ctypes.c_char_p() self.simulation = simulation @@ -148,26 +156,22 @@ def __init__(self, lib: str, prefix: str, lib = "Thorlabs.MotionControl." + lib if not lib.endswith(".dll"): lib = lib + ".dll" - lib = pathlib.Path(lib) - if not (dll := (self.dll_dir.path / lib)).exists(): + if not (dll := self.dll_dir / lib).exists(): raise FileNotFoundError(f'Did not find DLL {dll}') - self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary(str(lib)) + self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary(str(dll)) if simulation: self.enable_simulation() self.build_device_list() - def __del__(self): - self.dll_dir.close() - @staticmethod def parse_fw_version(fw: int) -> str: parts = [f'{i:02d}' for i in fw.to_bytes(length=4, byteorder='big')] return '.'.join(parts).lstrip('0.') def get_function(self, name: str, check_errors: bool = False, - check_success: bool = False) -> callable: + check_success: bool = False) -> Callable: """Convenience method for getting a function from the dll. If check_errors or check_success is True, the return value of @@ -416,12 +420,16 @@ def list_available_devices(self) -> List[int]: def connect(self, serial: int | None, polling_duration: int = 100): begin_time = time.time() - if serial is None or not self._initialized: - available_devices = self.list_available_devices() + if serial is None: + available_devices = self.list_available_devices() if not len(available_devices): raise RuntimeError(f'No {self.prefix} devices found!') serial = available_devices[0] + + if not self._initialized: + error_check(self.kinesis.lib.TLI_BuildDeviceList()) + if self.connected: warnings.warn('Already connected to device with serial ' f'{self.serial}. Disconnecting.', @@ -441,11 +449,11 @@ def disconnect(self): self.kinesis.close() self.kinesis.serialNo.value = None - def get_idn(self) -> dict[str, str]: + def get_idn(self) -> dict[str, str | None]: model, type, num_channels, notes, firmware, hardware, state = \ self.kinesis.get_hw_info() return {'vendor': 'Thorlabs', 'model': model, 'firmware': firmware, - 'serial': self.serial} + 'serial': str(self.serial)} def close(self): self.disconnect() @@ -457,21 +465,20 @@ class KinesisError(Exception): def list_available_devices( - lib: str | pathlib.Path | ctypes.CDLL | None = None, + lib: str | os.PathLike | ctypes.CDLL | None = None, hardware_type: Iterable[enums.KinesisHWType] | enums.KinesisHWType | None = None ) -> List[Tuple[enums.KinesisHWType, int]]: if not isinstance(lib, ctypes.CDLL): - # Open base directory if lib is None: lib = DLL_DIR - lib = pathlib.Path(lib) - if lib.is_file(): - lib = lib.parent - lib = os.add_dll_directory(str(lib)) - lib = ctypes.cdll.LoadLibrary(str(pathlib.Path( - lib.path, - 'Thorlabs.MotionControl.DeviceManager.dll' - ))) + + if not isinstance(lib, pathlib.Path): + lib = pathlib.Path(lib) + + if lib.is_dir(): + lib /= 'Thorlabs.MotionControl.DeviceManager.dll' + + lib = ctypes.cdll.LoadLibrary(str(lib)) error_check(lib.TLI_BuildDeviceList()) n: int = lib.TLI_GetDeviceListSize() @@ -479,19 +486,17 @@ def list_available_devices( if not n: return [] - devices = [] - hw_type_list = [] - if hardware_type is not None: + if hardware_type is None: + # Search for all models + hw_type_ids = list(range(1, 101)) + elif isinstance(hardware_type, Iterable): # Only search for devices of the passed hardware type (model) - if not np.iterable(hardware_type): - hardware_type = [hardware_type] - for hw in hardware_type: - hw_type_list.append(hw.value) + hw_type_ids = [hw.value for hw in hardware_type] else: - # Search for all models - hw_type_list = list(range(1, 101)) + hw_type_ids = [hardware_type.value] - for hw_type_id in hw_type_list: + devices = [] + for hw_type_id in hw_type_ids: # char array, 8 bytes for serial number, 1 for delimiter, plus 1 # surplus needed, apparently. Since the function returns all serials # of a given hardware type, the char buffer needs to be large enough diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py index 3621abdb3..29d0ad219 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py @@ -1,4 +1,5 @@ import ctypes +import enum from . import enums @@ -8,7 +9,7 @@ class StructureWithEnums(ctypes.Structure): Taken from https://gist.github.com/christoph2/9c390e5c094796903097 """ - _map = {} + _map: dict[str, enum.EnumMeta] = {} def __getattribute__(self, name): _map = ctypes.Structure.__getattribute__(self, '_map') From 1f45898aec9424f301b7ca56207090fff43f1b30 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Fri, 20 Oct 2023 16:42:45 +0200 Subject: [PATCH 27/61] Make list_available_devices available at top-level namespace --- qcodes_contrib_drivers/drivers/Thorlabs/__init__.py | 1 + .../drivers/Thorlabs/private/kinesis/core.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py index e69de29bb..73a352c3c 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py @@ -0,0 +1 @@ +from .private.kinesis.core import list_available_devices diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 6a8b0e6de..817c6db80 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -468,6 +468,16 @@ def list_available_devices( lib: str | os.PathLike | ctypes.CDLL | None = None, hardware_type: Iterable[enums.KinesisHWType] | enums.KinesisHWType | None = None ) -> List[Tuple[enums.KinesisHWType, int]]: + """Discover and list available Kinesis devices. + + Args: + lib: Either the path to a Kinesis dll or a CDLL instance. + hardware_type: List only devices of a given type. + + Returns: + A list of two-tuples (hardware_type_id, serial_number). + + """ if not isinstance(lib, ctypes.CDLL): if lib is None: lib = DLL_DIR From 7cb04dd5512afa82a03f64ae238d731f039ad1a0 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 12:59:16 +0100 Subject: [PATCH 28/61] Refactor base class using __init_subclass__ ThorlabsKinesis methods can be marked to be forwarded to a KinesisInstrument subclass. This avoids having to replicate many duplicate methods of the various DLLs. --- .../Thorlabs/Thorlabs_K10CR1/kinesis.py | 65 ---- .../Thorlabs/Thorlabs_MFF10x/kinesis.py | 47 ++- .../Thorlabs/private/kinesis/__init__.py | 2 +- .../drivers/Thorlabs/private/kinesis/core.py | 357 ++++++++++++++---- .../drivers/Thorlabs/private/kinesis/enums.py | 14 + .../drivers/Thorlabs/private/kinesis/isc.py | 22 +- 6 files changed, 341 insertions(+), 166 deletions(-) delete mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py deleted file mode 100644 index ac0ff11df..000000000 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -import pathlib -from functools import partial -from typing import Mapping, Any - -from qcodes import validators as vals - -from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums -from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.enums import ( - KinesisHWType -) -from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.isc import ( - KinesisISCIntrument -) - - -class ThorlabsK10CR1(KinesisISCIntrument): - """Kinesis driver for Thorlabs K10CR1 cage rotator. - - Args: - name: - An identifier for this instrument. - dll_dir (optional): - The directory where the kinesis dlls reside. - serial (optional): - The serial number of the device to connect to. If omitted, - the first available device found will be used. For a list - of all available devices, use - :meth:`list_available_devices` on an existing instance or - :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. - simulation (optional): - Enable the Kinesis simulator mode. Note that the serial - number assigned to the simulated device should be given - since otherwise the first available device will be - connected (which might not be a simulated but a real one). - metadata (optional): - Additional static metadata. - label (optional): - Nicely formatted name of the instrument. - - """ - - def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, - serial: int | None = None, simulation: bool = False, - metadata: Mapping[Any, Any] | None = None, - label: str | None = None): - super().__init__(name, dll_dir, serial, simulation, metadata, label) - - self.add_parameter( - "position", - get_cmd=self.kinesis.get_position, - set_cmd=self.kinesis.move_to_position, - get_parser=partial(self.kinesis.real_value_from_device_unit, - unit_type=enums.ISCUnitType.Distance), - set_parser=partial(self.kinesis.device_unit_from_real_value, - unit_type=enums.ISCUnitType.Distance), - vals=vals.Numbers(0, 360), - unit=u"\u00b0", - label="Position" - ) - - @classmethod - def hardware_type(cls) -> KinesisHWType: - return KinesisHWType.CageRotator diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index 29878b3dc..35c386610 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -1,15 +1,17 @@ from __future__ import annotations import pathlib -from typing import Mapping, Any, Literal +from typing import Any, Literal, Mapping -from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import ( +from qcodes import Parameter +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core import ( KinesisInstrument ) -from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums -class ThorlabsMFF10x(KinesisInstrument): +class ThorlabsMFF10x(KinesisInstrument, prefix='FF', + hardware_type=enums.KinesisHWType.FilterFlipper): """Kinesis driver for Thorlabs MFF10x filter flipper. Args: @@ -22,7 +24,7 @@ class ThorlabsMFF10x(KinesisInstrument): the first available device found will be used. For a list of all available devices, use :meth:`list_available_devices` on an existing instance or - :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. + :func:`~qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. simulation (optional): Enable the Kinesis simulator mode. Note that the serial number assigned to the simulated device should be given @@ -39,27 +41,32 @@ class ThorlabsMFF10x(KinesisInstrument): """ - def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', serial: int | None = None, simulation: bool = False, position_mapping: Mapping[str, Literal[1, 2]] | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): super().__init__(name, dll_dir, serial, simulation, metadata, label) - position_mapping = position_mapping or {'open': 1, 'close': 2} - self.add_parameter('position', - get_cmd=self.kinesis.get_position, - set_cmd=self.kinesis.move_to_position, - val_mapping=position_mapping, - label='Position') - - @classmethod - def _prefix(cls) -> str: - return 'FF' - - @classmethod - def hardware_type(cls) -> enums.KinesisHWType: - return enums.KinesisHWType.FilterFlipper + self.position = Parameter( + 'position', + get_cmd=self._kinesis.get_position, + set_cmd=self._kinesis.move_to_position, + val_mapping=position_mapping or {'open': 1, 'close': 2}, + label='Position', + instrument=self + ) + """The position of the flipper.""" + self.transit_time = Parameter( + 'transit_time', + get_cmd=self._kinesis.get_transit_time, + set_cmd=self._kinesis.set_transit_time, + set_parser=int, + unit='ms', + label='Transit time', + instrument=self + ) + """The transit time between two positions.""" def toggle_position(self): """Toggle the position of the flipper.""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py index 24bcc1dfa..d58d61844 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py @@ -1 +1 @@ -from .core import KinesisInstrument, list_available_devices +from .core import list_available_devices diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 817c6db80..385843a94 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -1,16 +1,23 @@ +"""Core Kinesis functionality. + +New instruments should inherit from :class:`KinesisInstrument` or one of +the specialized sublasses (eg :class:`isc.KinesisISCInstrument`). See +their docstrings for instructions. +""" from __future__ import annotations -import abc import os import pathlib import time import warnings -from functools import wraps, partial -from typing import Mapping, Any, List, Tuple, Iterable, Callable +from functools import partial, wraps +from typing import Any, Callable, Dict, Iterable, List, Mapping, Tuple +from typing import Sequence, TypeVar -from qcodes import Instrument +from typing_extensions import ParamSpec -from . import enums +from qcodes import Instrument +from . import enums, structs try: import ctypes.wintypes @@ -119,48 +126,70 @@ 'may be busy.' } +P = ParamSpec('P') +T = TypeVar('T') + + +def register_prefix(prefixes: Sequence[str]) -> Callable: + """Registers a warpped DLL function for a given Kinesis device. + + prefixes is a sequence of string identifier prefixes that are + returned by a KinesisInstrument's :meth:`_prefix` classmethod. + """ + + def decorator(func: Callable[P, T]) -> Callable[P, T]: + func.__prefixes = prefixes + return func + + return decorator + def success_check(func): """Wraps functions that return a boolean success code. - 1 means success, 0 means failure.""" + 1 means success, 0 means failure. + """ + @wraps(func) def wrapped(*args, **kwargs): if not func(*args, **kwargs): raise KinesisError('Unspecified failure.') + return wrapped def error_check(func): """Wraps functions that return an integer error code.""" + @wraps(func) def wrapped(*args, **kwargs): code = func(*args, **kwargs) if (status := ERROR_CODES.get(code)) != 'FT_OK': raise KinesisError(f'{status}: {ERROR_MESSAGES[status]}') + return wrapped class ThorlabsKinesis: + """The interface to Kinesis dlls.""" def __init__(self, lib: str, prefix: str, - dll_dir: str | os.PathLike | None = None, + dll_dir: str | os.PathLike | None = '', simulation: bool = False): - self.prefix = prefix - self.dll_dir = pathlib.Path(dll_dir or DLL_DIR) - self.serialNo = ctypes.c_char_p() - self.simulation = simulation - if not lib.startswith("Thorlabs.MotionControl"): lib = "Thorlabs.MotionControl." + lib if not lib.endswith(".dll"): lib = lib + ".dll" - if not (dll := self.dll_dir / lib).exists(): - raise FileNotFoundError(f'Did not find DLL {dll}') - self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary(str(dll)) - if simulation: + self.prefix = prefix + self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary( + os.path.join(dll_dir if dll_dir is not None else DLL_DIR, lib) + ) + self.serialNo = ctypes.c_char_p() + self.simulation = simulation + + if self.simulation: self.enable_simulation() self.build_device_list() @@ -200,24 +229,129 @@ def disable_simulation(self) -> None: self.lib.TLI_UninitializeSimulations() @error_check - def build_device_list(self): + def build_device_list(self) -> None: + """Build the DeviceList. + + This function builds an internal collection of all devices found + on the USB that are not currently open. + + Note: + If a device is open, it will not appear in the list until + the device has been closed. + + """ return self.lib.TLI_BuildDeviceList() - def load_settings(self): - return self.get_function('LoadSettings', check_success=True)() + def load_settings(self) -> None: + """Update device with stored settings.""" + self.get_function('LoadSettings', check_success=True)() + + def request_status(self) -> None: + """Request position and status bits. - def request_status(self): - return self.get_function('RequestStatus', check_errors=True)() + This needs to be called to get the device to send it's current + status. + Note: + This is called automatically if Polling is enabled for the + device using ISC_StartPolling(char const * serialNo, int + milliseconds). + + """ + self.get_function('RequestStatus', check_errors=True)() + + @register_prefix(['FF', 'ISC']) + def identify(self) -> None: + """Sends a command to the device to make it identify iteself.""" + self.get_function('Identify')() + + @register_prefix(['FF', 'ISC']) + def get_number_positions(self) -> int: + """Get number of positions. + + The GetNumberPositions function will get the maximum position + reachable by the device. The motor may need to be Homed before + this parameter can be used. + """ + return int(self.get_function('GetNumberPositions')()) + + @register_prefix(['FF', 'ISC']) def get_position(self) -> int | float | str: + """Get the current position. + + The current position is the last recorded position. + The current position is updated either by the polling mechanism + or by calling RequestPosition or RequestStatus. + + Returns: + The current position. + """ self.request_status() time.sleep(self.get_polling_duration() * 1e-3) return self.get_function('GetPosition')() - def move_to_position(self, val: int | str): - return self.get_function('MoveToPosition', check_errors=True)(val) + @register_prefix(['FF', 'ISC']) + def move_to_position(self, position: int | str, + block: bool = False) -> None: + """Move the device to the specified position (index). + The motor may need to be Homed before a position can be set. See + Positioning for more detail. + + Args: + position: + The required position. must be 1 or 2 for the filter + flipper or in device units else. + block: + Block the interpreter until the target position is + reached. + + """ + self.get_function('MoveToPosition', check_errors=True)(position) + if block: + while self.get_position() != position: + time.sleep(50e-3) + + @register_prefix(['ISC']) + def move_at_velocity( + self, + direction: enums.TravelDirection | str | int + ) -> None: + """Start moving at the current velocity in the specified + direction.""" + if isinstance(direction, str): + direction = getattr(enums.TravelDirection, direction) + elif isinstance(direction, int): + direction = enums.TravelDirection(direction) + self.get_function('MoveAtVelocity', check_errors=True)(direction.value) + + @register_prefix(['FF']) + def get_transit_time(self) -> int: + """Gets the transit time. + + Returns: + The transit time in milliseconds, range 300 to 2800 ms. + """ + return self.get_function('GetTransitTime')() + + @register_prefix(['FF']) + def set_transit_time(self, transit_time: int) -> None: + """Sets the transit time. + + Args: + transit_time: The transit time in milliseconds, range 300 + to 2800 ms. + """ + self.get_function('SetTransitTime', check_errors=True)(transit_time) + + @register_prefix(['ISC']) def get_motor_params_ext(self) -> Tuple[float, float, float]: + """Gets the motor stage parameters. + + These parameters, when combined define the stage motion in terms + of Real World Units. (mm or degrees) The real world unit is + defined from stepsPerRev * gearBoxRatio / pitch. + """ stepsPerRev = ctypes.c_double() gearBoxRatio = ctypes.c_double() pitch = ctypes.c_double() @@ -228,33 +362,90 @@ def get_motor_params_ext(self) -> Tuple[float, float, float]: ) return stepsPerRev.value, gearBoxRatio.value, pitch.value + @register_prefix(['ISC']) def set_motor_params_ext(self, steps_per_rev: float, gearbox_ratio: float, - pitch: float): + pitch: float) -> None: + """Sets the motor stage parameters. + + These parameters, when combined define the stage motion in terms + of Real World Units. (mm or degrees) The real world unit is + defined from stepsPerRev * gearBoxRatio / pitch. + """ self.get_function('SetMotorParamsExt', check_errors=True)( ctypes.c_double(steps_per_rev), ctypes.c_double(gearbox_ratio), ctypes.c_double(pitch) ) - def start_polling(self, duration: int): - return self.get_function('StartPolling', check_success=True)(duration) + def start_polling(self, duration: int) -> None: + """Starts the internal polling loop which continuously requests + position and status.""" + self.get_function('StartPolling', check_success=True)(duration) - def stop_polling(self): - return self.get_function('StopPolling')() + def stop_polling(self) -> None: + """Stops the internal polling loop.""" + self.get_function('StopPolling')() def get_polling_duration(self) -> int: + """Gets the polling loop duration.""" return self.get_function('PollingDuration')() - def set_polling_duration(self, duration: int): + def set_polling_duration(self, duration: int) -> None: + """Stops polling and starts it again with given duration.""" self.stop_polling() self.start_polling(duration) - def open(self): - return self.get_function('Open', check_errors=True)() + def open(self) -> None: + """Open the device for communications.""" + self.get_function('Open', check_errors=True)() - def close(self): + def close(self) -> None: + """Disconnect and close the device.""" self.get_function('Close')() + @register_prefix(['ISC']) + def disable_channel(self) -> None: + """Disable the channel so that motor can be moved by hand. + + When disabled power is removed from the motor and it can be + freely moved. + """ + self.get_function('DisableChannel', check_errors=True)() + + @register_prefix(['ISC']) + def enable_channel(self) -> None: + """Enable channel for computer control. + + When enabled power is applied to the motor so it is fixed in + position. + """ + self.get_function('EnableChannel', check_errors=True)() + + @register_prefix(['ISC']) + def stop(self) -> None: + """Stop the current move using the current velocity profile.""" + self.get_function('StopProfiled', check_errors=True)() + + @register_prefix(['ISC']) + def can_home(self) -> bool: + """Can the device perform a Home.""" + return bool(self.get_function('CanHome')()) + + @register_prefix(['ISC']) + def needs_homing(self) -> bool: + """Can this device be moved without Homing.""" + return not bool(self.get_function('CanMoveWithoutHomingFirst')()) + + @register_prefix(['FF', 'ISC']) + def home(self) -> None: + """Home the device. + + Homing the device will set the device to a known state and + determine the home position, see Homing for more detail. + """ + self.get_function('Home', check_errors=True)() + + @register_prefix(['FF', 'ISC']) def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: modelNo = ctypes.create_string_buffer(64) type = ctypes.wintypes.WORD() @@ -331,8 +522,19 @@ def real_value_from_device_unit(self, device_unit: ctypes.c_int, return real_unit.value -class KinesisInstrument(Instrument, abc.ABC): - """Qcodes Instrument subclass for Kinesis instruments. +class KinesisInstrument(Instrument): + """Base class for Qcodes Kinesis instruments. + + A subclass declaration requires two mandatory keyword arguments: + + prefix (str): + The Kinesis DLL function prefix for this hardware type + hardware_type (:class:`enums.HardwareType`): + The instrument hardware type id (an integer enumeration). + + To automatically forward common DLL methods, they should be marked + with the above prefix and the @register_prefix decorator in + :class:`ThorlabsKinesis`. This will expose them in the subclass. Args: name: @@ -357,28 +559,61 @@ class KinesisInstrument(Instrument, abc.ABC): """ - def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', serial: int | None = None, simulation: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): + if self._prefix is None or self.hardware_type is None: + raise NotImplementedError('Incorrectly implemented subclass. ' + 'Needs to be declared with prefix and ' + 'hardware_type arguments.') + try: - self.kinesis = ThorlabsKinesis(self.hardware_type().name, - self._prefix(), dll_dir, simulation) + self._kinesis = ThorlabsKinesis(self.hardware_type.name, + self._prefix, dll_dir, simulation) except FileNotFoundError: # Subclass needs to handle irregular dll name - self.kinesis = self._init_kinesis(dll_dir, simulation) + self._kinesis = self._init_kinesis(dll_dir, simulation) self._initialized: bool = False super().__init__(name, metadata, label) self.add_parameter('polling_duration', - get_cmd=self.kinesis.get_polling_duration, - set_cmd=self.kinesis.set_polling_duration, + get_cmd=self._kinesis.get_polling_duration, + set_cmd=self._kinesis.set_polling_duration, unit='ms') self.connect(serial) + def __init_subclass__(cls, + prefix: str | None = None, + hardware_type: enums.KinesisHWType | None = None, + **kwargs): + super().__init_subclass__(**kwargs) + + cls._prefix = prefix + """The prefix of DLL functions.""" + cls.hardware_type = hardware_type + """The hardware type identifier (an integer enumeration).""" + + def is_registered_method(item) -> bool: + key, val = item + return callable(val) and prefix in getattr(val, '__prefixes', []) + + # Forward functions marked by @register_prefix in ThorlabsKinesis + for name, meth in filter(is_registered_method, + ThorlabsKinesis.__dict__.items()): + def make_wrapper(method): + # Outer wrapper required to avoid closure problems. + @wraps(method) + def wrapped(self, *args, **kw): + return method(getattr(self, '_kinesis'), *args, **kw) + + return wrapped + + setattr(cls, name, make_wrapper(meth)) + def _init_kinesis(self, dll_dir: str | pathlib.Path | None, simulation: bool) -> ThorlabsKinesis: @@ -386,19 +621,9 @@ def _init_kinesis(self, 'the _init_kinesis() method for irregular ' 'dll name.') - @classmethod - @abc.abstractmethod - def _prefix(cls) -> str: - pass - - @classmethod - @abc.abstractmethod - def hardware_type(cls) -> enums.KinesisHWType: - pass - @property def serial(self) -> int | None: - sn = self.kinesis.serialNo.value + sn = self._kinesis.serialNo.value if sn is not None: return int(sn.decode()) return None @@ -412,7 +637,7 @@ def list_available_devices(self) -> List[int]: try: return [ serial for _, serial in - list_available_devices(self.kinesis.lib, self.hardware_type()) + list_available_devices(self._kinesis.lib, self.hardware_type) ] except KinesisError: self._initialized = False @@ -424,11 +649,11 @@ def connect(self, serial: int | None, polling_duration: int = 100): if serial is None: available_devices = self.list_available_devices() if not len(available_devices): - raise RuntimeError(f'No {self.prefix} devices found!') + raise RuntimeError(f'No {self._prefix} devices found!') serial = available_devices[0] if not self._initialized: - error_check(self.kinesis.lib.TLI_BuildDeviceList()) + error_check(self._kinesis.lib.TLI_BuildDeviceList()) if self.connected: warnings.warn('Already connected to device with serial ' @@ -436,22 +661,22 @@ def connect(self, serial: int | None, polling_duration: int = 100): UserWarning, stacklevel=2) self.disconnect() - self.kinesis.serialNo.value = str(serial).encode() - self.kinesis.open() - self.kinesis.start_polling(polling_duration) + self._kinesis.serialNo.value = str(serial).encode() + self._kinesis.open() + self._kinesis.start_polling(polling_duration) self.connect_message(begin_time=begin_time) def disconnect(self): - if self.kinesis.simulation: - self.kinesis.disable_simulation() + if self._kinesis.simulation: + self._kinesis.disable_simulation() if self.connected: - self.kinesis.stop_polling() - self.kinesis.close() - self.kinesis.serialNo.value = None + self._kinesis.stop_polling() + self._kinesis.close() + self._kinesis.serialNo.value = None def get_idn(self) -> dict[str, str | None]: model, type, num_channels, notes, firmware, hardware, state = \ - self.kinesis.get_hw_info() + self._kinesis.get_hw_info() return {'vendor': 'Thorlabs', 'model': model, 'firmware': firmware, 'serial': str(self.serial)} @@ -466,7 +691,8 @@ class KinesisError(Exception): def list_available_devices( lib: str | os.PathLike | ctypes.CDLL | None = None, - hardware_type: Iterable[enums.KinesisHWType] | enums.KinesisHWType | None = None + hardware_type: Iterable[ + enums.KinesisHWType] | enums.KinesisHWType | None = None ) -> List[Tuple[enums.KinesisHWType, int]]: """Discover and list available Kinesis devices. @@ -519,7 +745,8 @@ def list_available_devices( hw_type_id ) if serialNo.value: - devices.append((enums.KinesisHWType(hw_type_id), int(serialNo.value.split(b',')[0]))) + devices.append((enums.KinesisHWType(hw_type_id), + int(serialNo.value.split(b',')[0]))) if len(devices) == n: # Found all devices already break diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py index e8bfefee6..7e80fe1e6 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py @@ -6,6 +6,20 @@ class KinesisHWType(enum.Enum): FilterFlipper = 37 +class MotorTypes(enum.Enum): + """Values that represent different Motor Types.""" + NotMotor = 0 + """Not a motor.""" + DCMotor = 1 + """Motor is a DC Servo motor.""" + StepperMotor = 2 + """Motor is a Stepper Motor.""" + BrushlessMotor = 3 + """Motor is a Brushless Motor.""" + CustomMotor = 100 + """Motor is a custom motor.""" + + class ISCUnitType(enum.Enum): Distance = 0 Velocity = 1 diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 52d929979..63a6767f7 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -1,17 +1,16 @@ from __future__ import annotations import pathlib -from abc import ABC -from typing import Mapping, Any +from typing import Any, Mapping from .core import KinesisInstrument, ThorlabsKinesis -class KinesisISCIntrument(KinesisInstrument, ABC): +class KinesisISCInstrument(KinesisInstrument): """Devices which are controlled from the IntegratedStepperMotor dll. """ - def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', serial: int | None = None, simulation: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): @@ -20,20 +19,13 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = None, # Update the device with stored settings. This is necessary to be able # to convert units since there are specific formulae for each motor # taking into account Gearing, Pitch, Steps Per Revolution etc. - self.kinesis.load_settings() + self._kinesis.load_settings() - def _init_kinesis( - self, - dll_dir: str | pathlib.Path | None, - simulation: bool - ) -> ThorlabsKinesis: + def _init_kinesis(self, dll_dir: str | pathlib.Path | None, + simulation: bool) -> ThorlabsKinesis: return ThorlabsKinesis( 'Thorlabs.MotionControl.IntegratedStepperMotors.dll', - self._prefix(), + self._prefix, dll_dir, simulation ) - - @classmethod - def _prefix(cls) -> str: - return 'ISC' From 85215b0cb6e6d7298a80b0d5cabf1901ded862f0 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 12:59:43 +0100 Subject: [PATCH 29/61] Add K10CR1 driver. --- .../Thorlabs/Thorlabs_K10CR1/kinesis.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py new file mode 100644 index 000000000..a69e1e757 --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import pathlib +from functools import partial +from typing import Any, Mapping + +from qcodes import Parameter, validators as vals +from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums, isc + + +class ThorlabsK10CR1(isc.KinesisISCInstrument, prefix='ISC', + hardware_type=enums.KinesisHWType.CageRotator): + """Kinesis driver for Thorlabs K10CR1 cage rotator. + + Args: + name: + An identifier for this instrument. + dll_dir (optional): + The directory where the kinesis dlls reside. + serial (optional): + The serial number of the device to connect to. If omitted, + the first available device found will be used. For a list + of all available devices, use + :meth:`list_available_devices` on an existing instance or + :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. + simulation (optional): + Enable the Kinesis simulator mode. Note that the serial + number assigned to the simulated device should be given + since otherwise the first available device will be + connected (which might not be a simulated but a real one). + metadata (optional): + Additional static metadata. + label (optional): + Nicely formatted name of the instrument. + + """ + + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', + serial: int | None = None, simulation: bool = False, + metadata: Mapping[Any, Any] | None = None, + label: str | None = None): + super().__init__(name, dll_dir, serial, simulation, metadata, label) + + self.position = Parameter( + "position", + get_cmd=self._kinesis.get_position, + set_cmd=self._kinesis.move_to_position, + get_parser=partial(self._kinesis.real_value_from_device_unit, + unit_type=enums.ISCUnitType.Distance), + set_parser=partial(self._kinesis.device_unit_from_real_value, + unit_type=enums.ISCUnitType.Distance), + vals=vals.Numbers(0, 360), + unit=u"\u00b0", + label="Position", + instrument=self + ) + """The wheel position in degrees. + + Use :meth:`move_to_position` with argument block=True to block + execution until the targeted position is reached. You should + probably invalidate the parameter cache afterwards though. + """ From 83c77aea4509f937f866a19ab7be85e82377fc1c Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 13:00:12 +0100 Subject: [PATCH 30/61] Use abbreviated unit. --- qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index 9f25ed0e7..69ad84026 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -73,7 +73,7 @@ def __init__(self, name: str, addr: str = '', reset: bool = False, self.add_parameter('power', get_cmd=self._get_power, label='Power', - unit='Watt') + unit='W') self.add_parameter('wavelength', get_cmd=self._get_wavelength, set_cmd=self._set_wavelength, From 2639bb4ef3d96af9a6599a57b7731c4adf58ed73 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 15:16:33 +0100 Subject: [PATCH 31/61] Add CC instruments driver draft as an example. --- .../drivers/Thorlabs/TDC001.py | 37 ++------- .../drivers/Thorlabs/private/kinesis/cc.py | 80 ++++++++++++++++++ .../drivers/Thorlabs/private/kinesis/core.py | 82 +++++++++++++++---- .../drivers/Thorlabs/private/kinesis/enums.py | 52 +++++++++++- 4 files changed, 202 insertions(+), 49 deletions(-) create mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/TDC001.py b/qcodes_contrib_drivers/drivers/Thorlabs/TDC001.py index dcba4a894..454918c01 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/TDC001.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/TDC001.py @@ -5,39 +5,12 @@ Authors: Julien Barrier, """ -import logging -from typing import Optional -from .private.CC import _Thorlabs_CC +from .private.kinesis import enums +from .private.kinesis.cc import KinesisCCInstrument -log = logging.getLogger(__name__) -class Thorlabs_TDC001(_Thorlabs_CC): - """Instrument driver for the Thorlabs TDC001 servo motor controller - - Args: - name: Instrument name. - serial_number: Serial number of the device. - dll_path: Path to the kinesis dll for the instrument to use. - dll_dir: Directory in which the kinesis dll are stored. - simulation: Enables the simulation manager. Defaults to False. - polling: Polling rate in ms. Defaults to 200. - home: Sets the device to home state. Defaults to False. +class ThorlabsTDC001(KinesisCCInstrument, prefix='CC', + hardware_type=enums.KinesisHWType.TCubeDCServo): + """Instrument driver for the Thorlabs TDC001 servo motor controller. """ - _CONDITIONS = ['homed', 'moved', 'stopped', 'limit_updated'] - def __init__(self, - name: str, - serial_number: str, - dll_path: Optional[str] = None, - dll_dir: Optional[str] = None, - simulation: bool = False, - polling: int = 200, - home: bool = False, - **kwargs): - if dll_path: - self._dll_path = dll_path - else: - self._dll_path = 'Thorlabs.MotionControl.TCube.DCServo.dll' - self._dll_dir: Optional[str] = dll_dir if dll_dir else None - super().__init__(name, serial_number, self._dll_path, self._dll_dir, - simulation, polling, home, **kwargs) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py new file mode 100644 index 000000000..b2149c474 --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import pathlib +from functools import partial +from typing import Any, Mapping + +from qcodes import validators as vals +from qcodes.parameters import Parameter +from . import enums +from .core import KinesisInstrument, ThorlabsKinesis + + +class KinesisCCInstrument(KinesisInstrument): + def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', + serial: int | None = None, simulation: bool = False, + polling: int = 200, home: bool = False, + metadata: Mapping[Any, Any] | None = None, + label: str | None = None): + super().__init__(name, dll_dir, serial, simulation, polling, home, + metadata, label) + + self.position = Parameter( + "position", + get_cmd=self._kinesis.get_position, + set_cmd=self._kinesis.move_to_position, + get_parser=partial(self._kinesis.real_value_from_device_unit, + unit_type=enums.ISCUnitType.Distance), + set_parser=partial(self._kinesis.device_unit_from_real_value, + unit_type=enums.ISCUnitType.Distance), + vals=vals.Numbers(0, 360), + unit=u"\u00b0", + label="Position", + instrument=self + ) + """The position in degrees. + + Use :meth:`move_to_position` with argument block=True to block + execution until the targeted position is reached. You should + probably invalidate the parameter cache afterwards though. + """ + + # Would be nice to use Group and GroupParameter here, but + # they're restricted to VISA commands... + self.velocity = Parameter( + "velocity", + get_cmd=lambda: self._kinesis.get_vel_params()[0], + set_cmd=lambda val: self._kinesis.set_vel_params( + val, self.acceleration.get() + ), + get_parser=partial(self._kinesis.real_value_from_device_unit, + unit_type=enums.ISCUnitType.Velocity), + set_parser=partial(self._kinesis.device_unit_from_real_value, + unit_type=enums.ISCUnitType.Velocity), + unit=u"\u00b0/s", + label="Velocity", + instrument=self + ) + self.acceleration = Parameter( + "acceleration", + get_cmd=lambda: self._kinesis.get_vel_params()[1], + set_cmd=lambda val: self._kinesis.set_vel_params( + self.acceleration.get(), val + ), + get_parser=partial(self._kinesis.real_value_from_device_unit, + unit_type=enums.ISCUnitType.Acceleration), + set_parser=partial(self._kinesis.device_unit_from_real_value, + unit_type=enums.ISCUnitType.Acceleration), + unit=u"\u00b0/s\u00b2", + label="Acceleration", + instrument=self + ) + + def _init_kinesis(self, dll_dir: str | pathlib.Path | None, + simulation: bool) -> ThorlabsKinesis: + return ThorlabsKinesis( + 'Thorlabs.MotionControl.KCube.DCServo.dll', + self._prefix, + dll_dir, + simulation + ) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 385843a94..cdff4b0ee 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -260,12 +260,12 @@ def request_status(self) -> None: """ self.get_function('RequestStatus', check_errors=True)() - @register_prefix(['FF', 'ISC']) + @register_prefix(['FF', 'ISC', 'CC']) def identify(self) -> None: """Sends a command to the device to make it identify iteself.""" self.get_function('Identify')() - @register_prefix(['FF', 'ISC']) + @register_prefix(['FF', 'ISC', 'CC']) def get_number_positions(self) -> int: """Get number of positions. @@ -275,7 +275,7 @@ def get_number_positions(self) -> int: """ return int(self.get_function('GetNumberPositions')()) - @register_prefix(['FF', 'ISC']) + @register_prefix(['FF', 'ISC', 'CC']) def get_position(self) -> int | float | str: """Get the current position. @@ -290,7 +290,7 @@ def get_position(self) -> int | float | str: time.sleep(self.get_polling_duration() * 1e-3) return self.get_function('GetPosition')() - @register_prefix(['FF', 'ISC']) + @register_prefix(['FF', 'ISC', 'CC']) def move_to_position(self, position: int | str, block: bool = False) -> None: """Move the device to the specified position (index). @@ -312,7 +312,7 @@ def move_to_position(self, position: int | str, while self.get_position() != position: time.sleep(50e-3) - @register_prefix(['ISC']) + @register_prefix(['ISC', 'CC']) def move_at_velocity( self, direction: enums.TravelDirection | str | int @@ -325,6 +325,45 @@ def move_at_velocity( direction = enums.TravelDirection(direction) self.get_function('MoveAtVelocity', check_errors=True)(direction.value) + @register_prefix(['ISC', 'CC']) + def move_relative(self, displacement: int): + """Move the motor by a relative amount. + + Args: + displacement: Signed displacement in Device Units. + + """ + self.get_function('MoveRelative', check_errors=True)(displacement) + + @register_prefix(['ISC', 'CC']) + def get_vel_params(self) -> Tuple[int, int]: + """Gets the move velocity parameters. + + Returns: + acceleration: The new acceleration value in Device Units. + max_velocity: The new maximum velocity value in Device Units. + + """ + acceleration = ctypes.c_int() + maxVelocity = ctypes.c_int() + self.get_function('GetVelParams', check_errors=True)( + ctypes.byref(acceleration), + ctypes.byref(maxVelocity) + ) + return acceleration.value, maxVelocity.value + + @register_prefix(['ISC', 'CC']) + def set_vel_params(self, acceleration: int, max_velocity: int): + """Sets the move velocity parameters. + + Args: + acceleration: The new acceleration value in Device Units. + max_velocity: The new maximum velocity value in Device Units. + + """ + self.get_function('SetVelParams', check_errors=True)(acceleration, + max_velocity) + @register_prefix(['FF']) def get_transit_time(self) -> int: """Gets the transit time. @@ -344,7 +383,7 @@ def set_transit_time(self, transit_time: int) -> None: """ self.get_function('SetTransitTime', check_errors=True)(transit_time) - @register_prefix(['ISC']) + @register_prefix(['ISC', 'CC']) def get_motor_params_ext(self) -> Tuple[float, float, float]: """Gets the motor stage parameters. @@ -362,7 +401,7 @@ def get_motor_params_ext(self) -> Tuple[float, float, float]: ) return stepsPerRev.value, gearBoxRatio.value, pitch.value - @register_prefix(['ISC']) + @register_prefix(['ISC', 'CC']) def set_motor_params_ext(self, steps_per_rev: float, gearbox_ratio: float, pitch: float) -> None: """Sets the motor stage parameters. @@ -403,7 +442,7 @@ def close(self) -> None: """Disconnect and close the device.""" self.get_function('Close')() - @register_prefix(['ISC']) + @register_prefix(['ISC', 'CC']) def disable_channel(self) -> None: """Disable the channel so that motor can be moved by hand. @@ -412,7 +451,7 @@ def disable_channel(self) -> None: """ self.get_function('DisableChannel', check_errors=True)() - @register_prefix(['ISC']) + @register_prefix(['ISC', 'CC']) def enable_channel(self) -> None: """Enable channel for computer control. @@ -421,22 +460,22 @@ def enable_channel(self) -> None: """ self.get_function('EnableChannel', check_errors=True)() - @register_prefix(['ISC']) + @register_prefix(['ISC', 'CC']) def stop(self) -> None: """Stop the current move using the current velocity profile.""" self.get_function('StopProfiled', check_errors=True)() - @register_prefix(['ISC']) + @register_prefix(['ISC', 'CC']) def can_home(self) -> bool: """Can the device perform a Home.""" return bool(self.get_function('CanHome')()) - @register_prefix(['ISC']) + @register_prefix(['ISC', 'CC']) def needs_homing(self) -> bool: """Can this device be moved without Homing.""" return not bool(self.get_function('CanMoveWithoutHomingFirst')()) - @register_prefix(['FF', 'ISC']) + @register_prefix(['FF', 'ISC', 'CC']) def home(self) -> None: """Home the device. @@ -445,7 +484,7 @@ def home(self) -> None: """ self.get_function('Home', check_errors=True)() - @register_prefix(['FF', 'ISC']) + @register_prefix(['FF', 'ISC', 'CC']) def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: modelNo = ctypes.create_string_buffer(64) type = ctypes.wintypes.WORD() @@ -479,7 +518,7 @@ def device_unit_from_real_value( """Convert real values to device units. In order to do this, the device settings must be loaded using - :meth:`load_settings` + :meth:`load_settings`. """ if isinstance(unit_type, int): unit_type = enums.ISCUnitType(unit_type) @@ -561,6 +600,7 @@ class KinesisInstrument(Instrument): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', serial: int | None = None, simulation: bool = False, + polling: int = 200, home: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): if self._prefix is None or self.hardware_type is None: @@ -584,7 +624,13 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=self._kinesis.set_polling_duration, unit='ms') - self.connect(serial) + self.connect(serial, polling) + + if home: + if self._kinesis.can_home(): + self._kinesis.home() + else: + raise RuntimeError('Device `{}` is not homeable') def __init_subclass__(cls, prefix: str | None = None, @@ -664,6 +710,10 @@ def connect(self, serial: int | None, polling_duration: int = 100): self._kinesis.serialNo.value = str(serial).encode() self._kinesis.open() self._kinesis.start_polling(polling_duration) + # Update the device with stored settings. This is necessary to be able + # to convert units since there are specific formulae for each motor + # taking into account Gearing, Pitch, Steps Per Revolution etc. + self._kinesis.load_settings() self.connect_message(begin_time=begin_time) def disconnect(self): diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py index 7e80fe1e6..01a159938 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py @@ -2,8 +2,58 @@ class KinesisHWType(enum.Enum): - CageRotator = 55 + BenchtopBrushlessMotor20X = 73 + BenchtopBrushlessMotor30X = 103 + BenchtopDCServo1Ch = 43 + BenchtopDCServo3Ch = 79 + BenchtopNanoTrak = 22 + BenchtopPiezo1Ch = 41 + BenchtopPiezo3Ch = 71 + BenchtopPrecisionPiezo1Ch = 44 + BenchtopPrecisionPiezo2Ch = 95 + BenchtopStepperMotor1Ch = 40 + BenchtopStepperMotor3Ch = 70 + BenchtopVoiceCoil = 100 FilterFlipper = 37 + FilterWheel = 47 + IntegratedPrecisionPiezo = 92 + IntegratedXStage = 105 + IntegratedXYStage = 101 + KCubeBrushlessMotor = 28 + KCubeDCServo = 27 + KCubeInertialMotor1Ch = 74 + KCubeInertialMotor4Ch = 97 + KCubeLaserDiode = 98 + KCubeLaserSource = 56 + KCubeNanoTrak = 57 + KCubePiezo = 29 + KCubePositionAligner = 69 + KCubeSolenoid = 68 + KCubeStepperMotor = 26 + LongTravelStage = 45 + CageRotator = 55 + LabJack490 = 46 + LabJack050 = 49 + ModularRack = 48 + # ModularRack = 75 # ?? + ModularBrushless = 54 + ModularNanoTrak = 52 + ModularPiezo = 51 + ModularStepperMotor = 50 + Polarizer = 38 + PositionReadoutEncoder = 111 + TCubeBrushlessMotor = 67 + TCubeDCServo = 83 + TCubeInertialMotor = 65 + TCubeLaserSource = 86 + TCubeLaserDiode = 64 + TCubeNanoTrak = 82 + TCubeQuad = 89 + TCubeSolenoid = 85 + TCubeStepperMotor = 80 + TCubeStrainGauge = 84 + TCubeTEC = 87 + VerticalStage = 24 class MotorTypes(enum.Enum): From 3fdfb815f38e68657799c074b15897161b514652 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 15:18:12 +0100 Subject: [PATCH 32/61] Move more stuff to base classes --- .../Thorlabs/Thorlabs_K10CR1/kinesis.py | 56 +---------------- .../Thorlabs/Thorlabs_MFF10x/kinesis.py | 4 +- .../drivers/Thorlabs/private/kinesis/isc.py | 62 +++++++++++++++++-- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py index a69e1e757..2115ffd28 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py @@ -1,62 +1,8 @@ from __future__ import annotations -import pathlib -from functools import partial -from typing import Any, Mapping - -from qcodes import Parameter, validators as vals from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums, isc class ThorlabsK10CR1(isc.KinesisISCInstrument, prefix='ISC', hardware_type=enums.KinesisHWType.CageRotator): - """Kinesis driver for Thorlabs K10CR1 cage rotator. - - Args: - name: - An identifier for this instrument. - dll_dir (optional): - The directory where the kinesis dlls reside. - serial (optional): - The serial number of the device to connect to. If omitted, - the first available device found will be used. For a list - of all available devices, use - :meth:`list_available_devices` on an existing instance or - :func:`qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core.list_available_devices`. - simulation (optional): - Enable the Kinesis simulator mode. Note that the serial - number assigned to the simulated device should be given - since otherwise the first available device will be - connected (which might not be a simulated but a real one). - metadata (optional): - Additional static metadata. - label (optional): - Nicely formatted name of the instrument. - - """ - - def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', - serial: int | None = None, simulation: bool = False, - metadata: Mapping[Any, Any] | None = None, - label: str | None = None): - super().__init__(name, dll_dir, serial, simulation, metadata, label) - - self.position = Parameter( - "position", - get_cmd=self._kinesis.get_position, - set_cmd=self._kinesis.move_to_position, - get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.ISCUnitType.Distance), - set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.ISCUnitType.Distance), - vals=vals.Numbers(0, 360), - unit=u"\u00b0", - label="Position", - instrument=self - ) - """The wheel position in degrees. - - Use :meth:`move_to_position` with argument block=True to block - execution until the targeted position is reached. You should - probably invalidate the parameter cache afterwards though. - """ + """Kinesis driver for Thorlabs K10CR1 cage rotator.""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index 35c386610..b4911114d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -43,10 +43,12 @@ class ThorlabsMFF10x(KinesisInstrument, prefix='FF', def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', serial: int | None = None, simulation: bool = False, + polling: int = 200, home: bool = False, position_mapping: Mapping[str, Literal[1, 2]] | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, serial, simulation, metadata, label) + super().__init__(name, dll_dir, serial, simulation, polling, home, + metadata, label) self.position = Parameter( 'position', diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 63a6767f7..cf7023226 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -1,8 +1,12 @@ from __future__ import annotations import pathlib +from functools import partial from typing import Any, Mapping +from qcodes import validators as vals +from qcodes.parameters import Group, GroupParameter, Parameter +from . import enums from .core import KinesisInstrument, ThorlabsKinesis @@ -12,14 +16,62 @@ class KinesisISCInstrument(KinesisInstrument): def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', serial: int | None = None, simulation: bool = False, + polling: int = 200, home: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, serial, simulation, metadata, label) + super().__init__(name, dll_dir, serial, simulation, polling, home, + metadata, label) - # Update the device with stored settings. This is necessary to be able - # to convert units since there are specific formulae for each motor - # taking into account Gearing, Pitch, Steps Per Revolution etc. - self._kinesis.load_settings() + self.position = Parameter( + "position", + get_cmd=self._kinesis.get_position, + set_cmd=self._kinesis.move_to_position, + get_parser=partial(self._kinesis.real_value_from_device_unit, + unit_type=enums.ISCUnitType.Distance), + set_parser=partial(self._kinesis.device_unit_from_real_value, + unit_type=enums.ISCUnitType.Distance), + vals=vals.Numbers(0, 360), + unit=u"\u00b0", + label="Position", + instrument=self + ) + """The position in degrees. + + Use :meth:`move_to_position` with argument block=True to block + execution until the targeted position is reached. You should + probably invalidate the parameter cache afterwards though. + """ + + # Would be nice to use Group and GroupParameter here, but + # they're restricted to VISA commands... + self.velocity = Parameter( + "velocity", + get_cmd=lambda: self._kinesis.get_vel_params()[0], + set_cmd=lambda val: self._kinesis.set_vel_params( + val, self.acceleration.get() + ), + get_parser=partial(self._kinesis.real_value_from_device_unit, + unit_type=enums.ISCUnitType.Velocity), + set_parser=partial(self._kinesis.device_unit_from_real_value, + unit_type=enums.ISCUnitType.Velocity), + unit=u"\u00b0/s", + label="Velocity", + instrument=self + ) + self.acceleration = Parameter( + "acceleration", + get_cmd=lambda: self._kinesis.get_vel_params()[1], + set_cmd=lambda val: self._kinesis.set_vel_params( + self.acceleration.get(), val + ), + get_parser=partial(self._kinesis.real_value_from_device_unit, + unit_type=enums.ISCUnitType.Acceleration), + set_parser=partial(self._kinesis.device_unit_from_real_value, + unit_type=enums.ISCUnitType.Acceleration), + unit=u"\u00b0/s\u00b2", + label="Acceleration", + instrument=self + ) def _init_kinesis(self, dll_dir: str | pathlib.Path | None, simulation: bool) -> ThorlabsKinesis: From 13f8f3532b0f4f48963e84a585e4f8c9ece65c38 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 16:47:52 +0100 Subject: [PATCH 33/61] Add rotation modes. --- .../drivers/Thorlabs/private/kinesis/cc.py | 11 +-- .../drivers/Thorlabs/private/kinesis/core.py | 69 +++++++++++++------ .../drivers/Thorlabs/private/kinesis/enums.py | 18 +++++ .../drivers/Thorlabs/private/kinesis/isc.py | 2 +- 4 files changed, 73 insertions(+), 27 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py index b2149c474..700dc1198 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -27,16 +27,17 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', unit_type=enums.ISCUnitType.Distance), set_parser=partial(self._kinesis.device_unit_from_real_value, unit_type=enums.ISCUnitType.Distance), - vals=vals.Numbers(0, 360), + vals=vals.Numbers(), unit=u"\u00b0", label="Position", instrument=self ) """The position in degrees. - Use :meth:`move_to_position` with argument block=True to block - execution until the targeted position is reached. You should - probably invalidate the parameter cache afterwards though. + Note: + Use :meth:`move_to_position` with argument block=True to + block execution until the targeted position is reached. You + should probably invalidate the parameter cache afterwards. """ # Would be nice to use Group and GroupParameter here, but @@ -55,6 +56,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', label="Velocity", instrument=self ) + """The velocity in degrees per second.""" self.acceleration = Parameter( "acceleration", get_cmd=lambda: self._kinesis.get_vel_params()[1], @@ -69,6 +71,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', label="Acceleration", instrument=self ) + """The acceleration in degrees per square second.""" def _init_kinesis(self, dll_dir: str | pathlib.Path | None, simulation: bool) -> ThorlabsKinesis: diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index cdff4b0ee..88f4c7f27 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -10,14 +10,15 @@ import pathlib import time import warnings +from enum import EnumMeta from functools import partial, wraps -from typing import Any, Callable, Dict, Iterable, List, Mapping, Tuple +from typing import Any, Callable, Iterable, List, Mapping, Tuple from typing import Sequence, TypeVar from typing_extensions import ParamSpec from qcodes import Instrument -from . import enums, structs +from . import enums try: import ctypes.wintypes @@ -130,6 +131,16 @@ T = TypeVar('T') +def to_enum(arg: EnumMeta | str | int, enum: EnumMeta): + """Return an instance of type enum for a given name, value, or the + enum itself.""" + if isinstance(arg, str): + arg = getattr(enum, arg) + elif isinstance(arg, int): + arg = enum(arg) + return arg + + def register_prefix(prefixes: Sequence[str]) -> Callable: """Registers a warpped DLL function for a given Kinesis device. @@ -260,6 +271,26 @@ def request_status(self) -> None: """ self.get_function('RequestStatus', check_errors=True)() + @register_prefix(['ISC', 'CC']) + def reset_rotation_modes(self): + """Reset the rotation modes for a rotational device.""" + self.get_function('ResetRotationModes', check_errors=True)() + + @register_prefix(['ISC', 'CC']) + def set_rotation_modes(self, mode: enums.MovementModes | str | int, + direction: enums.MovementDirections | str | int): + """Set the rotation modes for a rotational device. + + Args: + mode: The rotation mode. + direction: The rotation direction when moving between two + angles. + """ + self.get_function('SetRotationModes', check_errors=True)( + to_enum(mode, enums.MovementModes).value, + to_enum(direction, enums.MovementDirections).value + ) + @register_prefix(['FF', 'ISC', 'CC']) def identify(self) -> None: """Sends a command to the device to make it identify iteself.""" @@ -319,11 +350,9 @@ def move_at_velocity( ) -> None: """Start moving at the current velocity in the specified direction.""" - if isinstance(direction, str): - direction = getattr(enums.TravelDirection, direction) - elif isinstance(direction, int): - direction = enums.TravelDirection(direction) - self.get_function('MoveAtVelocity', check_errors=True)(direction.value) + self.get_function('MoveAtVelocity', check_errors=True)( + to_enum(direction, enums.TravelDirection).value + ) @register_prefix(['ISC', 'CC']) def move_relative(self, displacement: int): @@ -513,18 +542,15 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: def device_unit_from_real_value( self, real_unit: float, - unit_type: enums.ISCUnitType + unit_type: enums.ISCUnitType | int | str ) -> ctypes.c_int: """Convert real values to device units. In order to do this, the device settings must be loaded using :meth:`load_settings`. """ - if isinstance(unit_type, int): - unit_type = enums.ISCUnitType(unit_type) - elif isinstance(unit_type, str): - unit_type = getattr(enums.ISCUnitType, unit_type) - elif not isinstance(unit_type, enums.ISCUnitType): + unit_type = to_enum(unit_type, enums.ISCUnitType) + if not isinstance(unit_type, enums.ISCUnitType): raise TypeError('unit_type should be int, str, or ISCUnitType, ' f'not {type(unit_type)}') @@ -537,18 +563,18 @@ def device_unit_from_real_value( ) return device_unit - def real_value_from_device_unit(self, device_unit: ctypes.c_int, - unit_type: enums.ISCUnitType) -> float: + def real_value_from_device_unit( + self, + device_unit: ctypes.c_int, + unit_type: enums.ISCUnitType | int | str + ) -> float: """Convert device units to real values. In order to do this, the device settings must be loaded using :meth:`load_settings` """ - if isinstance(unit_type, int): - unit_type = enums.ISCUnitType(unit_type) - elif isinstance(unit_type, str): - unit_type = getattr(enums.ISCUnitType, unit_type) - elif not isinstance(unit_type, enums.ISCUnitType): + unit_type = to_enum(unit_type, enums.ISCUnitType) + if not isinstance(unit_type, enums.ISCUnitType): raise TypeError('unit_type should be int, str, or ISCUnitType, ' f'not {type(unit_type)}') @@ -741,8 +767,7 @@ class KinesisError(Exception): def list_available_devices( lib: str | os.PathLike | ctypes.CDLL | None = None, - hardware_type: Iterable[ - enums.KinesisHWType] | enums.KinesisHWType | None = None + hardware_type: Iterable[enums.KinesisHWType] | enums.KinesisHWType | None = None ) -> List[Tuple[enums.KinesisHWType, int]]: """Discover and list available Kinesis devices. diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py index 01a159938..537f43e10 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py @@ -94,6 +94,24 @@ class StopModes(enum.Enum): """Stops using a velocity profile.""" +class MovementModes(enum.Enum): + LinearRange = 0 + """Fixed Limit, cannot rotate.""" + RotationalUnlimited = 1 + """Ranges between +/- Infinity.""" + RotationalWrapping = 2 + """Ranges between 0 to 360 with wrapping.""" + + +class MovementDirections(enum.Enum): + Quickest = 0 + """Always takes the shortest path.""" + Forwards = 1 + "Always moves forwards." + Backwards = 2 + "Always moves backwards." + + class TravelDirection(enum.Enum): TravelDirectionDisabled = 0 """Disabled or Undefined.""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index cf7023226..f04c2faa4 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -30,7 +30,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', unit_type=enums.ISCUnitType.Distance), set_parser=partial(self._kinesis.device_unit_from_real_value, unit_type=enums.ISCUnitType.Distance), - vals=vals.Numbers(0, 360), + vals=vals.Numbers(), unit=u"\u00b0", label="Position", instrument=self From c0a48c981b5869f2c0892923dfd3f54a4fbada22 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 18:12:36 +0100 Subject: [PATCH 34/61] Add jog_mode and stop_mode parameters --- .../drivers/Thorlabs/private/kinesis/cc.py | 23 ++++++++++- .../drivers/Thorlabs/private/kinesis/core.py | 39 +++++++++++++++++-- .../drivers/Thorlabs/private/kinesis/isc.py | 25 +++++++++++- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py index 700dc1198..cfbb36d84 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -7,7 +7,7 @@ from qcodes import validators as vals from qcodes.parameters import Parameter from . import enums -from .core import KinesisInstrument, ThorlabsKinesis +from .core import KinesisInstrument, ThorlabsKinesis, to_enum class KinesisCCInstrument(KinesisInstrument): @@ -73,6 +73,27 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', ) """The acceleration in degrees per square second.""" + self.jog_mode = Parameter( + "jog_mode", + get_cmd=lambda: self._kinesis.get_jog_mode()[0], + set_cmd=lambda val: self._kinesis.set_jog_mode( + val, self._kinesis.get_jog_mode()[1] + ), + set_parser=to_enum, + label="Jog mode", + instrument=self + ) + self.stop_mode = Parameter( + "stop_mode", + get_cmd=lambda: self._kinesis.get_jog_mode()[1], + set_cmd=lambda val: self._kinesis.set_jog_mode( + self._kinesis.get_jog_mode()[0], val + ), + set_parser=to_enum, + label="Stop mode", + instrument=self + ) + def _init_kinesis(self, dll_dir: str | pathlib.Path | None, simulation: bool) -> ThorlabsKinesis: return ThorlabsKinesis( diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 88f4c7f27..c65898921 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -12,7 +12,7 @@ import warnings from enum import EnumMeta from functools import partial, wraps -from typing import Any, Callable, Iterable, List, Mapping, Tuple +from typing import Any, Callable, Iterable, List, Literal, Mapping, Tuple from typing import Sequence, TypeVar from typing_extensions import ParamSpec @@ -135,7 +135,8 @@ def to_enum(arg: EnumMeta | str | int, enum: EnumMeta): """Return an instance of type enum for a given name, value, or the enum itself.""" if isinstance(arg, str): - arg = getattr(enum, arg) + # Try to catch at least single-noun names that are not capitalized. + arg = getattr(enum, arg.capitalize()) elif isinstance(arg, int): arg = enum(arg) return arg @@ -265,8 +266,7 @@ def request_status(self) -> None: Note: This is called automatically if Polling is enabled for the - device using ISC_StartPolling(char const * serialNo, int - milliseconds). + device using :meth:`start_polling`. """ self.get_function('RequestStatus', check_errors=True)() @@ -291,6 +291,37 @@ def set_rotation_modes(self, mode: enums.MovementModes | str | int, to_enum(direction, enums.MovementDirections).value ) + def get_jog_mode(self) -> Tuple[enums.JogModes, enums.StopModes]: + """Gets the jog mode. + + Returns: + jog_mode + stop_mode + + """ + mode = ctypes.c_short() + stopMode = ctypes.c_short() + self.get_function('GetJogMode', check_errors=True)( + ctypes.byref(mode), + ctypes.byref(stopMode) + ) + return enums.JogModes(mode.value), enums.StopModes(stopMode.value) + + def set_jog_mode(self, + jog_mode: enums.JogModes | int | str, + stop_mode: enums.StopModes | int | str): + """Sets the jog mode. + + Args: + jog_mode: The jog mode. + stop_mode: The StopMode. + + """ + self.get_function('SetJogMode', check_errors=True)( + to_enum(jog_mode, enums.JogModes), + to_enum(stop_mode, enums.StopModes) + ) + @register_prefix(['FF', 'ISC', 'CC']) def identify(self) -> None: """Sends a command to the device to make it identify iteself.""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index f04c2faa4..7252ea60d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -5,9 +5,9 @@ from typing import Any, Mapping from qcodes import validators as vals -from qcodes.parameters import Group, GroupParameter, Parameter +from qcodes.parameters import Parameter from . import enums -from .core import KinesisInstrument, ThorlabsKinesis +from .core import KinesisInstrument, ThorlabsKinesis, to_enum class KinesisISCInstrument(KinesisInstrument): @@ -73,6 +73,27 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', instrument=self ) + self.jog_mode = Parameter( + "jog_mode", + get_cmd=lambda: self._kinesis.get_jog_mode()[0], + set_cmd=lambda val: self._kinesis.set_jog_mode( + val, self._kinesis.get_jog_mode()[1] + ), + set_parser=to_enum, + label="Jog mode", + instrument=self + ) + self.stop_mode = Parameter( + "stop_mode", + get_cmd=lambda: self._kinesis.get_jog_mode()[1], + set_cmd=lambda val: self._kinesis.set_jog_mode( + self._kinesis.get_jog_mode()[0], val + ), + set_parser=to_enum, + label="Stop mode", + instrument=self + ) + def _init_kinesis(self, dll_dir: str | pathlib.Path | None, simulation: bool) -> ThorlabsKinesis: return ThorlabsKinesis( From 117c1b1ddff7101ed8a5d2a25077246f0da7452e Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 18:14:57 +0100 Subject: [PATCH 35/61] Add status update methods and is_moving() --- .../drivers/Thorlabs/private/kinesis/core.py | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index c65898921..e31c4c47e 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -271,6 +271,37 @@ def request_status(self) -> None: """ self.get_function('RequestStatus', check_errors=True)() + def request_status_bits(self) -> None: + """Request the status bits which identify the current motor + state. + + This needs to be called to get the device to send it's current + status bits. + + Note: + This is called automatically if Polling is enabled for the + device using :meth:`start_polling`. + + """ + self.get_function('RequestStatusBits', check_errors=True)() + + def get_status_bits(self) -> int: + """Get the current status bits. + + This returns the latest status bits received from the device. + To get new status bits, use :meth:`request_status` or use the + polling functions, :meth:`start_polling`. + + Returns: + The status bits from the device. See the API manual for more + information on their meaning. + + """ + self.request_status_bits() + function = self.get_function('GetStatusBits') + function.restype = ctypes.wintypes.DWORD + return function() + @register_prefix(['ISC', 'CC']) def reset_rotation_modes(self): """Reset the rotation modes for a rotational device.""" @@ -370,9 +401,9 @@ def move_to_position(self, position: int | str, """ self.get_function('MoveToPosition', check_errors=True)(position) - if block: - while self.get_position() != position: - time.sleep(50e-3) + + while block and self.is_moving(): + time.sleep(50e-3) @register_prefix(['ISC', 'CC']) def move_at_velocity( @@ -395,6 +426,15 @@ def move_relative(self, displacement: int): """ self.get_function('MoveRelative', check_errors=True)(displacement) + @register_prefix(['FF', 'ISC', 'CC']) + def is_moving(self) -> bool: + """If the device is moving or not. + + Note that for the FilterFlipper devices, this is always false. + """ + status = self.get_status_bits() + return bool((status & 0x00000010) | (status & 0x00000020)) + @register_prefix(['ISC', 'CC']) def get_vel_params(self) -> Tuple[int, int]: """Gets the move velocity parameters. From d62e8bfe6784d8641d3a34d53049743021455cef Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 18:16:53 +0100 Subject: [PATCH 36/61] Add stop profiles --- .../drivers/Thorlabs/private/kinesis/core.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index e31c4c47e..6b5085497 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -561,9 +561,15 @@ def enable_channel(self) -> None: self.get_function('EnableChannel', check_errors=True)() @register_prefix(['ISC', 'CC']) - def stop(self) -> None: + def stop(self, mode: enums.StopModes | int | str = 'Profiled') -> None: """Stop the current move using the current velocity profile.""" - self.get_function('StopProfiled', check_errors=True)() + mode = to_enum(mode, enums.StopModes) + if mode == enums.StopModes.Immediate: + self.get_function('StopImmediate', check_errors=True)() + elif mode == enums.StopModes.Profiled: + self.get_function('StopProfiled', check_errors=True)() + else: + raise ValueError('Invalid profile') @register_prefix(['ISC', 'CC']) def can_home(self) -> bool: From c1999eb794010694101f95a8cda38c84999719da Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 18:20:06 +0100 Subject: [PATCH 37/61] Rename enums.ISCUnitType -> enums.UnitType --- .../drivers/Thorlabs/private/kinesis/cc.py | 12 ++++++------ .../drivers/Thorlabs/private/kinesis/core.py | 12 ++++++------ .../drivers/Thorlabs/private/kinesis/enums.py | 2 +- .../drivers/Thorlabs/private/kinesis/isc.py | 12 ++++++------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py index cfbb36d84..13603e6f6 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -24,9 +24,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', get_cmd=self._kinesis.get_position, set_cmd=self._kinesis.move_to_position, get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.ISCUnitType.Distance), + unit_type=enums.UnitType.Distance), set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.ISCUnitType.Distance), + unit_type=enums.UnitType.Distance), vals=vals.Numbers(), unit=u"\u00b0", label="Position", @@ -49,9 +49,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', val, self.acceleration.get() ), get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.ISCUnitType.Velocity), + unit_type=enums.UnitType.Velocity), set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.ISCUnitType.Velocity), + unit_type=enums.UnitType.Velocity), unit=u"\u00b0/s", label="Velocity", instrument=self @@ -64,9 +64,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', self.acceleration.get(), val ), get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.ISCUnitType.Acceleration), + unit_type=enums.UnitType.Acceleration), set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.ISCUnitType.Acceleration), + unit_type=enums.UnitType.Acceleration), unit=u"\u00b0/s\u00b2", label="Acceleration", instrument=self diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 6b5085497..00b98bd51 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -619,15 +619,15 @@ def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: def device_unit_from_real_value( self, real_unit: float, - unit_type: enums.ISCUnitType | int | str + unit_type: enums.UnitType | int | str ) -> ctypes.c_int: """Convert real values to device units. In order to do this, the device settings must be loaded using :meth:`load_settings`. """ - unit_type = to_enum(unit_type, enums.ISCUnitType) - if not isinstance(unit_type, enums.ISCUnitType): + unit_type = to_enum(unit_type, enums.UnitType) + if not isinstance(unit_type, enums.UnitType): raise TypeError('unit_type should be int, str, or ISCUnitType, ' f'not {type(unit_type)}') @@ -643,15 +643,15 @@ def device_unit_from_real_value( def real_value_from_device_unit( self, device_unit: ctypes.c_int, - unit_type: enums.ISCUnitType | int | str + unit_type: enums.UnitType | int | str ) -> float: """Convert device units to real values. In order to do this, the device settings must be loaded using :meth:`load_settings` """ - unit_type = to_enum(unit_type, enums.ISCUnitType) - if not isinstance(unit_type, enums.ISCUnitType): + unit_type = to_enum(unit_type, enums.UnitType) + if not isinstance(unit_type, enums.UnitType): raise TypeError('unit_type should be int, str, or ISCUnitType, ' f'not {type(unit_type)}') diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py index 537f43e10..7b7c0cb3d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py @@ -70,7 +70,7 @@ class MotorTypes(enum.Enum): """Motor is a custom motor.""" -class ISCUnitType(enum.Enum): +class UnitType(enum.Enum): Distance = 0 Velocity = 1 Acceleration = 2 diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 7252ea60d..89d6cbe3d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -27,9 +27,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', get_cmd=self._kinesis.get_position, set_cmd=self._kinesis.move_to_position, get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.ISCUnitType.Distance), + unit_type=enums.UnitType.Distance), set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.ISCUnitType.Distance), + unit_type=enums.UnitType.Distance), vals=vals.Numbers(), unit=u"\u00b0", label="Position", @@ -51,9 +51,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', val, self.acceleration.get() ), get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.ISCUnitType.Velocity), + unit_type=enums.UnitType.Velocity), set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.ISCUnitType.Velocity), + unit_type=enums.UnitType.Velocity), unit=u"\u00b0/s", label="Velocity", instrument=self @@ -65,9 +65,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', self.acceleration.get(), val ), get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.ISCUnitType.Acceleration), + unit_type=enums.UnitType.Acceleration), set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.ISCUnitType.Acceleration), + unit_type=enums.UnitType.Acceleration), unit=u"\u00b0/s\u00b2", label="Acceleration", instrument=self From 78b1789dda97512887f3f5530bcb3241df72fb39 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 6 Nov 2023 18:21:19 +0100 Subject: [PATCH 38/61] Always use real units with the outside world --- .../drivers/Thorlabs/private/kinesis/core.py | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 00b98bd51..347a1f05d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -394,12 +394,20 @@ def move_to_position(self, position: int | str, Args: position: The required position. must be 1 or 2 for the filter - flipper or in device units else. + flipper or in real units else. block: Block the interpreter until the target position is reached. """ + try: + # FilterFlipper does not have device units + position = self.device_unit_from_real_value( + position, enums.UnitType.Distance + ) + except AttributeError: + pass + self.get_function('MoveToPosition', check_errors=True)(position) while block and self.is_moving(): @@ -417,13 +425,17 @@ def move_at_velocity( ) @register_prefix(['ISC', 'CC']) - def move_relative(self, displacement: int): + def move_relative(self, displacement: float): """Move the motor by a relative amount. Args: - displacement: Signed displacement in Device Units. + displacement: Signed displacement in real units. """ + displacement = self.device_unit_from_real_value( + displacement, enums.UnitType.Distance + ) + self.get_function('MoveRelative', check_errors=True)(displacement) @register_prefix(['FF', 'ISC', 'CC']) @@ -436,12 +448,12 @@ def is_moving(self) -> bool: return bool((status & 0x00000010) | (status & 0x00000020)) @register_prefix(['ISC', 'CC']) - def get_vel_params(self) -> Tuple[int, int]: + def get_vel_params(self) -> Tuple[float, float]: """Gets the move velocity parameters. Returns: - acceleration: The new acceleration value in Device Units. - max_velocity: The new maximum velocity value in Device Units. + acceleration: The new acceleration value in real units. + max_velocity: The new maximum velocity value in real units. """ acceleration = ctypes.c_int() @@ -450,17 +462,32 @@ def get_vel_params(self) -> Tuple[int, int]: ctypes.byref(acceleration), ctypes.byref(maxVelocity) ) - return acceleration.value, maxVelocity.value + + return ( + self.real_value_from_device_unit( + acceleration, enums.UnitType.Acceleration + ), + self.real_value_from_device_unit( + maxVelocity, enums.UnitType.Velocity + ) + ) @register_prefix(['ISC', 'CC']) - def set_vel_params(self, acceleration: int, max_velocity: int): + def set_vel_params(self, acceleration: float, max_velocity: float): """Sets the move velocity parameters. Args: - acceleration: The new acceleration value in Device Units. - max_velocity: The new maximum velocity value in Device Units. + acceleration: The new acceleration value in real units. + max_velocity: The new maximum velocity value in real units. """ + acceleration = self.device_unit_from_real_value( + acceleration, enums.UnitType.Acceleration + ) + max_velocity = self.device_unit_from_real_value( + max_velocity, enums.UnitType.Velocity + ) + self.get_function('SetVelParams', check_errors=True)(acceleration, max_velocity) From ce3e03f15c8f43b87a1035da92c7dd2ffe286b57 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 7 Nov 2023 11:53:13 +0100 Subject: [PATCH 39/61] Mypy --- .../drivers/Thorlabs/private/kinesis.py | 120 ----------- .../Thorlabs/private/kinesis/__init__.py | 2 +- .../drivers/Thorlabs/private/kinesis/core.py | 192 +++++++++++++++--- 3 files changed, 162 insertions(+), 152 deletions(-) delete mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis.py deleted file mode 100644 index 06eff4ebc..000000000 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis.py +++ /dev/null @@ -1,120 +0,0 @@ -# -*- coding: utf-8 -*- -"""QCoDeS base drivers for Thorlabs Kinesis instruments - -Authors: - Julien Barrier, -""" -import os -import sys -import ctypes -from typing import Any, Optional - -from qcodes.instrument import Instrument -from . import GeneralErrors, MotorErrors, ConnexionErrors - -class _Thorlabs_Kinesis(Instrument): - """A base class for Thorlabs kinesis instruments - - Args: - name: Instrument name. - serial_number: Serial number of the device. - dll_path: Path to the kinesis dll for the instrument to use. - dll_dir: Directory in which the kinesis dll are stored. - simulation: Enables the simulation manager. Defaults to False. - """ - def __init__(self, - name: str, - serial_number: str, - dll_path: str, - dll_dir: Optional[str] = None, - simulation: bool = False, - **kwargs): - super().__init__(name, **kwargs) - self.serial_number = serial_number - self._serial_number = ctypes.c_char_p(self.serial_number.encode('ascii')) - self._dll_path = dll_path - self._dll_dir: Optional[str] = dll_dir if dll_dir else r'C:\Program Files\Thorlabs\Kinesis' - if sys.platform != 'win32': - self._dll: Any = None - raise OSError('Thorlabs Kinesis only works on Windows') - else: - os.add_dll_directory(self._dll_dir) - self._dll = ctypes.cdll.LoadLibrary(self._dll_path) - - self._simulation = simulation - if self._simulation: - self.enable_simulation() - - self._device_info = dict(zip( - ['type_ID', 'description', 'PID', 'is_known_type', 'motor_type', - 'is_piezo', 'is_laser', 'is_custom', 'is_rack', 'max_channels'], - self._get_device_info())) - self._type_ID = self._device_info['type_ID'] - self._description = self._device_info['description'] - self._PID = self._device_info['PID'] - self._is_known_type = self._device_info['is_known_type'] - self._motor_type = self._device_info['motor_type'] - self._is_piezo = self._device_info['is_piezo'] - self._is_laser = self._device_info['is_laser'] - self._is_custom = self._device_info['is_custom'] - self._is_rack = self._device_info['is_rack'] - self._max_channels = self._device_info['max_channels'] - - def _get_device_info(self) -> list: - """Get the device information from the USB port - - Returns: - list: [type id, description, PID, is known type, motor type, - is piezo, is laser, is custom type, is rack, max channels] - """ - type_id = ctypes.c_ulong() - description = ctypes.c_char() - pid = ctypes.c_ulong() - is_known_type = ctypes.c_bool() - motor_type = ctypes.c_int() - is_piezo_device = ctypes.c_bool() - is_laser = ctypes.c_bool() - is_custom_type = ctypes.c_bool() - is_rack = ctypes.c_bool() - max_channels = ctypes.c_bool() - - ret = self._dll.TLI_GetDeviceInfo( - ctypes.byref(self._serial_number), - ctypes.byref(type_id), - ctypes.byref(description), - ctypes.byref(pid), - ctypes.byref(is_known_type), - ctypes.byref(motor_type), - ctypes.byref(is_piezo_device), - ctypes.byref(is_laser), - ctypes.byref(is_custom_type), - ctypes.byref(is_rack), - ctypes.byref(max_channels) - ) - self._check_error(ret) - return [type_id.value, description.value, pid.value, - is_known_type.value, motor_type.value, is_piezo_device.value, - is_laser.value, is_custom_type.value, is_rack.value, - max_channels.value] - - def _check_error(self, status: int) -> None: - if status != 0: - if status in ConnexionErrors: - raise ConnectionError(f'{ConnexionErrors[status]} ({status})') - elif status in GeneralErrors: - raise OSError(f'{GeneralErrors[status]} ({status})') - elif status in MotorErrors: - raise RuntimeError(f'{MotorErrors[status]} ({status})') - else: - raise ValueError(f'Unknown error code ({status})') - else: - pass - return None - - def enable_simulation(self) -> None: - """Initialise a connection to the simulation manager, which must already be running""" - self._dll.TLI_InitializeSimulations() - - def disable_simulation(self) -> None: - """Uninitialize a connection to the simulation manager, which must be running""" - self._dll.TLI_UninitializeSimulations() diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py index d58d61844..ae7115913 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py @@ -1 +1 @@ -from .core import list_available_devices +from .core import list_available_devices, _Thorlabs_Kinesis diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 347a1f05d..3f47c71dc 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -8,17 +8,21 @@ import os import pathlib +import sys import time import warnings -from enum import EnumMeta +from enum import Enum from functools import partial, wraps -from typing import Any, Callable, Iterable, List, Literal, Mapping, Tuple +from typing import ( + Any, Callable, Iterable, List, Mapping, Optional, Tuple, Type +) from typing import Sequence, TypeVar from typing_extensions import ParamSpec from qcodes import Instrument from . import enums +from .. import ConnexionErrors, GeneralErrors, MotorErrors try: import ctypes.wintypes @@ -129,16 +133,17 @@ P = ParamSpec('P') T = TypeVar('T') +EnumT = TypeVar('EnumT', bound=Enum) -def to_enum(arg: EnumMeta | str | int, enum: EnumMeta): +def to_enum(arg: EnumT | str | int, enum: Type[EnumT]) -> EnumT: """Return an instance of type enum for a given name, value, or the enum itself.""" if isinstance(arg, str): # Try to catch at least single-noun names that are not capitalized. - arg = getattr(enum, arg.capitalize()) - elif isinstance(arg, int): - arg = enum(arg) + return getattr(enum, arg.capitalize()) + if isinstance(arg, int): + return enum(arg) return arg @@ -150,33 +155,35 @@ def register_prefix(prefixes: Sequence[str]) -> Callable: """ def decorator(func: Callable[P, T]) -> Callable[P, T]: - func.__prefixes = prefixes + func.__prefixes = prefixes # type: ignore[attr-defined] return func return decorator -def success_check(func): +def success_check(func: Callable[P, int]) -> Callable[P, None]: """Wraps functions that return a boolean success code. 1 means success, 0 means failure. """ @wraps(func) - def wrapped(*args, **kwargs): + def wrapped(*args: P.args, **kwargs: P.kwargs) -> None: if not func(*args, **kwargs): raise KinesisError('Unspecified failure.') return wrapped -def error_check(func): +def error_check(func: Callable[P, int]) -> Callable[P, None]: """Wraps functions that return an integer error code.""" @wraps(func) - def wrapped(*args, **kwargs): + def wrapped(*args: P.args, **kwargs: P.kwargs) -> None: code = func(*args, **kwargs) if (status := ERROR_CODES.get(code)) != 'FT_OK': + if status is None: + raise RuntimeError('Unknown error code') raise KinesisError(f'{status}: {ERROR_MESSAGES[status]}') return wrapped @@ -224,10 +231,11 @@ def get_function(self, name: str, check_errors: bool = False, except AttributeError as err: raise AttributeError(f'Function {self.prefix}_{name} not found in ' f'dll {self.lib}') from err + if check_errors: - func = error_check(func) + return error_check(func) if check_success: - func = success_check(func) + return success_check(func) return func def enable_simulation(self) -> None: @@ -241,7 +249,7 @@ def disable_simulation(self) -> None: self.lib.TLI_UninitializeSimulations() @error_check - def build_device_list(self) -> None: + def build_device_list(self) -> int: """Build the DeviceList. This function builds an internal collection of all devices found @@ -299,7 +307,7 @@ def get_status_bits(self) -> int: """ self.request_status_bits() function = self.get_function('GetStatusBits') - function.restype = ctypes.wintypes.DWORD + function.restype = ctypes.wintypes.DWORD # type: ignore[attr-defined] return function() @register_prefix(['ISC', 'CC']) @@ -384,7 +392,7 @@ def get_position(self) -> int | float | str: return self.get_function('GetPosition')() @register_prefix(['FF', 'ISC', 'CC']) - def move_to_position(self, position: int | str, + def move_to_position(self, position: int | float, block: bool = False) -> None: """Move the device to the specified position (index). @@ -402,13 +410,13 @@ def move_to_position(self, position: int | str, """ try: # FilterFlipper does not have device units - position = self.device_unit_from_real_value( - position, enums.UnitType.Distance + device_position = self.device_unit_from_real_value( + float(position), enums.UnitType.Distance ) except AttributeError: - pass + device_position = ctypes.c_int(int(position)) - self.get_function('MoveToPosition', check_errors=True)(position) + self.get_function('MoveToPosition', check_errors=True)(device_position) while block and self.is_moving(): time.sleep(50e-3) @@ -432,11 +440,13 @@ def move_relative(self, displacement: float): displacement: Signed displacement in real units. """ - displacement = self.device_unit_from_real_value( + device_displacement = self.device_unit_from_real_value( displacement, enums.UnitType.Distance ) - self.get_function('MoveRelative', check_errors=True)(displacement) + self.get_function('MoveRelative', check_errors=True)( + device_displacement + ) @register_prefix(['FF', 'ISC', 'CC']) def is_moving(self) -> bool: @@ -481,15 +491,16 @@ def set_vel_params(self, acceleration: float, max_velocity: float): max_velocity: The new maximum velocity value in real units. """ - acceleration = self.device_unit_from_real_value( + device_acceleration = self.device_unit_from_real_value( acceleration, enums.UnitType.Acceleration ) - max_velocity = self.device_unit_from_real_value( + device_max_velocity = self.device_unit_from_real_value( max_velocity, enums.UnitType.Velocity ) - self.get_function('SetVelParams', check_errors=True)(acceleration, - max_velocity) + self.get_function('SetVelParams', check_errors=True)( + device_acceleration, device_max_velocity + ) @register_prefix(['FF']) def get_transit_time(self) -> int: @@ -727,12 +738,17 @@ class KinesisInstrument(Instrument): Nicely formatted name of the instrument. """ + _prefix: str + """The prefix of DLL functions.""" + hardware_type: enums.KinesisHWType + """The hardware type identifier (an integer enumeration).""" def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', serial: int | None = None, simulation: bool = False, polling: int = 200, home: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): + if self._prefix is None or self.hardware_type is None: raise NotImplementedError('Incorrectly implemented subclass. ' 'Needs to be declared with prefix and ' @@ -768,10 +784,10 @@ def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) - cls._prefix = prefix - """The prefix of DLL functions.""" - cls.hardware_type = hardware_type - """The hardware type identifier (an integer enumeration).""" + # Ignore type as mypy is not smart enough to detect that the None case + # is checked in __init__ + cls._prefix = prefix # type: ignore[assignment] + cls.hardware_type = hardware_type # type: ignore[assignment] def is_registered_method(item) -> bool: key, val = item @@ -871,7 +887,8 @@ class KinesisError(Exception): def list_available_devices( lib: str | os.PathLike | ctypes.CDLL | None = None, - hardware_type: Iterable[enums.KinesisHWType] | enums.KinesisHWType | None = None + hardware_type: Iterable[ + enums.KinesisHWType] | enums.KinesisHWType | None = None ) -> List[Tuple[enums.KinesisHWType, int]]: """Discover and list available Kinesis devices. @@ -931,3 +948,116 @@ def list_available_devices( break return devices + + +# Main branch implementation. +# TODO: merge +class _Thorlabs_Kinesis(Instrument): + """A base class for Thorlabs kinesis instruments + + Args: + name: Instrument name. + serial_number: Serial number of the device. + dll_path: Path to the kinesis dll for the instrument to use. + dll_dir: Directory in which the kinesis dll are stored. + simulation: Enables the simulation manager. Defaults to False. + """ + + def __init__(self, + name: str, + serial_number: str, + dll_path: str, + dll_dir: Optional[str] = None, + simulation: bool = False, + **kwargs): + super().__init__(name, **kwargs) + self.serial_number = serial_number + self._serial_number = ctypes.c_char_p( + self.serial_number.encode('ascii')) + self._dll_path = dll_path + self._dll_dir: Optional[ + str] = dll_dir if dll_dir else r'C:\Program Files\Thorlabs\Kinesis' + if sys.platform != 'win32': + self._dll: Any = None + raise OSError('Thorlabs Kinesis only works on Windows') + else: + os.add_dll_directory(self._dll_dir) + self._dll = ctypes.cdll.LoadLibrary(self._dll_path) + + self._simulation = simulation + if self._simulation: + self.enable_simulation() + + self._device_info = dict(zip( + ['type_ID', 'description', 'PID', 'is_known_type', 'motor_type', + 'is_piezo', 'is_laser', 'is_custom', 'is_rack', 'max_channels'], + self._get_device_info())) + self._type_ID = self._device_info['type_ID'] + self._description = self._device_info['description'] + self._PID = self._device_info['PID'] + self._is_known_type = self._device_info['is_known_type'] + self._motor_type = self._device_info['motor_type'] + self._is_piezo = self._device_info['is_piezo'] + self._is_laser = self._device_info['is_laser'] + self._is_custom = self._device_info['is_custom'] + self._is_rack = self._device_info['is_rack'] + self._max_channels = self._device_info['max_channels'] + + def _get_device_info(self) -> list: + """Get the device information from the USB port + + Returns: + list: [type id, description, PID, is known type, motor type, + is piezo, is laser, is custom type, is rack, max channels] + """ + type_id = ctypes.c_ulong() + description = ctypes.c_char() + pid = ctypes.c_ulong() + is_known_type = ctypes.c_bool() + motor_type = ctypes.c_int() + is_piezo_device = ctypes.c_bool() + is_laser = ctypes.c_bool() + is_custom_type = ctypes.c_bool() + is_rack = ctypes.c_bool() + max_channels = ctypes.c_bool() + + ret = self._dll.TLI_GetDeviceInfo( + ctypes.byref(self._serial_number), + ctypes.byref(type_id), + ctypes.byref(description), + ctypes.byref(pid), + ctypes.byref(is_known_type), + ctypes.byref(motor_type), + ctypes.byref(is_piezo_device), + ctypes.byref(is_laser), + ctypes.byref(is_custom_type), + ctypes.byref(is_rack), + ctypes.byref(max_channels) + ) + self._check_error(ret) + return [type_id.value, description.value, pid.value, + is_known_type.value, motor_type.value, is_piezo_device.value, + is_laser.value, is_custom_type.value, is_rack.value, + max_channels.value] + + def _check_error(self, status: int) -> None: + if status != 0: + if status in ConnexionErrors: + raise ConnectionError(f'{ConnexionErrors[status]} ({status})') + elif status in GeneralErrors: + raise OSError(f'{GeneralErrors[status]} ({status})') + elif status in MotorErrors: + raise RuntimeError(f'{MotorErrors[status]} ({status})') + else: + raise ValueError(f'Unknown error code ({status})') + else: + pass + return None + + def enable_simulation(self) -> None: + """Initialise a connection to the simulation manager, which must already be running""" + self._dll.TLI_InitializeSimulations() + + def disable_simulation(self) -> None: + """Uninitialize a connection to the simulation manager, which must be running""" + self._dll.TLI_UninitializeSimulations() From 156d40632333b14d0ab82a4de6e1557828e3ca28 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 7 Nov 2023 11:54:03 +0100 Subject: [PATCH 40/61] Try guessing the libary name based on some common patterns --- .../drivers/Thorlabs/private/kinesis/core.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 3f47c71dc..1edfc45ea 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -8,6 +8,7 @@ import os import pathlib +import re import sys import time import warnings @@ -201,6 +202,13 @@ def __init__(self, lib: str, prefix: str, if not lib.endswith(".dll"): lib = lib + ".dll" + # Cover some frequent libraries that have periods in their name + lib = lib.replace('Benchtop', 'Benchtop.') + lib = lib.replace('KCube', 'KCube.') + lib = lib.replace('TCube', 'TCube.') + # Remove # of channel suffixes from hardware type + lib = re.sub(r'(\d+Ch)*$', '', lib) + self.prefix = prefix self.lib: ctypes.CDLL = ctypes.cdll.LoadLibrary( os.path.join(dll_dir if dll_dir is not None else DLL_DIR, lib) From d54e8ed3becd7d7f9e565070e9b84fd461c5837c Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 7 Nov 2023 12:04:28 +0100 Subject: [PATCH 41/61] Return python int instead of c_int --- .../drivers/Thorlabs/private/kinesis/core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 1edfc45ea..6f5a3ab11 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -422,7 +422,7 @@ def move_to_position(self, position: int | float, float(position), enums.UnitType.Distance ) except AttributeError: - device_position = ctypes.c_int(int(position)) + device_position = int(position) self.get_function('MoveToPosition', check_errors=True)(device_position) @@ -666,7 +666,7 @@ def device_unit_from_real_value( self, real_unit: float, unit_type: enums.UnitType | int | str - ) -> ctypes.c_int: + ) -> int: """Convert real values to device units. In order to do this, the device settings must be loaded using @@ -684,7 +684,7 @@ def device_unit_from_real_value( ctypes.byref(device_unit), unit_type.value, ) - return device_unit + return device_unit.value def real_value_from_device_unit( self, From 0ff194862092609726f12a07a2ed412e3b6de4d3 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 7 Nov 2023 12:09:25 +0100 Subject: [PATCH 42/61] Fix unit mixup bug --- .../drivers/Thorlabs/private/kinesis/cc.py | 13 ------------- .../drivers/Thorlabs/private/kinesis/core.py | 7 ++++--- .../drivers/Thorlabs/private/kinesis/isc.py | 13 ------------- 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py index 13603e6f6..4ba6ba488 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -23,11 +23,6 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', "position", get_cmd=self._kinesis.get_position, set_cmd=self._kinesis.move_to_position, - get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.UnitType.Distance), - set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.UnitType.Distance), - vals=vals.Numbers(), unit=u"\u00b0", label="Position", instrument=self @@ -48,10 +43,6 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=lambda val: self._kinesis.set_vel_params( val, self.acceleration.get() ), - get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.UnitType.Velocity), - set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.UnitType.Velocity), unit=u"\u00b0/s", label="Velocity", instrument=self @@ -63,10 +54,6 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=lambda val: self._kinesis.set_vel_params( self.acceleration.get(), val ), - get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.UnitType.Acceleration), - set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.UnitType.Acceleration), unit=u"\u00b0/s\u00b2", label="Acceleration", instrument=self diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 6f5a3ab11..6731d4f6f 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -393,11 +393,12 @@ def get_position(self) -> int | float | str: or by calling RequestPosition or RequestStatus. Returns: - The current position. + The current position in real units. """ self.request_status() - time.sleep(self.get_polling_duration() * 1e-3) - return self.get_function('GetPosition')() + return self.real_value_from_device_unit( + self.get_function('GetPosition')(), enums.UnitType.Distance + ) @register_prefix(['FF', 'ISC', 'CC']) def move_to_position(self, position: int | float, diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 89d6cbe3d..f18603a5d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -26,11 +26,6 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', "position", get_cmd=self._kinesis.get_position, set_cmd=self._kinesis.move_to_position, - get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.UnitType.Distance), - set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.UnitType.Distance), - vals=vals.Numbers(), unit=u"\u00b0", label="Position", instrument=self @@ -50,10 +45,6 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=lambda val: self._kinesis.set_vel_params( val, self.acceleration.get() ), - get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.UnitType.Velocity), - set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.UnitType.Velocity), unit=u"\u00b0/s", label="Velocity", instrument=self @@ -64,10 +55,6 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=lambda val: self._kinesis.set_vel_params( self.acceleration.get(), val ), - get_parser=partial(self._kinesis.real_value_from_device_unit, - unit_type=enums.UnitType.Acceleration), - set_parser=partial(self._kinesis.device_unit_from_real_value, - unit_type=enums.UnitType.Acceleration), unit=u"\u00b0/s\u00b2", label="Acceleration", instrument=self From 3b75e1160e094ffea5d63678c98fc3d3d75fd29b Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 7 Nov 2023 12:09:57 +0100 Subject: [PATCH 43/61] Expose fewer methods to target instruments --- .../drivers/Thorlabs/private/kinesis/core.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 6731d4f6f..afb63e5b5 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -384,7 +384,6 @@ def get_number_positions(self) -> int: """ return int(self.get_function('GetNumberPositions')()) - @register_prefix(['FF', 'ISC', 'CC']) def get_position(self) -> int | float | str: """Get the current position. @@ -466,7 +465,6 @@ def is_moving(self) -> bool: status = self.get_status_bits() return bool((status & 0x00000010) | (status & 0x00000020)) - @register_prefix(['ISC', 'CC']) def get_vel_params(self) -> Tuple[float, float]: """Gets the move velocity parameters. @@ -491,7 +489,6 @@ def get_vel_params(self) -> Tuple[float, float]: ) ) - @register_prefix(['ISC', 'CC']) def set_vel_params(self, acceleration: float, max_velocity: float): """Sets the move velocity parameters. @@ -511,7 +508,6 @@ def set_vel_params(self, acceleration: float, max_velocity: float): device_acceleration, device_max_velocity ) - @register_prefix(['FF']) def get_transit_time(self) -> int: """Gets the transit time. @@ -520,7 +516,6 @@ def get_transit_time(self) -> int: """ return self.get_function('GetTransitTime')() - @register_prefix(['FF']) def set_transit_time(self, transit_time: int) -> None: """Sets the transit time. @@ -725,6 +720,10 @@ class KinesisInstrument(Instrument): with the above prefix and the @register_prefix decorator in :class:`ThorlabsKinesis`. This will expose them in the subclass. + Methods that are wrapped in qcodes parameters should not be + forwarded in this way for the sake of keeping the instrument + namespace free of clutter. + Args: name: An identifier for this instrument. From f37baa5a2ae93019996d8d941eed0685fed83b36 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 7 Nov 2023 12:11:04 +0100 Subject: [PATCH 44/61] Drop unused imports. --- qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py | 3 --- qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py index 4ba6ba488..5e8febb53 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -1,12 +1,9 @@ from __future__ import annotations import pathlib -from functools import partial from typing import Any, Mapping -from qcodes import validators as vals from qcodes.parameters import Parameter -from . import enums from .core import KinesisInstrument, ThorlabsKinesis, to_enum diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index f18603a5d..55337daf9 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -1,12 +1,9 @@ from __future__ import annotations import pathlib -from functools import partial from typing import Any, Mapping -from qcodes import validators as vals from qcodes.parameters import Parameter -from . import enums from .core import KinesisInstrument, ThorlabsKinesis, to_enum From 9228286db7c0492d7dec62a6e101aaa90ecf9f17 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Tue, 7 Nov 2023 12:45:50 +0100 Subject: [PATCH 45/61] Replace barebones visa driver with version from main --- .../drivers/Thorlabs/PM100D.py | 189 ---------------- .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 1 + .../drivers/Thorlabs/Thorlabs_PM100D/visa.py | 213 ++++++++++++++++-- 3 files changed, 194 insertions(+), 209 deletions(-) delete mode 100644 qcodes_contrib_drivers/drivers/Thorlabs/PM100D.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/PM100D.py b/qcodes_contrib_drivers/drivers/Thorlabs/PM100D.py deleted file mode 100644 index f8f2cdb1a..000000000 --- a/qcodes_contrib_drivers/drivers/Thorlabs/PM100D.py +++ /dev/null @@ -1,189 +0,0 @@ -# -*- coding: utf-8 -*- -"""QCoDes-Driver for Thorlab PM100D Handheld Optical Power and Energy Meter Console -https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=3341&pn=PM100D - -Authors: - Julien Barrier, -""" - -import logging -from time import sleep -from typing import Any - -from qcodes.instrument import VisaInstrument -from qcodes.parameters import Parameter, create_on_off_val_mapping -import qcodes.validators as vals - -log = logging.getLogger(__name__) - - -class Thorlab_PM100D(VisaInstrument): - """ - Class to represent a Thorlab PM100D optical powermeter - - status: beta-version - - Args: - name: name for the instrument - address: Visa Resource name to connect - terminator: Visa terminator - timeout. Visa timeout. - """ - def __init__(self, - name: str, - address: str, - terminator: str = '\n', - timeout: float = 20, - **kwargs: Any): - super().__init__(name, address, terminator=terminator, - timeout=timeout, **kwargs) - - self.averaging = Parameter( - 'averaging', - label='Averaging rate', - get_cmd='AVER?', - set_cmd='AVER', - vals=vals.Numbers(), - instrument=self - ) - - self.wavelength = Parameter( - 'wavelength', - label='Detected wavelength', - unit='m', - get_cmd='SENS:CORR:WAV?', - set_cmd='SENS:CORR:WAV {}', - scale=1e9, - vals=vals.Numbers(185e-9, 25e-6), - instrument=self - ) - - self.power = Parameter( - 'power', - label='Measured power', - unit='W', - get_cmd=self._get_power, - vals=vals.Numbers(), - instrument=self - ) - - self.attenuation = Parameter( - 'attenuation', - label='Attenuation', - unit='dB', - get_cmd='CORR?', - get_parser=float, - set_cmd='CORR {}', - set_parser=float, - vals=vals.Numbers(-60, 60), - instrument=self - ) - - self.power_range = Parameter( - 'power_range', - label='Power range', - unit='W', - get_cmd='SENS:POW:RANG:UPP?', - set_cmd='SENS:POW:RANG:UPP {}', - get_parser=float, - set_parser=float, - vals=vals.Numbers(), - instrument=self - ) - - self.auto_range = Parameter( - 'auto_range', - label='Auto range power', - get_cmd='SENS:POW:RANG:AUTO?', - set_cmd='SENS:POW:RANG:AUTO {}', - val_mapping=create_on_off_val_mapping(on_val='1', off_val='0'), - vals=vals.Ints(0, 1), - instrument=self - ) - - self.frequency = Parameter( - 'frequency', - unit='Hz', - get_cmd='MEAS:FREQ?', - get_parser=float, - vals=vals.Numbers(), - instrument=self - ) - - self.current = Parameter( - 'current', - label='Current', - unit='A', - get_cmd='MEAS:CURR?', - get_parser=float, - vals=vals.Numbers(), - instrument=self - ) - - self.current_range = Parameter( - 'current_range', - label='Current range', - unit='A', - get_cmd='SENS:CURR:RANG:UPP?', - get_parser=float, - vals=vals.Numbers(), - instrument=self - ) - - self.zero_value = Parameter( - 'zero_value', - unit='W', - get_cmd='CORR:COLL:ZERO:MAGN?', - get_parser=float, - vals=vals.Numbers(), - instrument=self - ) - - self.beam_diameter = Parameter( - 'beam_diameter', - label='Beam diameter', - unit='m', - get_cmd='CORR:BEAM?', - set_cmd='CORR:BEAM {}', - scale=1e3, - vals=vals.Numbers(), - instrument=self - ) - - self._set_transition_filter(512, 0) - self.averaging(300) - self._set_conf_power() - - self.connect_message() - - def _check_error(self) -> None: - err = self.ask('SYST:ERR?') - if err[:2] != '+0': - raise RuntimeError(f'PM100D call failed with error: {err}') - - def _set_conf_power(self) -> None: - """Set configuration to power mode - """ - self.write('CONF:POW') # set config to power mode - self.ask('ABOR;:STAT:OPER?') - self.write('INIT') - return None - - def _get_power(self) -> float: - """Get the power - """ - self._set_conf_power() - self.write('MEAS:POW') - sleep(.2) - power = self.ask('FETC?') - return float(power) - - def _set_transition_filter(self, positive: int, negative: int) -> None: - """Apply filters - """ - self.write(f'STAT:OPER:PTR {positive}') - sleep(.2) - self.write(f'STAT:OPER:NTR {negative}') - sleep(5) - self.ask('STAT:OPER?') # clear register - sleep(.2) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index 69ad84026..9c7e77c9b 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -54,6 +54,7 @@ class ThorlabsPM100D(Instrument): If the dll is not found. """ + def __init__(self, name: str, addr: str = '', reset: bool = False, thorlabs_tlpm: TLPM | None = None, metadata: Mapping[Any, Any] | None = None, diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py index 25c721b18..39a15d329 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py @@ -1,20 +1,193 @@ -from qcodes import VisaInstrument - - -class ThorlabsPM100D(VisaInstrument): - - def __init__(self, name, address, **kwargs): - super().__init__(name, address, terminator='\n', **kwargs) - - self.add_parameter('wav', - get_cmd='CORR:WAV?', - set_cmd='CORR:WAV {}', - get_parser=float, - set_parser=lambda value: '{:.8E}'.format(value), - label='wavelength', - unit='nm') - self.add_parameter('pow', - get_cmd='READ?', - get_parser=float, - label='power', - unit='Watt') +# -*- coding: utf-8 -*- +"""QCoDes-Driver for Thorlab PM100D Handheld Optical Power and Energy Meter Console +https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=3341&pn=PM100D + +Authors: + Julien Barrier, +""" + +import logging +from time import sleep +from typing import Any + +from qcodes.instrument import VisaInstrument +from qcodes.parameters import Parameter, create_on_off_val_mapping +import qcodes.validators as vals + +log = logging.getLogger(__name__) + + +class Thorlab_PM100D(VisaInstrument): + """ + Class to represent a Thorlab PM100D optical powermeter + + status: beta-version + + Args: + name: name for the instrument + address: Visa Resource name to connect + terminator: Visa terminator + timeout. Visa timeout. + """ + def __init__(self, + name: str, + address: str, + terminator: str = '\n', + timeout: float = 20, + **kwargs: Any): + super().__init__(name, address, terminator=terminator, + timeout=timeout, **kwargs) + + self.averaging = Parameter( + 'averaging', + label='Averaging rate', + get_cmd='AVER?', + set_cmd='AVER', + vals=vals.Numbers(), + instrument=self + ) + + self.wavelength = Parameter( + 'wavelength', + label='Detected wavelength', + unit='m', + get_cmd='SENS:CORR:WAV?', + set_cmd='SENS:CORR:WAV {}', + scale=1e9, + vals=vals.Numbers(185e-9, 25e-6), + instrument=self + ) + + self.power = Parameter( + 'power', + label='Measured power', + unit='W', + get_cmd=self._get_power, + vals=vals.Numbers(), + instrument=self + ) + + self.attenuation = Parameter( + 'attenuation', + label='Attenuation', + unit='dB', + get_cmd='CORR?', + get_parser=float, + set_cmd='CORR {}', + set_parser=float, + vals=vals.Numbers(-60, 60), + instrument=self + ) + + self.power_range = Parameter( + 'power_range', + label='Power range', + unit='W', + get_cmd='SENS:POW:RANG:UPP?', + set_cmd='SENS:POW:RANG:UPP {}', + get_parser=float, + set_parser=float, + vals=vals.Numbers(), + instrument=self + ) + + self.auto_range = Parameter( + 'auto_range', + label='Auto range power', + get_cmd='SENS:POW:RANG:AUTO?', + set_cmd='SENS:POW:RANG:AUTO {}', + val_mapping=create_on_off_val_mapping(on_val='1', off_val='0'), + vals=vals.Ints(0, 1), + instrument=self + ) + + self.frequency = Parameter( + 'frequency', + unit='Hz', + get_cmd='MEAS:FREQ?', + get_parser=float, + vals=vals.Numbers(), + instrument=self + ) + + self.current = Parameter( + 'current', + label='Current', + unit='A', + get_cmd='MEAS:CURR?', + get_parser=float, + vals=vals.Numbers(), + instrument=self + ) + + self.current_range = Parameter( + 'current_range', + label='Current range', + unit='A', + get_cmd='SENS:CURR:RANG:UPP?', + get_parser=float, + vals=vals.Numbers(), + instrument=self + ) + + self.zero_value = Parameter( + 'zero_value', + unit='W', + get_cmd='CORR:COLL:ZERO:MAGN?', + get_parser=float, + vals=vals.Numbers(), + instrument=self + ) + + self.beam_diameter = Parameter( + 'beam_diameter', + label='Beam diameter', + unit='m', + get_cmd='CORR:BEAM?', + set_cmd='CORR:BEAM {}', + scale=1e3, + vals=vals.Numbers(), + instrument=self + ) + + self._set_transition_filter(512, 0) + self.averaging(300) + self._set_conf_power() + + self.connect_message() + + def _check_error(self) -> None: + err = self.ask('SYST:ERR?') + if err[:2] != '+0': + raise RuntimeError(f'PM100D call failed with error: {err}') + + def _set_conf_power(self) -> None: + """Set configuration to power mode + """ + self.write('CONF:POW') # set config to power mode + self.ask('ABOR;:STAT:OPER?') + self.write('INIT') + return None + + def _get_power(self) -> float: + """Get the power + """ + self._set_conf_power() + self.write('MEAS:POW') + sleep(.2) + power = self.ask('FETC?') + return float(power) + + def _set_transition_filter(self, positive: int, negative: int) -> None: + """Apply filters + """ + self.write(f'STAT:OPER:PTR {positive}') + sleep(.2) + self.write(f'STAT:OPER:NTR {negative}') + sleep(5) + self.ask('STAT:OPER?') # clear register + sleep(.2) + + +class ThorlabsPM100D(Thorlab_PM100D): + pass From a300dc1c95e1b34911cce884e403388923e94073 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 20 Nov 2023 16:03:41 +0100 Subject: [PATCH 46/61] Raise Exception if no devices found --- qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index 9c7e77c9b..4fb2cc508 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -92,6 +92,8 @@ def _search_for_device(self) -> ctypes.Array[ctypes.c_char]: for i in range(0, deviceCount.value): self.tlpm.getRsrcName(ctypes.c_int(i), resourceName) break + else: + raise ValueError('No devices found.') return resourceName From dd446f4857cb7dce9931a0426e4197ec8f23bd80 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Wed, 15 Nov 2023 10:20:20 +0100 Subject: [PATCH 47/61] Bugfixes --- .../drivers/Thorlabs/private/kinesis/cc.py | 15 +++++---- .../drivers/Thorlabs/private/kinesis/core.py | 33 +++++++++++++------ .../drivers/Thorlabs/private/kinesis/isc.py | 20 +++++++---- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py index 5e8febb53..aedeb0a80 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -1,9 +1,12 @@ from __future__ import annotations import pathlib +from functools import partial from typing import Any, Mapping from qcodes.parameters import Parameter + +from . import enums from .core import KinesisInstrument, ThorlabsKinesis, to_enum @@ -24,12 +27,12 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', label="Position", instrument=self ) - """The position in degrees. + """The position in degrees. Note: - Use :meth:`move_to_position` with argument block=True to - block execution until the targeted position is reached. You - should probably invalidate the parameter cache afterwards. + Use :meth:`move_to_position` with argument block=False to + move asynchronously. You should probably invalidate the + parameter cache afterwards though. """ # Would be nice to use Group and GroupParameter here, but @@ -63,7 +66,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=lambda val: self._kinesis.set_jog_mode( val, self._kinesis.get_jog_mode()[1] ), - set_parser=to_enum, + set_parser=partial(to_enum, enum=enums.JogModes), label="Jog mode", instrument=self ) @@ -73,7 +76,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=lambda val: self._kinesis.set_jog_mode( self._kinesis.get_jog_mode()[0], val ), - set_parser=to_enum, + set_parser=partial(to_enum, enum=enums.StopModes), label="Stop mode", instrument=self ) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index afb63e5b5..c0fc9ec0d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -141,8 +141,7 @@ def to_enum(arg: EnumT | str | int, enum: Type[EnumT]) -> EnumT: """Return an instance of type enum for a given name, value, or the enum itself.""" if isinstance(arg, str): - # Try to catch at least single-noun names that are not capitalized. - return getattr(enum, arg.capitalize()) + return getattr(enum, arg) if isinstance(arg, int): return enum(arg) return arg @@ -365,8 +364,8 @@ def set_jog_mode(self, """ self.get_function('SetJogMode', check_errors=True)( - to_enum(jog_mode, enums.JogModes), - to_enum(stop_mode, enums.StopModes) + to_enum(jog_mode, enums.JogModes).value, + to_enum(stop_mode, enums.StopModes).value ) @register_prefix(['FF', 'ISC', 'CC']) @@ -384,7 +383,7 @@ def get_number_positions(self) -> int: """ return int(self.get_function('GetNumberPositions')()) - def get_position(self) -> int | float | str: + def get_position(self) -> int | float: """Get the current position. The current position is the last recorded position. @@ -395,9 +394,12 @@ def get_position(self) -> int | float | str: The current position in real units. """ self.request_status() - return self.real_value_from_device_unit( - self.get_function('GetPosition')(), enums.UnitType.Distance - ) + try: + return self.real_value_from_device_unit( + self.get_function('GetPosition')(), enums.UnitType.Distance + ) + except AttributeError: + return self.get_function('GetPosition')() @register_prefix(['FF', 'ISC', 'CC']) def move_to_position(self, position: int | float, @@ -421,8 +423,13 @@ def move_to_position(self, position: int | float, device_position = self.device_unit_from_real_value( float(position), enums.UnitType.Distance ) + # To avoid mismatch due to finite resolution + position = self.real_value_from_device_unit( + device_position, + enums.UnitType.Distance + ) except AttributeError: - device_position = int(position) + device_position = position = int(position) self.get_function('MoveToPosition', check_errors=True)(device_position) @@ -568,7 +575,13 @@ def stop_polling(self) -> None: self.get_function('StopPolling')() def get_polling_duration(self) -> int: - """Gets the polling loop duration.""" + """Gets the polling loop duration. + + Returns: + The time between polls in milliseconds or 0 if polling is + not active. + + """ return self.get_function('PollingDuration')() def set_polling_duration(self, duration: int) -> None: diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 55337daf9..19283836d 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -1,9 +1,12 @@ from __future__ import annotations import pathlib +from functools import partial from typing import Any, Mapping from qcodes.parameters import Parameter + +from . import enums from .core import KinesisInstrument, ThorlabsKinesis, to_enum @@ -27,11 +30,12 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', label="Position", instrument=self ) - """The position in degrees. - - Use :meth:`move_to_position` with argument block=True to block - execution until the targeted position is reached. You should - probably invalidate the parameter cache afterwards though. + """The position in degrees. + + Note: + Use :meth:`move_to_position` with argument block=False to + move asynchronously. You should probably invalidate the + parameter cache afterwards though. """ # Would be nice to use Group and GroupParameter here, but @@ -46,6 +50,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', label="Velocity", instrument=self ) + """The velocity in degrees per second.""" self.acceleration = Parameter( "acceleration", get_cmd=lambda: self._kinesis.get_vel_params()[1], @@ -56,6 +61,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', label="Acceleration", instrument=self ) + """The acceleration in degrees per square second.""" self.jog_mode = Parameter( "jog_mode", @@ -63,7 +69,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=lambda val: self._kinesis.set_jog_mode( val, self._kinesis.get_jog_mode()[1] ), - set_parser=to_enum, + set_parser=partial(to_enum, enum=enums.JogModes), label="Jog mode", instrument=self ) @@ -73,7 +79,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', set_cmd=lambda val: self._kinesis.set_jog_mode( self._kinesis.get_jog_mode()[0], val ), - set_parser=to_enum, + set_parser=partial(to_enum, enum=enums.StopModes), label="Stop mode", instrument=self ) From 75885aa3ee4bdff4868ed34ba0bb46d963504154 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Wed, 15 Nov 2023 10:27:57 +0100 Subject: [PATCH 48/61] Slightly simplify register_prefix --- .../drivers/Thorlabs/private/kinesis/core.py | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index c0fc9ec0d..3fd135db3 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -17,7 +17,7 @@ from typing import ( Any, Callable, Iterable, List, Mapping, Optional, Tuple, Type ) -from typing import Sequence, TypeVar +from typing import TypeVar from typing_extensions import ParamSpec @@ -147,7 +147,7 @@ def to_enum(arg: EnumT | str | int, enum: Type[EnumT]) -> EnumT: return arg -def register_prefix(prefixes: Sequence[str]) -> Callable: +def register_prefix(*prefixes: str) -> Callable: """Registers a warpped DLL function for a given Kinesis device. prefixes is a sequence of string identifier prefixes that are @@ -317,12 +317,12 @@ def get_status_bits(self) -> int: function.restype = ctypes.wintypes.DWORD # type: ignore[attr-defined] return function() - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def reset_rotation_modes(self): """Reset the rotation modes for a rotational device.""" self.get_function('ResetRotationModes', check_errors=True)() - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def set_rotation_modes(self, mode: enums.MovementModes | str | int, direction: enums.MovementDirections | str | int): """Set the rotation modes for a rotational device. @@ -368,12 +368,12 @@ def set_jog_mode(self, to_enum(stop_mode, enums.StopModes).value ) - @register_prefix(['FF', 'ISC', 'CC']) + @register_prefix('FF', 'ISC', 'CC') def identify(self) -> None: """Sends a command to the device to make it identify iteself.""" self.get_function('Identify')() - @register_prefix(['FF', 'ISC', 'CC']) + @register_prefix('FF', 'ISC', 'CC') def get_number_positions(self) -> int: """Get number of positions. @@ -401,9 +401,9 @@ def get_position(self) -> int | float: except AttributeError: return self.get_function('GetPosition')() - @register_prefix(['FF', 'ISC', 'CC']) def move_to_position(self, position: int | float, block: bool = False) -> None: + @register_prefix('FF', 'ISC', 'CC') """Move the device to the specified position (index). The motor may need to be Homed before a position can be set. See @@ -436,7 +436,7 @@ def move_to_position(self, position: int | float, while block and self.is_moving(): time.sleep(50e-3) - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def move_at_velocity( self, direction: enums.TravelDirection | str | int @@ -447,7 +447,7 @@ def move_at_velocity( to_enum(direction, enums.TravelDirection).value ) - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def move_relative(self, displacement: float): """Move the motor by a relative amount. @@ -463,7 +463,7 @@ def move_relative(self, displacement: float): device_displacement ) - @register_prefix(['FF', 'ISC', 'CC']) + @register_prefix('FF', 'ISC', 'CC') def is_moving(self) -> bool: """If the device is moving or not. @@ -532,7 +532,7 @@ def set_transit_time(self, transit_time: int) -> None: """ self.get_function('SetTransitTime', check_errors=True)(transit_time) - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def get_motor_params_ext(self) -> Tuple[float, float, float]: """Gets the motor stage parameters. @@ -550,7 +550,7 @@ def get_motor_params_ext(self) -> Tuple[float, float, float]: ) return stepsPerRev.value, gearBoxRatio.value, pitch.value - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def set_motor_params_ext(self, steps_per_rev: float, gearbox_ratio: float, pitch: float) -> None: """Sets the motor stage parameters. @@ -587,7 +587,8 @@ def get_polling_duration(self) -> int: def set_polling_duration(self, duration: int) -> None: """Stops polling and starts it again with given duration.""" self.stop_polling() - self.start_polling(duration) + if duration > 0: + self.start_polling(duration) def open(self) -> None: """Open the device for communications.""" @@ -597,7 +598,7 @@ def close(self) -> None: """Disconnect and close the device.""" self.get_function('Close')() - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def disable_channel(self) -> None: """Disable the channel so that motor can be moved by hand. @@ -606,7 +607,7 @@ def disable_channel(self) -> None: """ self.get_function('DisableChannel', check_errors=True)() - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def enable_channel(self) -> None: """Enable channel for computer control. @@ -615,8 +616,8 @@ def enable_channel(self) -> None: """ self.get_function('EnableChannel', check_errors=True)() - @register_prefix(['ISC', 'CC']) def stop(self, mode: enums.StopModes | int | str = 'Profiled') -> None: + @register_prefix('ISC', 'CC') """Stop the current move using the current velocity profile.""" mode = to_enum(mode, enums.StopModes) if mode == enums.StopModes.Immediate: @@ -626,18 +627,18 @@ def stop(self, mode: enums.StopModes | int | str = 'Profiled') -> None: else: raise ValueError('Invalid profile') - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def can_home(self) -> bool: """Can the device perform a Home.""" return bool(self.get_function('CanHome')()) - @register_prefix(['ISC', 'CC']) + @register_prefix('ISC', 'CC') def needs_homing(self) -> bool: """Can this device be moved without Homing.""" return not bool(self.get_function('CanMoveWithoutHomingFirst')()) - @register_prefix(['FF', 'ISC', 'CC']) def home(self) -> None: + @register_prefix('FF', 'ISC', 'CC') """Home the device. Homing the device will set the device to a known state and @@ -645,7 +646,7 @@ def home(self) -> None: """ self.get_function('Home', check_errors=True)() - @register_prefix(['FF', 'ISC', 'CC']) + @register_prefix('FF', 'ISC', 'CC') def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: modelNo = ctypes.create_string_buffer(64) type = ctypes.wintypes.WORD() From 8b6abcf42cf5266ecd647dc5164f2d68974801cd Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Wed, 15 Nov 2023 10:29:26 +0100 Subject: [PATCH 49/61] Default to blocking moves --- .../drivers/Thorlabs/private/kinesis/core.py | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 3fd135db3..8e362e41b 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -312,7 +312,8 @@ def get_status_bits(self) -> int: information on their meaning. """ - self.request_status_bits() + if not self.get_polling_duration(): + self.request_status() function = self.get_function('GetStatusBits') function.restype = ctypes.wintypes.DWORD # type: ignore[attr-defined] return function() @@ -401,9 +402,8 @@ def get_position(self) -> int | float: except AttributeError: return self.get_function('GetPosition')() - def move_to_position(self, position: int | float, - block: bool = False) -> None: @register_prefix('FF', 'ISC', 'CC') + def move_to_position(self, position: int | float, block: bool = True): """Move the device to the specified position (index). The motor may need to be Homed before a position can be set. See @@ -415,7 +415,8 @@ def move_to_position(self, position: int | float, flipper or in real units else. block: Block the interpreter until the target position is - reached. + reached. If a keyboard interrupt is sent while moving, + :meth:`stop` is called. """ try: @@ -433,8 +434,21 @@ def move_to_position(self, position: int | float, self.get_function('MoveToPosition', check_errors=True)(device_position) - while block and self.is_moving(): - time.sleep(50e-3) + if block: + # TODO: In principle, is_moving() should do, but does not. + # Neither request_status() nor request_status_bits() seem to do the + # job. Waiting for at least twice the polling duration should work, + # but can take much longer than necessary. Checking the message + # queue to see if the status changed also proved inconsistent. + + try: + while self.get_position() != position: + time.sleep(0.1) + except KeyboardInterrupt: + try: + self.stop() + except AttributeError: + return @register_prefix('ISC', 'CC') def move_at_velocity( From 6d018c9a150719706cdbe2ecc10c7e9182f11f73 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Wed, 15 Nov 2023 10:30:04 +0100 Subject: [PATCH 50/61] Implement jogging --- .../drivers/Thorlabs/private/kinesis/cc.py | 29 +++++++ .../drivers/Thorlabs/private/kinesis/core.py | 80 +++++++++++++++++++ .../drivers/Thorlabs/private/kinesis/isc.py | 29 +++++++ 3 files changed, 138 insertions(+) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py index aedeb0a80..9c08e5f2c 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -81,6 +81,35 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', instrument=self ) + self.jog_acceleration = Parameter( + 'jog_acceleration', + get_cmd=lambda: self._kinesis.get_jog_vel_params()[0], + set_cmd=lambda val: self._kinesis.set_jog_vel_params( + val, self.jog_max_velocity.get() + ), + unit=u"\u00b0/s\u00b2", + label="Jog acceleration", + instrument=self + ) + self.jog_max_velocity = Parameter( + 'jog_max_velocity', + get_cmd=lambda: self._kinesis.get_jog_vel_params()[1], + set_cmd=lambda val: self._kinesis.set_jog_vel_params( + self.jog_acceleration.get(), val + ), + unit=u"\u00b0/s", + label="Jog max velocity", + instrument=self + ) + self.jog_step_size = Parameter( + 'jog_step_size', + get_cmd=self._kinesis.get_jog_step_size, + set_cmd=self._kinesis.set_jog_step_size, + unit=u"\u00b0", + label="Jog step size", + instrument=self + ) + def _init_kinesis(self, dll_dir: str | pathlib.Path | None, simulation: bool) -> ThorlabsKinesis: return ThorlabsKinesis( diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 8e362e41b..1c351271e 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -369,6 +369,86 @@ def set_jog_mode(self, to_enum(stop_mode, enums.StopModes).value ) + def get_jog_step_size(self) -> float: + """Gets the distance to move when jogging.""" + return self.real_value_from_device_unit( + self.get_function('GetJogStepSize')(), + enums.UnitType.Distance + ) + + def set_jog_step_size(self, step_size: float): + """Sets the distance to move on jogging. + + Args: + step_size: The step in real units. + + """ + self.get_function('SetJogStepSize', check_errors=True)( + ctypes.c_uint(self.device_unit_from_real_value( + step_size, enums.UnitType.Distance + )) + ) + + def get_jog_vel_params(self) -> Tuple[float, float]: + """Gets the jog velocity parameters.""" + acceleration = ctypes.c_int() + max_velocity = ctypes.c_int() + self.get_function('GetJogVelParams', check_errors=True)( + ctypes.byref(acceleration), ctypes.byref(max_velocity) + ) + return ( + self.real_value_from_device_unit( + acceleration, enums.UnitType.Acceleration + ), + self.real_value_from_device_unit( + max_velocity, enums.UnitType.Velocity + ) + ) + + def set_jog_vel_params(self, acceleration: float, max_velocity: float): + """Sets jog velocity parameters. + + Args: + acceleration: The acceleration in real units. + max_velocity: The maximum velocity in real units. + + """ + self.get_function('SetJogVelParams', check_errors=True)( + self.device_unit_from_real_value( + acceleration, enums.UnitType.Acceleration + ), + self.device_unit_from_real_value( + max_velocity, enums.UnitType.Velocity + ) + ) + + @register_prefix('FF', 'ISC', 'CC') + def is_jogging(self) -> bool: + """If the device is jogging or not. + + Note that for the FilterFlipper devices, this is always false. + """ + status = self.get_status_bits() + return bool((status & 0x00000040) | (status & 0x00000080)) + + @register_prefix('ISC', 'CC') + def move_jog(self, jog_direction: enums.TravelDirection | str | int): + """Perform a jog. + + Args: + jog_direction: The jog direction. + + +----------+---+ + | Forwards | 1 | + +----------+---+ + | Reverse | 2 | + +----------+---+ + + """ + self.get_function('MoveJog', check_errors=True)( + to_enum(jog_direction, enums.TravelDirection).value + ) + @register_prefix('FF', 'ISC', 'CC') def identify(self) -> None: """Sends a command to the device to make it identify iteself.""" diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 19283836d..871d83439 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -84,6 +84,35 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', instrument=self ) + self.jog_acceleration = Parameter( + 'jog_acceleration', + get_cmd=lambda: self._kinesis.get_jog_vel_params()[0], + set_cmd=lambda val: self._kinesis.set_jog_vel_params( + val, self.jog_max_velocity.get() + ), + unit=u"\u00b0/s\u00b2", + label="Jog acceleration", + instrument=self + ) + self.jog_max_velocity = Parameter( + 'jog_max_velocity', + get_cmd=lambda: self._kinesis.get_jog_vel_params()[1], + set_cmd=lambda val: self._kinesis.set_jog_vel_params( + self.jog_acceleration.get(), val + ), + unit=u"\u00b0/s", + label="Jog max velocity", + instrument=self + ) + self.jog_step_size = Parameter( + 'jog_step_size', + get_cmd=self._kinesis.get_jog_step_size, + set_cmd=self._kinesis.set_jog_step_size, + unit=u"\u00b0", + label="Jog step size", + instrument=self + ) + def _init_kinesis(self, dll_dir: str | pathlib.Path | None, simulation: bool) -> ThorlabsKinesis: return ThorlabsKinesis( From 72d53651e12130f26bc49082248d2a9c4899d755 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Wed, 15 Nov 2023 10:30:34 +0100 Subject: [PATCH 51/61] Block while homing --- .../drivers/Thorlabs/private/kinesis/core.py | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 1c351271e..1d47eda98 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -726,13 +726,25 @@ def can_home(self) -> bool: """Can the device perform a Home.""" return bool(self.get_function('CanHome')()) + @register_prefix('ISC', 'CC') + def is_homing(self) -> bool: + """Is the device currently homing?""" + status = self.get_status_bits() + return bool(status & 0x00000200) + + @register_prefix('ISC', 'CC') + def is_homed(self) -> bool: + """Is the device homed?""" + status = self.get_status_bits() + return bool(status & 0x00000400) + @register_prefix('ISC', 'CC') def needs_homing(self) -> bool: """Can this device be moved without Homing.""" return not bool(self.get_function('CanMoveWithoutHomingFirst')()) - def home(self) -> None: @register_prefix('FF', 'ISC', 'CC') + def home(self, block: bool = True) -> None: """Home the device. Homing the device will set the device to a known state and @@ -740,6 +752,23 @@ def home(self) -> None: """ self.get_function('Home', check_errors=True)() + if block: + # TODO: In principle, is_homing() should do, but does not. + # Neither request_status() nor request_status_bits() seem to do the + # job. Waiting for at least twice the polling duration should work, + # but can take much longer than necessary. Hence, use the message + # queue to see if the status changed + self.clear_message_queue() + + try: + while not (self.message_queue_size and not self.is_homing()): + pass + except KeyboardInterrupt: + try: + self.stop() + except AttributeError: + return + @register_prefix('FF', 'ISC', 'CC') def get_hw_info(self) -> Tuple[str, int, int, str, str, int, int]: modelNo = ctypes.create_string_buffer(64) From 590b768a231f553fb124339022f6cc1dc0d6cf4f Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Wed, 15 Nov 2023 10:30:58 +0100 Subject: [PATCH 52/61] Use currently set stop mode for stopping. --- .../drivers/Thorlabs/private/kinesis/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 1d47eda98..2cdedb74f 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -710,10 +710,13 @@ def enable_channel(self) -> None: """ self.get_function('EnableChannel', check_errors=True)() - def stop(self, mode: enums.StopModes | int | str = 'Profiled') -> None: @register_prefix('ISC', 'CC') + def stop(self, mode: enums.StopModes | int | str | None = None) -> None: """Stop the current move using the current velocity profile.""" - mode = to_enum(mode, enums.StopModes) + if mode is None: + mode = self.get_jog_mode()[1] + else: + mode = to_enum(mode, enums.StopModes) if mode == enums.StopModes.Immediate: self.get_function('StopImmediate', check_errors=True)() elif mode == enums.StopModes.Profiled: From a19cea79657ef2ed9049296c36e66b35b8cc3078 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Wed, 15 Nov 2023 10:31:21 +0100 Subject: [PATCH 53/61] Add some message queue functionality --- .../drivers/Thorlabs/private/kinesis/core.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index 2cdedb74f..a827e714b 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -557,6 +557,15 @@ def move_relative(self, displacement: float): device_displacement ) + def clear_message_queue(self): + """Clears the device message queue.""" + self.get_function('ClearMessageQueue', check_errors=True)() + + @property + def message_queue_size(self) -> int: + """Gets the MessageQueue size.""" + return self.get_function('MessageQueueSize')() + @register_prefix('FF', 'ISC', 'CC') def is_moving(self) -> bool: """If the device is moving or not. From 6eb0708620609c2f00c93e48e6dd67b24f5e22b2 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 20 Nov 2023 17:24:06 +0100 Subject: [PATCH 54/61] Fix (ignore) Thorlabs PM100D tlpm driver for mypy --- pyproject.toml | 3 ++- .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 19d3829aa..f44d4fb6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,8 @@ show_error_codes = true [[tool.mypy.overrides]] module = [ - "qcodes_contrib_drivers.drivers.Spectrum.pyspcm" + "qcodes_contrib_drivers.drivers.Spectrum.pyspcm", + "qcodes_contrib_drivers.drivers.Thorlabs.Thorlabs_PM100D.tlpm", ] ignore_errors = true diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index 4fb2cc508..c349a9708 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -12,7 +12,7 @@ vxi_32 = os.environ.get('VXIPNPPATH') vxi_64 = os.environ.get('VXIPNPPATH64') -if vxi_32 is not None: +if sys.platform == 'win32' and vxi_32 is not None: if platform.architecture()[0][:2] == '64' and vxi_64 is not None: dll_path = pathlib.Path(vxi_64, 'Win64', 'Bin', 'TLPM_64.dll') else: @@ -59,6 +59,9 @@ def __init__(self, name: str, addr: str = '', reset: bool = False, thorlabs_tlpm: TLPM | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): + if sys.platform != 'win32': + raise NotImplementedError('This driver is only available on ' + 'Windows.') if vxi_32 is None: raise FileNotFoundError('IVI VXIPNP path not detected.') From d8f87b4106b46ffab01a786abf8f8744b18d874d Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Mon, 20 Nov 2023 22:01:14 +0100 Subject: [PATCH 55/61] Rename addr arg for consistency with visa driver --- .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index c349a9708..9b8c86ef6 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -36,7 +36,7 @@ class ThorlabsPM100D(Instrument): Args: name: An identifier for this instrument. - addr (optional): + address (optional): The USB address for the device. If omitted, the first available device found will be used. reset (optional): @@ -55,7 +55,7 @@ class ThorlabsPM100D(Instrument): """ - def __init__(self, name: str, addr: str = '', reset: bool = False, + def __init__(self, name: str, address: str = '', reset: bool = False, thorlabs_tlpm: TLPM | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): @@ -69,7 +69,7 @@ def __init__(self, name: str, addr: str = '', reset: bool = False, # NEED to call with IDQuery==True, otherwise the following error is # raised: NameError: The given session or object reference does not # support this operation. - self.tlpm.open(addr.encode() or self._search_for_device(), True, + self.tlpm.open(address.encode() or self._search_for_device(), True, reset) super().__init__(name, metadata, label) From 57f66eb6729554e0b5d7fcd05e4e1a93028ed484 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Tue, 21 Nov 2023 09:00:53 +0100 Subject: [PATCH 56/61] Fix mypy errors --- qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py | 3 ++- .../drivers/Thorlabs/private/kinesis/core.py | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py b/qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py index df3597edc..4cd29a224 100644 --- a/qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py +++ b/qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py @@ -32,10 +32,11 @@ class Dispatcher: 7: 'errAbort', 0xFFFFFFFF: 'errForce32bit'} + config: Mapping[str, str] + def __init__(self, cli, handle): self.cli = cli self.handle = handle - self.config: dict[str, str] def error_check(self, code: int): if code != 0: diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index a827e714b..a183fc09c 100644 --- a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -398,10 +398,10 @@ def get_jog_vel_params(self) -> Tuple[float, float]: ) return ( self.real_value_from_device_unit( - acceleration, enums.UnitType.Acceleration + acceleration.value, enums.UnitType.Acceleration ), self.real_value_from_device_unit( - max_velocity, enums.UnitType.Velocity + max_velocity.value, enums.UnitType.Velocity ) ) @@ -592,10 +592,10 @@ def get_vel_params(self) -> Tuple[float, float]: return ( self.real_value_from_device_unit( - acceleration, enums.UnitType.Acceleration + acceleration.value, enums.UnitType.Acceleration ), self.real_value_from_device_unit( - maxVelocity, enums.UnitType.Velocity + maxVelocity.value, enums.UnitType.Velocity ) ) @@ -833,7 +833,7 @@ def device_unit_from_real_value( def real_value_from_device_unit( self, - device_unit: ctypes.c_int, + device_unit: int, unit_type: enums.UnitType | int | str ) -> float: """Convert device units to real values. @@ -848,7 +848,7 @@ def real_value_from_device_unit( real_unit = ctypes.c_double() error_check(self.get_function('GetRealValueFromDeviceUnit'))( - device_unit, + ctypes.c_int(int(device_unit)), ctypes.byref(real_unit), unit_type.value ) From fff0dc13230b20fb67fb81a05c964eb8bc88980b Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Thu, 14 Mar 2024 17:18:02 +0100 Subject: [PATCH 57/61] Manually move to src-layout to merge main --- .../qcodes_contrib_drivers}/__init__.py | 0 .../qcodes_contrib_drivers}/_version.py | 0 .../qcodes_contrib_drivers}/drivers/Advantech/PCIE_1751.py | 0 .../qcodes_contrib_drivers}/drivers/Advantech/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Advantech/_bdaqctrl.h | 0 .../qcodes_contrib_drivers}/drivers/Agilent/Agilent_N9000A.py | 0 .../qcodes_contrib_drivers}/drivers/Agilent/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/AimTTi/EL320P.py | 0 .../qcodes_contrib_drivers}/drivers/AimTTi/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Andor/DU401.py | 0 .../qcodes_contrib_drivers}/drivers/Andor/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Attocube/ANC300.py | 0 .../qcodes_contrib_drivers}/drivers/Attocube/ANC300sim.py | 0 .../qcodes_contrib_drivers}/drivers/Attocube/ANC350.py | 0 .../drivers/Attocube/ANC350Lib/__init__.py | 0 .../drivers/Attocube/ANC350Lib/interface.py | 0 .../qcodes_contrib_drivers}/drivers/Attocube/ANC350Lib/v3.py | 0 .../qcodes_contrib_drivers}/drivers/Attocube/ANC350Lib/v4.py | 0 .../qcodes_contrib_drivers}/drivers/Attocube/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Aviosys/IP_Power_9258S.py | 0 .../qcodes_contrib_drivers}/drivers/Aviosys/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Bilt/ITest.py | 0 .../qcodes_contrib_drivers}/drivers/Bilt/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/BlueFors/BlueFors.py | 0 .../qcodes_contrib_drivers}/drivers/BlueFors/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/CMTS5048.py | 0 .../qcodes_contrib_drivers}/drivers/CopperMountain/M5180.py | 0 .../qcodes_contrib_drivers}/drivers/CopperMountain/S5048.py | 0 .../qcodes_contrib_drivers}/drivers/CopperMountain/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Cryocon/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Cryocon/cryocon_26.py | 0 .../qcodes_contrib_drivers}/drivers/Cryogenic/CryogenicSMS120C.py | 0 .../qcodes_contrib_drivers}/drivers/Cryogenic/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/DaylightSolutions/MIRcat.py | 0 .../qcodes_contrib_drivers}/drivers/DaylightSolutions/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/ERAInstruments/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/ERAInstruments/erasynth.py | 0 .../qcodes_contrib_drivers}/drivers/GeneralMicrowave/GM349.py | 0 .../qcodes_contrib_drivers}/drivers/GeneralMicrowave/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Gentec/Gentec_Maestro.py | 0 .../qcodes_contrib_drivers}/drivers/Gentec/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/HP/HP8594E.py | 0 .../qcodes_contrib_drivers}/drivers/Holzworth/HS900.py | 0 .../qcodes_contrib_drivers}/drivers/Holzworth/HS9008B.py | 0 .../qcodes_contrib_drivers}/drivers/Holzworth/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Horiba/Horiba_FHR.py | 0 .../qcodes_contrib_drivers}/drivers/Horiba/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Horiba/private/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Horiba/private/fhr_client.py | 0 .../qcodes_contrib_drivers}/drivers/Horiba/private/fhr_server.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/Keysight_E36313A.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/Keysight_E8267D.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/Keysight_J7211.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/Keysight_M3201A.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/Keysight_M3300A.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/M3202A.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/SD_common/SD_AWG.py | 0 .../drivers/Keysight/SD_common/SD_AWG_Async.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/SD_common/SD_DIG.py | 0 .../drivers/Keysight/SD_common/SD_Module.py | 0 .../drivers/Keysight/SD_common/__init__.py | 0 .../drivers/Keysight/SD_common/memory_manager.py | 0 .../qcodes_contrib_drivers}/drivers/Keysight/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Lakeshore/Model_331.py | 0 .../qcodes_contrib_drivers}/drivers/Lakeshore/Model_625.py | 0 .../qcodes_contrib_drivers}/drivers/Lakeshore/__init__.py | 0 .../drivers/LighthousePhotonics/Lighthouse_Photonics_Sprout_G.py | 0 .../drivers/LighthousePhotonics/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/M2/M2_Solstis_3.py | 0 .../qcodes_contrib_drivers}/drivers/M2/__init__.py | 0 .../drivers/MontanaInstruments/__init__.py | 0 .../drivers/MontanaInstruments/cryostation.py | 0 .../qcodes_contrib_drivers}/drivers/NationalInstruments/DAQ.py | 0 .../drivers/NationalInstruments/PXIe_2597.py | 0 .../drivers/NationalInstruments/PXIe_5654.py | 0 .../qcodes_contrib_drivers}/drivers/NationalInstruments/README.md | 0 .../qcodes_contrib_drivers}/drivers/NationalInstruments/RFSG.py | 0 .../qcodes_contrib_drivers}/drivers/NationalInstruments/Switch.py | 0 .../drivers/NationalInstruments/__init__.py | 0 .../drivers/NationalInstruments/dll_wrapper.py | 0 .../drivers/NationalInstruments/ni_dll_instrument.py | 0 .../drivers/NationalInstruments/visa_types.py | 0 .../qcodes_contrib_drivers}/drivers/Newport/AG_UC8.py | 0 .../qcodes_contrib_drivers}/drivers/Newport/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Oxford/ILM200.py | 0 .../qcodes_contrib_drivers}/drivers/Oxford/IPS120.py | 0 .../qcodes_contrib_drivers}/drivers/Oxford/Triton.py | 0 .../qcodes_contrib_drivers}/drivers/Oxford/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Oxford/kelvinox.py | 0 .../qcodes_contrib_drivers}/drivers/QDevil/QDAC1.py | 0 .../qcodes_contrib_drivers}/drivers/QDevil/QDAC2.py | 0 .../qcodes_contrib_drivers}/drivers/QDevil/QDAC2_Array.py | 0 .../qcodes_contrib_drivers}/drivers/QDevil/QSwitch.py | 0 .../qcodes_contrib_drivers}/drivers/QDevil/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/QuTech/D4.py | 0 .../qcodes_contrib_drivers}/drivers/QuTech/D5a.py | 0 .../qcodes_contrib_drivers}/drivers/QuTech/F1d.py | 0 .../qcodes_contrib_drivers}/drivers/QuTech/IVVI.py | 0 .../qcodes_contrib_drivers}/drivers/QuTech/M2j.py | 0 .../qcodes_contrib_drivers}/drivers/QuTech/S5i.py | 0 .../qcodes_contrib_drivers}/drivers/QuTech/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Rigol/Rigol_DSG3136B.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMC8041.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMC8042.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMC8043.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMP2020.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMP2030.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMP4030.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMP4040.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/SMB100A.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/SMR40.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/SMW200A.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/SMW200Asim.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/ZVL13.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/__init__.py | 0 .../drivers/RohdeSchwarz/private/HMC804x.py | 0 .../qcodes_contrib_drivers}/drivers/RohdeSchwarz/private/HMP.py | 0 .../drivers/RohdeSchwarz/private/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Shamrock/SR750.py | 0 .../qcodes_contrib_drivers}/drivers/Shamrock/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/SignalCore/SignalCore.py | 0 .../qcodes_contrib_drivers}/drivers/SignalCore/__init__.py | 0 .../drivers/SingleQuantum/SingleQuantum.py | 0 .../qcodes_contrib_drivers}/drivers/SingleQuantum/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Spectrum/M4i.py | 0 .../qcodes_contrib_drivers}/drivers/Spectrum/README.md | 0 .../qcodes_contrib_drivers}/drivers/Spectrum/__init__.py | 0 .../drivers/Spectrum/py_header/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Spectrum/py_header/h2py.py | 0 .../qcodes_contrib_drivers}/drivers/Spectrum/py_header/regs.py | 0 .../qcodes_contrib_drivers}/drivers/Spectrum/py_header/spcerr.py | 0 .../qcodes_contrib_drivers}/drivers/Spectrum/pyspcm.py | 0 .../qcodes_contrib_drivers}/drivers/Standa/Standa_10MWA168.py | 0 .../qcodes_contrib_drivers}/drivers/Standa/__init__.py | 0 .../drivers/StanfordResearchSystems/CS580.py | 0 .../drivers/StanfordResearchSystems/DG645.py | 0 .../drivers/StanfordResearchSystems/SIM928.py | 0 .../drivers/StanfordResearchSystems/SR844.py | 0 .../drivers/StanfordResearchSystems/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Tektronix/AFG3000.py | 0 .../qcodes_contrib_drivers}/drivers/Tektronix/AWG520.py | 0 .../drivers/Tektronix/Keithley_2000_Scan.py | 0 .../qcodes_contrib_drivers}/drivers/Tektronix/Keithley_2700.py | 0 .../qcodes_contrib_drivers}/drivers/Tektronix/Keithley_6430.py | 0 .../qcodes_contrib_drivers}/drivers/Tektronix/Keithley_6500.py | 0 .../qcodes_contrib_drivers}/drivers/Tektronix/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Thermotek/Thermotek_T255p.py | 0 .../qcodes_contrib_drivers}/drivers/Thermotek/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Thorlabs/KDC101.py | 0 .../qcodes_contrib_drivers}/drivers/Thorlabs/KLS1550.py | 0 .../qcodes_contrib_drivers}/drivers/Thorlabs/TDC001.py | 0 .../drivers/Thorlabs/Thorlabs_K10CR1/__init__.py | 0 .../drivers/Thorlabs/Thorlabs_K10CR1/apt.py | 0 .../drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py | 0 .../drivers/Thorlabs/Thorlabs_MFF10x/__init__.py | 0 .../drivers/Thorlabs/Thorlabs_MFF10x/apt.py | 0 .../drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py | 0 .../drivers/Thorlabs/Thorlabs_PM100D/__init__.py | 0 .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 0 .../drivers/Thorlabs/Thorlabs_PM100D/visa.py | 0 .../drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py | 0 .../drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py | 0 .../qcodes_contrib_drivers}/drivers/Thorlabs/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/Thorlabs/private/APT.py | 0 .../qcodes_contrib_drivers}/drivers/Thorlabs/private/CC.py | 0 .../qcodes_contrib_drivers}/drivers/Thorlabs/private/LS.py | 0 .../qcodes_contrib_drivers}/drivers/Thorlabs/private/__init__.py | 0 .../drivers/Thorlabs/private/kinesis/__init__.py | 0 .../drivers/Thorlabs/private/kinesis/cc.py | 0 .../drivers/Thorlabs/private/kinesis/core.py | 0 .../drivers/Thorlabs/private/kinesis/enums.py | 0 .../drivers/Thorlabs/private/kinesis/isc.py | 0 .../drivers/Thorlabs/private/kinesis/structs.py | 0 .../qcodes_contrib_drivers}/drivers/Vaunix/LDA.py | 0 .../qcodes_contrib_drivers}/drivers/Vaunix/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/ZurichInstruments/HF2LI.py | 0 .../qcodes_contrib_drivers}/drivers/ZurichInstruments/ZIHDAWG8.py | 0 .../qcodes_contrib_drivers}/drivers/ZurichInstruments/__init__.py | 0 .../qcodes_contrib_drivers}/drivers/__init__.py | 0 .../qcodes_contrib_drivers}/sims/HP8594E.yaml | 0 .../qcodes_contrib_drivers}/sims/QDAC2.yaml | 0 .../qcodes_contrib_drivers}/sims/QSwitch.yaml | 0 .../qcodes_contrib_drivers}/sims/__init__.py | 0 {qcodes_contrib_drivers/tests => tests}/HP/test_HP8594E.py | 0 .../tests => tests}/Keysight/test_memory_manager.py | 0 {qcodes_contrib_drivers/tests => tests}/QDevil/__init__.py | 0 {qcodes_contrib_drivers/tests => tests}/QDevil/common.py | 0 {qcodes_contrib_drivers/tests => tests}/QDevil/pytest.ini | 0 {qcodes_contrib_drivers/tests => tests}/QDevil/readme.md | 0 .../tests => tests}/QDevil/real_qdac2_fixtures.py | 0 .../tests => tests}/QDevil/sim_qdac2_fixtures.py | 0 .../tests => tests}/QDevil/sim_qswitch_fixtures.py | 0 .../tests => tests}/QDevil/test_real_qdac2_ieee_std.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_abstract_channel.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_arrangement.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_array.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_awg.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_channels.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_common.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_current.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_external.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_ieee_std.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_init.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_leakage.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_sine.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_source_dc.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_source_list.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_source_output_mode.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_source_sweep.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_square.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_system.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_triangle.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_triggers.py | 0 .../tests => tests}/QDevil/test_sim_qdac2_virtual_gates.py | 0 .../tests => tests}/QDevil/test_sim_qswitch_direct_operation.py | 0 .../tests => tests}/QDevil/test_sim_qswitch_errors.py | 0 .../tests => tests}/QDevil/test_sim_qswitch_ieee_std.py | 0 .../tests => tests}/QDevil/test_sim_qswitch_init.py | 0 .../tests => tests}/QDevil/test_sim_qswitch_internal.py | 0 .../tests => tests}/QDevil/test_sim_qswitch_operation.py | 0 .../tests => tests}/QDevil/test_sim_qswitch_snapshots.py | 0 {qcodes_contrib_drivers/tests => tests}/__init__.py | 0 {qcodes_contrib_drivers/tests => tests}/test_Keysight_M3201A.py | 0 {qcodes_contrib_drivers/tests => tests}/test_Spectrum_M4i.py | 0 {qcodes_contrib_drivers/tests => tests}/test_m2j.py | 0 {qcodes_contrib_drivers/tests => tests}/test_ni_pxie_2597.py | 0 .../tests => tests}/test_rohdeschwarz_HMC804x.py | 0 {qcodes_contrib_drivers/tests => tests}/test_zihdawg8.py | 0 228 files changed, 0 insertions(+), 0 deletions(-) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/_version.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Advantech/PCIE_1751.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Advantech/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Advantech/_bdaqctrl.h (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Agilent/Agilent_N9000A.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Agilent/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/AimTTi/EL320P.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/AimTTi/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Andor/DU401.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Andor/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Attocube/ANC300.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Attocube/ANC300sim.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Attocube/ANC350.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Attocube/ANC350Lib/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Attocube/ANC350Lib/interface.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Attocube/ANC350Lib/v3.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Attocube/ANC350Lib/v4.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Attocube/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Aviosys/IP_Power_9258S.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Aviosys/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Bilt/ITest.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Bilt/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/BlueFors/BlueFors.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/BlueFors/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/CMTS5048.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/CopperMountain/M5180.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/CopperMountain/S5048.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/CopperMountain/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Cryocon/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Cryocon/cryocon_26.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Cryogenic/CryogenicSMS120C.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Cryogenic/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/DaylightSolutions/MIRcat.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/DaylightSolutions/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/ERAInstruments/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/ERAInstruments/erasynth.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/GeneralMicrowave/GM349.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/GeneralMicrowave/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Gentec/Gentec_Maestro.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Gentec/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/HP/HP8594E.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Holzworth/HS900.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Holzworth/HS9008B.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Holzworth/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Horiba/Horiba_FHR.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Horiba/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Horiba/private/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Horiba/private/fhr_client.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Horiba/private/fhr_server.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/Keysight_E36313A.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/Keysight_E8267D.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/Keysight_J7211.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/Keysight_M3201A.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/Keysight_M3300A.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/M3202A.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/SD_common/SD_AWG.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/SD_common/SD_AWG_Async.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/SD_common/SD_DIG.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/SD_common/SD_Module.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/SD_common/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/SD_common/memory_manager.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Keysight/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Lakeshore/Model_331.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Lakeshore/Model_625.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Lakeshore/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/LighthousePhotonics/Lighthouse_Photonics_Sprout_G.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/LighthousePhotonics/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/M2/M2_Solstis_3.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/M2/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/MontanaInstruments/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/MontanaInstruments/cryostation.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/DAQ.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/PXIe_2597.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/PXIe_5654.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/README.md (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/RFSG.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/Switch.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/dll_wrapper.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/ni_dll_instrument.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/NationalInstruments/visa_types.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Newport/AG_UC8.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Newport/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Oxford/ILM200.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Oxford/IPS120.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Oxford/Triton.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Oxford/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Oxford/kelvinox.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QDevil/QDAC1.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QDevil/QDAC2.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QDevil/QDAC2_Array.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QDevil/QSwitch.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QDevil/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QuTech/D4.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QuTech/D5a.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QuTech/F1d.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QuTech/IVVI.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QuTech/M2j.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QuTech/S5i.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/QuTech/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Rigol/Rigol_DSG3136B.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMC8041.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMC8042.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMC8043.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMP2020.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMP2030.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMP4030.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/HMP4040.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/SMB100A.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/SMR40.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/SMW200A.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/SMW200Asim.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/ZVL13.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/private/HMC804x.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/private/HMP.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/RohdeSchwarz/private/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Shamrock/SR750.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Shamrock/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/SignalCore/SignalCore.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/SignalCore/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/SingleQuantum/SingleQuantum.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/SingleQuantum/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Spectrum/M4i.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Spectrum/README.md (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Spectrum/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Spectrum/py_header/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Spectrum/py_header/h2py.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Spectrum/py_header/regs.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Spectrum/py_header/spcerr.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Spectrum/pyspcm.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Standa/Standa_10MWA168.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Standa/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/StanfordResearchSystems/CS580.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/StanfordResearchSystems/DG645.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/StanfordResearchSystems/SIM928.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/StanfordResearchSystems/SR844.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/StanfordResearchSystems/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Tektronix/AFG3000.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Tektronix/AWG520.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Tektronix/Keithley_2000_Scan.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Tektronix/Keithley_2700.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Tektronix/Keithley_6430.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Tektronix/Keithley_6500.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Tektronix/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thermotek/Thermotek_T255p.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thermotek/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/KDC101.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/KLS1550.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/TDC001.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_K10CR1/apt.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_MFF10x/apt.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_PM100D/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_PM100D/visa.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/APT.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/CC.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/LS.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/kinesis/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/kinesis/cc.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/kinesis/core.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/kinesis/enums.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/kinesis/isc.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Thorlabs/private/kinesis/structs.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Vaunix/LDA.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/Vaunix/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/ZurichInstruments/HF2LI.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/ZurichInstruments/ZIHDAWG8.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/ZurichInstruments/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/drivers/__init__.py (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/sims/HP8594E.yaml (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/sims/QDAC2.yaml (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/sims/QSwitch.yaml (100%) rename {qcodes_contrib_drivers => src/qcodes_contrib_drivers}/sims/__init__.py (100%) rename {qcodes_contrib_drivers/tests => tests}/HP/test_HP8594E.py (100%) rename {qcodes_contrib_drivers/tests => tests}/Keysight/test_memory_manager.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/__init__.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/common.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/pytest.ini (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/readme.md (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/real_qdac2_fixtures.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/sim_qdac2_fixtures.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/sim_qswitch_fixtures.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_real_qdac2_ieee_std.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_abstract_channel.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_arrangement.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_array.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_awg.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_channels.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_common.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_current.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_external.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_ieee_std.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_init.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_leakage.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_sine.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_source_dc.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_source_list.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_source_output_mode.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_source_sweep.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_square.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_system.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_triangle.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_triggers.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qdac2_virtual_gates.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qswitch_direct_operation.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qswitch_errors.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qswitch_ieee_std.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qswitch_init.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qswitch_internal.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qswitch_operation.py (100%) rename {qcodes_contrib_drivers/tests => tests}/QDevil/test_sim_qswitch_snapshots.py (100%) rename {qcodes_contrib_drivers/tests => tests}/__init__.py (100%) rename {qcodes_contrib_drivers/tests => tests}/test_Keysight_M3201A.py (100%) rename {qcodes_contrib_drivers/tests => tests}/test_Spectrum_M4i.py (100%) rename {qcodes_contrib_drivers/tests => tests}/test_m2j.py (100%) rename {qcodes_contrib_drivers/tests => tests}/test_ni_pxie_2597.py (100%) rename {qcodes_contrib_drivers/tests => tests}/test_rohdeschwarz_HMC804x.py (100%) rename {qcodes_contrib_drivers/tests => tests}/test_zihdawg8.py (100%) diff --git a/qcodes_contrib_drivers/__init__.py b/src/qcodes_contrib_drivers/__init__.py similarity index 100% rename from qcodes_contrib_drivers/__init__.py rename to src/qcodes_contrib_drivers/__init__.py diff --git a/qcodes_contrib_drivers/_version.py b/src/qcodes_contrib_drivers/_version.py similarity index 100% rename from qcodes_contrib_drivers/_version.py rename to src/qcodes_contrib_drivers/_version.py diff --git a/qcodes_contrib_drivers/drivers/Advantech/PCIE_1751.py b/src/qcodes_contrib_drivers/drivers/Advantech/PCIE_1751.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Advantech/PCIE_1751.py rename to src/qcodes_contrib_drivers/drivers/Advantech/PCIE_1751.py diff --git a/qcodes_contrib_drivers/drivers/Advantech/__init__.py b/src/qcodes_contrib_drivers/drivers/Advantech/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Advantech/__init__.py rename to src/qcodes_contrib_drivers/drivers/Advantech/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Advantech/_bdaqctrl.h b/src/qcodes_contrib_drivers/drivers/Advantech/_bdaqctrl.h similarity index 100% rename from qcodes_contrib_drivers/drivers/Advantech/_bdaqctrl.h rename to src/qcodes_contrib_drivers/drivers/Advantech/_bdaqctrl.h diff --git a/qcodes_contrib_drivers/drivers/Agilent/Agilent_N9000A.py b/src/qcodes_contrib_drivers/drivers/Agilent/Agilent_N9000A.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Agilent/Agilent_N9000A.py rename to src/qcodes_contrib_drivers/drivers/Agilent/Agilent_N9000A.py diff --git a/qcodes_contrib_drivers/drivers/Agilent/__init__.py b/src/qcodes_contrib_drivers/drivers/Agilent/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Agilent/__init__.py rename to src/qcodes_contrib_drivers/drivers/Agilent/__init__.py diff --git a/qcodes_contrib_drivers/drivers/AimTTi/EL320P.py b/src/qcodes_contrib_drivers/drivers/AimTTi/EL320P.py similarity index 100% rename from qcodes_contrib_drivers/drivers/AimTTi/EL320P.py rename to src/qcodes_contrib_drivers/drivers/AimTTi/EL320P.py diff --git a/qcodes_contrib_drivers/drivers/AimTTi/__init__.py b/src/qcodes_contrib_drivers/drivers/AimTTi/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/AimTTi/__init__.py rename to src/qcodes_contrib_drivers/drivers/AimTTi/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Andor/DU401.py b/src/qcodes_contrib_drivers/drivers/Andor/DU401.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Andor/DU401.py rename to src/qcodes_contrib_drivers/drivers/Andor/DU401.py diff --git a/qcodes_contrib_drivers/drivers/Andor/__init__.py b/src/qcodes_contrib_drivers/drivers/Andor/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Andor/__init__.py rename to src/qcodes_contrib_drivers/drivers/Andor/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Attocube/ANC300.py b/src/qcodes_contrib_drivers/drivers/Attocube/ANC300.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Attocube/ANC300.py rename to src/qcodes_contrib_drivers/drivers/Attocube/ANC300.py diff --git a/qcodes_contrib_drivers/drivers/Attocube/ANC300sim.py b/src/qcodes_contrib_drivers/drivers/Attocube/ANC300sim.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Attocube/ANC300sim.py rename to src/qcodes_contrib_drivers/drivers/Attocube/ANC300sim.py diff --git a/qcodes_contrib_drivers/drivers/Attocube/ANC350.py b/src/qcodes_contrib_drivers/drivers/Attocube/ANC350.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Attocube/ANC350.py rename to src/qcodes_contrib_drivers/drivers/Attocube/ANC350.py diff --git a/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/__init__.py b/src/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/__init__.py rename to src/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/interface.py b/src/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/interface.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/interface.py rename to src/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/interface.py diff --git a/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/v3.py b/src/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/v3.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/v3.py rename to src/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/v3.py diff --git a/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/v4.py b/src/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/v4.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/v4.py rename to src/qcodes_contrib_drivers/drivers/Attocube/ANC350Lib/v4.py diff --git a/qcodes_contrib_drivers/drivers/Attocube/__init__.py b/src/qcodes_contrib_drivers/drivers/Attocube/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Attocube/__init__.py rename to src/qcodes_contrib_drivers/drivers/Attocube/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Aviosys/IP_Power_9258S.py b/src/qcodes_contrib_drivers/drivers/Aviosys/IP_Power_9258S.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Aviosys/IP_Power_9258S.py rename to src/qcodes_contrib_drivers/drivers/Aviosys/IP_Power_9258S.py diff --git a/qcodes_contrib_drivers/drivers/Aviosys/__init__.py b/src/qcodes_contrib_drivers/drivers/Aviosys/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Aviosys/__init__.py rename to src/qcodes_contrib_drivers/drivers/Aviosys/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Bilt/ITest.py b/src/qcodes_contrib_drivers/drivers/Bilt/ITest.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Bilt/ITest.py rename to src/qcodes_contrib_drivers/drivers/Bilt/ITest.py diff --git a/qcodes_contrib_drivers/drivers/Bilt/__init__.py b/src/qcodes_contrib_drivers/drivers/Bilt/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Bilt/__init__.py rename to src/qcodes_contrib_drivers/drivers/Bilt/__init__.py diff --git a/qcodes_contrib_drivers/drivers/BlueFors/BlueFors.py b/src/qcodes_contrib_drivers/drivers/BlueFors/BlueFors.py similarity index 100% rename from qcodes_contrib_drivers/drivers/BlueFors/BlueFors.py rename to src/qcodes_contrib_drivers/drivers/BlueFors/BlueFors.py diff --git a/qcodes_contrib_drivers/drivers/BlueFors/__init__.py b/src/qcodes_contrib_drivers/drivers/BlueFors/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/BlueFors/__init__.py rename to src/qcodes_contrib_drivers/drivers/BlueFors/__init__.py diff --git a/qcodes_contrib_drivers/drivers/CMTS5048.py b/src/qcodes_contrib_drivers/drivers/CMTS5048.py similarity index 100% rename from qcodes_contrib_drivers/drivers/CMTS5048.py rename to src/qcodes_contrib_drivers/drivers/CMTS5048.py diff --git a/qcodes_contrib_drivers/drivers/CopperMountain/M5180.py b/src/qcodes_contrib_drivers/drivers/CopperMountain/M5180.py similarity index 100% rename from qcodes_contrib_drivers/drivers/CopperMountain/M5180.py rename to src/qcodes_contrib_drivers/drivers/CopperMountain/M5180.py diff --git a/qcodes_contrib_drivers/drivers/CopperMountain/S5048.py b/src/qcodes_contrib_drivers/drivers/CopperMountain/S5048.py similarity index 100% rename from qcodes_contrib_drivers/drivers/CopperMountain/S5048.py rename to src/qcodes_contrib_drivers/drivers/CopperMountain/S5048.py diff --git a/qcodes_contrib_drivers/drivers/CopperMountain/__init__.py b/src/qcodes_contrib_drivers/drivers/CopperMountain/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/CopperMountain/__init__.py rename to src/qcodes_contrib_drivers/drivers/CopperMountain/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Cryocon/__init__.py b/src/qcodes_contrib_drivers/drivers/Cryocon/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Cryocon/__init__.py rename to src/qcodes_contrib_drivers/drivers/Cryocon/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Cryocon/cryocon_26.py b/src/qcodes_contrib_drivers/drivers/Cryocon/cryocon_26.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Cryocon/cryocon_26.py rename to src/qcodes_contrib_drivers/drivers/Cryocon/cryocon_26.py diff --git a/qcodes_contrib_drivers/drivers/Cryogenic/CryogenicSMS120C.py b/src/qcodes_contrib_drivers/drivers/Cryogenic/CryogenicSMS120C.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Cryogenic/CryogenicSMS120C.py rename to src/qcodes_contrib_drivers/drivers/Cryogenic/CryogenicSMS120C.py diff --git a/qcodes_contrib_drivers/drivers/Cryogenic/__init__.py b/src/qcodes_contrib_drivers/drivers/Cryogenic/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Cryogenic/__init__.py rename to src/qcodes_contrib_drivers/drivers/Cryogenic/__init__.py diff --git a/qcodes_contrib_drivers/drivers/DaylightSolutions/MIRcat.py b/src/qcodes_contrib_drivers/drivers/DaylightSolutions/MIRcat.py similarity index 100% rename from qcodes_contrib_drivers/drivers/DaylightSolutions/MIRcat.py rename to src/qcodes_contrib_drivers/drivers/DaylightSolutions/MIRcat.py diff --git a/qcodes_contrib_drivers/drivers/DaylightSolutions/__init__.py b/src/qcodes_contrib_drivers/drivers/DaylightSolutions/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/DaylightSolutions/__init__.py rename to src/qcodes_contrib_drivers/drivers/DaylightSolutions/__init__.py diff --git a/qcodes_contrib_drivers/drivers/ERAInstruments/__init__.py b/src/qcodes_contrib_drivers/drivers/ERAInstruments/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/ERAInstruments/__init__.py rename to src/qcodes_contrib_drivers/drivers/ERAInstruments/__init__.py diff --git a/qcodes_contrib_drivers/drivers/ERAInstruments/erasynth.py b/src/qcodes_contrib_drivers/drivers/ERAInstruments/erasynth.py similarity index 100% rename from qcodes_contrib_drivers/drivers/ERAInstruments/erasynth.py rename to src/qcodes_contrib_drivers/drivers/ERAInstruments/erasynth.py diff --git a/qcodes_contrib_drivers/drivers/GeneralMicrowave/GM349.py b/src/qcodes_contrib_drivers/drivers/GeneralMicrowave/GM349.py similarity index 100% rename from qcodes_contrib_drivers/drivers/GeneralMicrowave/GM349.py rename to src/qcodes_contrib_drivers/drivers/GeneralMicrowave/GM349.py diff --git a/qcodes_contrib_drivers/drivers/GeneralMicrowave/__init__.py b/src/qcodes_contrib_drivers/drivers/GeneralMicrowave/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/GeneralMicrowave/__init__.py rename to src/qcodes_contrib_drivers/drivers/GeneralMicrowave/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Gentec/Gentec_Maestro.py b/src/qcodes_contrib_drivers/drivers/Gentec/Gentec_Maestro.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Gentec/Gentec_Maestro.py rename to src/qcodes_contrib_drivers/drivers/Gentec/Gentec_Maestro.py diff --git a/qcodes_contrib_drivers/drivers/Gentec/__init__.py b/src/qcodes_contrib_drivers/drivers/Gentec/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Gentec/__init__.py rename to src/qcodes_contrib_drivers/drivers/Gentec/__init__.py diff --git a/qcodes_contrib_drivers/drivers/HP/HP8594E.py b/src/qcodes_contrib_drivers/drivers/HP/HP8594E.py similarity index 100% rename from qcodes_contrib_drivers/drivers/HP/HP8594E.py rename to src/qcodes_contrib_drivers/drivers/HP/HP8594E.py diff --git a/qcodes_contrib_drivers/drivers/Holzworth/HS900.py b/src/qcodes_contrib_drivers/drivers/Holzworth/HS900.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Holzworth/HS900.py rename to src/qcodes_contrib_drivers/drivers/Holzworth/HS900.py diff --git a/qcodes_contrib_drivers/drivers/Holzworth/HS9008B.py b/src/qcodes_contrib_drivers/drivers/Holzworth/HS9008B.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Holzworth/HS9008B.py rename to src/qcodes_contrib_drivers/drivers/Holzworth/HS9008B.py diff --git a/qcodes_contrib_drivers/drivers/Holzworth/__init__.py b/src/qcodes_contrib_drivers/drivers/Holzworth/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Holzworth/__init__.py rename to src/qcodes_contrib_drivers/drivers/Holzworth/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py b/src/qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py rename to src/qcodes_contrib_drivers/drivers/Horiba/Horiba_FHR.py diff --git a/qcodes_contrib_drivers/drivers/Horiba/__init__.py b/src/qcodes_contrib_drivers/drivers/Horiba/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Horiba/__init__.py rename to src/qcodes_contrib_drivers/drivers/Horiba/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Horiba/private/__init__.py b/src/qcodes_contrib_drivers/drivers/Horiba/private/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Horiba/private/__init__.py rename to src/qcodes_contrib_drivers/drivers/Horiba/private/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Horiba/private/fhr_client.py b/src/qcodes_contrib_drivers/drivers/Horiba/private/fhr_client.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Horiba/private/fhr_client.py rename to src/qcodes_contrib_drivers/drivers/Horiba/private/fhr_client.py diff --git a/qcodes_contrib_drivers/drivers/Horiba/private/fhr_server.py b/src/qcodes_contrib_drivers/drivers/Horiba/private/fhr_server.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Horiba/private/fhr_server.py rename to src/qcodes_contrib_drivers/drivers/Horiba/private/fhr_server.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/Keysight_E36313A.py b/src/qcodes_contrib_drivers/drivers/Keysight/Keysight_E36313A.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/Keysight_E36313A.py rename to src/qcodes_contrib_drivers/drivers/Keysight/Keysight_E36313A.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/Keysight_E8267D.py b/src/qcodes_contrib_drivers/drivers/Keysight/Keysight_E8267D.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/Keysight_E8267D.py rename to src/qcodes_contrib_drivers/drivers/Keysight/Keysight_E8267D.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/Keysight_J7211.py b/src/qcodes_contrib_drivers/drivers/Keysight/Keysight_J7211.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/Keysight_J7211.py rename to src/qcodes_contrib_drivers/drivers/Keysight/Keysight_J7211.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/Keysight_M3201A.py b/src/qcodes_contrib_drivers/drivers/Keysight/Keysight_M3201A.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/Keysight_M3201A.py rename to src/qcodes_contrib_drivers/drivers/Keysight/Keysight_M3201A.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/Keysight_M3300A.py b/src/qcodes_contrib_drivers/drivers/Keysight/Keysight_M3300A.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/Keysight_M3300A.py rename to src/qcodes_contrib_drivers/drivers/Keysight/Keysight_M3300A.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/M3202A.py b/src/qcodes_contrib_drivers/drivers/Keysight/M3202A.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/M3202A.py rename to src/qcodes_contrib_drivers/drivers/Keysight/M3202A.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_AWG.py b/src/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_AWG.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_AWG.py rename to src/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_AWG.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_AWG_Async.py b/src/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_AWG_Async.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_AWG_Async.py rename to src/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_AWG_Async.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_DIG.py b/src/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_DIG.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_DIG.py rename to src/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_DIG.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_Module.py b/src/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_Module.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_Module.py rename to src/qcodes_contrib_drivers/drivers/Keysight/SD_common/SD_Module.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/SD_common/__init__.py b/src/qcodes_contrib_drivers/drivers/Keysight/SD_common/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/SD_common/__init__.py rename to src/qcodes_contrib_drivers/drivers/Keysight/SD_common/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/SD_common/memory_manager.py b/src/qcodes_contrib_drivers/drivers/Keysight/SD_common/memory_manager.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/SD_common/memory_manager.py rename to src/qcodes_contrib_drivers/drivers/Keysight/SD_common/memory_manager.py diff --git a/qcodes_contrib_drivers/drivers/Keysight/__init__.py b/src/qcodes_contrib_drivers/drivers/Keysight/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Keysight/__init__.py rename to src/qcodes_contrib_drivers/drivers/Keysight/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Lakeshore/Model_331.py b/src/qcodes_contrib_drivers/drivers/Lakeshore/Model_331.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Lakeshore/Model_331.py rename to src/qcodes_contrib_drivers/drivers/Lakeshore/Model_331.py diff --git a/qcodes_contrib_drivers/drivers/Lakeshore/Model_625.py b/src/qcodes_contrib_drivers/drivers/Lakeshore/Model_625.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Lakeshore/Model_625.py rename to src/qcodes_contrib_drivers/drivers/Lakeshore/Model_625.py diff --git a/qcodes_contrib_drivers/drivers/Lakeshore/__init__.py b/src/qcodes_contrib_drivers/drivers/Lakeshore/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Lakeshore/__init__.py rename to src/qcodes_contrib_drivers/drivers/Lakeshore/__init__.py diff --git a/qcodes_contrib_drivers/drivers/LighthousePhotonics/Lighthouse_Photonics_Sprout_G.py b/src/qcodes_contrib_drivers/drivers/LighthousePhotonics/Lighthouse_Photonics_Sprout_G.py similarity index 100% rename from qcodes_contrib_drivers/drivers/LighthousePhotonics/Lighthouse_Photonics_Sprout_G.py rename to src/qcodes_contrib_drivers/drivers/LighthousePhotonics/Lighthouse_Photonics_Sprout_G.py diff --git a/qcodes_contrib_drivers/drivers/LighthousePhotonics/__init__.py b/src/qcodes_contrib_drivers/drivers/LighthousePhotonics/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/LighthousePhotonics/__init__.py rename to src/qcodes_contrib_drivers/drivers/LighthousePhotonics/__init__.py diff --git a/qcodes_contrib_drivers/drivers/M2/M2_Solstis_3.py b/src/qcodes_contrib_drivers/drivers/M2/M2_Solstis_3.py similarity index 100% rename from qcodes_contrib_drivers/drivers/M2/M2_Solstis_3.py rename to src/qcodes_contrib_drivers/drivers/M2/M2_Solstis_3.py diff --git a/qcodes_contrib_drivers/drivers/M2/__init__.py b/src/qcodes_contrib_drivers/drivers/M2/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/M2/__init__.py rename to src/qcodes_contrib_drivers/drivers/M2/__init__.py diff --git a/qcodes_contrib_drivers/drivers/MontanaInstruments/__init__.py b/src/qcodes_contrib_drivers/drivers/MontanaInstruments/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/MontanaInstruments/__init__.py rename to src/qcodes_contrib_drivers/drivers/MontanaInstruments/__init__.py diff --git a/qcodes_contrib_drivers/drivers/MontanaInstruments/cryostation.py b/src/qcodes_contrib_drivers/drivers/MontanaInstruments/cryostation.py similarity index 100% rename from qcodes_contrib_drivers/drivers/MontanaInstruments/cryostation.py rename to src/qcodes_contrib_drivers/drivers/MontanaInstruments/cryostation.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/DAQ.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/DAQ.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/DAQ.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/DAQ.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/PXIe_2597.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/PXIe_2597.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/PXIe_2597.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/PXIe_2597.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/PXIe_5654.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/PXIe_5654.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/PXIe_5654.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/PXIe_5654.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/README.md b/src/qcodes_contrib_drivers/drivers/NationalInstruments/README.md similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/README.md rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/README.md diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/RFSG.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/RFSG.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/RFSG.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/RFSG.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/Switch.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/Switch.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/Switch.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/Switch.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/__init__.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/__init__.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/__init__.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/dll_wrapper.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/dll_wrapper.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/dll_wrapper.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/dll_wrapper.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/ni_dll_instrument.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/ni_dll_instrument.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/ni_dll_instrument.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/ni_dll_instrument.py diff --git a/qcodes_contrib_drivers/drivers/NationalInstruments/visa_types.py b/src/qcodes_contrib_drivers/drivers/NationalInstruments/visa_types.py similarity index 100% rename from qcodes_contrib_drivers/drivers/NationalInstruments/visa_types.py rename to src/qcodes_contrib_drivers/drivers/NationalInstruments/visa_types.py diff --git a/qcodes_contrib_drivers/drivers/Newport/AG_UC8.py b/src/qcodes_contrib_drivers/drivers/Newport/AG_UC8.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Newport/AG_UC8.py rename to src/qcodes_contrib_drivers/drivers/Newport/AG_UC8.py diff --git a/qcodes_contrib_drivers/drivers/Newport/__init__.py b/src/qcodes_contrib_drivers/drivers/Newport/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Newport/__init__.py rename to src/qcodes_contrib_drivers/drivers/Newport/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Oxford/ILM200.py b/src/qcodes_contrib_drivers/drivers/Oxford/ILM200.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Oxford/ILM200.py rename to src/qcodes_contrib_drivers/drivers/Oxford/ILM200.py diff --git a/qcodes_contrib_drivers/drivers/Oxford/IPS120.py b/src/qcodes_contrib_drivers/drivers/Oxford/IPS120.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Oxford/IPS120.py rename to src/qcodes_contrib_drivers/drivers/Oxford/IPS120.py diff --git a/qcodes_contrib_drivers/drivers/Oxford/Triton.py b/src/qcodes_contrib_drivers/drivers/Oxford/Triton.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Oxford/Triton.py rename to src/qcodes_contrib_drivers/drivers/Oxford/Triton.py diff --git a/qcodes_contrib_drivers/drivers/Oxford/__init__.py b/src/qcodes_contrib_drivers/drivers/Oxford/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Oxford/__init__.py rename to src/qcodes_contrib_drivers/drivers/Oxford/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Oxford/kelvinox.py b/src/qcodes_contrib_drivers/drivers/Oxford/kelvinox.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Oxford/kelvinox.py rename to src/qcodes_contrib_drivers/drivers/Oxford/kelvinox.py diff --git a/qcodes_contrib_drivers/drivers/QDevil/QDAC1.py b/src/qcodes_contrib_drivers/drivers/QDevil/QDAC1.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QDevil/QDAC1.py rename to src/qcodes_contrib_drivers/drivers/QDevil/QDAC1.py diff --git a/qcodes_contrib_drivers/drivers/QDevil/QDAC2.py b/src/qcodes_contrib_drivers/drivers/QDevil/QDAC2.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QDevil/QDAC2.py rename to src/qcodes_contrib_drivers/drivers/QDevil/QDAC2.py diff --git a/qcodes_contrib_drivers/drivers/QDevil/QDAC2_Array.py b/src/qcodes_contrib_drivers/drivers/QDevil/QDAC2_Array.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QDevil/QDAC2_Array.py rename to src/qcodes_contrib_drivers/drivers/QDevil/QDAC2_Array.py diff --git a/qcodes_contrib_drivers/drivers/QDevil/QSwitch.py b/src/qcodes_contrib_drivers/drivers/QDevil/QSwitch.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QDevil/QSwitch.py rename to src/qcodes_contrib_drivers/drivers/QDevil/QSwitch.py diff --git a/qcodes_contrib_drivers/drivers/QDevil/__init__.py b/src/qcodes_contrib_drivers/drivers/QDevil/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QDevil/__init__.py rename to src/qcodes_contrib_drivers/drivers/QDevil/__init__.py diff --git a/qcodes_contrib_drivers/drivers/QuTech/D4.py b/src/qcodes_contrib_drivers/drivers/QuTech/D4.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QuTech/D4.py rename to src/qcodes_contrib_drivers/drivers/QuTech/D4.py diff --git a/qcodes_contrib_drivers/drivers/QuTech/D5a.py b/src/qcodes_contrib_drivers/drivers/QuTech/D5a.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QuTech/D5a.py rename to src/qcodes_contrib_drivers/drivers/QuTech/D5a.py diff --git a/qcodes_contrib_drivers/drivers/QuTech/F1d.py b/src/qcodes_contrib_drivers/drivers/QuTech/F1d.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QuTech/F1d.py rename to src/qcodes_contrib_drivers/drivers/QuTech/F1d.py diff --git a/qcodes_contrib_drivers/drivers/QuTech/IVVI.py b/src/qcodes_contrib_drivers/drivers/QuTech/IVVI.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QuTech/IVVI.py rename to src/qcodes_contrib_drivers/drivers/QuTech/IVVI.py diff --git a/qcodes_contrib_drivers/drivers/QuTech/M2j.py b/src/qcodes_contrib_drivers/drivers/QuTech/M2j.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QuTech/M2j.py rename to src/qcodes_contrib_drivers/drivers/QuTech/M2j.py diff --git a/qcodes_contrib_drivers/drivers/QuTech/S5i.py b/src/qcodes_contrib_drivers/drivers/QuTech/S5i.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QuTech/S5i.py rename to src/qcodes_contrib_drivers/drivers/QuTech/S5i.py diff --git a/qcodes_contrib_drivers/drivers/QuTech/__init__.py b/src/qcodes_contrib_drivers/drivers/QuTech/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/QuTech/__init__.py rename to src/qcodes_contrib_drivers/drivers/QuTech/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Rigol/Rigol_DSG3136B.py b/src/qcodes_contrib_drivers/drivers/Rigol/Rigol_DSG3136B.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Rigol/Rigol_DSG3136B.py rename to src/qcodes_contrib_drivers/drivers/Rigol/Rigol_DSG3136B.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8041.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8041.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8041.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8041.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8042.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8042.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8042.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8042.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8043.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8043.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8043.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMC8043.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP2020.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP2020.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP2020.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP2020.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP2030.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP2030.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP2030.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP2030.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP4030.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP4030.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP4030.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP4030.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP4040.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP4040.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP4040.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/HMP4040.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMB100A.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMB100A.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/SMB100A.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMB100A.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMR40.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMR40.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/SMR40.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMR40.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMW200A.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMW200A.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/SMW200A.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMW200A.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMW200Asim.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMW200Asim.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/SMW200Asim.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/SMW200Asim.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/ZVL13.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/ZVL13.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/ZVL13.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/ZVL13.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/__init__.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/__init__.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/__init__.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/HMC804x.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/HMC804x.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/private/HMC804x.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/HMC804x.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/HMP.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/HMP.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/private/HMP.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/HMP.py diff --git a/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/__init__.py b/src/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/RohdeSchwarz/private/__init__.py rename to src/qcodes_contrib_drivers/drivers/RohdeSchwarz/private/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Shamrock/SR750.py b/src/qcodes_contrib_drivers/drivers/Shamrock/SR750.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Shamrock/SR750.py rename to src/qcodes_contrib_drivers/drivers/Shamrock/SR750.py diff --git a/qcodes_contrib_drivers/drivers/Shamrock/__init__.py b/src/qcodes_contrib_drivers/drivers/Shamrock/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Shamrock/__init__.py rename to src/qcodes_contrib_drivers/drivers/Shamrock/__init__.py diff --git a/qcodes_contrib_drivers/drivers/SignalCore/SignalCore.py b/src/qcodes_contrib_drivers/drivers/SignalCore/SignalCore.py similarity index 100% rename from qcodes_contrib_drivers/drivers/SignalCore/SignalCore.py rename to src/qcodes_contrib_drivers/drivers/SignalCore/SignalCore.py diff --git a/qcodes_contrib_drivers/drivers/SignalCore/__init__.py b/src/qcodes_contrib_drivers/drivers/SignalCore/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/SignalCore/__init__.py rename to src/qcodes_contrib_drivers/drivers/SignalCore/__init__.py diff --git a/qcodes_contrib_drivers/drivers/SingleQuantum/SingleQuantum.py b/src/qcodes_contrib_drivers/drivers/SingleQuantum/SingleQuantum.py similarity index 100% rename from qcodes_contrib_drivers/drivers/SingleQuantum/SingleQuantum.py rename to src/qcodes_contrib_drivers/drivers/SingleQuantum/SingleQuantum.py diff --git a/qcodes_contrib_drivers/drivers/SingleQuantum/__init__.py b/src/qcodes_contrib_drivers/drivers/SingleQuantum/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/SingleQuantum/__init__.py rename to src/qcodes_contrib_drivers/drivers/SingleQuantum/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Spectrum/M4i.py b/src/qcodes_contrib_drivers/drivers/Spectrum/M4i.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Spectrum/M4i.py rename to src/qcodes_contrib_drivers/drivers/Spectrum/M4i.py diff --git a/qcodes_contrib_drivers/drivers/Spectrum/README.md b/src/qcodes_contrib_drivers/drivers/Spectrum/README.md similarity index 100% rename from qcodes_contrib_drivers/drivers/Spectrum/README.md rename to src/qcodes_contrib_drivers/drivers/Spectrum/README.md diff --git a/qcodes_contrib_drivers/drivers/Spectrum/__init__.py b/src/qcodes_contrib_drivers/drivers/Spectrum/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Spectrum/__init__.py rename to src/qcodes_contrib_drivers/drivers/Spectrum/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Spectrum/py_header/__init__.py b/src/qcodes_contrib_drivers/drivers/Spectrum/py_header/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Spectrum/py_header/__init__.py rename to src/qcodes_contrib_drivers/drivers/Spectrum/py_header/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Spectrum/py_header/h2py.py b/src/qcodes_contrib_drivers/drivers/Spectrum/py_header/h2py.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Spectrum/py_header/h2py.py rename to src/qcodes_contrib_drivers/drivers/Spectrum/py_header/h2py.py diff --git a/qcodes_contrib_drivers/drivers/Spectrum/py_header/regs.py b/src/qcodes_contrib_drivers/drivers/Spectrum/py_header/regs.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Spectrum/py_header/regs.py rename to src/qcodes_contrib_drivers/drivers/Spectrum/py_header/regs.py diff --git a/qcodes_contrib_drivers/drivers/Spectrum/py_header/spcerr.py b/src/qcodes_contrib_drivers/drivers/Spectrum/py_header/spcerr.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Spectrum/py_header/spcerr.py rename to src/qcodes_contrib_drivers/drivers/Spectrum/py_header/spcerr.py diff --git a/qcodes_contrib_drivers/drivers/Spectrum/pyspcm.py b/src/qcodes_contrib_drivers/drivers/Spectrum/pyspcm.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Spectrum/pyspcm.py rename to src/qcodes_contrib_drivers/drivers/Spectrum/pyspcm.py diff --git a/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py b/src/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py rename to src/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py diff --git a/qcodes_contrib_drivers/drivers/Standa/__init__.py b/src/qcodes_contrib_drivers/drivers/Standa/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Standa/__init__.py rename to src/qcodes_contrib_drivers/drivers/Standa/__init__.py diff --git a/qcodes_contrib_drivers/drivers/StanfordResearchSystems/CS580.py b/src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/CS580.py similarity index 100% rename from qcodes_contrib_drivers/drivers/StanfordResearchSystems/CS580.py rename to src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/CS580.py diff --git a/qcodes_contrib_drivers/drivers/StanfordResearchSystems/DG645.py b/src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/DG645.py similarity index 100% rename from qcodes_contrib_drivers/drivers/StanfordResearchSystems/DG645.py rename to src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/DG645.py diff --git a/qcodes_contrib_drivers/drivers/StanfordResearchSystems/SIM928.py b/src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/SIM928.py similarity index 100% rename from qcodes_contrib_drivers/drivers/StanfordResearchSystems/SIM928.py rename to src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/SIM928.py diff --git a/qcodes_contrib_drivers/drivers/StanfordResearchSystems/SR844.py b/src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/SR844.py similarity index 100% rename from qcodes_contrib_drivers/drivers/StanfordResearchSystems/SR844.py rename to src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/SR844.py diff --git a/qcodes_contrib_drivers/drivers/StanfordResearchSystems/__init__.py b/src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/StanfordResearchSystems/__init__.py rename to src/qcodes_contrib_drivers/drivers/StanfordResearchSystems/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Tektronix/AFG3000.py b/src/qcodes_contrib_drivers/drivers/Tektronix/AFG3000.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Tektronix/AFG3000.py rename to src/qcodes_contrib_drivers/drivers/Tektronix/AFG3000.py diff --git a/qcodes_contrib_drivers/drivers/Tektronix/AWG520.py b/src/qcodes_contrib_drivers/drivers/Tektronix/AWG520.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Tektronix/AWG520.py rename to src/qcodes_contrib_drivers/drivers/Tektronix/AWG520.py diff --git a/qcodes_contrib_drivers/drivers/Tektronix/Keithley_2000_Scan.py b/src/qcodes_contrib_drivers/drivers/Tektronix/Keithley_2000_Scan.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Tektronix/Keithley_2000_Scan.py rename to src/qcodes_contrib_drivers/drivers/Tektronix/Keithley_2000_Scan.py diff --git a/qcodes_contrib_drivers/drivers/Tektronix/Keithley_2700.py b/src/qcodes_contrib_drivers/drivers/Tektronix/Keithley_2700.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Tektronix/Keithley_2700.py rename to src/qcodes_contrib_drivers/drivers/Tektronix/Keithley_2700.py diff --git a/qcodes_contrib_drivers/drivers/Tektronix/Keithley_6430.py b/src/qcodes_contrib_drivers/drivers/Tektronix/Keithley_6430.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Tektronix/Keithley_6430.py rename to src/qcodes_contrib_drivers/drivers/Tektronix/Keithley_6430.py diff --git a/qcodes_contrib_drivers/drivers/Tektronix/Keithley_6500.py b/src/qcodes_contrib_drivers/drivers/Tektronix/Keithley_6500.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Tektronix/Keithley_6500.py rename to src/qcodes_contrib_drivers/drivers/Tektronix/Keithley_6500.py diff --git a/qcodes_contrib_drivers/drivers/Tektronix/__init__.py b/src/qcodes_contrib_drivers/drivers/Tektronix/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Tektronix/__init__.py rename to src/qcodes_contrib_drivers/drivers/Tektronix/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thermotek/Thermotek_T255p.py b/src/qcodes_contrib_drivers/drivers/Thermotek/Thermotek_T255p.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thermotek/Thermotek_T255p.py rename to src/qcodes_contrib_drivers/drivers/Thermotek/Thermotek_T255p.py diff --git a/qcodes_contrib_drivers/drivers/Thermotek/__init__.py b/src/qcodes_contrib_drivers/drivers/Thermotek/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thermotek/__init__.py rename to src/qcodes_contrib_drivers/drivers/Thermotek/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/TDC001.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/TDC001.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/TDC001.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/TDC001.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/apt.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_K10CR1/kinesis.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/apt.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/__init__.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/__init__.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/visa.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PRM1Z8/apt.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/__init__.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/APT.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/APT.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/APT.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/APT.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/__init__.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/__init__.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/__init__.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/enums.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py diff --git a/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py rename to src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/structs.py diff --git a/qcodes_contrib_drivers/drivers/Vaunix/LDA.py b/src/qcodes_contrib_drivers/drivers/Vaunix/LDA.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Vaunix/LDA.py rename to src/qcodes_contrib_drivers/drivers/Vaunix/LDA.py diff --git a/qcodes_contrib_drivers/drivers/Vaunix/__init__.py b/src/qcodes_contrib_drivers/drivers/Vaunix/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/Vaunix/__init__.py rename to src/qcodes_contrib_drivers/drivers/Vaunix/__init__.py diff --git a/qcodes_contrib_drivers/drivers/ZurichInstruments/HF2LI.py b/src/qcodes_contrib_drivers/drivers/ZurichInstruments/HF2LI.py similarity index 100% rename from qcodes_contrib_drivers/drivers/ZurichInstruments/HF2LI.py rename to src/qcodes_contrib_drivers/drivers/ZurichInstruments/HF2LI.py diff --git a/qcodes_contrib_drivers/drivers/ZurichInstruments/ZIHDAWG8.py b/src/qcodes_contrib_drivers/drivers/ZurichInstruments/ZIHDAWG8.py similarity index 100% rename from qcodes_contrib_drivers/drivers/ZurichInstruments/ZIHDAWG8.py rename to src/qcodes_contrib_drivers/drivers/ZurichInstruments/ZIHDAWG8.py diff --git a/qcodes_contrib_drivers/drivers/ZurichInstruments/__init__.py b/src/qcodes_contrib_drivers/drivers/ZurichInstruments/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/ZurichInstruments/__init__.py rename to src/qcodes_contrib_drivers/drivers/ZurichInstruments/__init__.py diff --git a/qcodes_contrib_drivers/drivers/__init__.py b/src/qcodes_contrib_drivers/drivers/__init__.py similarity index 100% rename from qcodes_contrib_drivers/drivers/__init__.py rename to src/qcodes_contrib_drivers/drivers/__init__.py diff --git a/qcodes_contrib_drivers/sims/HP8594E.yaml b/src/qcodes_contrib_drivers/sims/HP8594E.yaml similarity index 100% rename from qcodes_contrib_drivers/sims/HP8594E.yaml rename to src/qcodes_contrib_drivers/sims/HP8594E.yaml diff --git a/qcodes_contrib_drivers/sims/QDAC2.yaml b/src/qcodes_contrib_drivers/sims/QDAC2.yaml similarity index 100% rename from qcodes_contrib_drivers/sims/QDAC2.yaml rename to src/qcodes_contrib_drivers/sims/QDAC2.yaml diff --git a/qcodes_contrib_drivers/sims/QSwitch.yaml b/src/qcodes_contrib_drivers/sims/QSwitch.yaml similarity index 100% rename from qcodes_contrib_drivers/sims/QSwitch.yaml rename to src/qcodes_contrib_drivers/sims/QSwitch.yaml diff --git a/qcodes_contrib_drivers/sims/__init__.py b/src/qcodes_contrib_drivers/sims/__init__.py similarity index 100% rename from qcodes_contrib_drivers/sims/__init__.py rename to src/qcodes_contrib_drivers/sims/__init__.py diff --git a/qcodes_contrib_drivers/tests/HP/test_HP8594E.py b/tests/HP/test_HP8594E.py similarity index 100% rename from qcodes_contrib_drivers/tests/HP/test_HP8594E.py rename to tests/HP/test_HP8594E.py diff --git a/qcodes_contrib_drivers/tests/Keysight/test_memory_manager.py b/tests/Keysight/test_memory_manager.py similarity index 100% rename from qcodes_contrib_drivers/tests/Keysight/test_memory_manager.py rename to tests/Keysight/test_memory_manager.py diff --git a/qcodes_contrib_drivers/tests/QDevil/__init__.py b/tests/QDevil/__init__.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/__init__.py rename to tests/QDevil/__init__.py diff --git a/qcodes_contrib_drivers/tests/QDevil/common.py b/tests/QDevil/common.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/common.py rename to tests/QDevil/common.py diff --git a/qcodes_contrib_drivers/tests/QDevil/pytest.ini b/tests/QDevil/pytest.ini similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/pytest.ini rename to tests/QDevil/pytest.ini diff --git a/qcodes_contrib_drivers/tests/QDevil/readme.md b/tests/QDevil/readme.md similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/readme.md rename to tests/QDevil/readme.md diff --git a/qcodes_contrib_drivers/tests/QDevil/real_qdac2_fixtures.py b/tests/QDevil/real_qdac2_fixtures.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/real_qdac2_fixtures.py rename to tests/QDevil/real_qdac2_fixtures.py diff --git a/qcodes_contrib_drivers/tests/QDevil/sim_qdac2_fixtures.py b/tests/QDevil/sim_qdac2_fixtures.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/sim_qdac2_fixtures.py rename to tests/QDevil/sim_qdac2_fixtures.py diff --git a/qcodes_contrib_drivers/tests/QDevil/sim_qswitch_fixtures.py b/tests/QDevil/sim_qswitch_fixtures.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/sim_qswitch_fixtures.py rename to tests/QDevil/sim_qswitch_fixtures.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_real_qdac2_ieee_std.py b/tests/QDevil/test_real_qdac2_ieee_std.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_real_qdac2_ieee_std.py rename to tests/QDevil/test_real_qdac2_ieee_std.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_abstract_channel.py b/tests/QDevil/test_sim_qdac2_abstract_channel.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_abstract_channel.py rename to tests/QDevil/test_sim_qdac2_abstract_channel.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_arrangement.py b/tests/QDevil/test_sim_qdac2_arrangement.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_arrangement.py rename to tests/QDevil/test_sim_qdac2_arrangement.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_array.py b/tests/QDevil/test_sim_qdac2_array.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_array.py rename to tests/QDevil/test_sim_qdac2_array.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_awg.py b/tests/QDevil/test_sim_qdac2_awg.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_awg.py rename to tests/QDevil/test_sim_qdac2_awg.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_channels.py b/tests/QDevil/test_sim_qdac2_channels.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_channels.py rename to tests/QDevil/test_sim_qdac2_channels.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_common.py b/tests/QDevil/test_sim_qdac2_common.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_common.py rename to tests/QDevil/test_sim_qdac2_common.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_current.py b/tests/QDevil/test_sim_qdac2_current.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_current.py rename to tests/QDevil/test_sim_qdac2_current.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_external.py b/tests/QDevil/test_sim_qdac2_external.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_external.py rename to tests/QDevil/test_sim_qdac2_external.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_ieee_std.py b/tests/QDevil/test_sim_qdac2_ieee_std.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_ieee_std.py rename to tests/QDevil/test_sim_qdac2_ieee_std.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_init.py b/tests/QDevil/test_sim_qdac2_init.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_init.py rename to tests/QDevil/test_sim_qdac2_init.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_leakage.py b/tests/QDevil/test_sim_qdac2_leakage.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_leakage.py rename to tests/QDevil/test_sim_qdac2_leakage.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_sine.py b/tests/QDevil/test_sim_qdac2_sine.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_sine.py rename to tests/QDevil/test_sim_qdac2_sine.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_source_dc.py b/tests/QDevil/test_sim_qdac2_source_dc.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_source_dc.py rename to tests/QDevil/test_sim_qdac2_source_dc.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_source_list.py b/tests/QDevil/test_sim_qdac2_source_list.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_source_list.py rename to tests/QDevil/test_sim_qdac2_source_list.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_source_output_mode.py b/tests/QDevil/test_sim_qdac2_source_output_mode.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_source_output_mode.py rename to tests/QDevil/test_sim_qdac2_source_output_mode.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_source_sweep.py b/tests/QDevil/test_sim_qdac2_source_sweep.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_source_sweep.py rename to tests/QDevil/test_sim_qdac2_source_sweep.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_square.py b/tests/QDevil/test_sim_qdac2_square.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_square.py rename to tests/QDevil/test_sim_qdac2_square.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_system.py b/tests/QDevil/test_sim_qdac2_system.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_system.py rename to tests/QDevil/test_sim_qdac2_system.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_triangle.py b/tests/QDevil/test_sim_qdac2_triangle.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_triangle.py rename to tests/QDevil/test_sim_qdac2_triangle.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_triggers.py b/tests/QDevil/test_sim_qdac2_triggers.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_triggers.py rename to tests/QDevil/test_sim_qdac2_triggers.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_virtual_gates.py b/tests/QDevil/test_sim_qdac2_virtual_gates.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_virtual_gates.py rename to tests/QDevil/test_sim_qdac2_virtual_gates.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_direct_operation.py b/tests/QDevil/test_sim_qswitch_direct_operation.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_direct_operation.py rename to tests/QDevil/test_sim_qswitch_direct_operation.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_errors.py b/tests/QDevil/test_sim_qswitch_errors.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_errors.py rename to tests/QDevil/test_sim_qswitch_errors.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_ieee_std.py b/tests/QDevil/test_sim_qswitch_ieee_std.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_ieee_std.py rename to tests/QDevil/test_sim_qswitch_ieee_std.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_init.py b/tests/QDevil/test_sim_qswitch_init.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_init.py rename to tests/QDevil/test_sim_qswitch_init.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_internal.py b/tests/QDevil/test_sim_qswitch_internal.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_internal.py rename to tests/QDevil/test_sim_qswitch_internal.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_operation.py b/tests/QDevil/test_sim_qswitch_operation.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_operation.py rename to tests/QDevil/test_sim_qswitch_operation.py diff --git a/qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_snapshots.py b/tests/QDevil/test_sim_qswitch_snapshots.py similarity index 100% rename from qcodes_contrib_drivers/tests/QDevil/test_sim_qswitch_snapshots.py rename to tests/QDevil/test_sim_qswitch_snapshots.py diff --git a/qcodes_contrib_drivers/tests/__init__.py b/tests/__init__.py similarity index 100% rename from qcodes_contrib_drivers/tests/__init__.py rename to tests/__init__.py diff --git a/qcodes_contrib_drivers/tests/test_Keysight_M3201A.py b/tests/test_Keysight_M3201A.py similarity index 100% rename from qcodes_contrib_drivers/tests/test_Keysight_M3201A.py rename to tests/test_Keysight_M3201A.py diff --git a/qcodes_contrib_drivers/tests/test_Spectrum_M4i.py b/tests/test_Spectrum_M4i.py similarity index 100% rename from qcodes_contrib_drivers/tests/test_Spectrum_M4i.py rename to tests/test_Spectrum_M4i.py diff --git a/qcodes_contrib_drivers/tests/test_m2j.py b/tests/test_m2j.py similarity index 100% rename from qcodes_contrib_drivers/tests/test_m2j.py rename to tests/test_m2j.py diff --git a/qcodes_contrib_drivers/tests/test_ni_pxie_2597.py b/tests/test_ni_pxie_2597.py similarity index 100% rename from qcodes_contrib_drivers/tests/test_ni_pxie_2597.py rename to tests/test_ni_pxie_2597.py diff --git a/qcodes_contrib_drivers/tests/test_rohdeschwarz_HMC804x.py b/tests/test_rohdeschwarz_HMC804x.py similarity index 100% rename from qcodes_contrib_drivers/tests/test_rohdeschwarz_HMC804x.py rename to tests/test_rohdeschwarz_HMC804x.py diff --git a/qcodes_contrib_drivers/tests/test_zihdawg8.py b/tests/test_zihdawg8.py similarity index 100% rename from qcodes_contrib_drivers/tests/test_zihdawg8.py rename to tests/test_zihdawg8.py From 2fe93c0ee83e5e1eac3be25b2581fc06c3f93419 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Mon, 4 Mar 2024 10:44:11 +0100 Subject: [PATCH 58/61] Add averaging_time parameter --- .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index 9b8c86ef6..71fff606f 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -22,7 +22,7 @@ sys.path.append(str(pathlib.Path(vxi_32, 'WinNT', 'TLPM', 'Examples', 'Python'))) - from TLPM import TLPM + import TLPM class ThorlabsPM100D(Instrument): @@ -56,7 +56,7 @@ class ThorlabsPM100D(Instrument): """ def __init__(self, name: str, address: str = '', reset: bool = False, - thorlabs_tlpm: TLPM | None = None, + thorlabs_tlpm: TLPM.TLPM | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): if sys.platform != 'win32': @@ -65,7 +65,7 @@ def __init__(self, name: str, address: str = '', reset: bool = False, if vxi_32 is None: raise FileNotFoundError('IVI VXIPNP path not detected.') - self.tlpm = thorlabs_tlpm or TLPM() + self.tlpm = thorlabs_tlpm or TLPM.TLPM() # NEED to call with IDQuery==True, otherwise the following error is # raised: NameError: The given session or object reference does not # support this operation. @@ -83,6 +83,11 @@ def __init__(self, name: str, address: str = '', reset: bool = False, set_cmd=self._set_wavelength, label='Wavelength', unit='nm') + self.add_parameter('averaging_time', + get_cmd=self._get_averaging_time, + set_cmd=self._set_averaging_time, + label='Averaging time', + unit='s') self.connect_message() @@ -107,12 +112,21 @@ def _get_power(self) -> float: def _get_wavelength(self) -> float: wavelength = ctypes.c_double() - self.tlpm.getWavelength(0, ctypes.byref(wavelength)) + self.tlpm.getWavelength(TLPM.TLPM_ATTR_SET_VAL, + ctypes.byref(wavelength)) return wavelength.value def _set_wavelength(self, wavelength: float): self.tlpm.setWavelength(ctypes.c_double(wavelength)) + def _get_averaging_time(self) -> float: + avgTime = ctypes.c_double() + self.tlpm.getAvgTime(TLPM.TLPM_ATTR_SET_VAL, ctypes.byref(avgTime)) + return avgTime.value + + def _set_averaging_time(self, time: float): + self.tlpm.setAvgTime(ctypes.c_double(time)) + def get_idn(self) -> Dict[str, str | None]: manufacturerName = ctypes.create_string_buffer(1024) deviceName = ctypes.create_string_buffer(1024) From 8469b12025b927803ff66cd1c4cfb804ce8a0b85 Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Thu, 11 Apr 2024 14:38:09 +0200 Subject: [PATCH 59/61] Add validator to transit time --- .../drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index b4911114d..7ef2cb65f 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -3,7 +3,8 @@ import pathlib from typing import Any, Literal, Mapping -from qcodes import Parameter +from qcodes.parameters import Parameter +from qcodes.validators import validators as vals from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis import enums from qcodes_contrib_drivers.drivers.Thorlabs.private.kinesis.core import ( KinesisInstrument @@ -63,6 +64,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', 'transit_time', get_cmd=self._kinesis.get_transit_time, set_cmd=self._kinesis.set_transit_time, + vals=vals.Ints(300, 2800), set_parser=int, unit='ms', label='Transit time', From 8899e615a43154b0c4a1fa6136a69857e398cb7d Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Wed, 15 May 2024 13:45:09 +0200 Subject: [PATCH 60/61] Remove old file --- .../drivers/Thorlabs/private/kinesis.py | 120 ------------------ 1 file changed, 120 deletions(-) delete mode 100644 src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis.py diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis.py deleted file mode 100644 index 06eff4ebc..000000000 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis.py +++ /dev/null @@ -1,120 +0,0 @@ -# -*- coding: utf-8 -*- -"""QCoDeS base drivers for Thorlabs Kinesis instruments - -Authors: - Julien Barrier, -""" -import os -import sys -import ctypes -from typing import Any, Optional - -from qcodes.instrument import Instrument -from . import GeneralErrors, MotorErrors, ConnexionErrors - -class _Thorlabs_Kinesis(Instrument): - """A base class for Thorlabs kinesis instruments - - Args: - name: Instrument name. - serial_number: Serial number of the device. - dll_path: Path to the kinesis dll for the instrument to use. - dll_dir: Directory in which the kinesis dll are stored. - simulation: Enables the simulation manager. Defaults to False. - """ - def __init__(self, - name: str, - serial_number: str, - dll_path: str, - dll_dir: Optional[str] = None, - simulation: bool = False, - **kwargs): - super().__init__(name, **kwargs) - self.serial_number = serial_number - self._serial_number = ctypes.c_char_p(self.serial_number.encode('ascii')) - self._dll_path = dll_path - self._dll_dir: Optional[str] = dll_dir if dll_dir else r'C:\Program Files\Thorlabs\Kinesis' - if sys.platform != 'win32': - self._dll: Any = None - raise OSError('Thorlabs Kinesis only works on Windows') - else: - os.add_dll_directory(self._dll_dir) - self._dll = ctypes.cdll.LoadLibrary(self._dll_path) - - self._simulation = simulation - if self._simulation: - self.enable_simulation() - - self._device_info = dict(zip( - ['type_ID', 'description', 'PID', 'is_known_type', 'motor_type', - 'is_piezo', 'is_laser', 'is_custom', 'is_rack', 'max_channels'], - self._get_device_info())) - self._type_ID = self._device_info['type_ID'] - self._description = self._device_info['description'] - self._PID = self._device_info['PID'] - self._is_known_type = self._device_info['is_known_type'] - self._motor_type = self._device_info['motor_type'] - self._is_piezo = self._device_info['is_piezo'] - self._is_laser = self._device_info['is_laser'] - self._is_custom = self._device_info['is_custom'] - self._is_rack = self._device_info['is_rack'] - self._max_channels = self._device_info['max_channels'] - - def _get_device_info(self) -> list: - """Get the device information from the USB port - - Returns: - list: [type id, description, PID, is known type, motor type, - is piezo, is laser, is custom type, is rack, max channels] - """ - type_id = ctypes.c_ulong() - description = ctypes.c_char() - pid = ctypes.c_ulong() - is_known_type = ctypes.c_bool() - motor_type = ctypes.c_int() - is_piezo_device = ctypes.c_bool() - is_laser = ctypes.c_bool() - is_custom_type = ctypes.c_bool() - is_rack = ctypes.c_bool() - max_channels = ctypes.c_bool() - - ret = self._dll.TLI_GetDeviceInfo( - ctypes.byref(self._serial_number), - ctypes.byref(type_id), - ctypes.byref(description), - ctypes.byref(pid), - ctypes.byref(is_known_type), - ctypes.byref(motor_type), - ctypes.byref(is_piezo_device), - ctypes.byref(is_laser), - ctypes.byref(is_custom_type), - ctypes.byref(is_rack), - ctypes.byref(max_channels) - ) - self._check_error(ret) - return [type_id.value, description.value, pid.value, - is_known_type.value, motor_type.value, is_piezo_device.value, - is_laser.value, is_custom_type.value, is_rack.value, - max_channels.value] - - def _check_error(self, status: int) -> None: - if status != 0: - if status in ConnexionErrors: - raise ConnectionError(f'{ConnexionErrors[status]} ({status})') - elif status in GeneralErrors: - raise OSError(f'{GeneralErrors[status]} ({status})') - elif status in MotorErrors: - raise RuntimeError(f'{MotorErrors[status]} ({status})') - else: - raise ValueError(f'Unknown error code ({status})') - else: - pass - return None - - def enable_simulation(self) -> None: - """Initialise a connection to the simulation manager, which must already be running""" - self._dll.TLI_InitializeSimulations() - - def disable_simulation(self) -> None: - """Uninitialize a connection to the simulation manager, which must be running""" - self._dll.TLI_UninitializeSimulations() From 1cbc4e43c72da4cc1fcff1530c2333887f6d9cbd Mon Sep 17 00:00:00 2001 From: "Tobias Hangleiter (Valhalla)" Date: Fri, 28 Jun 2024 18:12:49 +0200 Subject: [PATCH 61/61] Update __init__ signatures for newest qcodes version --- src/qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py | 6 ++++-- src/qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py | 5 +++-- .../drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py | 5 +++-- .../drivers/Thorlabs/Thorlabs_PM100D/tlpm.py | 2 +- src/qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py | 6 +++--- src/qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py | 6 +++--- .../drivers/Thorlabs/private/kinesis/cc.py | 5 +++-- .../drivers/Thorlabs/private/kinesis/core.py | 2 +- .../drivers/Thorlabs/private/kinesis/isc.py | 5 +++-- 9 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py index 91dedf4e6..5572549b9 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/KDC101.py @@ -38,5 +38,7 @@ def __init__(self, else: self._dll_path = 'Thorlabs.MotionControl.KCube.DCServo.dll' self._dll_dir: Optional[str] = dll_dir if dll_dir else None - super().__init__(name, serial_number, self._dll_path, self._dll_dir, - simulation, polling, home, **kwargs) + super().__init__(name, serial_number=serial_number, + dll_path=self._dll_path, dll_dir=self._dll_dir, + simulation=simulation, polling=polling, home=home, + **kwargs) diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py index 2c42de72a..89f5b8aa3 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/KLS1550.py @@ -35,5 +35,6 @@ def __init__(self, else: self._dll_path = 'Thorlabs.MotionControl.KCube.LaserSource.dll' self._dll_dir: Optional[str] = dll_dir if dll_dir else None - super().__init__(name, serial_number, self._dll_path, self._dll_dir, - simulation, polling, **kwargs) + super().__init__(name, serial_number=serial_number, + dll_path=self._dll_path, dll_dir=self._dll_dir, + simulation=simulation, polling=polling, **kwargs) diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py index 7ef2cb65f..abefcd80b 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_MFF10x/kinesis.py @@ -48,8 +48,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', position_mapping: Mapping[str, Literal[1, 2]] | None = None, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, serial, simulation, polling, home, - metadata, label) + super().__init__(name, dll_dir=dll_dir, serial=serial, + simulation=simulation, polling=polling, home=home, + metadata=metadata, label=label) self.position = Parameter( 'position', diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py index 71fff606f..5476f351c 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/Thorlabs_PM100D/tlpm.py @@ -72,7 +72,7 @@ def __init__(self, name: str, address: str = '', reset: bool = False, self.tlpm.open(address.encode() or self._search_for_device(), True, reset) - super().__init__(name, metadata, label) + super().__init__(name, metadata=metadata, label=label) self.add_parameter('power', get_cmd=self._get_power, diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py index a134d4f53..765e66788 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/CC.py @@ -39,9 +39,9 @@ def __init__(self, home: bool = False, **kwargs): self._dll_path = dll_path - super().__init__(name, serial_number, - self._dll_path, dll_dir, simulation, - **kwargs) + super().__init__(name, serial_number=serial_number, + dll_path=self._dll_path, dll_dir=dll_dir, + simulation=simulation, **kwargs) if self._dll.TLI_BuildDeviceList() == 0: self._dll.CC_Open(self._serial_number) diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py index fc595ad66..3cec68f67 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/LS.py @@ -40,9 +40,9 @@ def __init__(self, home: bool = False, **kwargs): self._dll_path = dll_path - super().__init__(name, serial_number, - self._dll_path, dll_dir, simulation, - **kwargs) + super().__init__(name, serial_number=serial_number, + dll_path=self._dll_path, dll_dir=dll_dir, + simulation=simulation, **kwargs) if self._dll.TLI_BuildDeviceList() == 0: self._open_laser() diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py index 9c08e5f2c..c16d3d51c 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/cc.py @@ -16,8 +16,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', polling: int = 200, home: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, serial, simulation, polling, home, - metadata, label) + super().__init__(name, dll_dir=dll_dir, serial=serial, + simulation=simulation, polling=polling, + home=home, metadata=metadata, label=label) self.position = Parameter( "position", diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py index a183fc09c..7a4228f39 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/core.py @@ -920,7 +920,7 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', self._initialized: bool = False - super().__init__(name, metadata, label) + super().__init__(name, metadata=metadata, label=label) self.add_parameter('polling_duration', get_cmd=self._kinesis.get_polling_duration, diff --git a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py index 871d83439..6524b0ef3 100644 --- a/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py +++ b/src/qcodes_contrib_drivers/drivers/Thorlabs/private/kinesis/isc.py @@ -19,8 +19,9 @@ def __init__(self, name: str, dll_dir: str | pathlib.Path | None = '', polling: int = 200, home: bool = False, metadata: Mapping[Any, Any] | None = None, label: str | None = None): - super().__init__(name, dll_dir, serial, simulation, polling, home, - metadata, label) + super().__init__(name, dll_dir=dll_dir, serial=serial, + simulation=simulation, polling=polling, + home=home, metadata=metadata, label=label) self.position = Parameter( "position",