Source code for herosdevices.hardware.highfinesse.wsx

"""Device driver for Highfinesse WSx series wavelength meters."""
import ctypes
import os


[docs] class WSx: """A Highfinesse WSx series wavelength meter. Tested with WS6 and WS7. Hardware driver to communicate with a High Finesse WSx Wavemeter via the DLL in present in Windows systems with the vendor software installed. """ def __init__(self, dll_path: str = r"C:\Windows\System32\wlmData.dll", wavemeter_index: int = 0, channels: tuple = tuple(range(8)) ) -> None: """ Initialize the Highfinesse device driver. Args: dll_path: location of the wlmData.dll wavemeter_index: select specific wavemeter out of all connected. (hint: you may use `version` property to figure out the correct index channels: indices of the channels that should be available """ # access DLL and specify return types assert os.name == "nt", "The Highfinesse driver can only run in Windows OS" self.dll = ctypes.WinDLL(dll_path) # type: ignore[attr-defined] self.wavemeter_index = wavemeter_index self.dll.GetWavelengthNum.restype = ctypes.c_double self.dll.GetFrequencyNum.restype = ctypes.c_double self.dll.GetTemperature.restype = ctypes.c_double self.dll.GetExposureNum.restype = ctypes.c_long self.dll.SetExposureNum.restype = ctypes.c_long self.dll.GetExposureModeNum.restype = ctypes.c_bool self.dll.SetExposureModeNum.restype = ctypes.c_long self.dll.GetExposureRange.restype = ctypes.c_long self.dll.GetSwitcherMode.restype = ctypes.c_long self.dll.SetSwitcherMode.restype = ctypes.c_long self.dll.GetSwitcherSignalStates.restype = ctypes.c_long self.dll.SetSwitcherSignalStates.restype = ctypes.c_long self.dll.GetWLMVersion.restype = ctypes.c_long self.dll.PresetWLMIndex.restype = ctypes.c_long self._select_wavemeter(wavemeter_index=self.wavemeter_index) self.channels = channels def _select_wavemeter(self, wavemeter_index: int | None = None) -> bool | int: if wavemeter_index is None: wavemeter_index = self.wavemeter_index err = self.dll.PresetWLMIndex(ctypes.c_long(wavemeter_index)) if err == 0: return True return err @property def version(self) -> dict: """ Retrieve model, version, and software version from the device. Returns: dict: dictionary with keys model, version, software_revision """ return {"model": f"WS{self.dll.GetWLMVersion(ctypes.c_long(0))}", "version": self.dll.GetWLMVersion(ctypes.c_long(1)), "software_revision": self.dll.GetWLMVersion(ctypes.c_long(2)) }
[docs] def get_wavelength(self, channel: int = 1) -> float: """ Read the measured light wavelength at the given channel. Args: channel: channel to be read (if wavemeter is in switch mode) Returns: float: vac wavelength in nm, if channel is not used or underexposed returns -3, if channel is overexposed returns -4 """ assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" return self.dll.GetWavelengthNum(ctypes.c_long(channel), ctypes.c_double(0))
[docs] def get_frequency(self, channel: int = 1) -> float | int: """ Read the measured light frequency at the given channel. Args: channel: channel to be read (if wavemeter is in switch mode) Returns: float: frequency in THz, if channel is not used or underexposed returns -3, if channel is overexposed returns -4 """ assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" return self.dll.GetFrequencyNum(ctypes.c_long(channel), ctypes.c_double(0))
@property def temperature(self) -> float: """Read temperature of the wavemeter's internal temperature sensor.""" return self.dll.GetTemperature(ctypes.c_double(0))
[docs] def get_exposure_time(self, channel: int = 1) -> int: """ Read exposure time of the given channel. Args: channel: channel for which to read the exposure time (if wavemeter is in switch mode) Returns: int: exposure time in milliseconds """ assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" return self.dll.GetExposureNum(ctypes.c_long(channel), ctypes.c_long(1), ctypes.c_long(0))
[docs] def set_exposure_time(self, exposure_time: int, channel: int = 1, arr: int = 1) -> bool | int: """ Set the exposure time of the given channel. Args: channel: channel for which to set the exposure time (if wavemeter is in switch mode) arr: which sensor to set the exposure time for Returns: bool|int: success of the operation. Negative integers indicate errors. """ assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" assert arr in [1, 2], f"Array should be in [1,2], not {arr}" min_exposure, max_exposure = self.get_exposure_range(arr=arr) assert exposure_time >= min_exposure, f"ExposureTime should be larger than {exposure_time}, not {min_exposure}" assert exposure_time <= max_exposure, f"ExposureTime should be smaller than {exposure_time}, not {max_exposure}" err = self.dll.SetExposureNum(ctypes.c_long(channel), ctypes.c_long(arr), ctypes.c_long(exposure_time)) if err == 0: return True return err
[docs] def get_auto_exposure_mode(self, channel: int = 1) -> bool: """ Get the status of the automatic exposure time setting. Args: channel: channel for which to get the automatic exposure time setting (if wavemeter is in switch mode) Returns: bool: state of the automatic exposure time setting. """ assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" return self.dll.GetExposureModeNum(ctypes.c_long(channel), ctypes.c_bool(False))
[docs] def set_auto_exposure_mode(self, enable: bool, channel: int = 1) -> bool | int: """ Set the status of the automatic exposure time setting. Args: channel: channel for which to set the automatic exposure time setting (if wavemeter is in switch mode) Returns: bool|int: success of the operation. Negative integers indicate errors. """ assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" assert isinstance(enable, bool), f"Enable should be of type boolean, not {type(enable)}" err = self.dll.SetExposureModeNum(ctypes.c_long(channel), ctypes.c_bool(enable)) if err == 0: return True return err
[docs] def get_exposure_range(self, arr: int = 1) -> tuple[int, int]: """ Get the possible range of exposure times for the given array/sensor. Args: arr: array/sensor for which to get the possible exposure times Returns: tuple(int): minimum and maximum exposure time """ assert arr in [1, 2], f"Array should be in [1,2], not {arr}" min_exposure = self.dll.GetExposureRange(ctypes.c_long(2 * arr - 2)) max_exposure = self.dll.GetExposureRange(ctypes.c_long(2 * arr - 1)) return min_exposure, max_exposure
@property def switch_mode(self) -> bool: """Get state of the multiplex switcher.""" return self.dll.GetSwitcherMode(ctypes.c_long(0)) @switch_mode.setter def switch_mode(self, enable: bool) -> bool|int: assert isinstance(enable, bool) err = self.dll.SetSwitcherMode(ctypes.c_long(enable)) if err == 0: return True return err
[docs] def get_switcher_signal_state(self, channel: int = 1) -> int|tuple[bool, bool]: """Retrieve the status of the "use" and "show" options of the defined channel. Args: channel: channel for which to get the "use" and "show" state. Returns: tuple(bool): state of the switches in the form (use, show). """ assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" use = ctypes.c_long(0) show = ctypes.c_long(0) err = self.dll.GetSwitcherSignalStates(ctypes.c_long(channel), ctypes.byref(use), ctypes.byref(show)) if err == 0: return (bool(use.value), bool(show.value)) return err
[docs] def set_switcher_signal_state(self, use: bool, show: bool, channel: int = 1) -> bool|int: """Set the status of the "use" and "show" options of the defined channel. Args: use: state to set for the "use" switch. show: state to set for the "show" switch. channel: channel for which to set the "use" and "show" state. Returns: bool|int: success of the operation. Negative integers indicate errors. """ assert isinstance(use, bool), f"use should be of type boolean, not {type(use)}" assert isinstance(show, bool), f"show should be of type boolean, not {type(show)}" assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" err = self.dll.SetSwitcherSignalStates(ctypes.c_long(channel), ctypes.c_long(use), ctypes.c_long(show)) if err == 0: return True return err
[docs] def set_target_wavelength_vacuum(self, wavelength: float, channel: int = 1) -> bool | int: """ Set the target vacuum wavelength, i.e. if a laser is stabilized to the wavemeter. Args: wavelength: wavelength to set as target wavelength channel: channel for which to set the target wavelength. Returns: bool|int: success of the operation. Negative integers indicate errors. """ assert channel in range(1, 9, 1), f"Channel should be in [1,..8], not {channel}" err = self.dll.SetPIDCourseNum(ctypes.c_long(channel), ctypes.c_char_p(f"{wavelength*1e9:.9f}".encode())) if err == 0: return True return err
def _observable_data(self) -> dict: data = { "switchMode": (self.switch_mode, ""), "temperature": (self.temperature, "degC") } for channel in self.channels: switcher_signal = self.get_switcher_signal_state(channel) if not isinstance(switcher_signal, tuple): continue if not switcher_signal[0]: continue data[f"channel{channel}.frequency"] = (self.get_frequency(channel), "THz") data[f"channel{channel}.wavelength"] = (self.get_wavelength(channel), "nm") data[f"channel{channel}.exposureTime"] = (self.get_exposure_time(channel), "ms") return data