Source code for qcodes_contrib_drivers.drivers.DaylightSolutions.MIRcat

# -*- coding: utf-8 -*-
"""QCoDeS-Driver for DRS Daylight Solutions Ultra-broadly tunable mid-IR
external cavity CW/Pulsed MIRcat laser system:
https://daylightsolutions.com/product/mircat/

This driver relies on the MIRcatSDK. It requires a Windows system.

Authors:
    Julien Barrier, <julien@julienbarrier.eu>
"""

import logging
import time
import sys
import ctypes
from typing import Optional, Any, Sequence
from functools import partial

from qcodes import Instrument, Parameter
from qcodes import validators as vals

log = logging.getLogger(__name__)


[docs] class DRSDaylightSolutions_MIRcat(Instrument): """ Class to represent a DaylightSolutions MIRcat tunable mid-IR laser QCL system. status: beta-version Args: name: name for the instrument dll_path: path to the MIRcatSDK driver dll library file. Defaults to None. wavelength: sequence of 2-tuple for the wavelength boundaries of all chips. """ dll_path = 'C:\\MIRcat_laser\\libs\\x64\\MIRcatSDK.dll' _GET_STATUS = { 0: 'unarmed', 1: 'armed and not emitting', 2: 'armed and emitting' } _GET_ERROR = { 1: 'Unsupported `commType` for communication and transport', 32: 'MIRcat controller initialisation failed *[System Error]*', 64: 'Arm/Disarm failure *[System Error]*', 65: 'Start/Tune failure *[System Error]*', 66: 'Interlocks/Keyswitch not set', 67: 'Stop scan failure *[System Error]*', 68: 'Pause scan failure *[System Error]*', 69: 'Resume scan failure *[System Error]*', 70: 'Manual step scan failure *[System Error]*', 71: 'Start sweep scan failure *[System Error]*', 72: 'Start step/measure scan failure *[System Error]*', 73: 'Index out of bounds', 74: 'Start multi-spectral scan failure *[System Error]*', 75: 'Too many elements', 76: 'Not enough elements', 77: 'Buffer too small for the character array', 78: 'Favourite name not recognised', 79: 'Favourite recall failure', 80: 'Wavelength out of the valid tuning range', 81: 'No scan in progress', 82: 'Failure to enable the laser emission *[System Error]*', 83: 'Emission already off', 84: 'Failure to disable the laser emission *[System Error]*', 85: 'Emission already on', 86: 'Specified pulse rate out of range', 87: 'Specified pulse width out of range', 88: 'Specified current out of range', 89: 'Fuuilure to save the QCL settings *[System Error]*', 90: 'Specified QCL out of range. Must be comprised between 1 and 4', 91: 'Laser already armed', 92: 'Laser already disarmed', 93: 'Laser not armed', 94: 'Laser not tuned', 95: 'System not operating at the set temperature *[System Error]*', 96: 'Specified QCL does not support CW', 97: 'Invalid laser mode', 98: 'Temperature out of range', 99: 'Failure to power off the laser *[System Error]*', 100: 'Communication error *[System Error]*', 101: 'MIRcat not initialised', 102: 'MIRcat already created', 103: 'Failure to start a sweep-advanced scan *[System Error]*', 104: 'Failure to inject a process trigger *[System Error]*', } def __init__(self, name: str, dll_path: Optional[str] = None, **kwargs: Any) -> None: super().__init__(name, **kwargs) if sys.platform != 'win32': self._dll: Any = None raise OSError('MIRcat only works on Windows') else: self._dll = ctypes.cdll.LoadLibrary(dll_path or self.dll_path) # Initialise MIRcatSDK & connect self._execute('MIRcatSDK_Initialize') self._num_qcl = ctypes.c_uint8() self._execute('MIRcatSDK_GetNumInstalledQcls', [ctypes.byref(self._num_qcl)]) self._is_interlock_set = ctypes.c_bool(False) self._execute('MIRcatSDK_IsInterlockedStatusSet', [ctypes.byref(self._is_interlock_set)]) self._is_keyswitch_set = ctypes.c_bool(False) self._execute('MIRcatSDK_IsKeySwitchStatusSet', [ctypes.byref(self._is_keyswitch_set)]) self._limits_chip1 = self.get_limits(1) self._limits_chip2 = self.get_limits(2) self._limits_chip3 = self.get_limits(3) self._limits_chip4 = self.get_limits(4) self._range_chip1: tuple[float, float] = self.get_ranges(1) self._range_chip2: tuple[float, float] = self.get_ranges(2) self._range_chip3: tuple[float, float] = self.get_ranges(3) self._range_chip4: tuple[float, float] = self.get_ranges(4) self.status = Parameter( 'status', label='Status of the QCL', set_cmd=self._set_status, get_cmd=self._get_status, vals=vals.Ints(0, 2), instrument=self ) self.wavelength = Parameter( 'wavelength', label='QCL wavelength', get_cmd=self._get_wavelength, set_cmd=self._set_wavelength, vals=vals.Numbers(self._range_chip1[0], self._range_chip4[1]), unit='m', instrument=self ) self.wavenumber = Parameter( 'wavenumber', label='QCL wavenumber', get_cmd=self._get_wavenumber, set_cmd=self._set_wavenumber, vals=vals.Numbers(1/self._range_chip4[1]/100, 1/self._range_chip1[0]/100), unit='cm' + '\u207b\u00b9', instrument=self ) self.chip = Parameter( 'chip', label='chip currently in used', get_cmd=self._get_chip, get_parser=int, vals=vals.Ints(1, 4), instrument=self ) self.T1 = Parameter( 'T1', label='Temperature of chip 1', get_cmd=partial(self._get_temperature, chip=1), vals=vals.Numbers(), unit='\u00b0'+'C', instrument=self ) self.T2 = Parameter( 'T2', label='Temperature of chip 2', get_cmd=partial(self._get_temperature, chip=2), vals=vals.Numbers(), unit='\u00b0'+'C', instrument=self ) self.T3 = Parameter( 'T3', label='Temperature of chip 3', get_cmd=partial(self._get_temperature, chip=3), vals=vals.Numbers(), unit='\u00b0'+'C', instrument=self ) self.T4 = Parameter( 'T4', label='Temperature of chip 4', get_cmd=partial(self._get_temperature, chip=4), vals=vals.Numbers(), unit='\u00b0'+'C', instrument=self ) self.pulse_rate_1 = Parameter( 'pulse_rate_1', label='Pulse rate for chip 1', get_cmd=partial(self._get_pulse_rate, chip=1), set_cmd=partial(self._set_pulse_rate, chip=1), vals=vals.Numbers(max_value=self._limits_chip1[0]), unit='Hz', instrument=self ) self.pulse_rate_2 = Parameter( 'pulse_rate_2', label='Pulse rate for chip 2', get_cmd=partial(self._get_pulse_rate, chip=2), set_cmd=partial(self._set_pulse_rate, chip=2), vals=vals.Numbers(max_value=self._limits_chip2[0]), unit='Hz', instrument=self ) self.pulse_rate_3 = Parameter( 'pulse_rate_3', label='Pulse rate for chip 3', get_cmd=partial(self._get_pulse_rate, chip=3), set_cmd=partial(self._set_pulse_rate, chip=3), vals=vals.Numbers(max_value=self._limits_chip3[0]), unit='Hz', instrument=self ) self.pulse_rate_4 = Parameter( 'pulse_rate_4', label='Pulse rate for chip 4', get_cmd=partial(self._get_pulse_rate, chip=4), set_cmd=partial(self._set_pulse_rate, chip=4), vals=vals.Numbers(max_value=self._limits_chip4[0]), unit='Hz', instrument=self ) self.pulse_width_1 = Parameter( 'pulse_width_1', label='Pulse width for chip 1', get_cmd=partial(self._get_pulse_width, chip=1), set_cmd=partial(self._set_pulse_width, chip=1), vals=vals.Numbers(max_value=self._limits_chip1[1]), unit='s', instrument=self ) self.pulse_width_2 = Parameter( 'pulse_width_2', label='Pulse width for chip 2', get_cmd=partial(self._get_pulse_width, chip=2), set_cmd=partial(self._set_pulse_width, chip=2), vals=vals.Numbers(max_value=self._limits_chip2[1]), unit='s', instrument=self ) self.pulse_width_3 = Parameter( 'pulse_width_3', label='Pulse width for chip 3', get_cmd=partial(self._get_pulse_width, chip=3), set_cmd=partial(self._set_pulse_width, chip=3), vals=vals.Numbers(max_value=self._limits_chip3[1]), unit='s', instrument=self ) self.pulse_width_4 = Parameter( 'pulse_width_4', label='Pulse width for chip 4', get_cmd=partial(self._get_pulse_width, chip=4), set_cmd=partial(self._set_pulse_width, chip=4), vals=vals.Numbers(max_value=self._limits_chip4[1]), unit='s', instrument=self ) self.pulse_current_1 = Parameter( 'pulse_current_1', label='Pulse current for chip 1', get_cmd=partial(self._get_pulse_current, chip=1), set_cmd=partial(self._set_pulse_current, chip=1), vals=vals.Numbers(max_value=self._limits_chip1[3]), unit='A', instrument=self ) self.pulse_current_2 = Parameter( 'pulse_current_2', label='Pulse current for chip 2', get_cmd=partial(self._get_pulse_current, chip=2), set_cmd=partial(self._set_pulse_current, chip=2), vals=vals.Numbers(max_value=self._limits_chip2[3]), unit='A', instrument=self ) self.pulse_current_3 = Parameter( 'pulse_current_3', label='Pulse current for chip 3', get_cmd=partial(self._get_pulse_current, chip=3), set_cmd=partial(self._set_pulse_current, chip=3), vals=vals.Numbers(max_value=self._limits_chip3[3]), unit='A', instrument=self ) self.pulse_current_4 = Parameter( 'pulse_current_4', label='Pulse current for chip 4', get_cmd=partial(self._get_pulse_current, chip=4), set_cmd=partial(self._set_pulse_current, chip=4), vals=vals.Numbers(max_value=self._limits_chip4[3]), unit='A', instrument=self ) self.connect_message()
[docs] def get_pulse_parameters(self, chip: int = 0) -> tuple: """Get all pulse parameters for a given QCL chip. Args: chip (int, optional). Defaults to =0 (active chip). Returns: tuple: (pulse_rate (Hz), pulse_width (s), current (A)) """ tup = ( self._get_pulse_rate(chip), self._get_pulse_width(chip), self._get_pulse_current(chip) ) return tup
[docs] def set_pulse_parameters(self, pulse_rate: float, pulse_width: float, current: float, chip: int) -> None: """Set pulse parameters for the QCL. Args: pulse_rate (float): pulse rate in Hz pulse_width (float): pulse width in s current (float): current in A chip (int): QCL chip """ self.log.info('Set the following pulse parameters for QCL chip' f'{chip}:\npulse rate: {pulse_rate}, pulse width: ' f'{pulse_width}, current: {current}.') self._execute('MIRcatSDK_SetQCLParams', [ctypes.c_uint8(chip), ctypes.c_float(pulse_rate), ctypes.c_float(pulse_width*1e9), ctypes.c_float(current*1e3)])
[docs] def get_limits(self, chip: int = 0) -> tuple[float, ...]: """Get the limits for a given QCL chip. Args: chip (int, optional). Defaults to =0 (active chip). Returns: tuple: (pulse_rate_max (Hz), pulse_width_max (s), duty_cycle_max, current_max(A)) """ if chip == 0: units = ctypes.c_uint8() tuned_ww = ctypes.c_float() _chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(_chip)]) chip = _chip.value self.log.info(f'Get limits for QCL chip {chip}.') pulse_rate_max = ctypes.c_float() pulse_width_max = ctypes.c_float() duty_cycle_max = ctypes.c_uint16() current_max = ctypes.c_float() self._execute('MIRcatSDK_GetQCLPulseLimits', [chip, ctypes.byref(pulse_rate_max), ctypes.byref(pulse_width_max), ctypes.byref(duty_cycle_max)]) self._execute('MIRcatSDK_GetQCLMaxPulsedCurrent', [chip, ctypes.byref(current_max)]) return (pulse_rate_max.value, pulse_width_max.value/1e9, duty_cycle_max.value, current_max.value/1e3)
[docs] def arm(self) -> None: """Arm the MIRcat QCL system. """ self.log.info('Arm the MIRcat QCL system.') at_temperature = ctypes.c_bool(False) is_armed = ctypes.c_bool(False) self._execute('MIRcatSDK_IsLaserArmed', [ctypes.byref(is_armed)]) if not is_armed.value: self._execute('MIRcatSDK_ArmDisarmLaser') while not is_armed.value: self._execute('MIRcatSDK_IsLaserArmed', [ctypes.byref(is_armed)]) time.sleep(1) self._execute('MIRcatSDK_AreTECsAtSetTemperature', [ctypes.byref(at_temperature)]) tec_current = ctypes.c_uint16(0) qcl_temp = ctypes.c_float(0) num_qcl = ctypes.c_uint8() self._execute('MIRcatSDK_GetNumInstalledQcls', [ctypes.byref(num_qcl)]) while not at_temperature.value: for i in range(0, num_qcl.value): self._execute('MIRcatSDK_GetQCLTemperature', [ctypes.c_uint8(i+1), ctypes.byref(qcl_temp)]) self._execute('MIRcatSDK_GetTecCurrent', [ctypes.c_uint8(i+1), ctypes.byref(tec_current)]) self._execute('MIRcatSDK_AreTECsAtSetTemperature', [ctypes.byref(at_temperature)]) time.sleep(.1)
[docs] def disarm(self) -> None: """Disarm the MIRcat QCL system. """ self.log.info('Disarm the MIRcat QCL system.') is_armed = ctypes.c_bool() is_emitting = ctypes.c_bool() self._execute('MIRcatSDK_IsEmissionOn', [ctypes.byref(is_emitting)]) self._execute('MIRcatSDK_IsLaserArmed', [ctypes.byref(is_armed)]) if is_emitting.value: self._execute('MIRcatSDK_TurnEmissionOff') while is_emitting.value: self._execute('MIRcatSDK_IsEmissionOn', [ctypes.byref(is_emitting)]) time.sleep(1) if is_armed.value: self._execute('MIRcatSDK_DisarmLaser') while is_armed.value: self._execute('MIRcatSDK_IsLaserArmed', [ctypes.byref(is_armed)]) time.sleep(1)
[docs] def get_ranges(self, chip: int = 0) -> tuple[float, float]: """Get the acceptable range for a given QCL chip. Args: chip (int, optional). Defaults to 0. Returns: tuple: (pf_min_range (m), pf_max_range (m)) """ if chip == 0: units = ctypes.c_uint8() tuned_ww = ctypes.c_float() _chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(_chip)]) chip = _chip.value self.log.info(f'Get range for QCL QCL chip {chip}.') pf_min_range = ctypes.c_float() pf_max_range = ctypes.c_float() pb_units = ctypes.c_uint8() self._execute('MIRcatSDK_GetQclTuningRange', [chip, ctypes.byref(pf_min_range), ctypes.byref(pf_max_range), ctypes.byref(pb_units)]) return (pf_min_range.value*1e-6, pf_max_range.value*1e-6)
[docs] def check_tune(self) -> float: """Check the QCL tune. """ if self.log.isEnabledFor(logging.DEBUG): self.log.debug('Check tune.') is_tuned = ctypes.c_bool(False) tuned_ww = ctypes.c_float() qcl = ctypes.c_uint8() units = ctypes.c_uint8() while not is_tuned.value: self._execute('MIRcatSDK_IsTuned', [ctypes.byref(is_tuned)]) self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(qcl)]) if units == ctypes.c_ubyte(1): return tuned_ww.value*1e-6 elif units == ctypes.c_ubyte(2): return 1e2/tuned_ww.value # convert from cm-1 to m else: raise ValueError
[docs] def get_idn(self) -> dict: idparts = [ 'DRS Daylight Solutions', 'MIRcat', None, self._get_api_version() ] return dict(zip(('vendor', 'model', 'serial', 'firmware'), idparts))
def _execute(self, func: str, params: Sequence = []) -> int: ret = self._dll.__getattr__(func)(*params) return self._check_error(ret) def _check_error(self, ret: int): if not ret: return None if self._GET_ERROR[ret][-16:] == '*[System Error]*': raise RuntimeError(self._GET_ERROR[ret][:-16]) else: raise ValueError(self._GET_ERROR[ret]) def _get_api_version(self) -> str: self.log.info('Get API version.') major = ctypes.c_uint16() minor = ctypes.c_uint16() patch = ctypes.c_uint16() self._execute('MIRcatSDK_GetAPIVersion', [ctypes.byref(major), ctypes.byref(minor), ctypes.byref(patch)]) return f'{major.value}.{minor.value}.{patch.value}' def _get_status(self) -> str: self.log.info('Get status.') is_armed = ctypes.c_bool(True) is_emitting = ctypes.c_bool(True) self._execute('MIRcatSDK_IsLaserArmed', [ctypes.byref(is_armed)]) time.sleep(0.05) self._execute('MIRcatSDK_IsEmissionOn', [ctypes.byref(is_emitting)]) res = int(is_armed.value) + int(is_emitting.value) return self._GET_STATUS[res] def _set_status(self, mode: int) -> None: """ Args: mode (int): see dictionary of allowed values _GET_SATUS """ if mode in self._GET_STATUS.keys(): self.log.info('Set device remote status to' + self._GET_STATUS[mode] + '.') is_armed = ctypes.c_bool() is_emitting = ctypes.c_bool() self._execute('MIRcatSDK_IsLaserArmed', [ctypes.byref(is_armed)]) time.sleep(0.05) self._execute('MIRcatSDK_IsEmissionOn', [ctypes.byref(is_emitting)]) state = int(is_armed.value) + int(is_emitting.value) if not state and mode: self.arm() time.sleep(.05) if mode == 2: self._execute('MIRcatSDK_TurnEmissionOn') elif not mode and state: if state == 2: self._execute('MIRcatSDK_TurnEmissionOff') time.sleep(.05) self._execute('MIRcatSDK_DisarmLaser') time.sleep(.05) elif mode == 2 and state == 1: self._execute('MIRcatSDK_TurnEmissionOn') time.sleep(.05) elif mode == 1 and state == 2: self._execute('MIRcatSDK_TurnEmissionOff') time.sleep(.05) else: print('Invalid mode inserted.') def _get_wavelength(self) -> float: self.log.info('Get wavelength.') actual_ww = ctypes.c_float() units = ctypes.c_uint8() light_valid = ctypes.c_bool() tuned_ww = ctypes.c_float() qcl = ctypes.c_uint8() self._execute('MIRcatSDK_GetActualWW', [ctypes.byref(actual_ww), ctypes.byref(units), ctypes.byref(light_valid)]) self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(qcl)]) return actual_ww.value/1e6 def _get_wavenumber(self): self.log.info('Get wavenumber.') actual_ww = ctypes.c_float() units = ctypes.c_uint8() light_valid = ctypes.c_bool() tuned_ww = ctypes.c_float() qcl = ctypes.c_uint8() self._execute('MIRcatSDK_GetActualWW', [ctypes.byref(actual_ww), ctypes.byref(units), ctypes.byref(light_valid)]) self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(qcl)]) if actual_ww.value < 6: wavenum = 1e4/tuned_ww.value # from um to cm-1 else: wavenum = 1e4/actual_ww.value return wavenum def _get_temperature(self, chip: int) -> float: self.log.info(f'Get temperature of QCL chip {chip}.') temp = ctypes.c_float() self._execute('MIRcatSDK_GetQCLTemperature', [chip, ctypes.byref(temp)]) return temp.value def _get_pulse_rate(self, chip: int = 0) -> float: if chip == 0: units = ctypes.c_uint8() tuned_ww = ctypes.c_float() _chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(_chip)]) chip = _chip.value self.log.info(f'Get pulse rate for QCL chip {chip}.') pulse_rate = ctypes.c_float() self._execute('MIRcatSDK_GetQCLPulseRate', [ctypes.c_uint8(chip), ctypes.byref(pulse_rate)]) return pulse_rate.value def _get_pulse_width(self, chip: int = 0) -> float: if chip == 0: units = ctypes.c_uint8() tuned_ww = ctypes.c_float() _chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(_chip)]) chip = _chip.value self.log.info(f'Get pulse width for QCL chip {chip}.') pulse_width = ctypes.c_float() self._execute('MIRcatSDK_GetQCLPulseWidth', [ctypes.c_uint8(chip), ctypes.byref(pulse_width)]) return pulse_width.value/1e9 def _get_pulse_current(self, chip: int = 0) -> float: if chip == 0: units = ctypes.c_uint8() tuned_ww = ctypes.c_float() _chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(_chip)]) chip = _chip.value self.log.info(f'Get pulse current for QCL chip {chip}.') pulse_current = ctypes.c_float() self._execute('MIRcatSDK_GetQCLCurrent', [ctypes.c_uint8(chip), ctypes.byref(pulse_current)]) return pulse_current.value/1e3 def _get_chip(self) -> int: """Check the active chip""" self.log.info('Get the active chip number.') actual_ww = ctypes.c_float() units = ctypes.c_uint8() light_valid = ctypes.c_bool() tuned_ww = ctypes.c_float() chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetActualWW', [ctypes.byref(actual_ww), ctypes.byref(units), ctypes.byref(light_valid)]) self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(chip)]) time.sleep(.05) return chip.value def _set_pulse_rate(self, pulse_rate: float, chip: int = 0) -> None: if chip == 0: units = ctypes.c_uint8() tuned_ww = ctypes.c_float() _chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(_chip)]) chip = _chip.value self.log.info(f'Set pulse rate to {pulse_rate} on QCL chip {chip}.') pulse_width = ctypes.c_float() pulse_current = ctypes.c_float() self._execute('MIRcatSDK_GetQCLPulseWidth', [ctypes.c_uint8(chip), ctypes.byref(pulse_width)]) time.sleep(.05) self._execute('MIRcatSDK_GetQCLCurrent', [ctypes.c_uint8(chip), ctypes.byref(pulse_current)]) time.sleep(.05) self._execute('MIRcatSDK_SetQCLParams', [ctypes.c_uint8(chip), ctypes.c_float(pulse_rate), ctypes.c_float(pulse_width.value), ctypes.c_float(pulse_current.value)]) def _set_pulse_width(self, pulse_width: float, chip: int = 0) -> None: if chip == 0: units = ctypes.c_uint8() tuned_ww = ctypes.c_float() _chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(_chip)]) chip = _chip.value self.log.info(f'Set pulse width to {pulse_width} on QCL chip {chip}.') pulse_rate = ctypes.c_float() pulse_current = ctypes.c_float() self._execute('MIRcatSDK_GetQCLPulseRate', [ctypes.c_uint8(chip), ctypes.byref(pulse_rate)]) time.sleep(.05) self._execute('MIRcatSDK_GetQCLCurrent', [ctypes.c_uint8(chip), ctypes.byref(pulse_current)]) time.sleep(.05) self._execute('MIRcatSDK_SetQCLParams', [ctypes.c_uint8(chip), ctypes.c_float(pulse_rate.value), ctypes.c_float(pulse_width*1e9), ctypes.c_float(pulse_current.value)]) def _set_pulse_current(self, pulse_current: float, chip: int = 0) -> None: if chip == 0: units = ctypes.c_uint8() tuned_ww = ctypes.c_float() _chip = ctypes.c_uint8() self._execute('MIRcatSDK_GetTuneWW', [ctypes.byref(tuned_ww), ctypes.byref(units), ctypes.byref(_chip)]) chip = _chip.value self.log.info(f'Set pulse current to {pulse_current} on QCL chip ' f'{chip}.') pulse_rate = ctypes.c_float() pulse_width = ctypes.c_float() self._execute('MIRcatSDK_GetQCLPulseRate', [ctypes.c_uint8(chip), ctypes.byref(pulse_rate)]) time.sleep(.05) self._execute('MIRcatSDK_GetQCLPulseWidth', [ctypes.c_uint8(chip), ctypes.byref(pulse_width)]) time.sleep(.05) self._execute('MIRcatSDK_SetQCLParams', [ctypes.c_uint8(chip), ctypes.c_float(pulse_rate.value), ctypes.c_float(pulse_width.value), ctypes.c_float(pulse_current*1e3)]) def _set_wavelength(self, wavelength: float, chip: int = 0) -> None: wavelength = wavelength*1e6 if chip == 0: if wavelength <= self._range_chip1[1]: chip = 1 elif self._range_chip2[0] < wavelength <= self._range_chip2[1]: chip = 2 elif self._range_chip3[0] < wavelength <= self._range_chip3[1]: chip = 3 elif self._range_chip4[0] < wavelength: chip = 4 else: raise ValueError('selected wavelength is not supported') self.log.info(f'Set wavelength to {wavelength} on QCL chip {chip}.') self._execute('MIRcatSDK_TuneToWW', [ctypes.c_float(wavelength), ctypes.c_ubyte(1), ctypes.c_uint8(chip)]) self._get_wavenumber() def _set_wavenumber(self, wavenumber: float, chip: int = 0) -> None: if chip == 0: if wavenumber >= 1/self._range_chip4[1]/100: chip = 4 elif 1/self._range_chip3[0]/100 > wavenumber >= 1/self._range_chip3[1]/100: chip = 3 elif 1/self._range_chip2[0]/100 > wavenumber >= 1/self._range_chip2[1]/100: chip = 2 elif 1/self._range_chip1[0]/100 > wavenumber: chip = 1 else: raise ValueError(f'selected wavenumber {wavenumber} is not supported') self.log.info(f'Set wavenumber to {wavenumber} on QCL chip {chip}.') self._execute('MIRcatSDK_TuneToWW', [ctypes.c_float(wavenumber), ctypes.c_ubyte(2), ctypes.c_uint8(chip)]) self._get_wavelength()