diff --git a/cfg/terascan.cfg b/cfg/terascan.cfg
index ed61d18..a398735 100644
--- a/cfg/terascan.cfg
+++ b/cfg/terascan.cfg
@@ -43,7 +43,7 @@ logic:
module.Class: 'terascan_logic.TerascanLogic'
connect:
laser: scanning_laser_logic
- wavemeter: time_series_reader_logic
+ wavemeter: wavemeter_logic
counter: fast_counter_logic
daq: daq_reader_logic # Note that this logic assumes there is exactly one (digital) input to the DAQ.
options:
@@ -53,19 +53,15 @@ logic:
daq_reader_logic:
module.Class: 'common.daq_reader_logic.DAQReaderLogic'
connect:
- daq: nidaq #daq_reader_dummy
+ daq: nidaq # daq_reader_dummy
options:
update_interval: 0 # Period in ms to check for data updates. Integers only. 0 is as fast as possible
- time_series_reader_logic:
- module.Class: 'common.data_in_stream_logic.DataInStreamLogic'
- options:
- max_frame_rate: 20 # optional (default: 20Hz)
- channel_buffer_size: 1048576 # optional (default: 1MSample)
- max_raw_data_bytes: 1073741824 # optional (default: 1GB)
+ wavemeter_logic:
+ module.Class: 'common.wavemeter_logic.WavemeterLogic'
connect:
- streamer: wavemeter # instream_dummy
+ wavemeter: wavemeter # wavemeter_dummy
fast_counter_logic:
@@ -96,23 +92,9 @@ hardware:
type: 0 # 0 for Digital, 1 for Analog
name: 'line0' # The name as idintified by the card
port: 1 # port number identified by the card
-
- wavemeter_proxy:
- module.Class: 'wavemeter.high_finesse_proxy.HighFinesseProxy'
- options:
- watchdog_interval: 1.0 # how often the watchdog checks for errors/changes in s
- dllPath: 'C:\Program Files (x86)\HighFinesse\Wavelength Meter WS6 3167\Projects\64\wlmData.dll'
wavemeter:
module.Class: 'wavemeter.high_finesse_wavemeter.HighFinesseWavemeter'
- connect:
- proxy: wavemeter_proxy
- options:
- channels:
- laser:
- switch_ch: 1 # channel on the wavemeter switch
- unit: 'm' # wavelength (m) or frequency (Hz)
- exposure: 10 # exposure time in ms, optional
swabian_timetagger:
module.Class: 'timetagger.swabian_tagger.SwabianTimeTagger'
@@ -133,27 +115,11 @@ hardware:
daq_reader_dummy:
module.Class: 'dummy.daq_reader_dummy.DAQReaderDummy'
- instream_dummy:
- module.Class: 'dummy.data_instream_dummy.InStreamDummy'
- options:
- channel_names:
- - 'digital 1'
- - 'analog 1'
- - 'digital 2'
- channel_units:
- - 'Hz'
- - 'V'
- - 'Hz'
- channel_signals: # Can be 'counts' or 'sine'
- - 'counts'
- - 'sine'
- - 'counts'
- data_type: 'float64'
- sample_timing: 'CONSTANT' # Can be 'CONSTANT', 'TIMESTAMP' or 'RANDOM'
-
fast_counter_dummy:
module.Class: 'dummy.fast_counter_dummy.FastCounterDummy'
+ wavemeter_dummy:
+ module.Class: 'dummy.wavemeter_dummy.WavemeterDummy'
scanning_laser_dummy:
module.Class: 'dummy.scanning_laser_dummy.ScanningLaserDummy'
diff --git a/src/qudi/gui/terascan/terascan_gui.py b/src/qudi/gui/terascan/terascan_gui.py
index 2391139..ca152bb 100644
--- a/src/qudi/gui/terascan/terascan_gui.py
+++ b/src/qudi/gui/terascan/terascan_gui.py
@@ -3,7 +3,8 @@
__all__ = ['TerascanGui']
import numpy as np
-from PySide2 import QtCore
+import os
+from PySide2 import QtCore, QtGui
from typing import List
from qudi.util.datastorage import TextDataStorage
@@ -11,6 +12,7 @@
from qudi.core.connector import Connector
from qudi.core.statusvariable import StatusVar
from qudi.gui.terascan.terascan_main_window import TerascanMainWindow
+from qudi.util.paths import get_artwork_dir
from qudi.logic.terascan_logic import TerascanData
@@ -61,6 +63,10 @@ def on_activate(self) -> None:
self._terascan_logic().sigScanFinished.connect(
self._scan_finished, QtCore.Qt.QueuedConnection
)
+
+ self._terascan_logic().sigLaserLocked.connect(
+ self._laser_lock_ui, QtCore.Qt.QueuedConnection
+ )
# Outputs:
@@ -76,6 +82,11 @@ def on_activate(self) -> None:
)
self._data = []
+
+ # Turn on update timer:
+ self.__timer = QtCore.QTimer()
+ self.__timer.setSingleShot(False)
+ self.__timer.timeout.connect(self._update_plot)
# Show the main window and raise it above all others
self.show()
@@ -85,6 +96,7 @@ def on_deactivate(self) -> None:
self._terascan_logic().sigWavelengthUpdated.disconnect(self._wavelength_changed)
self._terascan_logic().sigCountsUpdated.disconnect(self._receive_data)
self._terascan_logic().sigScanFinished.disconnect(self._scan_finished)
+ self._terascan_logic().sigLaserLocked.disconnect(self._laser_lock_ui)
# Use "plain" disconnects (without argument) only on signals owned by this module
self._mw.start_wavelength.valueChanged.disconnect()
@@ -94,8 +106,14 @@ def on_deactivate(self) -> None:
self.sigStopMeasurement.disconnect()
self.sigSetWavelengths.disconnect()
+ # disable update timer:
+ self.__timer.stop()
+ self.__timer.timeout.disconnect()
+ self.__timer = None
+
# Close main window
self._mw.close()
+
def show(self) -> None:
""" Mandatory method to show the main window """
@@ -107,6 +125,7 @@ def _start_changed(self, wave: float) -> None:
""" Qt slot to be called upon wavelength change """
self._start_wavelength = wave
self.sigSetWavelengths.emit(self._start_wavelength, self._stop_wavelength)
+
@QtCore.Slot(float)
def _stop_changed(self, wave: float) -> None:
@@ -119,13 +138,26 @@ def _stop_changed(self, wave: float) -> None:
def _start_stop_pressed(self) -> None:
""" Qt slot to be called upon wavelength change """
if self._mw.start_stop_button.text() == 'Start Measurement':
+ self._update_ui(True)
+ self.__timer.start(250)
+ self.sigStartMeasurement.emit()
+ else:
+ self._update_ui(False)
+ self.__timer.stop()
+ self.sigStopMeasurement.emit()
+
+
+
+ def _update_ui(self, running: bool) -> None:
+ if running:
self._mw.start_stop_button.setText('Stop Measurement')
self._mw.plot_widget.setXRange(self._start_wavelength, self._stop_wavelength)
- print('start measurement in terascan_gui')
- self.sigStartMeasurement.emit()
+ self._mw._statusbar.clearMessage()
+ self._mw._progress_bar.setValue(0)
+
else:
self._mw.start_stop_button.setText('Start Measurement')
- self.sigStopMeasurement.emit()
+ self._mw._statusbar.showMessage('Ready')
@QtCore.Slot()
def _scan_finished(self) -> None:
@@ -133,20 +165,14 @@ def _scan_finished(self) -> None:
@QtCore.Slot(object)
def _receive_data(self, data: List[TerascanData]) -> None:
- img = np.zeros((len(data),2))
- for i in range(len(data) - 1):
- # print(np.average(data[i].counts))
- img[i][0] = data[i].wavelength
- img[i][1] = data[i].counts
- # img[i][0] = 775 + 15 * np.random.rand()
- # img[i][1] = 100 * np.random.rand()
-
- self._mw.data_item.setData(x = img[:, 0], y = img[:, 1])
+ self._data = data
@QtCore.Slot(float)
def _wavelength_changed(self, wave: float) -> None:
self._current_wavelength = wave
+ percent = 100 * (wave*1e-3 - self._start_wavelength) / (self._stop_wavelength - self._start_wavelength);
+ self._mw._progress_bar.setValue(int(round(percent)))
@QtCore.Slot()
def _save_data(self) -> None:
@@ -156,3 +182,23 @@ def _save_data(self) -> None:
)
ds.save_data(self._data)
+
+ @QtCore.Slot(bool)
+ def _laser_lock_ui(self, locked: bool) -> None:
+ icon = 'network-connect' if locked else 'network-disconnect'
+ pix = QtGui.QPixmap(os.path.join(get_artwork_dir(), 'icons', icon))
+ self._mw._locked_indicator.setPixmap(pix.scaled(16, 16))
+
+
+
+ def _update_plot(self) -> None:
+ if (len(self._data) == 0):
+ return
+
+ img = np.zeros((len(self._data),2))
+ for i in range(len(self._data) - 1):
+ img[i][0] = self._data[i].wavelength*1e-3
+ img[i][1] = self._data[i].counts
+
+ self._mw.data_item.setData(x = img[:, 0], y = img[:, 1])
+
diff --git a/src/qudi/gui/terascan/terascan_main_window.py b/src/qudi/gui/terascan/terascan_main_window.py
index 6bd9453..8dbe594 100644
--- a/src/qudi/gui/terascan/terascan_main_window.py
+++ b/src/qudi/gui/terascan/terascan_main_window.py
@@ -35,16 +35,20 @@ def __init__(self, *args, **kwargs):
menu.addAction(self.action_close)
self.setMenuBar(menu_bar)
- # Create toolbar
- # toolbar = QtWidgets.QToolBar()
- # toolbar.setAllowedAreas(QtCore.Qt.AllToolBarAreas)
- # self.action_start_video = QtWidgets.QAction('Start Video')
- # self.action_start_video.setCheckable(True)
- # toolbar.addAction(self.action_start_video)
- # self.action_capture_frame = QtWidgets.QAction('Capture Frame')
- # self.action_capture_frame.setCheckable(True)
- # toolbar.addAction(self.action_capture_frame)
- # self.addToolBar(QtCore.Qt.TopToolBarArea, toolbar)
+ # Create statusbar and indicators
+ self._statusbar = self.statusBar()
+ self._statusbar.showMessage('Ready')
+
+ self._progress_bar = QtWidgets.QProgressBar()
+ self._progress_bar.setRange(0, 100)
+ self._progress_bar.setValue(0)
+
+
+ self._locked_indicator = QtWidgets.QLabel()
+ self._locked_indicator.setPixmap(QtGui.QPixmap(os.path.join(get_artwork_dir(), 'icons', 'network-disconnect')).scaled(16, 16))
+
+ self._statusbar.addWidget(self._locked_indicator)
+ self._statusbar.addWidget(self._progress_bar)
# Initialize widgets
self.start_stop_button = QtWidgets.QPushButton('Start Measurement')
diff --git a/src/qudi/hardware/dummy/data_instream_dummy.py b/src/qudi/hardware/dummy/data_instream_dummy.py
deleted file mode 100644
index 9ac59fc..0000000
--- a/src/qudi/hardware/dummy/data_instream_dummy.py
+++ /dev/null
@@ -1,588 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-This file contains the qudi hardware module to use a National Instruments X-series card as mixed
-signal input data streamer.
-
-Copyright (c) 2021, the qudi developers. See the AUTHORS.md file at the top-level directory of this
-distribution and on
-
-This file is part of qudi.
-
-Qudi is free software: you can redistribute it and/or modify it under the terms of
-the GNU Lesser General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version.
-
-Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along with qudi.
-If not, see .
-"""
-
-import time
-import numpy as np
-from enum import Enum
-from typing import List, Iterable, Union, Optional, Tuple, Callable, Sequence
-
-from qudi.core.configoption import ConfigOption
-from qudi.util.constraints import ScalarConstraint
-from qudi.util.mutex import Mutex
-from qudi.util.helpers import is_integer_type
-from qudi.interface.data_instream_interface import DataInStreamInterface, DataInStreamConstraints
-from qudi.interface.data_instream_interface import StreamingMode, SampleTiming
-
-
-def _make_sine_func(sample_rate: float) -> Callable[[np.ndarray, np.ndarray], None]:
- freq = sample_rate / (20 + 80 * np.random.rand())
- amp = 1 + np.random.rand() * 9
- noise_lvl = amp * (0.1 + np.random.rand() * 0.4)
- def make_sine(x, y):
- # y[:] = np.sin(2 * np.pi * freq * x)
- np.sin(2 * np.pi * freq * x, out=y)
- y *= amp
- noise = np.random.rand(x.size)
- noise *= 2 * noise_lvl
- noise -= noise_lvl
- y += noise
- return make_sine
-
-
-def _make_counts_func(sample_rate: float) -> Callable[[np.ndarray, np.ndarray], None]:
- count_lvl = 1_00 + np.random.rand() * (5_000 - 1_00)
- def make_counts(x, y):
- y[:] = count_lvl
- y += np.random.poisson(count_lvl, y.size)
- return make_counts
-
-
-class SignalShape(Enum):
- INVALID = -1
- SINE = 0
- COUNTS = 1
-
-
-class SampleGenerator:
- """ Generator object that periodically generates new samples based on a certain timebase but
- the actual sample timestamps can be irregular depending on configured SampleTiming
- """
- def __init__(self,
- signal_shapes: Iterable[SignalShape],
- sample_rate: float,
- sample_timing: SampleTiming,
- streaming_mode: StreamingMode,
- data_type: Union[type, str],
- buffer_size: int,
- ) -> None:
- self.data_type = np.dtype(data_type).type
- self.sample_rate = float(sample_rate)
- self.sample_timing = SampleTiming(sample_timing)
- self.streaming_mode = StreamingMode(streaming_mode)
- self.signal_shapes = [SignalShape(shape) for shape in signal_shapes]
- self.buffer_size = buffer_size
-
- self.__start = 0 # buffer start sample index
- self.__end = 0 # buffer end sample index
- self.__available_samples = 0
- self._sample_buffer = None
- self._timestamp_buffer = None
- self._generator_functions = list()
- self._start_time = self._last_time = 0.0
- self.restart()
-
- @property
- def available_samples(self) -> int:
- self.generate_samples()
- return self.__available_samples
-
- @property
- def channel_count(self) -> int:
- return len(self._generator_functions)
-
- @property
- def _buffer_size(self) -> int:
- return self._sample_buffer.size
-
- @property
- def _buffer_sample_size(self) -> int:
- return self._buffer_size // self.channel_count
-
- @property
- def _free_samples(self) -> int:
- return max(0, self._buffer_sample_size - self.__available_samples)
-
- def restart(self) -> None:
- # Init generator functions
- self._generator_functions = list()
- for shape in self.signal_shapes:
- if shape == SignalShape.SINE:
- self._generator_functions.append(_make_sine_func(self.sample_rate))
- elif shape == SignalShape.COUNTS:
- self._generator_functions.append(_make_counts_func(self.sample_rate))
- else:
- raise ValueError(f'Invalid SignalShape encountered: {shape}')
- # Init buffer
- self.__start = self.__end = 0
- self.__available_samples = 0
- self._sample_buffer = np.empty(self.buffer_size * self.channel_count, dtype=self.data_type)
- if self.sample_timing == SampleTiming.TIMESTAMP:
- self._timestamp_buffer = np.zeros(self.buffer_size, dtype=np.float64)
- else:
- self._timestamp_buffer = None
- # Set start time
- self._start_time = self._last_time = time.perf_counter()
-
- def generate_samples(self) -> float:
- """ Generates new samples in free buffer space and updates buffer pointers. If new samples
- do not fit into buffer, raise an OverflowError.
- """
- now = time.perf_counter()
- elapsed_time = now - self._last_time
- time_offset = self._last_time - self._start_time
- samples_per_channel = int(elapsed_time * self.sample_rate) # truncate
- if self.streaming_mode == StreamingMode.FINITE:
- samples_per_channel = min(samples_per_channel, self._free_samples)
- elapsed_time = samples_per_channel / self.sample_rate
- ch_count = self.channel_count
-
- if samples_per_channel > 0:
- # Generate x-axis (time) for sample generation
- if self.sample_timing == SampleTiming.CONSTANT:
- x = np.arange(samples_per_channel, dtype=np.float64)
- x /= self.sample_rate
- else:
- # randomize ticks within time interval for non-regular sampling
- x = np.random.rand(samples_per_channel)
- x.sort()
- x *= elapsed_time
- x += time_offset
-
- # ToDo:
- # Generate samples and write into buffer
- buffer = self._sample_buffer.reshape([self._buffer_sample_size, ch_count])
- end = self.__end + samples_per_channel
- if end > self._buffer_sample_size:
- first_samples = self._buffer_sample_size - self.__end
- end -= self._buffer_sample_size
- for ch_idx, generator in enumerate(self._generator_functions):
- generator(x[:first_samples], buffer[self.__end:, ch_idx])
- generator(x[first_samples:], buffer[:end, ch_idx])
- if self.sample_timing == SampleTiming.TIMESTAMP:
- self._timestamp_buffer[self.__end:] = x[:first_samples]
- self._timestamp_buffer[:end] = x[first_samples:]
- else:
- for ch_idx, generator in enumerate(self._generator_functions):
- generator(x, buffer[self.__end:end, ch_idx])
- if self.sample_timing == SampleTiming.TIMESTAMP:
- self._timestamp_buffer[self.__end:end] = x
- # Update pointers
- self.__available_samples += samples_per_channel
- self.__end = end
- self._last_time += elapsed_time
- if self.__available_samples > self._buffer_sample_size:
- raise OverflowError('Sample buffer has overflown. Decrease sample rate or increase '
- 'data readout rate.')
- return self._last_time
-
- def read_samples(self,
- sample_buffer: np.ndarray,
- samples_per_channel: int,
- timestamp_buffer: Optional[np.ndarray] = None) -> int:
- ch_count = self.channel_count
- buf_size = self._buffer_size
- samples = min(self.__available_samples, samples_per_channel) * ch_count
- start = self.__start * ch_count
- end = start + samples
- if end > buf_size:
- first_samples = buf_size - start
- end -= buf_size
- sample_buffer[:first_samples] = self._sample_buffer[start:]
- sample_buffer[first_samples:first_samples + end] = self._sample_buffer[:end]
- if timestamp_buffer is not None:
- timestamp_buffer[:first_samples // ch_count] = self._timestamp_buffer[self.__start:]
- timestamp_buffer[first_samples // ch_count:(first_samples + end) // ch_count] = self._timestamp_buffer[:end // ch_count]
- else:
- sample_buffer[:samples] = self._sample_buffer[start:end]
- if timestamp_buffer is not None:
- timestamp_buffer[:samples // ch_count] = self._timestamp_buffer[self.__start:end // ch_count]
- # Update pointers
- samples //= ch_count
- self.__start = end // ch_count
- self.__available_samples -= samples
- return samples
-
- def wait_get_available_samples(self, samples: int) -> int:
- available = self.available_samples
- if available < samples:
- # Wait for bulk time
- time.sleep((samples - available) / self.sample_rate)
- available = self.available_samples
- # Wait a little more if necessary
- while available < samples:
- time.sleep(1 / self.sample_rate)
- available = self.available_samples
- return available
-
-
-class InStreamDummy(DataInStreamInterface):
- """
- A dummy module to act as data in-streaming device (continuously read values)
-
- Example config for copy-paste:
-
- instream_dummy:
- module.Class: 'dummy.data_instream_dummy.InStreamDummy'
- options:
- channel_names:
- - 'digital 1'
- - 'analog 1'
- - 'digital 2'
- channel_units:
- - 'Hz'
- - 'V'
- - 'Hz'
- channel_signals: # Can be 'counts' or 'sine'
- - 'counts'
- - 'sine'
- - 'counts'
- data_type: 'float64'
- sample_timing: 'CONSTANT' # Can be 'CONSTANT', 'TIMESTAMP' or 'RANDOM'
- """
- # config options
- _channel_names = ConfigOption(name='channel_names',
- missing='error',
- constructor=lambda names: [str(x) for x in names])
- _channel_units = ConfigOption(name='channel_units',
- missing='error',
- constructor=lambda units: [str(x) for x in units])
- _channel_signals = ConfigOption(
- name='channel_signals',
- missing='error',
- constructor=lambda signals: [SignalShape[x.upper()] for x in signals]
- )
- _data_type = ConfigOption(name='data_type',
- default='float64',
- missing='info',
- constructor=lambda typ: np.dtype(typ).type)
- _sample_timing = ConfigOption(name='sample_timing',
- default='CONSTANT',
- missing='info',
- constructor=lambda timing: SampleTiming[timing.upper()])
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- self._thread_lock = Mutex()
- self._active_channels = list()
- self._sample_generator = None
- self._constraints = None
-
- def on_activate(self):
- # Sanity check ConfigOptions
- if not (len(self._channel_names) == len(self._channel_units) == len(self._channel_signals)):
- raise ValueError('ConfigOptions "channel_names", "channel_units" and "channel_signals" '
- 'must contain same number of elements')
- if len(set(self._channel_names)) != len(self._channel_names):
- raise ValueError('ConfigOptions "channel_names" must not contain duplicates')
- if is_integer_type(self._data_type) and any(sig == SignalShape.SINE for sig in self._channel_signals):
- self.log.warning('Integer data type not supported for Sine signal shape. '
- 'Falling back to numpy.float64 data type instead.')
- self._data_type = np.float64
-
- self._constraints = DataInStreamConstraints(
- channel_units=dict(zip(self._channel_names, self._channel_units)),
- sample_timing=self._sample_timing,
- streaming_modes=[StreamingMode.CONTINUOUS, StreamingMode.FINITE],
- data_type=self._data_type,
- channel_buffer_size=ScalarConstraint(default=1024**2,
- bounds=(128, 1024**3),
- increment=1,
- enforce_int=True),
- sample_rate=ScalarConstraint(default=10.0, bounds=(0.1, 1024**2), increment=0.1)
- )
- self._active_channels = list(self._constraints.channel_units)
- self._sample_generator = SampleGenerator(
- signal_shapes=self._channel_signals,
- sample_rate=self._constraints.sample_rate.default,
- sample_timing=self._constraints.sample_timing,
- streaming_mode=self._constraints.streaming_modes[0],
- data_type=self._constraints.data_type,
- buffer_size=self._constraints.channel_buffer_size.default
- )
-
- def on_deactivate(self):
- # Free memory
- self._sample_generator = None
-
- @property
- def constraints(self) -> DataInStreamConstraints:
- """ Read-only property returning the constraints on the settings for this data streamer. """
- return self._constraints
-
- @property
- def available_samples(self) -> int:
- """ Read-only property to return the currently available number of samples per channel ready
- to read from buffer.
- """
- with self._thread_lock:
- if self.module_state() == 'locked':
- return self._sample_generator.available_samples
- return 0
-
- @property
- def sample_rate(self) -> float:
- """ Read-only property returning the currently set sample rate in Hz.
- For SampleTiming.CONSTANT this is the sample rate of the hardware, for any other timing mode
- this property represents only a hint to the actual hardware timebase and can not be
- considered accurate.
- """
- return self._sample_generator.sample_rate
-
- @property
- def channel_buffer_size(self) -> int:
- """ Read-only property returning the currently set buffer size in samples per channel.
- The total buffer size in bytes can be estimated by:
- * * numpy.nbytes[]
-
- For StreamingMode.FINITE this will also be the total number of samples to acquire per
- channel.
- """
- return self._sample_generator.buffer_size
-
- @property
- def streaming_mode(self) -> StreamingMode:
- """ Read-only property returning the currently configured StreamingMode Enum """
- return self._sample_generator.streaming_mode
-
- @property
- def active_channels(self) -> List[str]:
- """ Read-only property returning the currently configured active channel names """
- return self._active_channels.copy()
-
- def configure(self,
- active_channels: Sequence[str],
- streaming_mode: Union[StreamingMode, int],
- channel_buffer_size: int,
- sample_rate: float) -> None:
- """ Configure a data stream. See read-only properties for information on each parameter. """
- with self._thread_lock:
- if self.module_state() == 'locked':
- raise RuntimeError('Unable to configure data stream while it is already running')
-
- # Cache current values to restore them if configuration fails
- old_channels = self.active_channels
- old_streaming_mode = self.streaming_mode
- old_buffer_size = self.channel_buffer_size
- old_sample_rate = self.sample_rate
- try:
- self._set_active_channels(active_channels)
- self._set_streaming_mode(streaming_mode)
- self._set_channel_buffer_size(channel_buffer_size)
- self._set_sample_rate(sample_rate)
- except Exception as err:
- self._set_active_channels(old_channels)
- self._set_streaming_mode(old_streaming_mode)
- self._set_channel_buffer_size(old_buffer_size)
- self._set_sample_rate(old_sample_rate)
- raise RuntimeError('Error while trying to configure data in-streamer') from err
-
- def _set_active_channels(self, channels: Iterable[str]) -> None:
- channels = set(channels)
- if not channels.issubset(self._constraints.channel_units):
- raise ValueError(f'Invalid channels to set active {channels}. Allowed channels are '
- f'{set(self._constraints.channel_units)}')
- channel_shapes = {ch: self._channel_signals[idx] for idx, ch in
- enumerate(self._constraints.channel_units) if ch in channels}
- self._sample_generator.signal_shapes = [shape for shape in channel_shapes.values()]
- self._active_channels = list(channel_shapes)
-
- def _set_streaming_mode(self, mode: Union[StreamingMode, int]) -> None:
- try:
- mode = StreamingMode(mode.value)
- except AttributeError:
- mode = StreamingMode(mode)
- if (mode == StreamingMode.INVALID) or mode not in self._constraints.streaming_modes:
- raise ValueError(
- f'Invalid streaming mode to set ({mode}). Allowed StreamingMode values are '
- f'[{", ".join(str(mod) for mod in self._constraints.streaming_modes)}]'
- )
- self._sample_generator.streaming_mode = mode
-
- def _set_channel_buffer_size(self, samples: int) -> None:
- self._constraints.channel_buffer_size.check(samples)
- self._sample_generator.buffer_size = samples
-
- def _set_sample_rate(self, rate: Union[int, float]) -> None:
- rate = float(rate)
- self._constraints.sample_rate.check(rate)
- self._sample_generator.sample_rate = rate
-
- def start_stream(self) -> None:
- """ Start the data acquisition/streaming """
- with self._thread_lock:
- if self.module_state() == 'idle':
- self.module_state.lock()
- try:
- self._sample_generator.restart()
- except:
- self.module_state.unlock()
- raise
- else:
- self.log.warning('Unable to start input stream. It is already running.')
-
- def stop_stream(self) -> None:
- """ Stop the data acquisition/streaming """
- with self._thread_lock:
- if self.module_state() == 'locked':
- self.module_state.unlock()
-
- def read_data_into_buffer(self,
- data_buffer: np.ndarray,
- samples_per_channel: int,
- timestamp_buffer: Optional[np.ndarray] = None) -> None:
- """ Read data from the stream buffer into a 1D numpy array given as parameter.
- Samples of all channels are stored interleaved in contiguous memory.
- In case of a multidimensional buffer array, this buffer will be flattened before written
- into.
- The 1D data_buffer can be unraveled into channel and sample indexing with:
-
- data_buffer.reshape([, ])
-
- The data_buffer array must have the same data type as self.constraints.data_type.
-
- In case of SampleTiming.TIMESTAMP a 1D numpy.float64 timestamp_buffer array has to be
- provided to be filled with timestamps corresponding to the data_buffer array. It must be
- able to hold at least items:
-
- This function is blocking until the required number of samples has been acquired.
- """
- with self._thread_lock:
- if self.module_state() != 'locked':
- raise RuntimeError('Unable to read data. Stream is not running.')
- if (self.constraints.sample_timing == SampleTiming.TIMESTAMP) and timestamp_buffer is None:
- raise RuntimeError('SampleTiming.TIMESTAMP mode requires a timestamp buffer array')
-
- channel_count = len(self.active_channels)
- if data_buffer.size < samples_per_channel * channel_count:
- raise RuntimeError(
- f'data_buffer too small ({data_buffer.size:d}) to hold all requested '
- f'samples for all channels ({channel_count:d} * {samples_per_channel:d} = '
- f'{samples_per_channel * channel_count:d})'
- )
- if (timestamp_buffer is not None) and (timestamp_buffer.size < samples_per_channel):
- raise RuntimeError(
- f'timestamp_buffer too small ({timestamp_buffer.size:d}) to hold all requested '
- f'samples ({samples_per_channel:d})'
- )
-
- self._sample_generator.wait_get_available_samples(samples_per_channel)
- if timestamp_buffer is None:
- self._sample_generator.read_samples(
- sample_buffer=data_buffer,
- samples_per_channel=samples_per_channel
- )
- else:
- self._sample_generator.read_samples(
- sample_buffer=data_buffer,
- samples_per_channel=samples_per_channel,
- timestamp_buffer=timestamp_buffer
- )
-
- def read_available_data_into_buffer(self,
- data_buffer: np.ndarray,
- timestamp_buffer: Optional[np.ndarray] = None) -> int:
- """ Read data from the stream buffer into a 1D numpy array given as parameter.
- All samples for each channel are stored in consecutive blocks one after the other.
- The number of samples read per channel is returned and can be used to slice out valid data
- from the buffer arrays like:
-
- valid_data = data_buffer[: * ]
- valid_timestamps = timestamp_buffer[:]
-
- See "read_data_into_buffer" documentation for more details.
-
- This method will read all currently available samples into buffer. If number of available
- samples exceeds buffer size, read only as many samples as fit into the buffer.
- """
- with self._thread_lock:
- if self.module_state() != 'locked':
- raise RuntimeError('Unable to read data. Stream is not running.')
- self._sample_generator.generate_samples()
- available_samples = self._sample_generator.available_samples
- if self.constraints.sample_timing == SampleTiming.TIMESTAMP:
- if timestamp_buffer is None:
- raise RuntimeError(
- 'SampleTiming.TIMESTAMP mode requires a timestamp buffer array'
- )
- timestamp_buffer = timestamp_buffer[:available_samples]
- channel_count = len(self.active_channels)
- data_buffer = data_buffer[:channel_count * available_samples]
- return self._sample_generator.read_samples(sample_buffer=data_buffer,
- samples_per_channel=available_samples,
- timestamp_buffer=timestamp_buffer)
-
- def read_data(self,
- samples_per_channel: Optional[int] = None
- ) -> Tuple[np.ndarray, Union[np.ndarray, None]]:
- """ Read data from the stream buffer into a 1D numpy array and return it.
- All samples for each channel are stored in consecutive blocks one after the other.
- The returned data_buffer can be unraveled into channel samples with:
-
- data_buffer.reshape([, ])
-
- The numpy array data type is the one defined in self.constraints.data_type.
-
- In case of SampleTiming.TIMESTAMP a 1D numpy.float64 timestamp_buffer array will be
- returned as well with timestamps corresponding to the data_buffer array.
-
- If samples_per_channel is omitted all currently available samples are read from buffer.
- This method will not return until all requested samples have been read or a timeout occurs.
- """
- with self._thread_lock:
- if self.module_state() != 'locked':
- raise RuntimeError('Unable to read data. Stream is not running.')
-
- self._sample_generator.generate_samples()
- if samples_per_channel is None:
- samples_per_channel = self._sample_generator.available_samples
- else:
- self._sample_generator.wait_get_available_samples(samples_per_channel)
-
- data_buffer = np.empty(len(self.active_channels) * samples_per_channel,
- dtype=self._constraints.data_type)
- if self.constraints.sample_timing == SampleTiming.TIMESTAMP:
- timestamp_buffer = np.empty(samples_per_channel, dtype=np.float64)
- else:
- timestamp_buffer = None
- if samples_per_channel > 0:
- self._sample_generator.read_samples(sample_buffer=data_buffer,
- samples_per_channel=samples_per_channel,
- timestamp_buffer=timestamp_buffer)
- return data_buffer, timestamp_buffer
-
- def read_single_point(self):
- """ This method will initiate a single sample read on each configured data channel.
- In general this sample may not be acquired simultaneous for all channels and timing in
- general can not be assured. Us this method if you want to have a non-timing-critical
- snapshot of your current data channel input.
- May not be available for all devices.
- The returned 1D numpy array will contain one sample for each channel.
-
- @return numpy.ndarray: 1D array containing one sample for each channel. Empty array
- indicates error.
- """
- with self._thread_lock:
- if self.module_state() != 'locked':
- raise RuntimeError('Unable to read data. Stream is not running.')
-
- data_buffer = np.empty(len(self.active_channels), dtype=self._constraints.data_type)
- if self.constraints.sample_timing == SampleTiming.TIMESTAMP:
- timestamp_buffer = np.empty(1, dtype=np.float64)
- else:
- timestamp_buffer = None
- self._sample_generator.wait_get_available_samples(1)
- self._sample_generator.read_samples(sample_buffer=np.expand_dims(data_buffer, axis=0),
- samples_per_channel=1,
- timestamp_buffer=timestamp_buffer)
- return data_buffer, timestamp_buffer
diff --git a/src/qudi/hardware/dummy/wavemeter_dummy.py b/src/qudi/hardware/dummy/wavemeter_dummy.py
new file mode 100644
index 0000000..8be8c68
--- /dev/null
+++ b/src/qudi/hardware/dummy/wavemeter_dummy.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+"""
+This module reads data from High Finesse wavemeters
+
+Copyright (c) 2025, the QuPIDC qudi developers.
+
+Qudi is free software: you can redistribute it and/or modify it under the terms of
+the GNU Lesser General Public License as published by the Free Software Foundation,
+either version 3 of the License, or (at your option) any later version.
+
+Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with qudi.
+If not, see .
+"""
+
+import numpy as np
+from PySide2 import QtCore
+
+from qudi.interface.simple_wavemeter_interface import SimpleWavemeterInterface
+
+import qudi.hardware.wavemeter.wlmConst as wlmConst
+import qudi.hardware.wavemeter.wlmData as wlmData
+
+
+class WavemeterDummy(SimpleWavemeterInterface):
+ """
+ Hardware file for high finesse wavemeter
+
+ Assumes the DLL is installed in the system path so does not need to be manually located
+
+ Example config for copy-paste:
+
+ dummy_wavemeter:
+ module.Class: 'dummy.dummy_wavemeter.WavemeterDummy'
+ """
+
+ sigWavelengthUpdated = QtCore.Signal(np.ndarray) # 1-d array of wavelengths in nm
+
+ def on_activate(self):
+ """ Activate module.
+ """
+
+ self._data = np.zeros((4, 1))
+
+
+ def on_deactivate(self):
+ """ Deactivate module.
+ """
+ pass
+
+
+ def get_wavelengths(self) -> np.ndarray:
+ """ Get the wavelengths from the wavemeter
+ """
+ self._data = 700 + 50*np.random.rand((4, 1))
+ return self._data
+
diff --git a/src/qudi/hardware/timetagger/swabian_tagger.py b/src/qudi/hardware/timetagger/swabian_tagger.py
index 2f32926..b4b9548 100644
--- a/src/qudi/hardware/timetagger/swabian_tagger.py
+++ b/src/qudi/hardware/timetagger/swabian_tagger.py
@@ -144,7 +144,7 @@ def configure(self, bin_width_s, record_length_s):
tagger=self._tagger,
channels=[self._channel_config[i] for i in self._channel_config],
binwidth=int(np.round(self._bin_width * 1000)), # in ps
- n_values=int(self._record_length),
+ n_values=1,
)
self.counter.stop()
diff --git a/src/qudi/hardware/wavemeter/high_finesse_proxy.py b/src/qudi/hardware/wavemeter/high_finesse_proxy.py
deleted file mode 100644
index fd2ab34..0000000
--- a/src/qudi/hardware/wavemeter/high_finesse_proxy.py
+++ /dev/null
@@ -1,500 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-This module acts as a proxy for the HighFinesse wavemeter. It directly communicates
-with the hardware and can process callbacks from it. Being a module, there should only
-ever be a single instance running in one qudi process.
-
-Copyright (c) 2023, the qudi developers. See the AUTHORS.md file at the top-level directory of this
-distribution and on
-
-This file is part of qudi.
-
-Qudi is free software: you can redistribute it and/or modify it under the terms of
-the GNU Lesser General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version.
-
-Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along with qudi.
-If not, see .
-"""
-import os
-import time
-from typing import Optional, List, Set, TYPE_CHECKING, Dict
-from ctypes import byref, cast, c_double, c_int, c_char_p, c_long, POINTER, WINFUNCTYPE, WinDLL
-from PySide2.QtCore import QObject
-from qudi.core.threadmanager import ThreadManager
-from qudi.core.logger import get_logger
-from qudi.core.module import Base
-from qudi.core.configoption import ConfigOption
-from qudi.util.mutex import Mutex
-
-import qudi.hardware.wavemeter.high_finesse_constants as high_finesse_constants
-from qudi.hardware.wavemeter.high_finesse_wrapper import load_dll, setup_dll, MIN_VERSION
-if TYPE_CHECKING:
- from qudi.hardware.wavemeter.high_finesse_wavemeter import HighFinesseWavemeter
-
-
-THREAD_NAME_WATCHDOG = 'wavemeter_callback_error_watchdog'
-
-
-class Watchdog(QObject):
- """A watchdog that can take care of errors in the callback function and checks for other changes."""
- def __init__(self, proxy: 'HighFinesseProxy', watch_interval: float):
- super().__init__()
- self._proxy = proxy
- self.log = get_logger(__name__)
- self._watch_interval = watch_interval
- self._stop = False
-
- def loop(self) -> None:
- while not self._stop:
- if self._proxy.error_in_callback:
- self.handle_error()
- if self._proxy.module_state() == 'locked':
- self.check_for_channel_activation_change()
- time.sleep(self._watch_interval)
-
- def stop_loop(self) -> None:
- self._stop = True
-
- def check_for_channel_activation_change(self) -> None:
- actual_active_channels = set(self._proxy.get_active_channels())
- if self._proxy.get_connected_channels() != actual_active_channels:
- self.log.warning('Channel was deactivated or activated through GUI.')
- self._proxy.stop_everything()
-
- def handle_error(self) -> None:
- self.log.warning('Error in callback function.')
- self._proxy.stop_everything()
- self._proxy.error_in_callback = False
-
-
-class HighFinesseProxy(Base):
- """Proxy between a physical HighFinesse wavemeter and one or multiple HighFinesse wavemeter hardware modules.
-
- Example config for copy-paste:
-
- wavemeter_proxy:
- module.Class: 'wavemeter.high_finesse_proxy.HighFinesseProxy'
- options:
- watchdog_interval: 1.0 # how often the watchdog checks for errors/changes in s
- dllPath: 'C:\Program Files (x86)\HighFinesse\Wavelength Meter WS6 3167\Projects\64\wlmData.dll'
- """
-
- _watchdog_interval: float = ConfigOption(name='watchdog_interval', default=1.0)
- _dllPath: str = ConfigOption(
- name='dllPath',
- default=r'C:\Program Files (x86)\HighFinesse\Wavelength Meter WS6 3167\Projects\64\wlmData.dll',
- missing="warn"
- )
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self._lock = Mutex()
-
- self._wavemeter_dll: Optional[WinDLL] = None
- self._watchdog: Optional[Watchdog] = None
- self._thread_manager: ThreadManager = ThreadManager.instance()
- self._callback_function: Optional[callable] = None
- self.error_in_callback: bool = False
- self._wm_has_switch: bool = False
-
- self._connected_instream_modules: Dict['HighFinesseWavemeter', Set[int]] = {}
-
- def on_activate(self) -> None:
- if self._check_for_second_instance():
- raise RuntimeError('There is already a running proxy instance. '
- 'Did you configure more than a single instance of this proxy?')
-
- # load and prepare the wavemeter DLL
- try:
- self._wavemeter_dll = load_dll(self._dllPath)
- except FileNotFoundError as e:
- raise ValueError('There is no wavemeter installed on this computer.\n'
- 'Please install a High Finesse wavemeter and try again.') from e
- else:
- v = [self._wavemeter_dll.GetWLMVersion(i) for i in range(4)]
- if v[0] == high_finesse_constants.GetFrequencyError.ErrWlmMissing.value:
- raise RuntimeError('The wavemeter application is not active. '
- 'Start the wavemeter application before activating the qudi module.')
-
- self.log.info(f'Successfully loaded wavemeter DLL of WS{v[0]} {v[1]},'
- f' software revision {v[2]}, compilation number {v[3]}.')
-
- software_rev = v[2]
- if software_rev < MIN_VERSION:
- self.log.warning(f'The wavemeter DLL software revision {software_rev} is older than the lowest revision '
- f'tested to be working with the wrapper ({MIN_VERSION}). '
- f'Setting up the wavemeter DLL might fail.')
- try:
- setup_dll(self._wavemeter_dll)
- except AttributeError:
- self.log.warning('One or more function is not available. The wavemeter version is likely outdated.')
-
- # try to activate the multi-channel switch and check if switch is present
- self._wavemeter_dll.SetSwitcherMode(True)
- is_active = self._wavemeter_dll.GetSwitcherMode(0)
- if is_active:
- self._wm_has_switch = True
- else:
- self._wm_has_switch = False
-
- self._set_up_watchdog()
-
- def on_deactivate(self) -> None:
- self._tear_down_watchdog()
- del self._wavemeter_dll
-
- def connect_instreamer(self, module: 'HighFinesseWavemeter', channels: List[int]):
- """
- Connect an instreamer module to the proxy.
- The proxy will start to put new samples into the instreamer buffer.
- """
- if module not in self._connected_instream_modules:
- with self._lock:
- # do channel activation in a lock to prevent the watchdog from stopping things
- not_connected_yet = set(channels) - self.get_connected_channels()
- for ch in not_connected_yet:
- self._activate_channel(ch)
- self._connected_instream_modules[module] = set(channels)
- if self._callback_function is None:
- self._activate_only_connected_channels()
- self._start_measurement()
- self._start_callback()
- else:
- self.log.warning('Instream module is already connected.')
-
- def disconnect_instreamer(self, module: 'HighFinesseWavemeter'):
- """ Disconnect an instreamer module from the proxy. """
- if module in self._connected_instream_modules:
- channels_disconnecting_instreamer = self._connected_instream_modules[module]
- with self._lock:
- del self._connected_instream_modules[module]
- if not self._connected_instream_modules:
- self._stop_callback()
- else:
- # deactivate channels that are not connected by other instreamers
- for ch in (channels_disconnecting_instreamer - self.get_connected_channels()):
- self._deactivate_channel(ch)
- else:
- self.log.warning('Instream module is not connected and can therefore not be disconnected.')
-
- def sample_rate(self) -> float:
- """
- Estimate the current sample rate by the exposure times per channel and switching times.
- :return: sample rate in Hz
- """
- exposure_times = []
- active_channels = self.get_active_channels()
- for ch in active_channels:
- t = self._wavemeter_dll.GetExposureNum(ch, 1, 0)
- exposure_times.append(t)
- total_exposure_time = sum(exposure_times)
-
- switching_time = 12
- n_channels = len(active_channels)
- turnaround_time_ms = total_exposure_time + n_channels * switching_time
-
- return 1e3 / turnaround_time_ms
-
- def set_exposure_time(self, ch: int, exp_time: float) -> None:
- """ Set the exposure time for a specific switch channel. """
- err = self._wavemeter_dll.SetExposureNum(ch, 1, exp_time)
- if err:
- raise RuntimeError(f'Wavemeter error while setting exposure time of channel {ch}: '
- f'{high_finesse_constants.ResultError(err)}')
-
- def get_active_channels(self) -> List[int]:
- """
- Get a list of all active channels on the multi-channel switch.
- :return: list of active channels
- """
- if not self._wm_has_switch:
- return [1]
-
- active_channels = []
- active = c_long()
- err = 0
- ch = 1
- while err != high_finesse_constants.ResultError.ChannelNotAvailable.value:
- err = self._wavemeter_dll.GetSwitcherSignalStates(ch, byref(active), byref(c_long()))
- if active:
- active_channels.append(ch)
- ch += 1
- return active_channels
-
- def get_connected_channels(self) -> Set[int]:
- """Channels on the multi-channel switch which are active on a connected instreamer."""
- channels = set()
- for i in self._connected_instream_modules.values():
- channels = channels | i
- return channels
-
- def stop_everything(self) -> None:
- """Meant to be called from watchdog."""
- self.log.warning('Stopping all streams.')
- streamers = list(self._connected_instream_modules).copy()
- self._stop_callback()
- self._connected_instream_modules = {}
- for streamer in streamers:
- # stop all streams without them triggering the proxy disconnect
- streamer.stop_stream_watchdog()
-
- # --- PID related methods ---
-
- def get_pid_setting(self, output_port: int, cmi_val: int):
- """
- Generic method to get PID values and settings
- @return: PID value or setting
- """
- i_val = c_long()
- d_val = c_double()
- err = self._wavemeter_dll.GetPIDSetting(cmi_val, output_port, byref(i_val), byref(d_val))
- if err == 1:
- return d_val.value, i_val.value
- else:
- raise RuntimeError(f'Error while getting PID value/setting: {high_finesse_constants.ResultError(err)}')
-
- def set_pid_setting(self, output_port: int, cmi_val: int, d_val: float = 0.0, i_val: int = 0):
- """ Generic method to set PID values and settings """
- i_val = c_long(i_val)
- d_val = c_double(d_val)
- err = self._wavemeter_dll.SetPIDSetting(cmi_val, output_port, i_val, d_val)
- if err:
- raise RuntimeError(f'Error while setting PID value/setting: {high_finesse_constants.ResultError(err)}')
-
- def get_setpoint(self, output_port: int) -> float:
- """
- Get the setpoint for a specific control voltage output port
- @return (float): The setpoint for this output port
- """
- pidc = c_char_p(b'0' * 1024)
- err = self._wavemeter_dll.GetPIDCourseNum(output_port, pidc)
- if err == high_finesse_constants.ResultError.NoErr.value:
- # wavemeter returns comma instead of point
- val = pidc.value.decode('utf-8').replace(',', '.')
- # wavemeter returns '= 123,456' when first turned on
- if val.startswith('= '):
- val = val[2:]
- try:
- val = float(val)
- except ValueError:
- raise ValueError('Could not convert PID course to a number.')
- return val
- else:
- raise RuntimeError(f'Error while getting setpoint: {high_finesse_constants.ResultError(err)}')
-
- def set_setpoint(self, output_port: int, setpoint: float):
- """ Set the setpoint for a specific output port """
- # wavemeter wants comma instead of point
- setpoint = str(setpoint).replace('.', ',').encode('utf-8')
- # convert setpoint to char array with 1024 bytes
- err = self._wavemeter_dll.SetPIDCourseNum(output_port, setpoint)
- if err:
- raise RuntimeError(f'Error while setting setpoint: {high_finesse_constants.ResultError(err)}')
-
- def set_manual_value(self, output_port: int, voltage: float) -> None:
- """ Set the control value to put out when PID is not running. """
- d_voltage = c_double(1e3 * voltage) # wavemeter wants mV
- err = self._wavemeter_dll.SetDeviationSignalNum(output_port, d_voltage)
- if err:
- raise RuntimeError(f'Error while manual value: {high_finesse_constants.ResultError(err)}')
-
- def get_pid_enabled(self) -> bool:
- """
- Get the PID status
- @return (bool): True if PID is enabled, False otherwise
- """
- return self._wavemeter_dll.GetDeviationMode(False)
-
- def set_pid_enabled(self, enabled: bool):
- """ Set the PID status """
- err = self._wavemeter_dll.SetDeviationMode(enabled)
- if err:
- raise RuntimeError(f'Error while setting PID enabled: {high_finesse_constants.ResultError(err)}')
-
- def get_laser_control_setting(self, output_port: int, cmi_val: int):
- """
- Generic method to get laser control settings
- @return: laser control setting
- """
- pidc = c_char_p(b'0' * 1024)
- i_val = c_long()
- d_val = c_double()
- err = self._wavemeter_dll.GetLaserControlSetting(cmi_val, output_port, byref(i_val), byref(d_val), pidc)
- if err == 1:
- return d_val.value, i_val.value, pidc.value
- else:
- raise RuntimeError(f'Error while getting laser control setting: {high_finesse_constants.ResultError(err)}')
-
- def get_control_value(self, output_port: int):
- """
- Get the control value for a specific voltage output port
- @return (float): The control value in V for the output port
- """
- i_val = c_long(output_port)
- d_val = c_double()
- return 1e-3 * self._wavemeter_dll.GetDeviationSignalNum(i_val, d_val)
-
- def get_wavelength(self, channel: int) -> float:
- """
- Get the current wavelength for a specific input channel
- @return (float): wavelength in m
- """
- i_val = c_long(channel)
- d_val = c_double()
- res = self._wavemeter_dll.GetWavelengthNum(i_val, d_val)
- if res in [e.value for e in high_finesse_constants.GetFrequencyError]:
- raise RuntimeError(f'Error while getting process value: {high_finesse_constants.ResultError(res)}')
- else:
- return 1e-9 * res
-
- # --- protected methods ---
-
- def _check_for_second_instance(self) -> bool:
- """Check if there already is a proxy running."""
- return THREAD_NAME_WATCHDOG in self._thread_manager.thread_names
-
- def _activate_channel(self, ch: int) -> None:
- """ Activate a channel on the multi-channel switch. """
- if not self._wm_has_switch:
- if ch == 1:
- return
- else:
- raise RuntimeError(f'Cannot activate channel {ch}: wavemeter does not have a multi-channel switch.')
-
- err = self._wavemeter_dll.SetSwitcherSignalStates(ch, 1, 1)
- if err:
- raise RuntimeError(
- f'Wavemeter error while activating channel {ch}: {high_finesse_constants.ResultError(err)}'
- )
-
- def _deactivate_channel(self, ch: int) -> None:
- """ Deactivate a channel on the multi-channel switch. """
- if not self._wm_has_switch:
- if ch == 1:
- return
- else:
- raise RuntimeError(f'Cannot deactivate channel {ch}: wavemeter does not have a multi-channel switch.')
-
- err = self._wavemeter_dll.SetSwitcherSignalStates(ch, 0, 0)
- if err:
- raise RuntimeError(f'Wavemeter error while deactivating channel {ch}: '
- f'{high_finesse_constants.ResultError(err)}')
-
- def _activate_only_connected_channels(self) -> None:
- """Activate all channels active on a connected instreamer and disable all others."""
- connected_channels = self.get_connected_channels()
- if not connected_channels:
- raise RuntimeError('Cannot deactivate all channels.')
-
- for ch in connected_channels:
- self._activate_channel(ch)
- for ch in self.get_active_channels():
- if ch not in connected_channels:
- self._deactivate_channel(ch)
-
- def _start_measurement(self) -> None:
- if self._wm_has_switch:
- self._wavemeter_dll.SetSwitcherMode(True)
- err = self._wavemeter_dll.Operation(high_finesse_constants.cCtrlStartMeasurement)
- if err:
- raise RuntimeError(f'Wavemeter error while starting measurement: {high_finesse_constants.ResultError(err)}')
-
- def _set_up_watchdog(self) -> None:
- self._watchdog_thread = self._thread_manager.get_new_thread(THREAD_NAME_WATCHDOG)
- self._watchdog = Watchdog(self, self._watchdog_interval)
- self._watchdog.moveToThread(self._watchdog_thread)
- self._watchdog_thread.started.connect(self._watchdog.loop)
- self._watchdog_thread.start()
-
- def _tear_down_watchdog(self) -> None:
- self._watchdog.stop_loop()
- self._thread_manager.quit_thread(self._watchdog_thread)
- del self._watchdog
-
- def _start_callback(self) -> None:
- """ Start the callback procedure. """
- self._callback_function = self._get_callback_function()
- self._wavemeter_dll.Instantiate(
- high_finesse_constants.cInstNotification, # long ReasonForCall
- high_finesse_constants.cNotifyInstallCallbackEx, # long Mode
- cast(self._callback_function, POINTER(c_long)), # long P1: function
- 0 # long P2: callback thread priority, 0 = standard
- )
- self.module_state.lock()
- self.log.debug('Started callback procedure.')
-
- def _stop_callback(self) -> None:
- """ Stop the callback procedure. """
- self._wavemeter_dll.Instantiate(
- high_finesse_constants.cInstNotification, # long ReasonForCall
- high_finesse_constants.cNotifyRemoveCallback, # long mode
- cast(self._callback_function, POINTER(c_long)),
- # long P1: function
- 0) # long P2: callback thread priority, 0 = standard
- self._callback_function = None
- self.module_state.unlock()
- self.log.debug('Stopped callback procedure.')
-
- def _get_callback_function(self) -> WINFUNCTYPE:
- """
- Define the callback procedure that should be called by the DLL every time a new measurement result
- is available or any of the wavelength meter's states changes.
- :return: callback function
- """
- def handle_callback(version, mode: int, intval: int, dblval: float, res1) -> int:
- """
- Function called upon wavelength meter state change or if a new measurement result is available.
- See wavemeter manual section on CallbackProc for details.
-
- In this implementation, the new wavelength is converted to the desired unit and
- appended to a list together with the current timestamp.
-
- :param version: Device version number which called the procedure.
- Only relevant if multiple wavemeter applications are running.
- :param mode: Indicates which state has changed or what new result is available.
- :param intval: Contains the time stamp rounded to ms if mode indicates that the new value is in dblval.
- If not, it contains the new value itself.
- :param dblval: May contain the new value (e.g. wavelength), depending on mode.
- :param res1: Mostly meaningless.
- :return: 0
- """
- # check if an evil user messed with the manufacturer GUI
- if mode == high_finesse_constants.cmiOperation and intval == high_finesse_constants.cStop:
- self.log.warning('Wavemeter acquisition was stopped during stream.')
- self.error_in_callback = True
- return 0
- elif mode == high_finesse_constants.cmiSwitcherMode:
- self.log.warning('Wavemeter switcher mode was changed during stream.')
- self.error_in_callback = True
- return 0
- elif mode == high_finesse_constants.cmiPulseMode:
- self.log.warning('Wavemeter pulse mode was changed during stream.')
- self.error_in_callback = True
- return 0
-
- # see if new data is from one of the active channels
- ch = high_finesse_constants.cmi_wavelength_n.get(mode)
- if ch is None:
- # not a new sample, something else happened at the wavemeter
- return 0
-
- if dblval < 0:
- # the wavemeter is either over- or underexposed
- # retain error code for further processing
- wavelength = dblval
- else:
- wavelength = 1e-9 * dblval # measurement is in nm
- timestamp = 1e-3 * intval # wavemeter records timestamps in ms
- for instreamer, channels in self._connected_instream_modules.items():
- if ch in channels:
- instreamer.process_new_wavelength(ch, wavelength, timestamp)
- return 0
-
- _CALLBACK = WINFUNCTYPE(c_int, c_long, c_long, c_long, c_double, c_long)
- return _CALLBACK(handle_callback)
diff --git a/src/qudi/hardware/wavemeter/high_finesse_wavemeter.py b/src/qudi/hardware/wavemeter/high_finesse_wavemeter.py
index 9edb3f8..0af0f7f 100644
--- a/src/qudi/hardware/wavemeter/high_finesse_wavemeter.py
+++ b/src/qudi/hardware/wavemeter/high_finesse_wavemeter.py
@@ -1,17 +1,8 @@
# -*- coding: utf-8 -*-
-
"""
-This file contains the qudi hardware module for the HighFinesse wavemeter. It implements the
-DataInStreamInterface. Communication with the hardware is done via callback functions such that no new data is missed.
-Measurement timestamps provided by the hardware are used for sample timing. As an approximation, the timestamps
-corresponding to the first active channel are equally used for all channels. Considering the fixed round-trip time
-composing the individual channel exposure times and switching times, this implementation should satisfy all
-synchronization needs to an extent that is possible to implement on this software level.
-
-Copyright (c) 2021, the qudi developers. See the AUTHORS.md file at the top-level directory of this
-distribution and on
+This module reads data from High Finesse wavemeters
-This file is part of qudi.
+Copyright (c) 2025, the QuPIDC qudi developers.
Qudi is free software: you can redistribute it and/or modify it under the terms of
the GNU Lesser General Public License as published by the Free Software Foundation,
@@ -25,438 +16,82 @@
If not, see .
"""
-import time
-from typing import Union, Optional, List, Tuple, Sequence, Any, Dict
-
import numpy as np
-from scipy.constants import lambda2nu
from PySide2 import QtCore
-from qudi.core.configoption import ConfigOption
-from qudi.core.connector import Connector
-from qudi.util.mutex import Mutex
-from qudi.util.constraints import ScalarConstraint
-from qudi.interface.data_instream_interface import DataInStreamInterface, DataInStreamConstraints, StreamingMode, \
- SampleTiming
-from qudi.hardware.wavemeter.high_finesse_proxy import HighFinesseProxy
-from qudi.hardware.wavemeter.high_finesse_constants import GetFrequencyError
+from qudi.interface.simple_wavemeter_interface import SimpleWavemeterInterface
+import qudi.hardware.wavemeter.wlmConst as wlmConst
+import qudi.hardware.wavemeter.wlmData as wlmData
-class HighFinesseWavemeter(DataInStreamInterface):
- """
- HighFinesse wavelength meter as an in-streaming device.
-
- The HighFinesseProxy hardware module is required. It takes care of all communication with the hardware.
+class HighFinesseWavemeter(SimpleWavemeterInterface):
+ """
+ Hardware file for high finesse wavemeter
+
+ Assumes the DLL is installed in the system path so does not need to be manually located
+
Example config for copy-paste:
wavemeter:
module.Class: 'wavemeter.high_finesse_wavemeter.HighFinesseWavemeter'
- connect:
- proxy: wavemeter_proxy
- options:
- channels:
- laser:
- switch_ch: 1 # channel on the wavemeter switch
- unit: 'm' # wavelength (m) or frequency (Hz)
- exposure: 10 # exposure time in ms, optional
"""
-
- # declare signals
- sigNewWavelength = QtCore.Signal(object)
-
- _proxy: HighFinesseProxy = Connector(name='proxy', interface='HighFinesseProxy')
-
- # config options
- _wavemeter_ch_config: Dict[str, Dict[str, Any]] = ConfigOption(
- name='channels',
- default={
- 'default_channel': {'switch_ch': 1, 'unit': 'm', 'exposure': None}
- },
- missing='info'
- )
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self._lock = Mutex()
-
- # internal settings
- # dictionary with switch channel numbers as keys, channel names as values
- self._channel_names: Dict[int, str] = {}
- self._channel_units: Dict[int, str] = {}
- self._channel_buffer_size = 1024**2
- self._active_switch_channels: Optional[List[int]] = None # list of active switch channel numbers
- self._last_measurement_error: Dict[int, float] = {}
-
- # data buffer
- self._wm_start_time: Optional[float] = None
- self._data_buffer: Optional[np.ndarray] = None
- self._timestamp_buffer: Optional[np.ndarray] = None
- self._current_buffer_position = 0
- self._buffer_overflow = False
-
- # stored hardware constraints
- self._constraints: Optional[DataInStreamConstraints] = None
-
- def on_activate(self) -> None:
- # configure wavemeter channels
- for ch_name, info in self._wavemeter_ch_config.items():
- ch = info['switch_ch']
- unit = info['unit']
- self._channel_names[ch] = ch_name
-
- if unit == 'THz' or unit == 'Hz':
- self._channel_units[ch] = 'Hz'
- elif unit == 'nm' or unit == 'm':
- self._channel_units[ch] = 'm'
- else:
- self.log.warning(f'Invalid unit: {unit}. Valid units are Hz and m. Using m as default.')
- self._channel_units[ch] = 'm'
-
- exp_time = info.get('exposure')
- if exp_time is not None:
- self._proxy().set_exposure_time(ch, exp_time)
-
- self._active_switch_channels = list(self._channel_names)
-
- # set up constraints
- sample_rate = self.sample_rate
- self._constraints = DataInStreamConstraints(
- channel_units={self._channel_names[ch]: self._channel_units[ch] for ch in self._active_switch_channels},
- sample_timing=SampleTiming.TIMESTAMP,
- # TODO: implement fixed streaming mode
- streaming_modes=[StreamingMode.CONTINUOUS],
- data_type=np.float64,
- channel_buffer_size=ScalarConstraint(default=1024**2, # 8 MB
- bounds=(128, 1024**3), # max = 8 GB
- increment=1,
- enforce_int=True),
- sample_rate=ScalarConstraint(default=sample_rate,
- bounds=(0.01, 1e3))
- )
-
- def on_deactivate(self) -> None:
- self.stop_stream()
-
- # free memory
- self._data_buffer = None
- self._timestamp_buffer = None
-
- @property
- def constraints(self) -> DataInStreamConstraints:
- """ Read-only property returning the constraints on the settings for this data streamer. """
- return self._constraints
-
- def start_stream(self) -> None:
- """ Start the data acquisition/streaming """
- with self._lock:
- if self.module_state() == 'idle':
- self.module_state.lock()
- self._init_buffers()
- self._last_measurement_error = {ch: 0 for ch in self._active_switch_channels}
- self._proxy().connect_instreamer(self, self._active_switch_channels)
- else:
- self.log.warning('Unable to start input stream. It is already running.')
-
- def stop_stream(self) -> None:
- """ Stop the data acquisition/streaming """
- with self._lock:
- if self.module_state() == 'locked':
- self._proxy().disconnect_instreamer(self)
- self._wm_start_time = None
- self.module_state.unlock()
- else:
- self.log.warning('Unable to stop wavemeter input stream as nothing is running.')
-
- def stop_stream_watchdog(self) -> None:
- """Meant to be called from proxy watchdog only. Skips the disconnecting."""
- with self._lock:
- if self.module_state() == 'locked':
- self._wm_start_time = None
- self.module_state.unlock()
- else:
- self.log.warning('Unable to stop wavemeter input stream as nothing is running.')
-
- def read_data_into_buffer(self,
- data_buffer: np.ndarray,
- samples_per_channel: int,
- timestamp_buffer: Optional[np.ndarray] = None) -> None:
- """ Read data from the stream buffer into a 1D numpy array given as parameter.
- Samples of all channels are stored interleaved in contiguous memory.
- In case of a multidimensional buffer array, this buffer will be flattened before written
- into.
- The 1D data_buffer can be unraveled into channel and sample indexing with:
-
- data_buffer.reshape([, ])
-
- The data_buffer array must have the same data type as self.constraints.data_type.
-
- In case of SampleTiming.TIMESTAMP a 1D numpy.float64 timestamp_buffer array has to be
- provided to be filled with timestamps corresponding to the data_buffer array. It must be
- able to hold at least items:
-
- This function is blocking until the required number of samples has been acquired.
+
+ sigWavelengthUpdated = QtCore.Signal(np.ndarray) # 1-d array of wavelengths in nm
+
+ def on_activate(self):
+ """ Activate module.
"""
- self._validate_buffers(data_buffer, timestamp_buffer)
-
- # wait until requested number of samples is available
- while self.available_samples < samples_per_channel:
- if self.module_state() != 'locked':
- break
- # wait for 10 ms
- time.sleep(0.01)
-
- with self._lock:
- if self.module_state() != 'locked':
- raise RuntimeError('Unable to read data. Stream is not running.')
-
- total_samples = samples_per_channel * len(self.active_channels)
- data_buffer[:total_samples] = self._data_buffer[:total_samples]
- timestamp_buffer[:samples_per_channel] = self._timestamp_buffer[:samples_per_channel]
- self._remove_samples_from_buffer(samples_per_channel)
-
- def read_available_data_into_buffer(self,
- data_buffer: np.ndarray,
- timestamp_buffer: Optional[np.ndarray] = None) -> int:
- """ Read data from the stream buffer into a 1D numpy array given as parameter.
- All samples for each channel are stored in consecutive blocks one after the other.
- The number of samples read per channel is returned and can be used to slice out valid data
- from the buffer arrays like:
-
- valid_data = data_buffer[: * ]
- valid_timestamps = timestamp_buffer[:]
-
- See "read_data_into_buffer" documentation for more details.
-
- This method will read all currently available samples into buffer. If number of available
- samples exceeds buffer size, read only as many samples as fit into the buffer.
+ try:
+ self._dll = wlmData.LoadDLL()
+ except OSError as err:
+ self.log.exception(f'{err}\nPlease check if the wlmData DLL is installed correctly!')
+
+ # Check the number of WLM server instances
+ if self._dll.GetWLMCount(0) == 0:
+ self.log.exception('There is no running WLM server instance.')
+
+
+ self._data = np.zeros((4, 1))
+
+ @wlmData.CALLBACK_TYPE
+ def cb(mode, _intval, dblval):
+ self._update(mode, _intval, dblval)
+
+ self._cb = cb
+
+ # Install callback function
+ callback_thread_priority = 2
+ self._dll.Instantiate(
+ wlmConst.cInstNotification,
+ wlmConst.cNotifyInstallCallback,
+ self._cb,
+ callback_thread_priority,
+ )
+
+
+ def on_deactivate(self):
+ """ Deactivate module.
"""
- with self._lock:
- if self.module_state() != 'locked':
- raise RuntimeError('Unable to read data. Stream is not running.')
-
- req_samples_per_channel = self._validate_buffers(data_buffer, timestamp_buffer)
- number_of_channels = len(self.active_channels)
- samples_per_channel = min(req_samples_per_channel, self.available_samples)
- total_samples = number_of_channels * samples_per_channel
-
- data_buffer[:total_samples] = self._data_buffer[:total_samples]
- timestamp_buffer[:samples_per_channel] = self._timestamp_buffer[:samples_per_channel]
- self._remove_samples_from_buffer(samples_per_channel)
-
- return samples_per_channel
-
- def read_data(self,
- samples_per_channel: Optional[int] = None
- ) -> Tuple[np.ndarray, Union[np.ndarray, None]]:
- """ Read data from the stream buffer into a 1D numpy array and return it.
- All samples for each channel are stored in consecutive blocks one after the other.
- The returned data_buffer can be unraveled into channel samples with:
-
- data_buffer.reshape([, samples_per_channel])
-
- The numpy array data type is the one defined in self.constraints.data_type.
-
- In case of SampleTiming.TIMESTAMP a 1D numpy.float64 timestamp_buffer array will be
- returned as well with timestamps corresponding to the data_buffer array.
-
- If samples_per_channel is omitted all currently available samples are read from buffer.
- This method will not return until all requested samples have been read or a timeout occurs.
- """
- samples_per_channel = samples_per_channel if samples_per_channel is not None else self.available_samples
- total_samples = len(self.active_channels) * samples_per_channel
-
- data_buffer = np.empty(total_samples, dtype=self.constraints.data_type)
- timestamp_buffer = np.empty(samples_per_channel, dtype=np.float64)
- self.read_data_into_buffer(data_buffer, samples_per_channel, timestamp_buffer)
-
- return data_buffer, timestamp_buffer
-
- def read_single_point(self) -> Tuple[np.ndarray, Union[None, np.float64]]:
- """ This method will initiate a single sample read on each configured data channel.
- In general this sample may not be acquired simultaneous for all channels and timing in
- general can not be assured. Us this method if you want to have a non-timing-critical
- snapshot of your current data channel input.
- May not be available for all devices.
- The returned 1D numpy array will contain one sample for each channel.
-
- In case of SampleTiming.TIMESTAMP a single numpy.float64 timestamp value will be returned
- as well.
- """
- if self.module_state() != 'locked':
- raise RuntimeError('Unable to read data. Device is not running.')
-
- with self._lock:
- n = len(self.active_channels)
- available_samples = self.available_samples
- # get the most recent samples for each channel
- data = self._data_buffer[n * (available_samples - 1):n * available_samples]
- timestamp = self._timestamp_buffer[available_samples - 1]
-
- return data, timestamp
-
- @property
- def sample_rate(self) -> float:
- """ Read-only property returning the currently set sample rate in Hz.
- For SampleTiming.CONSTANT this is the sample rate of the hardware, for any other timing mode
- this property represents only a hint to the actual hardware timebase and can not be
- considered accurate.
-
- For the wavemeter, it is estimated by the exposure times per channel and switching times if
- more than one channel is active.
- """
- return self._proxy().sample_rate()
-
- @property
- def streaming_mode(self) -> StreamingMode:
- """ Read-only property returning the currently configured StreamingMode Enum """
- return StreamingMode.CONTINUOUS
-
- @property
- def active_channels(self) -> List[str]:
- """ Read-only property returning the currently configured active channel names """
- ch_names = [self._channel_names[ch] for ch in self._active_switch_channels]
- return ch_names
-
- @property
- def available_samples(self) -> int:
- """
- Read-only property to return the currently available number of samples per channel ready
- to read from buffer.
- """
- if self.module_state() != 'locked':
- return 0
-
- # all channels must have been read out in order to count as an available sample
- return self._current_buffer_position // len(self.active_channels)
-
- @property
- def channel_buffer_size(self) -> int:
- """ Read-only property returning the currently set buffer size in samples per channel.
- The total buffer size in bytes can be estimated by:
- * * numpy.nbytes[]
-
- For StreamingMode.FINITE this will also be the total number of samples to acquire per
- channel.
- """
- return self._channel_buffer_size
-
- def configure(self,
- active_channels: Sequence[str],
- streaming_mode: Union[StreamingMode, int],
- channel_buffer_size: int,
- sample_rate: float) -> None:
- """ Configure a data stream. See read-only properties for information on each parameter. """
- if self.module_state() == 'locked':
- raise RuntimeError('Unable to configure data stream while it is already running')
-
- if active_channels is not None:
- self._active_switch_channels = []
- for ch in active_channels:
- if ch in self._wavemeter_ch_config:
- self._active_switch_channels.append(self._wavemeter_ch_config[ch]['switch_ch'])
- else:
- raise ValueError(f'Channel {ch} is not set up in the config file. Available channels '
- f'are {list(self._channel_names.keys())}.')
-
- if streaming_mode is not None and streaming_mode.value != StreamingMode.CONTINUOUS.value:
- self.log.warning('Only continuous streaming is supported, ignoring this setting.')
-
- if channel_buffer_size is not None:
- self.constraints.channel_buffer_size.is_valid(channel_buffer_size)
- self._channel_buffer_size = channel_buffer_size
-
- def process_new_wavelength(self, ch, wavelength, timestamp):
- with self._lock:
- try:
- i = self._active_switch_channels.index(ch)
- except ValueError:
- # channel is not active on this instreamer
- return
-
- if self._last_measurement_error[ch] != 0:
- if wavelength > 0:
- # reset error flag
- self._last_measurement_error[ch] = 0
-
- if wavelength <= 0:
- # negative values indicate an error
- if self._last_measurement_error[ch] != wavelength:
- # error is new
- self._last_measurement_error[ch] = wavelength
- self.log.warning(f'The last wavemeter measurement of channel {ch} was unsuccessful '
- f'due to {GetFrequencyError(wavelength).name}.')
- wavelength = np.nan
-
- with self._lock:
- number_of_channels = len(self.active_channels)
- current_timestamp_buffer_position = self._current_buffer_position // number_of_channels
- if current_timestamp_buffer_position >= self.channel_buffer_size:
- self._buffer_overflow = True
- raise OverflowError(
- 'Streaming buffer encountered an overflow while receiving a callback from the wavemeter. '
- 'Please increase the buffer size or speed up data reading.'
- )
-
- # unit conversion
- if self._channel_units[ch] == 'Hz':
- converted_value = lambda2nu(wavelength)
- else:
- converted_value = wavelength
-
- # check if this is the first time this callback runs during a stream
- if self._wm_start_time is None:
- # set the timing offset to the start of the stream
- self._wm_start_time = timestamp
-
- if i != self._current_buffer_position % number_of_channels:
- # discard the sample if a sample was missed before and the buffer position is off
- return
-
- timestamp -= self._wm_start_time
- # insert the new data into the buffers
- self._data_buffer[self._current_buffer_position] = converted_value
- if i == 0:
- # only record the timestamp of the first active channel
- self._timestamp_buffer[current_timestamp_buffer_position] = timestamp
- self._current_buffer_position += 1
-
- self.sigNewWavelength.emit(converted_value)
-
- def _init_buffers(self) -> None:
- """ Initialize buffers and the current buffer position marker. """
- n = len(self._active_switch_channels)
- self._data_buffer = np.zeros(n * self._channel_buffer_size, dtype=self.constraints.data_type)
- self._timestamp_buffer = np.zeros(self._channel_buffer_size, dtype=np.float64)
- self._current_buffer_position = 0
- self._buffer_overflow = False
-
- def _remove_samples_from_buffer(self, samples_per_channel: int) -> None:
- """
- Remove samples that have been read from buffer to make space for new samples.
- :param samples_per_channel: number of samples per channel to clear off the buffer
- :return: None
+ self._dll.Instantiate(wlmConst.cInstNotification, wlmConst.cNotifyRemoveCallback, None, 0)
+
+
+ def get_wavelengths(self) -> np.ndarray:
+ """ Get the wavelengths from the wavemeter
"""
- total_samples = len(self.active_channels) * samples_per_channel
- self._data_buffer = np.roll(self._data_buffer, -total_samples)
- self._timestamp_buffer = np.roll(self._timestamp_buffer, -samples_per_channel)
- self._current_buffer_position -= total_samples
-
- def _validate_buffers(self,
- data_buffer: np.ndarray,
- timestamp_buffer: np.ndarray) -> Tuple[int, Union[int, Any]]:
- """ Validate arguments for read_[available]_data_into_buffer methods. """
- if not isinstance(data_buffer, np.ndarray) or data_buffer.dtype != self.constraints.data_type:
- raise TypeError(f'data_buffer must be numpy.ndarray with dtype {self.constraints.data_type}.')
-
- if not isinstance(timestamp_buffer, np.ndarray) or timestamp_buffer.dtype != np.float64:
- raise TypeError(f'timestamp_buffer must be provided for the wavemeter and '
- f'it must be a numpy.ndarray with dtype np.float64.')
-
- number_of_channels = len(self.active_channels)
- samples_per_channel = data_buffer.size // number_of_channels
-
- if timestamp_buffer.size != samples_per_channel:
- raise ValueError(f'timestamp_buffer must be exactly of length data_buffer // ')
-
- return samples_per_channel
+ return self._data
+
+ def _update(self, mode, _intval, dblval):
+ i = -1
+ if mode == wlmConst.cmiWavelength1:
+ i = 0
+ elif mode == wlmConst.cmiWavelength2:
+ i = 1
+ elif mode == wlmConst.cmiWavelength3:
+ i = 2
+ elif mode == wlmConst.cmiWavelength4:
+ i = 3
+
+ self._data[i] = dblval
+ self.sigWavelengthUpdated.emit(self._data)
+
diff --git a/src/qudi/hardware/wavemeter/high_finesse_wrapper.py b/src/qudi/hardware/wavemeter/high_finesse_wrapper.py
deleted file mode 100644
index b2fd04b..0000000
--- a/src/qudi/hardware/wavemeter/high_finesse_wrapper.py
+++ /dev/null
@@ -1,664 +0,0 @@
-######################################################################################################
-# @package wlmData
-# @file wlmData.py
-# @copyright HighFinesse GmbH.
-# @date 2020.06.02
-# @version 0.4
-#
-# Homepage: http://www.highfinesse.com/
-#
-# @brief Python wrapper for wlmData.dll.
-#
-# Changelog:
-# ----------
-# 2018.09.12
-# v0.1 - Initial release
-# 2018.09.14
-# v0.2 - Constant values added
-# 2018.09.15
-# v0.3 - Constant values separated to wlmConst.py, LoadDLL() added
-# 2020.06.02
-# v0.4 - GetPattern... and GetAnalysisData argtypes adapted
-# /
-
-import warnings
-from ctypes import c_bool, c_double, c_char_p, c_long, c_longlong, c_short, c_ulong, c_ushort, POINTER, windll
-
-MIN_VERSION = 6491
-
-
-def load_dll(dll_path='wlmData.dll'):
- dll = windll.LoadLibrary(dll_path)
- return dll
-
-
-def setup_dll(dll):
- # LONG_PTR Instantiate(long RFC, long Mode, LONG_PTR P1, long P2)
- dll.Instantiate.argtypes = [c_long, c_long, POINTER(c_long), c_long]
- dll.Instantiate.restype = POINTER(c_long)
-
- # long WaitForWLMEvent(lref Mode, lref IntVal, dref DblVal)
- dll.WaitForWLMEvent.argtypes = [POINTER(c_long), POINTER(c_long), POINTER(c_double)]
- dll.WaitForWLMEvent.restype = c_long
-
- # long WaitForWLMEventEx(lref Ver, lref Mode, lref IntVal, dref DblVal, lref Res1)
- dll.WaitForWLMEventEx.argtypes = [POINTER(c_long), POINTER(c_long), POINTER(c_long), POINTER(c_double),
- POINTER(c_long)]
- dll.WaitForWLMEventEx.restype = c_long
-
- # long WaitForNextWLMEvent(lref Mode, lref IntVal, dref DblVal)
- dll.WaitForNextWLMEvent.argtypes = [POINTER(c_long), POINTER(c_long), POINTER(c_double)]
- dll.WaitForNextWLMEvent.restype = c_long
-
- # long WaitForNextWLMEventEx(lref Ver, lref Mode, lref IntVal, dref DblVal, lref Res1)
- dll.WaitForNextWLMEventEx.argtypes = [POINTER(c_long), POINTER(c_long), POINTER(c_long), POINTER(c_double),
- POINTER(c_long)]
- dll.WaitForNextWLMEventEx.restype = c_long
-
- # void ClearWLMEvents(void)
- dll.ClearWLMEvents.argtypes = []
- dll.ClearWLMEvents.restype = None
-
- # long ControlWLM(long Action, LONG_PTR App, long Ver)
- dll.ControlWLM.argtypes = [c_long, POINTER(c_long), c_long]
- dll.ControlWLM.restype = c_long
-
- # long ControlWLMEx(long Action, LONG_PTR App, long Ver, long Delay, long Res)
- dll.ControlWLMEx.argtypes = [c_long, POINTER(c_long), c_long, c_long, c_long]
- dll.ControlWLMEx.restype = c_long
-
- # __int64 SynchroniseWLM(long Mode, __int64 TS)
- dll.SynchroniseWLM.argtypes = [c_long, c_longlong]
- dll.SynchroniseWLM.restype = c_longlong
-
- # long SetMeasurementDelayMethod(long Mode, long Delay)
- dll.SetMeasurementDelayMethod.argtypes = [c_long, c_long]
- dll.SetMeasurementDelayMethod.restype = c_long
-
- # long SetWLMPriority(long PPC, long Res1, long Res2)
- dll.SetWLMPriority.argtypes = [c_long, c_long, c_long]
- dll.SetWLMPriority.restype = c_long
-
- # long PresetWLMIndex(long Ver)
- dll.PresetWLMIndex.argtypes = [c_long]
- dll.PresetWLMIndex.restype = c_long
-
- # long GetWLMVersion(long Ver)
- dll.GetWLMVersion.argtypes = [c_long]
- dll.GetWLMVersion.restype = c_long
-
- # long GetWLMIndex(long Ver)
- dll.GetWLMIndex.argtypes = [c_long]
- dll.GetWLMIndex.restype = c_long
-
- # long GetWLMCount(long V)
- dll.GetWLMCount.argtypes = [c_long]
- dll.GetWLMCount.restype = c_long
-
- # double GetWavelength(double WL)
- dll.GetWavelength.argtypes = [c_double]
- dll.GetWavelength.restype = c_double
-
- # double GetWavelength2(double WL2)
- dll.GetWavelength2.argtypes = [c_double]
- dll.GetWavelength2.restype = c_double
-
- # double GetWavelengthNum(long num, double WL)
- dll.GetWavelengthNum.argtypes = [c_long, c_double]
- dll.GetWavelengthNum.restype = c_double
-
- # double GetCalWavelength(long ba, double WL)
- dll.GetCalWavelength.argtypes = [c_long, c_double]
- dll.GetCalWavelength.restype = c_double
-
- # double GetCalibrationEffect(double CE)
- dll.GetCalibrationEffect.argtypes = [c_double]
- dll.GetCalibrationEffect.restype = c_double
-
- # double GetFrequency(double F)
- dll.GetFrequency.argtypes = [c_double]
- dll.GetFrequency.restype = c_double
-
- # double GetFrequency2(double F2)
- dll.GetFrequency2.argtypes = [c_double]
- dll.GetFrequency2.restype = c_double
-
- # double GetFrequencyNum(long num, double F)
- dll.GetFrequencyNum.argtypes = [c_long, c_double]
- dll.GetFrequencyNum.restype = c_double
-
- # double GetLinewidth(long Index, double LW)
- dll.GetLinewidth.argtypes = [c_long, c_double]
- dll.GetLinewidth.restype = c_double
-
- # double GetLinewidthNum(long num, double LW)
- dll.GetLinewidthNum.argtypes = [c_long, c_double]
- dll.GetLinewidthNum.restype = c_double
-
- # double GetDistance(double D)
- dll.GetDistance.argtypes = [c_double]
- dll.GetDistance.restype = c_double
-
- # double GetAnalogIn(double AI)
- dll.GetAnalogIn.argtypes = [c_double]
- dll.GetAnalogIn.restype = c_double
-
- # double GetTemperature(double T)
- dll.GetTemperature.argtypes = [c_double]
- dll.GetTemperature.restype = c_double
-
- # long SetTemperature(double T)
- dll.SetTemperature.argtypes = [c_double]
- dll.SetTemperature.restype = c_long
-
- # double GetPressure(double P)
- dll.GetPressure.argtypes = [c_double]
- dll.GetPressure.restype = c_double
-
- # long SetPressure(long Mode, double P)
- dll.SetPressure.argtypes = [c_long, c_double]
- dll.SetPressure.restype = c_long
-
- # double GetExternalInput(long Index, double I)
- dll.GetExternalInput.argtypes = [c_long, c_double]
- dll.GetExternalInput.restype = c_double
-
- # long SetExternalInput(long Index, double I)
- dll.SetExternalInput.argtypes = [c_long, c_double]
- dll.SetExternalInput.restype = c_long
-
- # long GetExtraSetting(long Index, lref lGet, dref dGet, sref sGet)
- dll.GetExtraSetting.argtypes = [c_long, POINTER(c_long), POINTER(c_double), c_char_p]
- dll.GetExtraSetting.restype = c_long
-
- # long SetExtraSetting(long Index, long lSet, double dSet, sref sSet)
- dll.SetExtraSetting.argtypes = [c_long, c_long, c_double, c_char_p]
- dll.SetExtraSetting.restype = c_long
-
- # unsigned short GetExposure(unsigned short E)
- dll.GetExposure.argtypes = [c_ushort]
- dll.GetExposure.restype = c_ushort
-
- # long SetExposure(unsigned short E)
- dll.SetExposure.argtypes = [c_ushort]
- dll.SetExposure.restype = c_long
-
- # unsigned short GetExposure2(unsigned short E2)
- dll.GetExposure2.argtypes = [c_ushort]
- dll.GetExposure2.restype = c_ushort
-
- # long SetExposure2(unsigned short E2)
- dll.SetExposure2.argtypes = [c_ushort]
- dll.SetExposure2.restype = c_long
-
- # long GetExposureNum(long num, long arr, long E)
- dll.GetExposureNum.argtypes = [c_long, c_long, c_long]
- dll.GetExposureNum.restype = c_long
-
- # long SetExposureNum(long num, long arr, long E)
- dll.SetExposureNum.argtypes = [c_long, c_long, c_long]
- dll.SetExposureNum.restype = c_long
-
- # double GetExposureNumEx(long num, long arr, double E)
- dll.GetExposureNumEx.argtypes = [c_long, c_long, c_double]
- dll.GetExposureNumEx.restype = c_double
-
- # long SetExposureNumEx(long num, long arr, double E)
- dll.SetExposureNumEx.argtypes = [c_long, c_long, c_double]
- dll.SetExposureNumEx.restype = c_long
-
- # bool GetExposureMode(bool EM)
- dll.GetExposureMode.argtypes = [c_bool]
- dll.GetExposureMode.restype = c_bool
-
- # long SetExposureMode(bool EM)
- dll.SetExposureMode.argtypes = [c_bool]
- dll.SetExposureMode.restype = c_long
-
- # long GetExposureModeNum(long num, bool EM)
- dll.GetExposureModeNum.argtypes = [c_long, c_bool]
- dll.GetExposureModeNum.restype = c_long
-
- # long SetExposureModeNum(long num, bool EM)
- dll.SetExposureModeNum.argtypes = [c_long, c_bool]
- dll.SetExposureModeNum.restype = c_long
-
- # long GetExposureRange(long ER)
- dll.GetExposureRange.argtypes = [c_long]
- dll.GetExposureRange.restype = c_long
-
- # long GetAutoExposureSetting(long num, long AES, lref iVal, dref dVal)
- dll.GetAutoExposureSetting.argtypes = [c_long, c_long, POINTER(c_long), POINTER(c_double)]
- dll.GetAutoExposureSetting.restype = c_long
-
- # long SetAutoExposureSetting(long num, long AES, long iVal, double dVal)
- dll.SetAutoExposureSetting.argtypes = [c_long, c_long, c_long, c_double]
- dll.SetAutoExposureSetting.restype = c_long
-
- # unsigned short GetResultMode(unsigned short RM)
- dll.GetResultMode.argtypes = [c_ushort]
- dll.GetResultMode.restype = c_ushort
-
- # long SetResultMode(unsigned short RM)
- dll.SetResultMode.argtypes = [c_ushort]
- dll.SetResultMode.restype = c_long
-
- # unsigned short GetRange(unsigned short R)
- dll.GetRange.argtypes = [c_ushort]
- dll.GetRange.restype = c_ushort
-
- # long SetRange(unsigned short R)
- dll.SetRange.argtypes = [c_ushort]
- dll.SetRange.restype = c_long
-
- # unsigned short GetPulseMode(unsigned short PM)
- dll.GetPulseMode.argtypes = [c_ushort]
- dll.GetPulseMode.restype = c_ushort
-
- # long SetPulseMode(unsigned short PM)
- dll.SetPulseMode.argtypes = [c_ushort]
- dll.SetPulseMode.restype = c_long
-
- # long GetPulseDelay(long PD)
- dll.GetPulseDelay.argtypes = [c_long]
- dll.GetPulseDelay.restype = c_long
-
- # long SetPulseDelay(long PD)
- dll.SetPulseDelay.argtypes = [c_long]
- dll.SetPulseDelay.restype = c_long
-
- # unsigned short GetWideMode(unsigned short WM)
- dll.GetWideMode.argtypes = [c_ushort]
- dll.GetWideMode.restype = c_ushort
-
- # long SetWideMode(unsigned short WM)
- dll.SetWideMode.argtypes = [c_ushort]
- dll.SetWideMode.restype = c_long
-
- # long GetDisplayMode(long DM)
- dll.GetDisplayMode.argtypes = [c_long]
- dll.GetDisplayMode.restype = c_long
-
- # long SetDisplayMode(long DM)
- dll.SetDisplayMode.argtypes = [c_long]
- dll.SetDisplayMode.restype = c_long
-
- # bool GetFastMode(bool FM)
- dll.GetFastMode.argtypes = [c_bool]
- dll.GetFastMode.restype = c_bool
-
- # long SetFastMode(bool FM)
- dll.SetFastMode.argtypes = [c_bool]
- dll.SetFastMode.restype = c_long
-
- # bool GetLinewidthMode(bool LM)
- dll.GetLinewidthMode.argtypes = [c_bool]
- dll.GetLinewidthMode.restype = c_bool
-
- # long SetLinewidthMode(bool LM)
- dll.SetLinewidthMode.argtypes = [c_bool]
- dll.SetLinewidthMode.restype = c_long
-
- # bool GetDistanceMode(bool DM)
- dll.GetDistanceMode.argtypes = [c_bool]
- dll.GetDistanceMode.restype = c_bool
-
- # long SetDistanceMode(bool DM)
- dll.SetDistanceMode.argtypes = [c_bool]
- dll.SetDistanceMode.restype = c_long
-
- # long GetSwitcherMode(long SM)
- dll.GetSwitcherMode.argtypes = [c_long]
- dll.GetSwitcherMode.restype = c_long
-
- # long SetSwitcherMode(long SM)
- dll.SetSwitcherMode.argtypes = [c_long]
- dll.SetSwitcherMode.restype = c_long
-
- # long GetSwitcherChannel(long CH)
- dll.GetSwitcherChannel.argtypes = [c_long]
- dll.GetSwitcherChannel.restype = c_long
-
- # long SetSwitcherChannel(long CH)
- dll.SetSwitcherChannel.argtypes = [c_long]
- dll.SetSwitcherChannel.restype = c_long
-
- # long GetSwitcherSignalStates(long Signal, lref Use, lref Show)
- dll.GetSwitcherSignalStates.argtypes = [c_long, POINTER(c_long), POINTER(c_long)]
- dll.GetSwitcherSignalStates.restype = c_long
-
- # long SetSwitcherSignalStates(long Signal, long Use, long Show)
- dll.SetSwitcherSignalStates.argtypes = [c_long, c_long, c_long]
- dll.SetSwitcherSignalStates.restype = c_long
-
- # long SetSwitcherSignal(long Signal, long Use, long Show)
- dll.SetSwitcherSignal.argtypes = [c_long, c_long, c_long]
- dll.SetSwitcherSignal.restype = c_long
-
- # long GetAutoCalMode(long ACM)
- dll.GetAutoCalMode.argtypes = [c_long]
- dll.GetAutoCalMode.restype = c_long
-
- # long SetAutoCalMode(long ACM)
- dll.SetAutoCalMode.argtypes = [c_long]
- dll.SetAutoCalMode.restype = c_long
-
- # long GetAutoCalSetting(long ACS, lref val, long Res1, lref Res2)
- dll.GetAutoCalSetting.argtypes = [c_long, POINTER(c_long), c_long, POINTER(c_long)]
- dll.GetAutoCalSetting.restype = c_long
-
- # long SetAutoCalSetting(long ACS, long val, long Res1, long Res2)
- dll.SetAutoCalSetting.argtypes = [c_long, c_long, c_long, c_long]
- dll.SetAutoCalSetting.restype = c_long
-
- # long GetActiveChannel(long Mode, lref Port, long Res1)
- dll.GetActiveChannel.argtypes = [c_long, POINTER(c_long), c_long]
- dll.GetActiveChannel.restype = c_long
-
- # long SetActiveChannel(long Mode, long Port, long CH, long Res1)
- dll.SetActiveChannel.argtypes = [c_long, c_long, c_long, c_long]
- dll.SetActiveChannel.restype = c_long
-
- # long GetChannelsCount(long C)
- dll.GetChannelsCount.argtypes = [c_long]
- dll.GetChannelsCount.restype = c_long
-
- # unsigned short GetOperationState(unsigned short OS)
- dll.GetOperationState.argtypes = [c_ushort]
- dll.GetOperationState.restype = c_ushort
-
- # long Operation(unsigned short Op)
- dll.Operation.argtypes = [c_ushort]
- dll.Operation.restype = c_long
-
- # long SetOperationFile(sref lpFile)
- dll.SetOperationFile.argtypes = [c_char_p]
- dll.SetOperationFile.restype = c_long
-
- # long Calibration(long Type, long Unit, double Value, long Channel)
- dll.Calibration.argtypes = [c_long, c_long, c_double, c_long]
- dll.Calibration.restype = c_long
-
- # long RaiseMeasurementEvent(long Mode)
- dll.RaiseMeasurementEvent.argtypes = [c_long]
- dll.RaiseMeasurementEvent.restype = c_long
-
- # long TriggerMeasurement(long Action)
- dll.TriggerMeasurement.argtypes = [c_long]
- dll.TriggerMeasurement.restype = c_long
-
- # long GetTriggerState(long TS)
- dll.GetTriggerState.argtypes = [c_long]
- dll.GetTriggerState.restype = c_long
-
- # long GetInterval(long I)
- dll.GetInterval.argtypes = [c_long]
- dll.GetInterval.restype = c_long
-
- # long SetInterval(long I)
- dll.SetInterval.argtypes = [c_long]
- dll.SetInterval.restype = c_long
-
- # bool GetIntervalMode(bool IM)
- dll.GetIntervalMode.argtypes = [c_bool]
- dll.GetIntervalMode.restype = c_bool
-
- # long SetIntervalMode(bool IM)
- dll.SetIntervalMode.argtypes = [c_bool]
- dll.SetIntervalMode.restype = c_long
-
- # long GetBackground(long BG)
- dll.GetBackground.argtypes = [c_long]
- dll.GetBackground.restype = c_long
-
- # long SetBackground(long BG)
- dll.SetBackground.argtypes = [c_long]
- dll.SetBackground.restype = c_long
-
- # long GetAveragingSettingNum(long num, long AS, long Value)
- dll.GetAveragingSettingNum.argtypes = [c_long, c_long, c_long]
- dll.GetAveragingSettingNum.restype = c_long
-
- # long SetAveragingSettingNum(long num, long AS, long Value)
- dll.SetAveragingSettingNum.argtypes = [c_long, c_long, c_long]
- dll.SetAveragingSettingNum.restype = c_long
-
- # bool GetLinkState(bool LS)
- dll.GetLinkState.argtypes = [c_bool]
- dll.GetLinkState.restype = c_bool
-
- # long SetLinkState(bool LS)
- dll.SetLinkState.argtypes = [c_bool]
- dll.SetLinkState.restype = c_long
-
- # void LinkSettingsDlg(void)
- dll.LinkSettingsDlg.argtypes = []
- dll.LinkSettingsDlg.restype = None
-
- # long GetPatternItemSize(long Index)
- dll.GetPatternItemSize.argtypes = [c_long]
- dll.GetPatternItemSize.restype = c_long
-
- # long GetPatternItemCount(long Index)
- dll.GetPatternItemCount.argtypes = [c_long]
- dll.GetPatternItemCount.restype = c_long
-
- # ULONG_PTR GetPattern(long Index)
- dll.GetPattern.argtypes = [c_long]
- dll.GetPattern.restype = POINTER(c_ulong)
-
- # ULONG_PTR GetPatternNum(long Chn, long Index)
- dll.GetPatternNum.argtypes = [c_long, c_long]
- dll.GetPatternNum.restype = POINTER(c_ulong)
-
- # long GetPatternData(long Index, ULONG_PTR PArray)
- dll.GetPatternData.argtypes = [c_long, POINTER(c_short)]
- dll.GetPatternData.restype = c_long
-
- # long GetPatternDataNum(long Chn, long Index, ULONG_PTR PArray)
- dll.GetPatternDataNum.argtypes = [c_long, c_long, POINTER(c_short)]
- dll.GetPatternDataNum.restype = c_long
-
- # long SetPattern(long Index, long iEnable)
- dll.SetPattern.argtypes = [c_long, c_long]
- dll.SetPattern.restype = c_long
-
- # long SetPatternData(long Index, ULONG_PTR PArray)
- dll.SetPatternData.argtypes = [c_long, POINTER(c_ulong)]
- dll.SetPatternData.restype = c_long
-
- # bool GetAnalysisMode(bool AM)
- dll.GetAnalysisMode.argtypes = [c_bool]
- dll.GetAnalysisMode.restype = c_bool
-
- # long SetAnalysisMode(bool AM)
- dll.SetAnalysisMode.argtypes = [c_bool]
- dll.SetAnalysisMode.restype = c_long
-
- # long GetAnalysisItemSize(long Index)
- dll.GetAnalysisItemSize.argtypes = [c_long]
- dll.GetAnalysisItemSize.restype = c_long
-
- # long GetAnalysisItemCount(long Index)
- dll.GetAnalysisItemCount.argtypes = [c_long]
- dll.GetAnalysisItemCount.restype = c_long
-
- # ULONG_PTR GetAnalysis(long Index)
- dll.GetAnalysis.argtypes = [c_long]
- dll.GetAnalysis.restype = POINTER(c_ulong)
-
- # long GetAnalysisData(long Index, ULONG_PTR PArray)
- dll.GetAnalysisData.argtypes = [c_long, POINTER(c_double)]
- dll.GetAnalysisData.restype = c_long
-
- # long SetAnalysis(long Index, long iEnable)
- dll.SetAnalysis.argtypes = [c_long, c_long]
- dll.SetAnalysis.restype = c_long
-
- # long GetMinPeak(long M1)
- dll.GetMinPeak.argtypes = [c_long]
- dll.GetMinPeak.restype = c_long
-
- # long GetMinPeak2(long M2)
- dll.GetMinPeak2.argtypes = [c_long]
- dll.GetMinPeak2.restype = c_long
-
- # long GetMaxPeak(long X1)
- dll.GetMaxPeak.argtypes = [c_long]
- dll.GetMaxPeak.restype = c_long
-
- # long GetMaxPeak2(long X2)
- dll.GetMaxPeak2.argtypes = [c_long]
- dll.GetMaxPeak2.restype = c_long
-
- # long GetAvgPeak(long A1)
- dll.GetAvgPeak.argtypes = [c_long]
- dll.GetAvgPeak.restype = c_long
-
- # long GetAvgPeak2(long A2)
- dll.GetAvgPeak2.argtypes = [c_long]
- dll.GetAvgPeak2.restype = c_long
-
- # long SetAvgPeak(long PA)
- dll.SetAvgPeak.argtypes = [c_long]
- dll.SetAvgPeak.restype = c_long
-
- # long GetAmplitudeNum(long num, long Index, long A)
- dll.GetAmplitudeNum.argtypes = [c_long, c_long, c_long]
- dll.GetAmplitudeNum.restype = c_long
-
- # double GetIntensityNum(long num, double I)
- dll.GetIntensityNum.argtypes = [c_long, c_double]
- dll.GetIntensityNum.restype = c_double
-
- # double GetPowerNum(long num, double P)
- dll.GetPowerNum.argtypes = [c_long, c_double]
- dll.GetPowerNum.restype = c_double
-
- # unsigned short GetDelay(unsigned short D)
- dll.GetDelay.argtypes = [c_ushort]
- dll.GetDelay.restype = c_ushort
-
- # long SetDelay(unsigned short D)
- dll.SetDelay.argtypes = [c_ushort]
- dll.SetDelay.restype = c_long
-
- # unsigned short GetShift(unsigned short S)
- dll.GetShift.argtypes = [c_ushort]
- dll.GetShift.restype = c_ushort
-
- # long SetShift(unsigned short S)
- dll.SetShift.argtypes = [c_ushort]
- dll.SetShift.restype = c_long
-
- # unsigned short GetShift2(unsigned short S2)
- dll.GetShift2.argtypes = [c_ushort]
- dll.GetShift2.restype = c_ushort
-
- # long SetShift2(unsigned short S2)
- dll.SetShift2.argtypes = [c_ushort]
- dll.SetShift2.restype = c_long
-
- # bool GetDeviationMode(bool DM)
- dll.GetDeviationMode.argtypes = [c_bool]
- dll.GetDeviationMode.restype = c_bool
-
- # long SetDeviationMode(bool DM)
- dll.SetDeviationMode.argtypes = [c_bool]
- dll.SetDeviationMode.restype = c_long
-
- # double GetDeviationReference(double DR)
- dll.GetDeviationReference.argtypes = [c_double]
- dll.GetDeviationReference.restype = c_double
-
- # long SetDeviationReference(double DR)
- dll.SetDeviationReference.argtypes = [c_double]
- dll.SetDeviationReference.restype = c_long
-
- # long GetDeviationSensitivity(long DS)
- dll.GetDeviationSensitivity.argtypes = [c_long]
- dll.GetDeviationSensitivity.restype = c_long
-
- # long SetDeviationSensitivity(long DS)
- dll.SetDeviationSensitivity.argtypes = [c_long]
- dll.SetDeviationSensitivity.restype = c_long
-
- # double GetDeviationSignal(double DS)
- dll.GetDeviationSignal.argtypes = [c_double]
- dll.GetDeviationSignal.restype = c_double
-
- # double GetDeviationSignalNum(long Port, double DS)
- dll.GetDeviationSignalNum.argtypes = [c_long, c_double]
- dll.GetDeviationSignalNum.restype = c_double
-
- # long SetDeviationSignal(double DS)
- dll.SetDeviationSignal.argtypes = [c_double]
- dll.SetDeviationSignal.restype = c_long
-
- # long SetDeviationSignalNum(long Port, double DS)
- dll.SetDeviationSignalNum.argtypes = [c_long, c_double]
- dll.SetDeviationSignalNum.restype = c_long
-
- # double RaiseDeviationSignal(long iType, double dSignal)
- dll.RaiseDeviationSignal.argtypes = [c_long, c_double]
- dll.RaiseDeviationSignal.restype = c_double
-
- # long GetPIDCourse(sref PIDC)
- dll.GetPIDCourse.argtypes = [c_char_p]
- dll.GetPIDCourse.restype = c_long
-
- # long SetPIDCourse(sref PIDC)
- dll.SetPIDCourse.argtypes = [c_char_p]
- dll.SetPIDCourse.restype = c_long
-
- # long GetPIDCourseNum(long Port, sref PIDC)
- dll.GetPIDCourseNum.argtypes = [c_long, c_char_p]
- dll.GetPIDCourseNum.restype = c_long
-
- # long SetPIDCourseNum(long Port, sref PIDC)
- dll.SetPIDCourseNum.argtypes = [c_long, c_char_p]
- dll.SetPIDCourseNum.restype = c_long
-
- # long GetPIDSetting(long PS, long Port, lref iSet, dref dSet)
- dll.GetPIDSetting.argtypes = [c_long, c_long, POINTER(c_long), POINTER(c_double)]
- dll.GetPIDSetting.restype = c_long
-
- # long SetPIDSetting(long PS, long Port, long iSet, double dSet)
- dll.SetPIDSetting.argtypes = [c_long, c_long, c_long, c_double]
- dll.SetPIDSetting.restype = c_long
-
- # long GetLaserControlSetting(long PS, long Port, lref iSet, dref dSet, sref sSet)
- dll.GetLaserControlSetting.argtypes = [c_long, c_long, POINTER(c_long), POINTER(c_double), c_char_p]
- dll.GetLaserControlSetting.restype = c_long
-
- # long SetLaserControlSetting(long PS, long Port, long iSet, double dSet, sref sSet)
- dll.SetLaserControlSetting.argtypes = [c_long, c_long, c_long, c_double, c_char_p]
- dll.SetLaserControlSetting.restype = c_long
-
- # long ClearPIDHistory(long Port)
- dll.ClearPIDHistory.argtypes = [c_long]
- dll.ClearPIDHistory.restype = c_long
-
- # double ConvertUnit(double Val, long uFrom, long uTo)
- dll.ConvertUnit.argtypes = [c_double, c_long, c_long]
- dll.ConvertUnit.restype = c_double
-
- # double ConvertDeltaUnit(double Base, double Delta, long uBase, long uFrom, long uTo)
- dll.ConvertDeltaUnit.argtypes = [c_double, c_double, c_long, c_long, c_long]
- dll.ConvertDeltaUnit.restype = c_double
-
- # bool GetReduced(bool R)
- dll.GetReduced.argtypes = [c_bool]
- dll.GetReduced.restype = c_bool
-
- # long SetReduced(bool R)
- dll.SetReduced.argtypes = [c_bool]
- dll.SetReduced.restype = c_long
-
- # unsigned short GetScale(unsigned short S)
- dll.GetScale.argtypes = [c_ushort]
- dll.GetScale.restype = c_ushort
-
- # long SetScale(unsigned short S)
- dll.SetScale.argtypes = [c_ushort]
- dll.SetScale.restype = c_long
diff --git a/src/qudi/hardware/wavemeter/high_finesse_constants.py b/src/qudi/hardware/wavemeter/wlmConst.py
similarity index 51%
rename from src/qudi/hardware/wavemeter/high_finesse_constants.py
rename to src/qudi/hardware/wavemeter/wlmConst.py
index 1d48a1a..52aa605 100644
--- a/src/qudi/hardware/wavemeter/high_finesse_constants.py
+++ b/src/qudi/hardware/wavemeter/wlmConst.py
@@ -1,24 +1,9 @@
-######################################################################################################
-# @package wlmData
-# @file wlmConst.py
-# @copyright HighFinesse GmbH.
-# @date 2018.09.15
-# @version 0.1
-#
-# Homepage: http://www.highfinesse.com/
-#
-# @brief Constant values for wlmData.dll API.
-#
-# Changelog:
-# ----------
-# 2018.09.15
-# v0.1 - Initial release
-#/
-
-from enum import Enum
-
-## ########### Constants ##############################################
-## Instantiating Constants for 'RFC' parameter
+"""
+wlmData API constants generated from wlmData.h on 2024-11-12
+"""
+# pylint: disable=invalid-name
+
+# Instantiating Constants for 'RFC' parameter
cInstCheckForWLM = -1
cInstResetCalc = 0
cInstReturnMode = cInstResetCalc
@@ -28,8 +13,9 @@
cInstControlWLM = 3
cInstControlDelay = 4
cInstControlPriority = 5
+cInstNetworkControl = 6
-## Notification Constants for 'Mode' parameter
+# Notification Constants for 'Mode' parameter
cNotifyInstallCallback = 0
cNotifyRemoveCallback = 1
cNotifyInstallWaitEvent = 2
@@ -37,32 +23,38 @@
cNotifyInstallCallbackEx = 4
cNotifyInstallWaitEventEx = 5
-
-class ResultError(Enum):
- """ ResultError Constants of Set...-functions """
- NoErr = 0
- WlmMissing = -1
- CouldNotSet = -2
- ParmOutOfRange = -3
- WlmOutOfResources = -4
- WlmInternalError = -5
- NotAvailable = -6
- WlmBusy = -7
- NotInMeasurementMode = -8
- OnlyInMeasurementMode = -9
- ChannelNotAvailable = -10
- ChannelTemporarilyNotAvailable = -11
- CalOptionNotAvailable = -12
- CalWavelengthOutOfRange = -13
- BadCalibrationSignal = -14
- UnitNotAvailable = -15
- FileNotFound = -16
- FileCreation = -17
- TriggerPending = -18
- TriggerWaiting = -19
- NoLegitimation = -20
-
-## Mode Constants for Callback-Export and WaitForWLMEvent-function
+# ResultError Constants of Set...-functions
+ResERR_NoErr = 0
+ResERR_WlmMissing = -1
+ResERR_CouldNotSet = -2
+ResERR_ParmOutOfRange = -3
+ResERR_WlmOutOfResources = -4
+ResERR_WlmInternalError = -5
+ResERR_NotAvailable = -6
+ResERR_WlmBusy = -7
+ResERR_NotInMeasurementMode = -8
+ResERR_OnlyInMeasurementMode = -9
+ResERR_ChannelNotAvailable = -10
+ResERR_ChannelTemporarilyNotAvailable = -11
+ResERR_CalOptionNotAvailable = -12
+ResERR_CalWavelengthOutOfRange = -13
+ResERR_BadCalibrationSignal = -14
+ResERR_UnitNotAvailable = -15
+ResERR_FileNotFound = -16
+ResERR_FileCreation = -17
+ResERR_TriggerPending = -18
+ResERR_TriggerWaiting = -19
+ResERR_NoLegitimation = -20
+ResERR_NoTCPLegitimation = -21
+ResERR_NotInPulseMode = -22
+ResERR_OnlyInPulseMode = -23
+ResERR_NotInSwitchMode = -24
+ResERR_OnlyInSwitchMode = -25
+ResERR_TCPErr = -26
+ResERR_StringTooLong = -29
+ResERR_InterruptedByUser = -30
+
+# Mode Constants for Callback-Export and WaitForWLMEvent-function
cmiResultMode = 1
cmiRange = 2
cmiPulse = 3
@@ -122,6 +114,7 @@ class ResultError(Enum):
cmiVersion0 = cmiVersion
cmiVersion1 = 96
cmiPulseDelay = 99
+cmiPulseIntegration = cmiPulseDelay
cmiDLLAttach = 121
cmiSwitcherSignal = 123
cmiSwitcherMode = 124
@@ -276,6 +269,7 @@ class ResultError(Enum):
cmiTriggerState = 1497
cmiDeviceAttach = 1501
cmiDeviceDetach = 1502
+cmiTimePerMeasurement = 1514
cmiAutoExpoMin = 1517
cmiAutoExpoMax = 1518
cmiAutoExpoStepUp = 1519
@@ -286,32 +280,130 @@ class ResultError(Enum):
cmiAveragingCount = 1524
cmiAveragingMode = 1525
cmiAveragingType = 1526
-
-# Helper dictionary for multichannel wavelength readout
-cmi_wavelength_n = {
- cmiWavelength1: 1,
- cmiWavelength2: 2,
- cmiWavelength3: 3,
- cmiWavelength4: 4,
- cmiWavelength5: 5,
- cmiWavelength6: 6,
- cmiWavelength7: 7,
- cmiWavelength8: 8,
- cmiWavelength9: 9,
- cmiWavelength10: 10,
- cmiWavelength11: 11,
- cmiWavelength12: 12,
- cmiWavelength13: 13,
- cmiWavelength14: 14,
- cmiWavelength15: 15,
- cmiWavelength16: 16,
- cmiWavelength17: 17
-}
-
-## Index constants for Get- and SetExtraSetting
+cmiNowTick_d = 1527
+cmiAirMode = 1532
+cmiAirTemperature = 1534
+cmiAirPressure = 1535
+cmiAirHumidity = 1536
+cmiAirCO2 = 1651
+cmiSubSnapshotID = 1539
+cmiInternalTriggerRate = 1540
+cmiGain11 = 1541
+cmiGain12 = 1542
+cmiGain13 = 1543
+cmiGain14 = 1544
+cmiGain15 = 1545
+cmiGain16 = 1546
+cmiGain17 = 1547
+cmiGain18 = 1548
+cmiGain19 = 1549
+cmiGain110 = 1550
+cmiGain111 = 1551
+cmiGain112 = 1552
+cmiGain113 = 1553
+cmiGain114 = 1554
+cmiGain115 = 1555
+cmiGain116 = 1556
+cmiGain117 = 1557
+cmiGain21 = 1558
+cmiGain22 = 1559
+cmiGain23 = 1560
+cmiGain24 = 1561
+cmiGain25 = 1562
+cmiGain26 = 1563
+cmiGain27 = 1564
+cmiGain28 = 1565
+cmiGain29 = 1566
+cmiGain210 = 1567
+cmiGain211 = 1568
+cmiGain212 = 1569
+cmiGain213 = 1570
+cmiGain214 = 1571
+cmiGain215 = 1572
+cmiGain216 = 1573
+cmiGain217 = 1574
+cmiGain31 = 1575
+cmiGain32 = 1576
+cmiGain33 = 1577
+cmiGain34 = 1578
+cmiGain35 = 1579
+cmiGain36 = 1580
+cmiGain37 = 1581
+cmiGain38 = 1582
+cmiGain39 = 1583
+cmiGain310 = 1584
+cmiGain311 = 1585
+cmiGain312 = 1586
+cmiGain313 = 1587
+cmiGain314 = 1588
+cmiGain315 = 1589
+cmiGain316 = 1590
+cmiGain317 = 1591
+cmiGain41 = 1592
+cmiGain42 = 1593
+cmiGain43 = 1594
+cmiGain44 = 1595
+cmiGain45 = 1596
+cmiGain46 = 1597
+cmiGain47 = 1598
+cmiGain48 = 1599
+cmiGain49 = 1600
+cmiGain410 = 1601
+cmiGain411 = 1602
+cmiGain412 = 1603
+cmiGain413 = 1604
+cmiGain414 = 1605
+cmiGain415 = 1606
+cmiGain416 = 1607
+cmiGain417 = 1608
+cmiMultimodeLevel1 = 1609
+cmiMultimodeLevel2 = 1610
+cmiMultimodeLevel3 = 1611
+cmiMultimodeLevel4 = 1612
+cmiMultimodeLevel5 = 1613
+cmiMultimodeLevel6 = 1614
+cmiMultimodeLevel7 = 1615
+cmiMultimodeLevel8 = 1616
+cmiMultimodeLevel9 = 1617
+cmiMultimodeLevel10 = 1618
+cmiMultimodeLevel11 = 1619
+cmiMultimodeLevel12 = 1620
+cmiMultimodeLevel13 = 1621
+cmiMultimodeLevel14 = 1622
+cmiMultimodeLevel15 = 1623
+cmiMultimodeLevel16 = 1624
+cmiMultimodeLevel17 = 1625
+cmiFastBasedLinewidthAnalysis = 1630
+cmiMultimodeCount1 = 1633
+cmiMultimodeCount2 = 1634
+cmiMultimodeCount3 = 1635
+cmiMultimodeCount4 = 1636
+cmiMultimodeCount5 = 1637
+cmiMultimodeCount6 = 1638
+cmiMultimodeCount7 = 1639
+cmiMultimodeCount8 = 1640
+cmiMultimodeCount9 = 1641
+cmiMultimodeCount10 = 1642
+cmiMultimodeCount11 = 1643
+cmiMultimodeCount12 = 1644
+cmiMultimodeCount13 = 1645
+cmiMultimodeCount14 = 1646
+cmiMultimodeCount15 = 1647
+cmiMultimodeCount16 = 1648
+cmiMultimodeCount17 = 1649
+cmiPowerCalibration = 1652
+cmiStartPowerCalibration = 1653
+cmiEndPowerCalibration = 1654
+
+# Index constants for Get- and SetExtraSetting
cesCalculateLive = 4501
+cesTryCalcOnAllErrors = 4530
+cesTryCalcOnUnderexposure = 4531
+cesCalcUnderexposureMinLevel = 4561
+cesTrancheAverage = 4600
+cesTTLBatchCount = 4601
-## WLM Control Mode Constants
+# WLM Control Mode Constants
cCtrlWLMShow = 1
cCtrlWLMHide = 2
cCtrlWLMExit = 3
@@ -322,13 +414,13 @@ class ResultError(Enum):
cCtrlWLMSilent = 0x0040
cCtrlWLMStartDelay = 0x0080
-## Operation Mode Constants (for "Operation" and "GetOperationState" functions)
+# Operation Mode Constants (for "Operation" and "GetOperationState" functions)
cStop = 0
cAdjustment = 1
cMeasurement = 2
-## Base Operation Constants (To be used exclusively, only one of this list at a time,
-## but still can be combined with "Measurement Action Addition Constants". See below.)
+# Base Operation Constants (To be used exclusively, only one of this list at a time,
+# but still can be combined with "Measurement Action Addition Constants". See below.)
cCtrlStopAll = cStop
cCtrlStartAdjustment = cAdjustment
cCtrlStartMeasurement = cMeasurement
@@ -337,15 +429,15 @@ class ResultError(Enum):
cCtrlStoreArray = 0x0010
cCtrlLoadArray = 0x0020
-## Additional Operation Flag Constants (combine with "Base Operation Constants" above.)
+# Additional Operation Flag Constants (combine with "Base Operation Constants" above.)
cCtrlDontOverwrite = 0x0000
-cCtrlOverwrite = 0x1000 ## don't combine with cCtrlFileDialog
+cCtrlOverwrite = 0x1000 # don't combine with cCtrlFileDialog
cCtrlFileGiven = 0x0000
-cCtrlFileDialog = 0x2000 ## don't combine with cCtrlOverwrite and cCtrlFileASCII
-cCtrlFileBinary = 0x0000 ## *.smr, *.ltr
-cCtrlFileASCII = 0x4000 ## *.smx, *.ltx, don't combine with cCtrlFileDialog
+cCtrlFileDialog = 0x2000 # don't combine with cCtrlOverwrite and cCtrlFileASCII
+cCtrlFileBinary = 0x0000 # *.smr, *.ltr
+cCtrlFileASCII = 0x4000 # *.smx, *.ltx, don't combine with cCtrlFileDialog
-## Measurement Control Mode Constants
+# Measurement Control Mode Constants
cCtrlMeasDelayRemove = 0
cCtrlMeasDelayGenerally = 1
cCtrlMeasDelayOnce = 2
@@ -354,20 +446,20 @@ class ResultError(Enum):
cCtrlMeasDelayIdleEach = 5
cCtrlMeasDelayDefault = 6
-## Measurement Triggering Action Constants
+# Measurement Triggering Action Constants
cCtrlMeasurementContinue = 0
cCtrlMeasurementInterrupt = 1
cCtrlMeasurementTriggerPoll = 2
cCtrlMeasurementTriggerSuccess = 3
cCtrlMeasurementEx = 0x0100
-## ExposureRange Constants
+# ExposureRange Constants
cExpoMin = 0
cExpoMax = 1
cExpo2Min = 2
cExpo2Max = 3
-## Amplitude Constants
+# Amplitude Constants
cMin1 = 0
cMin2 = 1
cMax1 = 2
@@ -375,7 +467,7 @@ class ResultError(Enum):
cAvg1 = 4
cAvg2 = 5
-## Measurement Range Constants
+# Measurement Range Constants
cRange_250_410 = 4
cRange_250_425 = 0
cRange_300_410 = 3
@@ -387,41 +479,48 @@ class ResultError(Enum):
cRange_1100_1700 = 7
cRange_1100_1800 = cRange_1100_1700
-## Measurement Range Model Constants
+# Measurement Range Model Constants
cRangeModelOld = 65535
cRangeModelByOrder = 65534
cRangeModelByWavelength = 65533
-## Unit Constants for Get-/SetResultMode, GetLinewidth, Convert... and Calibration
+# Unit Constants for Get-/SetResultMode, GetLinewidth, Convert... and Calibration
cReturnWavelengthVac = 0
cReturnWavelengthAir = 1
cReturnFrequency = 2
cReturnWavenumber = 3
cReturnPhotonEnergy = 4
-## Power Unit Constants
+# Power Unit Constants
cPower_muW = 0
-cPower_dBm = 1
+cPower_mW = 1
+cPower_dBm = 2
-## Source Type Constants for Calibration
+# Source Type Constants for Calibration
cHeNe633 = 0
cHeNe1152 = 0
cNeL = 1
cOther = 2
cFreeHeNe = 3
+cSLR1530 = 5
-## Unit Constants for Autocalibration
+# Unit Constants for Autocalibration
cACOnceOnStart = 0
cACMeasurements = 1
cACDays = 2
cACHours = 3
cACMinutes = 4
-## ExposureRange Constants
+# Options flags for PowerCalibration
+cIntSensor1 = 0x00000000
+cIntSensor2 = 0x00000001
+cTryWOStoreDark = 0x00000010
+
+# ExposureRange Constants
cGetSync = 1
cSetSync = 2
-## Pattern- and Analysis Constants
+# Pattern- and Analysis Constants
cPatternDisable = 0
cPatternEnable = 1
cAnalysisDisable = cPatternDisable
@@ -436,7 +535,7 @@ class ResultError(Enum):
cSignalAnalysisX = cSignalAnalysis
cSignalAnalysisY = cSignalAnalysis + 1
-## State constants used with AutoExposureSetting functions
+# State constants used with AutoExposureSetting functions
cJustStepDown = 0
cRestartAtMinimum = 1
cJustStepUp = 0
@@ -444,43 +543,83 @@ class ResultError(Enum):
cConsiderFeedback = 1
cDontConsiderFeedback = 0
-## State constants used with AveragingSetting functions
+# Options identifiers used with GetOptionInfo
+cInfoSwitch = 1
+cInfoSwitchChannelsCount = 2
+cInfoIntNeonLamp = 11
+cInfo2ndExternalPort = 13
+cInfoPID = 21
+cInfoPIDPortsCount = 22
+cInfoPIDPortType = 23
+cInfoPIDPortRes = 24
+cInfoPIDPortUMin = 25
+cInfoPIDPortUMax = 26
+
+# PID type constants
+cInfoPIDPortTypeInt = 1
+cInfoPIDPortTypeExt = 2
+cInfoPIDPortTypeDigi = 3
+
+# State constants used with AveragingSetting functions
cAvrgFloating = 1
cAvrgSucceeding = 2
cAvrgSimple = 0
cAvrgPattern = 1
-
-class GetFrequencyError(Enum):
- """ Return error values of GetFrequency, GetWavelength and GetWLMVersion
- """
- ErrNoValue = 0
- ErrNoSignal = -1
- ErrBadSignal = -2
- ErrLowSignal = -3
- ErrBigSignal = -4
- ErrWlmMissing = -5
- ErrNotAvailable = -6
- InfNothingChanged = -7
- ErrNoPulse = -8
- ErrChannelNotAvailable = -10
- ErrDiv0 = -13
- ErrOutOfRange = -14
- ErrUnitNotAvailable_ErrMaxErr = -15
-
-## Return errorvalues of GetTemperature and GetPressure
+# Return errorvalues of GetFrequency, GetWavelength, GetWLMVersion, GetOptionInfo and GetResultInfo
+ErrNoError = 2 # only with GetResultInfo, other functions return the measured value in this case
+ErrNoValue = 0
+ErrNoSignal = -1
+ErrBadSignal = -2
+ErrLowSignal = -3
+ErrBigSignal = -4
+ErrWlmMissing = -5
+ErrNotAvailable = -6
+InfNothingChanged = -7
+ErrNoPulse = -8
+ErrChannelNotAvailable = -10
+ErrDiv0 = -13
+ErrOutOfRange = -14
+ErrUnitNotAvailable = -15
+ErrWOCalc = -16
+ErrTCPErr = -26
+ErrParameterOutOfRange = -28
+ErrStringTooLong = -29
+ErrInterruptedByUser = -30
+ErrInfoAlreadyFetched = -31
+ErrNoValueYet = -32
+ErrNoNewValueYet = -33
+ErrMaxErr = ErrNoNewValueYet
+
+# Return errorvalues of GetTemperature and GetPressure
ErrTemperature = -1000
-ErrTempNotMeasured = ErrTemperature + GetFrequencyError.ErrNoValue.value
-ErrTempNotAvailable = ErrTemperature + GetFrequencyError.ErrNotAvailable.value
-ErrTempWlmMissing = ErrTemperature + GetFrequencyError.ErrWlmMissing.value
-
-## Return errorvalues of GetDistance
-## real errorvalues are ErrDistance combined with those of GetWavelength
+ErrTempNotMeasured = ErrTemperature + ErrNoValue
+ErrTempNotAvailable = ErrTemperature + ErrNotAvailable
+ErrTempWlmMissing = ErrTemperature + ErrWlmMissing
+
+# Return errorvalues of GetGain
+ErrGain = -1000
+ErrGainNotAvailable = ErrGain + ErrNotAvailable
+ErrGainWlmMissing = ErrGain + ErrWlmMissing
+ErrGainChannelNotAvailable = ErrGain + ErrChannelNotAvailable
+ErrGainOutOfRange = ErrGain + ErrOutOfRange
+ErrGainParameterOutOfRange = ErrGain + ErrParameterOutOfRange
+
+# Return errorvalues of GetMultimodeInfo
+ErrMMI = -1000
+ErrMMINotAvailable = ErrMMI + ErrNotAvailable
+ErrMMIWlmMissing = ErrMMI + ErrWlmMissing
+ErrMMIChannelNotAvailable = ErrMMI + ErrChannelNotAvailable
+ErrMMIOutOfRange = ErrMMI + ErrOutOfRange
+ErrMMIParameterOutOfRange = ErrMMI + ErrParameterOutOfRange
+
+# Return errorvalues of GetDistance
+# real errorvalues are ErrDistance combined with those of GetWavelength
ErrDistance = -1000000000
-ErrDistanceNotAvailable = ErrDistance + GetFrequencyError.ErrNotAvailable.value
-ErrDistanceWlmMissing = ErrDistance + GetFrequencyError.ErrWlmMissing.value
+ErrDistanceNotAvailable = ErrDistance + ErrNotAvailable
+ErrDistanceWlmMissing = ErrDistance + ErrWlmMissing
-## Return flags of ControlWLMEx in combination with Show or Hide, Wait and Res = 1
+# Return flags of ControlWLMEx in combination with Show or Hide, Wait and Res = 1
flServerStarted = 0x00000001
flErrDeviceNotFound = 0x00000002
flErrDriverError = 0x00000004
@@ -507,10 +646,13 @@ class GetFrequencyError(Enum):
flDeviceStampNewer = 0x01000000
flFileStampNewer = 0x02000000
-## Return file info flags of SetOperationFile
-flFileInfoDoesntExist = 0x0000
-flFileInfoExists = 0x0001
-flFileInfoCantWrite = 0x0002
-flFileInfoCantRead = 0x0004
-flFileInfoInvalidName = 0x0008
+# Return file info flags of SetOperationFile
+flFileInfoDoesntExist = 0x00000000
+flFileInfoExists = 0x00000001
+flFileInfoCantWrite = 0x00000002
+flFileInfoCantRead = 0x00000004
+flFileInfoInvalidName = 0x00000008
cFileParameterError = -1
+
+
+# *** end of wlmData.h
diff --git a/src/qudi/hardware/wavemeter/wlmData.py b/src/qudi/hardware/wavemeter/wlmData.py
new file mode 100644
index 0000000..d1597e1
--- /dev/null
+++ b/src/qudi/hardware/wavemeter/wlmData.py
@@ -0,0 +1,588 @@
+"""
+wlmData API function bindings generated from wlmData.h on 2024-11-12
+"""
+# pylint: disable=line-too-long
+
+import ctypes
+import platform
+
+_FUNCTYPE = ctypes.WINFUNCTYPE if platform.system() == 'Windows' else ctypes.CFUNCTYPE
+
+CALLBACK_TYPE = _FUNCTYPE(None, ctypes.c_int32, ctypes.c_int32, ctypes.c_double)
+CALLBACK_EX_TYPE = _FUNCTYPE(None, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_double,
+ ctypes.c_int32)
+
+_PROTOTYPES = {
+# *********** Functions for general usage ****************************
+ # intptr_t Instantiate(int32_t RFC, int32_t Mode, intptr_t P1, int32_t P2)
+ 'Instantiate': (ctypes.c_void_p, (ctypes.c_int32, ctypes.c_int32, ctypes.c_void_p, ctypes.c_int32)),
+
+
+ # void CallbackProc(int32_t Mode, int32_t IntVal, double DblVal)
+
+ # void CallbackProcEx(int32_t Ver, int32_t Mode, int32_t IntVal, double DblVal, int32_t Res1)
+
+ # int32_t WaitForWLMEvent(int32_t* Mode, int32_t* IntVal, double* DblVal)
+ 'WaitForWLMEvent': (ctypes.c_int32, (ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double))),
+
+ # int32_t WaitForWLMEventEx(int32_t* Ver, int32_t* Mode, int32_t* IntVal, double* DblVal, int32_t* Res1)
+ 'WaitForWLMEventEx': (ctypes.c_int32, (ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_int32))),
+
+ # int32_t WaitForNextWLMEvent(int32_t* Mode, int32_t* IntVal, double* DblVal)
+ 'WaitForNextWLMEvent': (ctypes.c_int32, (ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double))),
+
+ # int32_t WaitForNextWLMEventEx(int32_t* Ver, int32_t* Mode, int32_t* IntVal, double* DblVal, int32_t* Res1)
+ 'WaitForNextWLMEventEx': (ctypes.c_int32, (ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_int32))),
+
+ # void ClearWLMEvents(void)
+ 'ClearWLMEvents': (None, ()),
+
+
+ # int32_t ControlWLM(int32_t Action, intptr_t App, int32_t Ver)
+ 'ControlWLM': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_void_p, ctypes.c_int32)),
+
+ # int32_t ControlWLMEx(int32_t Action, intptr_t App, int32_t Ver, int32_t Delay, int32_t Res)
+ 'ControlWLMEx': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_void_p, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # int64_t SynchroniseWLM(int32_t Mode, int64_t TS)
+ 'SynchroniseWLM': (ctypes.c_int64, (ctypes.c_int32, ctypes.c_int64)),
+
+ # int32_t SetMeasurementDelayMethod(int32_t Mode, int32_t Delay)
+ 'SetMeasurementDelayMethod': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t SetWLMPriority(int32_t PPC, int32_t Res1, int32_t Res2)
+ 'SetWLMPriority': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t PresetWLMIndex(int32_t Ver)
+ 'PresetWLMIndex': (ctypes.c_int32, (ctypes.c_int32,)),
+
+
+ # int32_t GetWLMVersion(int32_t Ver)
+ 'GetWLMVersion': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetWLMIndex(int32_t Ver)
+ 'GetWLMIndex': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetWLMCount(int32_t V)
+ 'GetWLMCount': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetOptionInfo(int32_t Index, int32_t Detail, int64_t* I64Val, double* DblVal, char* sVal)
+ 'GetOptionInfo': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.POINTER(ctypes.c_int64), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_char))),
+
+
+# *********** General Get... & Set...-functions **********************
+ # double GetWavelength(double WL)
+ 'GetWavelength': (ctypes.c_double, (ctypes.c_double,)),
+
+ # double GetWavelength2(double WL2)
+ 'GetWavelength2': (ctypes.c_double, (ctypes.c_double,)),
+
+ # double GetWavelengthNum(int32_t num, double WL)
+ 'GetWavelengthNum': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+ # double GetCalWavelength(int32_t ba, double WL)
+ 'GetCalWavelength': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+ # double GetCalibrationEffect(double CE)
+ 'GetCalibrationEffect': (ctypes.c_double, (ctypes.c_double,)),
+
+ # double GetFrequency(double F)
+ 'GetFrequency': (ctypes.c_double, (ctypes.c_double,)),
+
+ # double GetFrequency2(double F2)
+ 'GetFrequency2': (ctypes.c_double, (ctypes.c_double,)),
+
+ # double GetFrequencyNum(int32_t num, double F)
+ 'GetFrequencyNum': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+ # double GetLinewidth(int32_t Index, double LW)
+ 'GetLinewidth': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+ # double GetLinewidthNum(int32_t num, double LW)
+ 'GetLinewidthNum': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+ # double GetDistance(double D)
+ 'GetDistance': (ctypes.c_double, (ctypes.c_double,)),
+
+ # double GetAnalogIn(double AI)
+ 'GetAnalogIn': (ctypes.c_double, (ctypes.c_double,)),
+
+ # double GetMultimodeInfo(int32_t num, int32_t type, int32_t mode, double* Val)
+ 'GetMultimodeInfo': (ctypes.c_double, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.POINTER(ctypes.c_double))),
+
+ # int32_t GetResultInfo(int32_t num, int32_t info)
+ 'GetResultInfo': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32)),
+
+ # double GetMeasurementUncertainty(int32_t Index, int32_t num, double MU)
+ 'GetMeasurementUncertainty': (ctypes.c_double, (ctypes.c_int32, ctypes.c_int32, ctypes.c_double)),
+
+ # double GetTemperature(double T)
+ 'GetTemperature': (ctypes.c_double, (ctypes.c_double,)),
+
+ # int32_t SetTemperature(double T)
+ 'SetTemperature': (ctypes.c_int32, (ctypes.c_double,)),
+
+ # double GetPressure(double P)
+ 'GetPressure': (ctypes.c_double, (ctypes.c_double,)),
+
+ # int32_t SetPressure(int32_t Mode, double P)
+ 'SetPressure': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_double)),
+
+ # int32_t GetAirParameters(int32_t Mode, int32_t* State, double* Val)
+ 'GetAirParameters': (ctypes.c_int32, (ctypes.c_int32, ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double))),
+
+ # int32_t SetAirParameters(int32_t Mode, int32_t State, double Val)
+ 'SetAirParameters': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_double)),
+
+ # double GetExternalInput(int32_t Index, double I)
+ 'GetExternalInput': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+ # int32_t SetExternalInput(int32_t Index, double I)
+ 'SetExternalInput': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_double)),
+
+ # int32_t GetExtraSetting(int32_t Index, int32_t* lGet, double* dGet, char* sGet)
+ 'GetExtraSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_char))),
+
+ # int32_t SetExtraSetting(int32_t Index, int32_t lSet, double dSet, char* sSet)
+ 'SetExtraSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_double, ctypes.POINTER(ctypes.c_char))),
+
+
+ # uint16_t GetExposure(uint16_t E)
+ 'GetExposure': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetExposure(uint16_t E)
+ 'SetExposure': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # uint16_t GetExposure2(uint16_t E2)
+ 'GetExposure2': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetExposure2(uint16_t E2)
+ 'SetExposure2': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # int32_t GetExposureNum(int32_t num, int32_t arr, int32_t E)
+ 'GetExposureNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t SetExposureNum(int32_t num, int32_t arr, int32_t E)
+ 'SetExposureNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # double GetExposureNumEx(int32_t num, int32_t arr, double E)
+ 'GetExposureNumEx': (ctypes.c_double, (ctypes.c_int32, ctypes.c_int32, ctypes.c_double)),
+
+ # int32_t SetExposureNumEx(int32_t num, int32_t arr, double E)
+ 'SetExposureNumEx': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_double)),
+
+ # bool GetExposureMode(bool EM)
+ 'GetExposureMode': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetExposureMode(bool EM)
+ 'SetExposureMode': (ctypes.c_int32, (ctypes.c_bool,)),
+
+ # int32_t GetExposureModeNum(int32_t num, bool EM)
+ 'GetExposureModeNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_bool)),
+
+ # int32_t SetExposureModeNum(int32_t num, bool EM)
+ 'SetExposureModeNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_bool)),
+
+ # int32_t GetExposureRange(int32_t ER)
+ 'GetExposureRange': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # double GetExposureRangeEx(int32_t ER)
+ 'GetExposureRangeEx': (ctypes.c_double, (ctypes.c_int32,)),
+
+ # int32_t GetAutoExposureSetting(int32_t num, int32_t AES, int32_t* iVal, double* dVal)
+ 'GetAutoExposureSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double))),
+
+ # int32_t SetAutoExposureSetting(int32_t num, int32_t AES, int32_t iVal, double dVal)
+ 'SetAutoExposureSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_double)),
+
+
+ # uint16_t GetResultMode(uint16_t RM)
+ 'GetResultMode': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetResultMode(uint16_t RM)
+ 'SetResultMode': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # uint16_t GetRange(uint16_t R)
+ 'GetRange': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetRange(uint16_t R)
+ 'SetRange': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # uint16_t GetPulseMode(uint16_t PM)
+ 'GetPulseMode': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetPulseMode(uint16_t PM)
+ 'SetPulseMode': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # int32_t GetPulseDelay(int32_t PD)
+ 'GetPulseDelay': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetPulseDelay(int32_t PD)
+ 'SetPulseDelay': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetPulseIntegration(int32_t PI)
+ 'GetPulseIntegration': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetPulseIntegration(int32_t PI)
+ 'SetPulseIntegration': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # uint16_t GetWideMode(uint16_t WM)
+ 'GetWideMode': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetWideMode(uint16_t WM)
+ 'SetWideMode': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+
+ # int32_t GetDisplayMode(int32_t DM)
+ 'GetDisplayMode': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetDisplayMode(int32_t DM)
+ 'SetDisplayMode': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # bool GetFastMode(bool FM)
+ 'GetFastMode': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetFastMode(bool FM)
+ 'SetFastMode': (ctypes.c_int32, (ctypes.c_bool,)),
+
+
+ # bool GetLinewidthMode(bool LM)
+ 'GetLinewidthMode': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetLinewidthMode(bool LM)
+ 'SetLinewidthMode': (ctypes.c_int32, (ctypes.c_bool,)),
+
+
+ # bool GetDistanceMode(bool DM)
+ 'GetDistanceMode': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetDistanceMode(bool DM)
+ 'SetDistanceMode': (ctypes.c_int32, (ctypes.c_bool,)),
+
+
+ # int32_t GetSwitcherMode(int32_t SM)
+ 'GetSwitcherMode': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetSwitcherMode(int32_t SM)
+ 'SetSwitcherMode': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetSwitcherChannel(int32_t CH)
+ 'GetSwitcherChannel': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetSwitcherChannel(int32_t CH)
+ 'SetSwitcherChannel': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetSwitcherSignalStates(int32_t Signal, int32_t* Use, int32_t* Show)
+ 'GetSwitcherSignalStates': (ctypes.c_int32, (ctypes.c_int32, ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_int32))),
+
+ # int32_t SetSwitcherSignalStates(int32_t Signal, int32_t Use, int32_t Show)
+ 'SetSwitcherSignalStates': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t SetSwitcherSignal(int32_t Signal, int32_t Use, int32_t Show)
+ 'SetSwitcherSignal': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+
+ # int32_t GetAutoCalMode(int32_t ACM)
+ 'GetAutoCalMode': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetAutoCalMode(int32_t ACM)
+ 'SetAutoCalMode': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetAutoCalSetting(int32_t ACS, int32_t* val, int32_t Res1, int32_t* Res2)
+ 'GetAutoCalSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.POINTER(ctypes.c_int32), ctypes.c_int32, ctypes.POINTER(ctypes.c_int32))),
+
+ # int32_t SetAutoCalSetting(int32_t ACS, int32_t val, int32_t Res1, int32_t Res2)
+ 'SetAutoCalSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+
+ # int32_t GetActiveChannel(int32_t Mode, int32_t* Port, int32_t Res1)
+ 'GetActiveChannel': (ctypes.c_int32, (ctypes.c_int32, ctypes.POINTER(ctypes.c_int32), ctypes.c_int32)),
+
+ # int32_t SetActiveChannel(int32_t Mode, int32_t Port, int32_t CH, int32_t Res1)
+ 'SetActiveChannel': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t GetChannelsCount(int32_t C)
+ 'GetChannelsCount': (ctypes.c_int32, (ctypes.c_int32,)),
+
+
+ # uint16_t GetOperationState(uint16_t OS)
+ 'GetOperationState': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t Operation(uint16_t Op)
+ 'Operation': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # int32_t SetOperationFile(char* lpFile)
+ 'SetOperationFile': (ctypes.c_int32, (ctypes.POINTER(ctypes.c_char),)),
+
+ # int32_t Calibration(int32_t Type, int32_t Unit, double Value, int32_t Channel)
+ 'Calibration': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_double, ctypes.c_int32)),
+
+ # int32_t PowerCalibration(int32_t Unit, double Value, int32_t Channel, int32_t Options, int32_t Res)
+ 'PowerCalibration': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_double, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t RaiseMeasurementEvent(int32_t Mode)
+ 'RaiseMeasurementEvent': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t TriggerMeasurement(int32_t Action)
+ 'TriggerMeasurement': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetTriggerState(int32_t TS)
+ 'GetTriggerState': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetInterval(int32_t I)
+ 'GetInterval': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetInterval(int32_t I)
+ 'SetInterval': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # bool GetIntervalMode(bool IM)
+ 'GetIntervalMode': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetIntervalMode(bool IM)
+ 'SetIntervalMode': (ctypes.c_int32, (ctypes.c_bool,)),
+
+ # double GetInternalTriggerRate(double TR)
+ 'GetInternalTriggerRate': (ctypes.c_double, (ctypes.c_double,)),
+
+ # int32_t SetInternalTriggerRate(double TR)
+ 'SetInternalTriggerRate': (ctypes.c_int32, (ctypes.c_double,)),
+
+ # int32_t GetBackground(int32_t BG)
+ 'GetBackground': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetBackground(int32_t BG)
+ 'SetBackground': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetAveragingSettingNum(int32_t num, int32_t AS, int32_t Value)
+ 'GetAveragingSettingNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t SetAveragingSettingNum(int32_t num, int32_t AS, int32_t Value)
+ 'SetAveragingSettingNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+
+ # bool GetLinkState(bool LS)
+ 'GetLinkState': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetLinkState(bool LS)
+ 'SetLinkState': (ctypes.c_int32, (ctypes.c_bool,)),
+
+ # void LinkSettingsDlg(void)
+ 'LinkSettingsDlg': (None, ()),
+
+
+ # int32_t GetPatternItemSize(int32_t Index)
+ 'GetPatternItemSize': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetPatternItemCount(int32_t Index)
+ 'GetPatternItemCount': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # uintptr_t GetPattern(int32_t Index)
+ 'GetPattern': (ctypes.c_void_p, (ctypes.c_int32,)),
+
+ # uintptr_t GetPatternNum(int32_t Chn, int32_t Index)
+ 'GetPatternNum': (ctypes.c_void_p, (ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t GetPatternData(int32_t Index, uintptr_t PArray)
+ 'GetPatternData': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_void_p)),
+
+ # int32_t GetPatternDataNum(int32_t Chn, int32_t Index, uintptr_t PArray)
+ 'GetPatternDataNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_void_p)),
+
+ # int32_t SetPattern(int32_t Index, int32_t iEnable)
+ 'SetPattern': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32)),
+
+ # int32_t SetPatternData(int32_t Index, uintptr_t PArray)
+ 'SetPatternData': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_void_p)),
+
+
+ # bool GetAnalysisMode(bool AM)
+ 'GetAnalysisMode': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetAnalysisMode(bool AM)
+ 'SetAnalysisMode': (ctypes.c_int32, (ctypes.c_bool,)),
+
+ # int32_t GetAnalysisItemSize(int32_t Index)
+ 'GetAnalysisItemSize': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetAnalysisItemCount(int32_t Index)
+ 'GetAnalysisItemCount': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # uintptr_t GetAnalysis(int32_t Index)
+ 'GetAnalysis': (ctypes.c_void_p, (ctypes.c_int32,)),
+
+ # int32_t GetAnalysisData(int32_t Index, uintptr_t PArray)
+ 'GetAnalysisData': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_void_p)),
+
+ # int32_t SetAnalysis(int32_t Index, int32_t iEnable)
+ 'SetAnalysis': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32)),
+
+
+ # int32_t GetMinPeak(int32_t M1)
+ 'GetMinPeak': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetMinPeak2(int32_t M2)
+ 'GetMinPeak2': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetMaxPeak(int32_t X1)
+ 'GetMaxPeak': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetMaxPeak2(int32_t X2)
+ 'GetMaxPeak2': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetAvgPeak(int32_t A1)
+ 'GetAvgPeak': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t GetAvgPeak2(int32_t A2)
+ 'GetAvgPeak2': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetAvgPeak(int32_t PA)
+ 'SetAvgPeak': (ctypes.c_int32, (ctypes.c_int32,)),
+
+
+ # int32_t GetAmplitudeNum(int32_t num, int32_t Index, int32_t A)
+ 'GetAmplitudeNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+ # double GetIntensityNum(int32_t num, double I)
+ 'GetIntensityNum': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+ # double GetPowerNum(int32_t num, double P)
+ 'GetPowerNum': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+
+ # uint16_t GetDelay(uint16_t D)
+ 'GetDelay': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetDelay(uint16_t D)
+ 'SetDelay': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # uint16_t GetShift(uint16_t S)
+ 'GetShift': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetShift(uint16_t S)
+ 'SetShift': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # uint16_t GetShift2(uint16_t S2)
+ 'GetShift2': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetShift2(uint16_t S2)
+ 'SetShift2': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+ # double GetGain(int32_t num, int32_t index, int32_t mode, double* Gain)
+ 'GetGain': (ctypes.c_double, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.POINTER(ctypes.c_double))),
+
+ # int32_t SetGain(int32_t num, int32_t index, int32_t mode, double Gain)
+ 'SetGain': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_double)),
+
+
+# *********** Deviation (Laser Control) and PID-functions ************
+ # bool GetDeviationMode(bool DM)
+ 'GetDeviationMode': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetDeviationMode(bool DM)
+ 'SetDeviationMode': (ctypes.c_int32, (ctypes.c_bool,)),
+
+ # double GetDeviationReference(double DR)
+ 'GetDeviationReference': (ctypes.c_double, (ctypes.c_double,)),
+
+ # int32_t SetDeviationReference(double DR)
+ 'SetDeviationReference': (ctypes.c_int32, (ctypes.c_double,)),
+
+ # int32_t GetDeviationSensitivity(int32_t DS)
+ 'GetDeviationSensitivity': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # int32_t SetDeviationSensitivity(int32_t DS)
+ 'SetDeviationSensitivity': (ctypes.c_int32, (ctypes.c_int32,)),
+
+ # double GetDeviationSignal(double DS)
+ 'GetDeviationSignal': (ctypes.c_double, (ctypes.c_double,)),
+
+ # double GetDeviationSignalNum(int32_t Port, double DS)
+ 'GetDeviationSignalNum': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+ # int32_t SetDeviationSignal(double DS)
+ 'SetDeviationSignal': (ctypes.c_int32, (ctypes.c_double,)),
+
+ # int32_t SetDeviationSignalNum(int32_t Port, double DS)
+ 'SetDeviationSignalNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_double)),
+
+ # double RaiseDeviationSignal(int32_t iType, double dSignal)
+ 'RaiseDeviationSignal': (ctypes.c_double, (ctypes.c_int32, ctypes.c_double)),
+
+
+ # int32_t GetPIDCourse(char* PIDC)
+ 'GetPIDCourse': (ctypes.c_int32, (ctypes.POINTER(ctypes.c_char),)),
+
+ # int32_t SetPIDCourse(char* PIDC)
+ 'SetPIDCourse': (ctypes.c_int32, (ctypes.POINTER(ctypes.c_char),)),
+
+ # int32_t GetPIDCourseNum(int32_t Port, char* PIDC)
+ 'GetPIDCourseNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.POINTER(ctypes.c_char))),
+
+ # int32_t SetPIDCourseNum(int32_t Port, char* PIDC)
+ 'SetPIDCourseNum': (ctypes.c_int32, (ctypes.c_int32, ctypes.POINTER(ctypes.c_char))),
+
+ # int32_t GetPIDSetting(int32_t PS, int32_t Port, int32_t* iSet, double* dSet)
+ 'GetPIDSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double))),
+
+ # int32_t SetPIDSetting(int32_t PS, int32_t Port, int32_t iSet, double dSet)
+ 'SetPIDSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_double)),
+
+ # int32_t GetLaserControlSetting(int32_t PS, int32_t Port, int32_t* iSet, double* dSet, char* sSet)
+ 'GetLaserControlSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_char))),
+
+ # int32_t SetLaserControlSetting(int32_t PS, int32_t Port, int32_t iSet, double dSet, char* sSet)
+ 'SetLaserControlSetting': (ctypes.c_int32, (ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_double, ctypes.POINTER(ctypes.c_char))),
+
+ # int32_t ClearPIDHistory(int32_t Port)
+ 'ClearPIDHistory': (ctypes.c_int32, (ctypes.c_int32,)),
+
+
+# *********** Other...-functions *************************************
+ # double ConvertUnit(double Val, int32_t uFrom, int32_t uTo)
+ 'ConvertUnit': (ctypes.c_double, (ctypes.c_double, ctypes.c_int32, ctypes.c_int32)),
+
+ # double ConvertDeltaUnit(double Base, double Delta, int32_t uBase, int32_t uFrom, int32_t uTo)
+ 'ConvertDeltaUnit': (ctypes.c_double, (ctypes.c_double, ctypes.c_double, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32)),
+
+
+# *********** Obsolete...-functions **********************************
+ # bool GetReduced(bool R)
+ 'GetReduced': (ctypes.c_bool, (ctypes.c_bool,)),
+
+ # int32_t SetReduced(bool R)
+ 'SetReduced': (ctypes.c_int32, (ctypes.c_bool,)),
+
+ # uint16_t GetScale(uint16_t S)
+ 'GetScale': (ctypes.c_uint16, (ctypes.c_uint16,)),
+
+ # int32_t SetScale(uint16_t S)
+ 'SetScale': (ctypes.c_int32, (ctypes.c_uint16,)),
+
+
+}
+
+dll = None # pylint: disable=invalid-name
+
+def LoadDLL(path = None): # pylint: disable=invalid-name
+ """Load wlmData library"""
+ global dll # pylint: disable=global-statement
+
+ if path is None:
+ if platform.system() == 'Windows':
+ path = 'wlmData.dll'
+ elif platform.system() == 'Darwin':
+ path = 'libwlmData.dylib'
+ else:
+ path = 'libwlmData.so'
+
+ dll = ctypes.WinDLL(path) if platform.system() == 'Windows' else ctypes.CDLL(path)
+
+ for name, (restype, argtypes) in _PROTOTYPES.items():
+ try:
+ fnptr = getattr(dll, name)
+ except AttributeError:
+ continue
+ fnptr.argtypes = argtypes
+ fnptr.restype = restype
+
+ return dll
diff --git a/src/qudi/interface/data_instream_interface.py b/src/qudi/interface/data_instream_interface.py
deleted file mode 100644
index a1a37c0..0000000
--- a/src/qudi/interface/data_instream_interface.py
+++ /dev/null
@@ -1,273 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-Interface for a generic input stream of data points with fixed sampling rate and data type.
-
-Copyright (c) 2021, the qudi developers. See the AUTHORS.md file at the top-level directory of this
-distribution and on
-
-This file is part of qudi.
-
-Qudi is free software: you can redistribute it and/or modify it under the terms of
-the GNU Lesser General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version.
-
-Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along with qudi.
-If not, see .
-"""
-
-__all__ = ['StreamingMode', 'SampleTiming', 'DataInStreamConstraints', 'DataInStreamInterface']
-
-import numpy as np
-from typing import Union, Type, Iterable, Mapping, Optional, Dict, List, Tuple, Sequence
-from enum import Enum
-from abc import abstractmethod
-from qudi.core.module import Base
-from qudi.util.constraints import ScalarConstraint
-
-
-class StreamingMode(Enum):
- INVALID = -1
- CONTINUOUS = 0
- FINITE = 1
-
-
-class SampleTiming(Enum):
- INVALID = -1
- CONSTANT = 0
- TIMESTAMP = 1
- RANDOM = 2
-
-
-class DataInStreamConstraints:
- """ Collection of constraints for hardware modules implementing DataInStreamInterface """
- def __init__(self,
- channel_units: Mapping[str, str],
- sample_timing: Union[SampleTiming, int],
- streaming_modes: Iterable[Union[StreamingMode, int]],
- data_type: Union[Type[int], Type[float], Type[np.integer], Type[np.floating]],
- channel_buffer_size: Optional[ScalarConstraint],
- sample_rate: Optional[ScalarConstraint] = None):
- if not isinstance(sample_rate, ScalarConstraint) and sample_rate is not None:
- raise TypeError(
- f'"sample_rate" must be None or'
- f'{ScalarConstraint.__module__}.{ScalarConstraint.__qualname__} instance'
- )
- if not isinstance(channel_buffer_size, ScalarConstraint):
- raise TypeError(
- f'"channel_buffer_size" must be '
- f'{ScalarConstraint.__module__}.{ScalarConstraint.__qualname__} instance'
- )
- self._channel_units = {**channel_units}
- self._sample_timing = SampleTiming(sample_timing)
- self._streaming_modes = [StreamingMode(mode) for mode in streaming_modes]
- self._data_type = np.dtype(data_type).type
- self._channel_buffer_size = channel_buffer_size
- if sample_rate is None:
- if self._sample_timing != SampleTiming.RANDOM:
- raise ValueError('"sample_rate" ScalarConstraint must be provided if '
- '"sample_timing" is not SampleTiming.RANDOM')
- self._sample_rate = ScalarConstraint(default=1, bounds=(1, 1), increment=0)
- else:
- self._sample_rate = sample_rate
-
- @property
- def channel_units(self) -> Dict[str, str]:
- return self._channel_units.copy()
-
- @property
- def sample_timing(self) -> SampleTiming:
- return self._sample_timing
-
- @property
- def streaming_modes(self) -> List[StreamingMode]:
- return self._streaming_modes.copy()
-
- @property
- def data_type(self) -> np.dtype:
- return self._data_type
-
- @property
- def sample_rate(self) -> ScalarConstraint:
- return self._sample_rate
-
- @property
- def channel_buffer_size(self) -> ScalarConstraint:
- return self._channel_buffer_size
-
-
-class DataInStreamInterface(Base):
- """ Interface for a generic input stream (finite or infinite) of data points from multiple
- channels with common data type.
-
- A connecting logic module can choose to manage its own buffer array(s) and let the hardware
- module read samples directly into the provided arrays for maximum data throughput using
- "read_data_into_buffer" and "read_available_data_into_buffer". Alternatively one can call
- "read_data" or "read_single_point" which will return sufficiently large data arrays that are
- newly allocated each time the method is called (less efficient but no buffer handling needed).
- In any case each time a "read_..." method is called, the samples returned are not available
- anymore and will be consumed. So multiple logic modules can not read from the same data stream.
-
- The sample timing can behave according to 3 different modes (Enum). Check constraints to see
- which mode is used by the hardware.
-
- SampleTiming.CONSTANT: The sample rate is deterministic and constant. Each sample in the stream
- has a fixed timing determined by the sample rate.
- SampleTiming.TIMESTAMP: The sample rate is just a hint for the hardware but can not be
- considered constant. The hardware will provide a numpy.float64 timestamp
- in seconds from the start of the stream for each sample. This requires
- an additional timestamp buffer array in addition to the normal channel
- sample buffer.
- SampleTiming.RANDOM: The sample rate is just a hint for the hardware but can not be
- considered constant. There is no deterministic time correlation between
- samples, except that they are acquired one after another.
- """
-
- @property
- @abstractmethod
- def constraints(self) -> DataInStreamConstraints:
- """ Read-only property returning the constraints on the settings for this data streamer. """
- pass
-
- @property
- @abstractmethod
- def available_samples(self) -> int:
- """ Read-only property to return the currently available number of samples per channel ready
- to read from buffer.
- It must be ensured that each channel can provide at least the number of samples returned
- by this property.
- """
- pass
-
- @property
- @abstractmethod
- def sample_rate(self) -> float:
- """ Read-only property returning the currently set sample rate in Hz.
- For SampleTiming.CONSTANT this is the sample rate of the hardware, for any other timing mode
- this property represents only a hint to the actual hardware timebase and can not be
- considered accurate.
- """
- pass
-
- @property
- @abstractmethod
- def channel_buffer_size(self) -> int:
- """ Read-only property returning the currently set buffer size in samples per channel.
- The total buffer size in bytes can be estimated by:
- * * numpy.nbytes[]
-
- For StreamingMode.FINITE this will also be the total number of samples to acquire per
- channel.
- """
- pass
-
- @property
- @abstractmethod
- def streaming_mode(self) -> StreamingMode:
- """ Read-only property returning the currently configured StreamingMode Enum """
- pass
-
- @property
- @abstractmethod
- def active_channels(self) -> List[str]:
- """ Read-only property returning the currently configured active channel names """
- pass
-
- @abstractmethod
- def configure(self,
- active_channels: Sequence[str],
- streaming_mode: Union[StreamingMode, int],
- channel_buffer_size: int,
- sample_rate: float) -> None:
- """ Configure a data stream. See read-only properties for information on each parameter. """
- pass
-
- @abstractmethod
- def start_stream(self) -> None:
- """ Start the data acquisition/streaming """
- pass
-
- @abstractmethod
- def stop_stream(self) -> None:
- """ Stop the data acquisition/streaming """
- pass
-
- @abstractmethod
- def read_data_into_buffer(self,
- data_buffer: np.ndarray,
- samples_per_channel: int,
- timestamp_buffer: Optional[np.ndarray] = None) -> None:
- """ Read data from the stream buffer into a 1D numpy array given as parameter.
- Samples of all channels are stored interleaved in contiguous memory.
- In case of a multidimensional buffer array, this buffer will be flattened before written
- into.
- The 1D data_buffer can be unraveled into channel and sample indexing with:
-
- data_buffer.reshape([, ])
-
- The data_buffer array must have the same data type as self.constraints.data_type.
-
- In case of SampleTiming.TIMESTAMP a 1D numpy.float64 timestamp_buffer array has to be
- provided to be filled with timestamps corresponding to the data_buffer array. It must be
- able to hold at least items:
-
- This function is blocking until the required number of samples has been acquired.
- """
- pass
-
- @abstractmethod
- def read_available_data_into_buffer(self,
- data_buffer: np.ndarray,
- timestamp_buffer: Optional[np.ndarray] = None) -> int:
- """ Read data from the stream buffer into a 1D numpy array given as parameter.
- All samples for each channel are stored in consecutive blocks one after the other.
- The number of samples read per channel is returned and can be used to slice out valid data
- from the buffer arrays like:
-
- valid_data = data_buffer[: * ]
- valid_timestamps = timestamp_buffer[:]
-
- See "read_data_into_buffer" documentation for more details.
-
- This method will read all currently available samples into buffer. If number of available
- samples exceeds buffer size, read only as many samples as fit into the buffer.
- """
- pass
-
- @abstractmethod
- def read_data(self,
- samples_per_channel: Optional[int] = None
- ) -> Tuple[np.ndarray, Union[np.ndarray, None]]:
- """ Read data from the stream buffer into a 1D numpy array and return it.
- All samples for each channel are stored in consecutive blocks one after the other.
- The returned data_buffer can be unraveled into channel samples with:
-
- data_buffer.reshape([, ])
-
- The numpy array data type is the one defined in self.constraints.data_type.
-
- In case of SampleTiming.TIMESTAMP a 1D numpy.float64 timestamp_buffer array will be
- returned as well with timestamps corresponding to the data_buffer array.
-
- If samples_per_channel is omitted all currently available samples are read from buffer.
- This method will not return until all requested samples have been read or a timeout occurs.
- """
- pass
-
- @abstractmethod
- def read_single_point(self) -> Tuple[np.ndarray, Union[None, np.float64]]:
- """ This method will initiate a single sample read on each configured data channel.
- In general this sample may not be acquired simultaneous for all channels and timing in
- general can not be assured. Us this method if you want to have a non-timing-critical
- snapshot of your current data channel input.
- May not be available for all devices.
- The returned 1D numpy array will contain one sample for each channel.
-
- In case of SampleTiming.TIMESTAMP a single numpy.float64 timestamp value will be returned
- as well.
- """
- pass
diff --git a/src/qudi/interface/simple_wavemeter_interface.py b/src/qudi/interface/simple_wavemeter_interface.py
new file mode 100644
index 0000000..843a15b
--- /dev/null
+++ b/src/qudi/interface/simple_wavemeter_interface.py
@@ -0,0 +1,17 @@
+import numpy as np
+from abc import abstractmethod
+from qudi.core.module import Base
+
+
+class SimpleWavemeterInterface(Base):
+ """ This interface is for simple use of a wavemeter. It just gets wavelength for any available channels
+
+ """
+
+ @abstractmethod
+ def get_wavelengths(self) -> np.ndarray:
+ """ Retrieve the current wavelength(s) from the wavemeter
+
+ @return np.ndarray: wavelength in nm per-channel
+ """
+ pass
\ No newline at end of file
diff --git a/src/qudi/logic/common/data_in_stream_logic.py b/src/qudi/logic/common/data_in_stream_logic.py
deleted file mode 100644
index 670517b..0000000
--- a/src/qudi/logic/common/data_in_stream_logic.py
+++ /dev/null
@@ -1,834 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-This file contains the qudi logic to continuously read data from a streaming device as time series.
-
-Copyright (c) 2021, the qudi developers. See the AUTHORS.md file at the top-level directory of this
-distribution and on
-
-This file is part of qudi.
-
-Qudi is free software: you can redistribute it and/or modify it under the terms of
-the GNU Lesser General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version.
-
-Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along with qudi.
-If not, see .
-"""
-
-import numpy as np
-import datetime as dt
-import matplotlib.pyplot as plt
-from PySide2 import QtCore
-from scipy.signal import decimate
-from typing import Union, Optional, Sequence, Iterable, List, Dict, Mapping, Tuple
-
-from qudi.core.connector import Connector
-from qudi.core.statusvariable import StatusVar
-from qudi.core.configoption import ConfigOption
-from qudi.core.module import LogicBase
-from qudi.util.mutex import Mutex
-from qudi.util.helpers import is_integer_type
-from qudi.util.network import netobtain
-from qudi.interface.data_instream_interface import StreamingMode, SampleTiming
-from qudi.interface.data_instream_interface import DataInStreamConstraints
-from qudi.util.datastorage import TextDataStorage
-from qudi.util.units import ScaledFloat
-
-
-class DataInStreamLogic(LogicBase):
- """
- This logic module gathers data from a hardware streaming device.
-
- Example config for copy-paste:
-
- time_series_reader_logic:
- module.Class: 'common.data_in_stream_logic.DataInStreamLogic'
- options:
- max_frame_rate: 20 # optional (default: 20Hz)
- channel_buffer_size: 1048576 # optional (default: 1MSample)
- max_raw_data_bytes: 1073741824 # optional (default: 1GB)
- connect:
- streamer: instream_dummy
- """
- # declare signals
- sigDataChanged = QtCore.Signal(object, object, object, object)
- sigNewRawData = QtCore.Signal(object, object) # raw data samples, timestamp samples (optional)
- sigStatusChanged = QtCore.Signal(bool, bool)
- sigTraceSettingsChanged = QtCore.Signal(dict)
- sigChannelSettingsChanged = QtCore.Signal(list, list)
- _sigNextDataFrame = QtCore.Signal() # internal signal
-
- # declare connectors
- _streamer = Connector(name='streamer', interface='DataInStreamInterface')
-
- # config options
- _max_frame_rate = ConfigOption('max_frame_rate', default=20, missing='warn')
- _channel_buffer_size = ConfigOption(name='channel_buffer_size',
- default=1024**2,
- missing='info',
- constructor=lambda x: int(round(x)))
- _max_raw_data_bytes = ConfigOption(name='max_raw_data_bytes',
- default=1024**3,
- missing='info',
- constructor=lambda x: int(round(x)))
-
- # status vars
- _trace_window_size = StatusVar('trace_window_size', default=6)
- _moving_average_width = StatusVar('moving_average_width', default=9)
- _oversampling_factor = StatusVar('oversampling_factor', default=1)
- _data_rate = StatusVar('data_rate', default=50)
- _active_channels = StatusVar('active_channels', default=None)
- _averaged_channels = StatusVar('averaged_channels', default=None)
-
- @_data_rate.representer
- def __repr_data_rate(self, value):
- return self.data_rate
-
- @_active_channels.representer
- def __repr_active_channels(self, value):
- return self.active_channel_names
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- # locking for thread safety
- self._threadlock = Mutex()
- self._samples_per_frame = None
-
- # Data arrays
- self._data_buffer = None
- self._times_buffer = None
- self._trace_data = None
- self._trace_times = None
- self._trace_data_averaged = None
- self.__moving_filter = None
-
- # for data recording
- self._recorded_raw_data = None
- self._recorded_raw_times = None
- self._recorded_sample_count = 0
- self._data_recording_active = False
- self._record_start_time = None
-
- # important to know for method of reading the buffer
- self._streamer_is_remote = False
-
- def on_activate(self) -> None:
- """ Initialisation performed during activation of the module. """
- # Temp reference to connected hardware module
- streamer = self._streamer()
-
- # check if streamer is a remote module
- constraints = streamer.constraints
- if type(constraints) != type(netobtain(constraints)):
- self._streamer_is_remote = True
- self.log.debug('Streamer is a remote module. Do not use a shared buffer.')
-
- # Flag to stop the loop and process variables
- self._recorded_raw_data = None
- self._recorded_raw_times = None
- self._recorded_sample_count = 0
- self._data_recording_active = False
- self._record_start_time = None
-
- # Check valid StatusVar
- # active channels
- avail_channels = list(constraints.channel_units)
- if self._active_channels is None:
- if streamer.active_channels:
- self._active_channels = streamer.active_channels.copy()
- else:
- self._active_channels = avail_channels
- self._averaged_channels = self._active_channels
- elif any(ch not in avail_channels for ch in self._active_channels):
- self.log.warning('Invalid active channels found in StatusVar. StatusVar ignored.')
- if streamer.active_channels:
- self._active_channels = streamer.active_channels.copy()
- else:
- self._active_channels = avail_channels
- self._averaged_channels = self._active_channels
- elif self._averaged_channels is not None:
- self._averaged_channels = [
- ch for ch in self._averaged_channels if ch in self._active_channels
- ]
-
- # Check for odd moving averaging window
- if self._moving_average_width % 2 == 0:
- self.log.warning('Moving average width ConfigOption must be odd integer number. '
- 'Changing value from {0:d} to {1:d}.'
- ''.format(self._moving_average_width, self._moving_average_width + 1))
- self._moving_average_width += 1
-
- # set settings in streamer hardware
- self.set_channel_settings(self._active_channels, self._averaged_channels)
- self.set_trace_settings(data_rate=self._data_rate)
- # set up internal frame loop connection
- self._sigNextDataFrame.connect(self._acquire_data_block, QtCore.Qt.QueuedConnection)
-
- def on_deactivate(self) -> None:
- """ De-initialisation performed during deactivation of the module.
- """
- try:
- self._sigNextDataFrame.disconnect()
- # Stop measurement
- if self.module_state() == 'locked':
- self._stop()
- finally:
- # Free (potentially) large raw data buffers
- self._data_buffer = None
- self._times_buffer = None
-
- def _init_data_arrays(self) -> None:
- channel_count = len(self.active_channel_names)
- averaged_channel_count = len(self._averaged_channels)
- window_size = int(round(self._trace_window_size * self.data_rate))
- constraints = self.streamer_constraints
- trace_dtype = np.float64 if is_integer_type(constraints.data_type) else constraints.data_type
-
- # processed data arrays
- self._trace_data = np.zeros(
- [window_size + self._moving_average_width // 2, channel_count],
- dtype=trace_dtype
- )
- self._trace_data_averaged = np.zeros(
- [window_size - self._moving_average_width // 2, averaged_channel_count],
- dtype=trace_dtype
- )
- self._trace_times = np.arange(window_size, dtype=np.float64)
- if constraints.sample_timing == SampleTiming.TIMESTAMP:
- self._trace_times -= window_size
- if constraints.sample_timing != SampleTiming.RANDOM:
- self._trace_times /= self.data_rate
-
- # raw data buffers
- self._data_buffer = np.empty(channel_count * self._channel_buffer_size,
- dtype=constraints.data_type)
- if constraints.sample_timing == SampleTiming.TIMESTAMP:
- self._times_buffer = np.zeros(self._channel_buffer_size, dtype=np.float64)
- else:
- self._times_buffer = None
-
- @property
- def streamer_constraints(self) -> DataInStreamConstraints:
- """ Retrieve the hardware constrains from the counter device """
- # netobtain is required if streamer is a remote module
- return netobtain(self._streamer().constraints)
-
- @property
- def data_rate(self) -> float:
- """ Data rate in Hz. The data rate describes the effective sample rate of the processed
- sample trace taking into account oversampling:
-
- data_rate = hardware_sample_rate / oversampling_factor
- """
- return self.sampling_rate / self.oversampling_factor
-
- @data_rate.setter
- def data_rate(self, val: float) -> None:
- self.set_trace_settings(data_rate=val)
-
- @property
- def trace_window_size(self) -> float:
- """ The size of the running trace window in seconds """
- return self._trace_window_size
-
- @trace_window_size.setter
- def trace_window_size(self, val: Union[int, float]) -> None:
- self.set_trace_settings(trace_window_size=val)
-
- @property
- def moving_average_width(self) -> int:
- """ The width of the moving average filter in samples. Must be an odd number. """
- return self._moving_average_width
-
- @moving_average_width.setter
- def moving_average_width(self, val: int) -> None:
- self.set_trace_settings(moving_average_width=val)
-
- @property
- def data_recording_active(self) -> bool:
- """ Read-only bool flag indicating active data logging so it can be saved to file later """
- return self._data_recording_active
-
- @property
- def oversampling_factor(self) -> int:
- """ This integer value determines how many times more samples are acquired by the hardware
- and averaged before being processed by the trace logic.
- An oversampling factor <= 1 means no oversampling is performed.
- """
- return self._oversampling_factor
-
- @oversampling_factor.setter
- def oversampling_factor(self, val: int) -> None:
- self.set_trace_settings(oversampling_factor=val)
-
- @property
- def sampling_rate(self) -> float:
- """ Read-only property returning the actually set sample rate of the streaming hardware.
- If not oversampling is used, this should be the same value as data_rate.
- If oversampling is active, this value will be larger (by the oversampling factor) than
- data_rate.
- """
- return self._streamer().sample_rate
-
- @property
- def active_channel_names(self) -> List[str]:
- """ Read-only property returning the currently active channel names """
- return netobtain(self._streamer().active_channels.copy())
-
- @property
- def averaged_channel_names(self) -> List[str]:
- """ Read-only property returning the currently active and averaged channel names """
- return self._averaged_channels.copy()
-
- @property
- def trace_data(self) -> Tuple[np.ndarray, Dict[str, np.ndarray]]:
- """ Read-only property returning the x-axis of the data trace and a dictionary of the
- corresponding trace data arrays for each channel
- """
- data_offset = self._trace_data.shape[0] - self._moving_average_width // 2
- data = {ch: self._trace_data[:data_offset, i] for i, ch in
- enumerate(self.active_channel_names)}
- return self._trace_times, data
-
- @property
- def averaged_trace_data(self) -> Tuple[np.ndarray, Dict[str, np.ndarray]]:
- """ Read-only property returning the x-axis of the averaged data trace and a dictionary of
- the corresponding averaged trace data arrays for each channel
- """
- if not self.averaged_channel_names or self.moving_average_width <= 1:
- return None, None
- data = {ch: self._trace_data_averaged[:, i] for i, ch in
- enumerate(self.averaged_channel_names)}
- return self._trace_times[-self._trace_data_averaged.shape[0]:], data
-
- @property
- def trace_settings(self) -> Dict[str, Union[int, float]]:
- """ Read-only property returning the current trace settings as dictionary """
- return {'oversampling_factor' : self.oversampling_factor,
- 'moving_average_width': self.moving_average_width,
- 'trace_window_size' : self.trace_window_size,
- 'data_rate' : self.data_rate}
-
- @property
- def channel_settings(self) -> Tuple[List[str], List[str]]:
- """ Read-only property returning the currently active channel names and the currently
- averaged channel names.
- """
- return self.active_channel_names, self.averaged_channel_names
-
- @QtCore.Slot(dict)
- def set_trace_settings(self,
- settings_dict: Optional[Mapping[str, Union[int, float]]] = None,
- **kwargs) -> None:
- """ Method to set new trace settings.
- Can either provide (a subset of) trace_settings via dict as first positional argument
- and/or as keyword arguments (**kwargs overwrites settings_dict for duplicate keys).
-
- See property trace_settings for valid setting keywords.
-
- Calling this method while a trace is running will stop the trace first and restart after
- successful application of new settings.
- """
- if self.data_recording_active:
- self.log.warning('Unable to configure settings while data is being recorded.')
- return
-
- # Flag indicating if the stream should be restarted
- restart = self.module_state() == 'locked'
- if restart:
- self._stop()
-
- try:
- # Refine and sanity check settings
- settings = self.trace_settings
- if settings_dict is not None:
- settings.update(settings_dict)
- settings.update(kwargs)
- settings['oversampling_factor'] = int(settings['oversampling_factor'])
- settings['moving_average_width'] = int(settings['moving_average_width'])
- settings['data_rate'] = float(settings['data_rate'])
- settings['trace_window_size'] = int(round(settings['trace_window_size'] * settings['data_rate'])) / settings['data_rate']
-
- if settings['oversampling_factor'] < 1:
- raise ValueError(f'Oversampling factor must be integer value >= 1 '
- f'(received: {settings["oversampling_factor"]:d})')
-
- if settings['moving_average_width'] < 1:
- raise ValueError(f'Moving average width must be integer value >= 1 '
- f'(received: {settings["moving_average_width"]:d})')
- if settings['moving_average_width'] % 2 == 0:
- settings['moving_average_width'] += 1
- self.log.warning(f'Moving average window must be odd integer number in order to '
- f'ensure perfect data alignment. Increased value to '
- f'{settings["moving_average_width"]:d}.')
- if settings['moving_average_width'] / settings['data_rate'] > settings['trace_window_size']:
- self.log.warning(f'Moving average width ({settings["moving_average_width"]:d}) is '
- f'smaller than the trace window size. Will adjust trace window '
- f'size to match.')
- settings['trace_window_size'] = float(
- settings['moving_average_width'] / settings['data_rate']
- )
-
- with self._threadlock:
- # Apply settings to hardware if needed
- self._streamer().configure(
- active_channels=self.active_channel_names,
- streaming_mode=StreamingMode.CONTINUOUS,
- channel_buffer_size=self._channel_buffer_size,
- sample_rate=settings['data_rate'] * settings['oversampling_factor']
- )
- # update actually set values
- self._oversampling_factor = settings['oversampling_factor']
- self._moving_average_width = settings['moving_average_width']
- self._trace_window_size = settings['trace_window_size']
- self.__moving_filter = np.full(shape=self._moving_average_width,
- fill_value=1.0 / self._moving_average_width)
- self._samples_per_frame = max(1, int(round(self.data_rate / self._max_frame_rate)))
- self._init_data_arrays()
- except:
- self.log.exception('Error while trying to configure new trace settings:')
- raise
- finally:
- self.sigTraceSettingsChanged.emit(self.trace_settings)
- if restart:
- self.start_reading()
- else:
- self.sigDataChanged.emit(*self.trace_data, *self.averaged_trace_data)
-
- @QtCore.Slot(list, list)
- def set_channel_settings(self, enabled: Sequence[str], averaged: Sequence[str]) -> None:
- """ Method to set new channel settings by providing a sequence of active channel names
- (enabled) as well as a sequence of channel names to be averaged (averaged).
-
- Calling this method while a trace is running will stop the trace first and restart after
- successful application of new settings.
- """
- if self.data_recording_active:
- self.log.warning('Unable to configure settings while data is being recorded.')
- return
-
- # Flag indicating if the stream should be restarted
- restart = self.module_state() == 'locked'
- if restart:
- self._stop()
-
- try:
- self._streamer().configure(
- active_channels=enabled,
- streaming_mode=StreamingMode.CONTINUOUS,
- channel_buffer_size=self._channel_buffer_size,
- sample_rate=self.sampling_rate
- )
- self._averaged_channels = [ch for ch in averaged if ch in enabled]
- self._init_data_arrays()
- except:
- self.log.exception('Error while trying to configure new channel settings:')
- raise
- finally:
- self.sigChannelSettingsChanged.emit(*self.channel_settings)
- if restart:
- self.start_reading()
- else:
- self.sigDataChanged.emit(*self.trace_data, *self.averaged_trace_data)
-
- @QtCore.Slot()
- def start_reading(self) -> None:
- """ Start data acquisition loop """
- with self._threadlock:
- if self.module_state() == 'locked':
- self.log.warning('Data acquisition already running. "start_reading" call ignored.')
- self.sigStatusChanged.emit(True, self._data_recording_active)
- return
-
- self.module_state.lock()
- try:
- if self._data_recording_active:
- self._init_recording_arrays()
- self._record_start_time = dt.datetime.now()
- self._streamer().start_stream()
- except:
- self.module_state.unlock()
- self.log.exception('Error while starting stream reader:')
- raise
- finally:
- self._sigNextDataFrame.emit()
- self.sigStatusChanged.emit(self.module_state() == 'locked',
- self._data_recording_active)
-
- @QtCore.Slot()
- def stop_reading(self) -> None:
- """ Send a request to stop counting """
- with self._threadlock:
- self._stop()
-
- def _stop(self) -> None:
- if self.module_state() == 'locked':
- try:
- self._streamer().stop_stream()
- except:
- self.log.exception('Error while trying to stop stream reader:')
- raise
- finally:
- self._stop_cleanup()
-
- def _stop_cleanup(self) -> None:
- self.module_state.unlock()
- self._stop_recording()
-
- @QtCore.Slot()
- def _acquire_data_block(self) -> None:
- """ This method gets the available data from the hardware. It runs repeatedly by being
- connected to a QTimer timeout signal.
- """
- with self._threadlock:
- if self.module_state() == 'locked':
- try:
- streamer = self._streamer()
- samples_to_read = max(
- (streamer.available_samples // self._oversampling_factor) * self._oversampling_factor,
- self._samples_per_frame * self._oversampling_factor
- )
- samples_to_read = min(
- samples_to_read,
- (self._channel_buffer_size // self._oversampling_factor) * self._oversampling_factor
- )
- # read the current counter values
- if not self._streamer_is_remote:
- # we can use the more efficient method of using a shared buffer
- streamer.read_data_into_buffer(data_buffer=self._data_buffer,
- samples_per_channel=samples_to_read,
- timestamp_buffer=self._times_buffer)
- else:
- # streamer is remote, we need to have a new buffer created and passed to us
- self._data_buffer, self._times_buffer = streamer.read_data(
- samples_per_channel=samples_to_read
- )
- self._data_buffer = netobtain(self._data_buffer)
- self._times_buffer = netobtain(self._times_buffer)
-
- # Process data
- channel_count = len(self.active_channel_names)
- data_view = self._data_buffer[:channel_count * samples_to_read]
- self._process_trace_data(data_view)
- if self._times_buffer is None:
- times_view = None
- else:
- times_view = self._times_buffer[:samples_to_read]
- self._process_trace_times(times_view)
-
- if self._data_recording_active:
- self._add_to_recording_array(data_view, times_view)
- self.sigNewRawData.emit(data_view, times_view)
- # Emit update signal
- self.sigDataChanged.emit(*self.trace_data, *self.averaged_trace_data)
- except Exception as e:
- self.log.warning(f'Reading data from streamer went wrong: {e}')
- self._stop_cleanup()
- return
- self._sigNextDataFrame.emit()
-
- def _process_trace_times(self, times_buffer: np.ndarray) -> None:
- if self.oversampling_factor > 1:
- times_buffer = times_buffer.reshape(
- (times_buffer.size // self.oversampling_factor, self.oversampling_factor)
- )
- times_buffer = np.mean(times_buffer, axis=1)
-
- # discard data outside time frame
- times_buffer = times_buffer[-self._trace_times.size:]
-
- # Roll data array to have a continuously running time trace
- self._trace_times = np.roll(self._trace_times, -times_buffer.size)
- # Insert new data
- self._trace_times[-times_buffer.size:] = times_buffer
-
- def _process_trace_data(self, data_buffer: np.ndarray) -> None:
- """ Processes raw data from the streaming device """
- channel_count = len(self.active_channel_names)
- samples_per_channel = data_buffer.size // channel_count
- data_view = data_buffer.reshape([samples_per_channel, channel_count])
- # Down-sample and average according to oversampling factor
- if self.oversampling_factor > 1:
- data_view = data_view.reshape(
- [samples_per_channel // self.oversampling_factor,
- self.oversampling_factor,
- channel_count]
- )
- data_view = np.mean(data_view, axis=1)
-
- # discard data outside time frame
- data_view = data_view[-self._trace_data.shape[0]:, :]
- new_channel_samples = data_view.shape[0]
-
- # Roll data array to have a continuously running time trace
- self._trace_data = np.roll(self._trace_data, -new_channel_samples, axis=0)
- # Insert new data
- self._trace_data[-new_channel_samples:, :] = data_view
-
- # Calculate moving average by using numpy.convolve with a normalized uniform filter
- if self.moving_average_width > 1 and self.averaged_channel_names:
- # Only convolve the new data and roll the previously calculated moving average
- self._trace_data_averaged = np.roll(self._trace_data_averaged,
- -new_channel_samples,
- axis=0)
- offset = new_channel_samples + len(self.__moving_filter) - 1
- for i, ch in enumerate(self.averaged_channel_names):
- data_index = self.active_channel_names.index(ch)
- self._trace_data_averaged[-new_channel_samples:, i] = np.convolve(
- self._trace_data[-offset:, data_index],
- self.__moving_filter,
- mode='valid'
- )
-
- def _init_recording_arrays(self) -> None:
- constraints = self.streamer_constraints
- try:
- sample_bytes = np.finfo(constraints.data_type).bits // 8
- except ValueError:
- sample_bytes = np.iinfo(constraints.data_type).bits // 8
- # Try to allocate space for approx. 10sec of samples (limited by ConfigOption)
- channel_count = len(self.active_channel_names)
- channel_samples = int(10 * self.sampling_rate)
- data_byte_size = sample_bytes * channel_count * channel_samples
- if constraints.sample_timing == SampleTiming.TIMESTAMP:
- if (8 * channel_samples + data_byte_size) > self._max_raw_data_bytes:
- channel_samples = max(
- 1,
- self._max_raw_data_bytes // (channel_count * sample_bytes + 8)
- )
- self._recorded_raw_times = np.zeros(channel_samples, dtype=np.float64)
- else:
- if data_byte_size > self._max_raw_data_bytes:
- channel_samples = max(1, self._max_raw_data_bytes // (channel_count * sample_bytes))
- self._recorded_raw_times = None
- self._recorded_raw_data = np.empty(channel_count * channel_samples,
- dtype=constraints.data_type)
- self._recorded_sample_count = 0
-
- def _expand_recording_arrays(self) -> int:
- total_samples = self._recorded_raw_data.size
- channel_count = len(self.active_channel_names)
- current_samples = total_samples // channel_count
- byte_granularity = channel_count * self._recorded_raw_data.itemsize
- new_byte_size = 2 * current_samples * byte_granularity
- if self._recorded_raw_times is not None:
- new_byte_size += 2 * current_samples * self._recorded_raw_times.itemsize
- byte_granularity += self._recorded_raw_times.itemsize
- new_samples_per_channel = min(self._max_raw_data_bytes, new_byte_size) // byte_granularity
- additional_samples = new_samples_per_channel - current_samples
- self._recorded_raw_data = np.append(
- self._recorded_raw_data,
- np.empty(channel_count * additional_samples, dtype=self._recorded_raw_data.dtype),
- )
- if self._recorded_raw_times is not None:
- self._recorded_raw_times = np.append(
- self._recorded_raw_times,
- np.empty(additional_samples, dtype=self._recorded_raw_times.dtype)
- )
- return additional_samples
-
- def _add_to_recording_array(self, data, times=None) -> None:
- channel_count = len(self.active_channel_names)
- free_samples_per_channel = (self._recorded_raw_data.size // channel_count) - \
- self._recorded_sample_count
- new_samples = data.size // channel_count
- total_new_samples = new_samples * channel_count
- if new_samples > free_samples_per_channel:
- free_samples_per_channel += self._expand_recording_arrays()
- if new_samples > free_samples_per_channel:
- self.log.error(
- f'Configured maximum allowed amount of raw data reached '
- f'({self._max_raw_data_bytes:d} bytes). Saving raw data so far and terminating '
- f'data recording.'
- )
- self._recorded_raw_data[channel_count * self._recorded_sample_count:] = data[:channel_count * free_samples_per_channel]
- if self._recorded_raw_times is not None:
- self._recorded_raw_times[self._recorded_sample_count:] = times[:free_samples_per_channel]
- self._recorded_sample_count += free_samples_per_channel
- self._stop_recording()
- return
-
- begin = self._recorded_sample_count * channel_count
- end = begin + total_new_samples
- self._recorded_raw_data[begin:end] = data[:total_new_samples]
- if self._recorded_raw_times is not None:
- begin = self._recorded_sample_count
- end = begin + new_samples
- self._recorded_raw_times[begin:end] = times[:new_samples]
- self._recorded_sample_count += new_samples
-
- @QtCore.Slot()
- def start_recording(self):
- """ Will start to continuously accumulate raw data from the streaming hardware (without
- running average and oversampling). Data will be saved to file once the trace acquisition is
- stopped.
- If the streamer is not running it will be started in order to have data to save.
- """
- with self._threadlock:
- if self._data_recording_active:
- self.sigStatusChanged.emit(self.module_state() == 'locked', True)
- else:
- self._data_recording_active = True
- if self.module_state() == 'locked':
- self._init_recording_arrays()
- self._record_start_time = dt.datetime.now()
- self.sigStatusChanged.emit(True, True)
- else:
- self.start_reading()
-
- @QtCore.Slot()
- def stop_recording(self):
- """ Stop the accumulative data recording and save data to file. Will not stop the data
- streaming. Ignored if no stream is running (module is in idle state).
- """
- with self._threadlock:
- self._stop_recording()
-
- def _stop_recording(self) -> None:
- try:
- if self._data_recording_active:
- self._save_recorded_data(save_figure=True)
- finally:
- self._data_recording_active = False
- self.sigStatusChanged.emit(self.module_state() == 'locked', False)
-
- def _save_recorded_data(self, name_tag='', save_figure=True):
- """ Save the recorded counter trace data and writes it to a file """
- try:
- constraints = self.streamer_constraints
- metadata = {
- 'Start recoding time': self._record_start_time.strftime('%d.%m.%Y, %H:%M:%S.%f'),
- 'Sample rate (Hz)' : self.sampling_rate,
- 'Sample timing' : constraints.sample_timing.name
- }
- column_headers = [
- f'{ch} ({constraints.channel_units[ch]})' for ch in self.active_channel_names
- ]
- channel_count = len(column_headers)
- nametag = f'data_trace_{name_tag}' if name_tag else 'data_trace'
-
- data = self._recorded_raw_data[:channel_count * self._recorded_sample_count].reshape(
- [self._recorded_sample_count, channel_count]
- )
- if self._recorded_raw_times is not None:
- print(data.shape)
- data = np.column_stack(
- [self._recorded_raw_times[:self._recorded_sample_count], data]
- )
- print(data.shape, '\n')
- column_headers.insert(0, 'Time (s)')
- try:
- fig = self._draw_raw_data_thumbnail(data) if save_figure else None
- finally:
- storage = TextDataStorage(root_dir=self.module_default_data_dir)
- filepath, _, _ = storage.save_data(data,
- metadata=metadata,
- nametag=nametag,
- column_headers=column_headers)
- if fig is not None:
- storage.save_thumbnail(mpl_figure=fig, file_path=filepath)
- except:
- self.log.exception('Something went wrong while saving raw data:')
- raise
-
- def _draw_raw_data_thumbnail(self, data: np.ndarray) -> plt.Figure:
- """ Draw figure to save with data file """
- constraints = self.streamer_constraints
- # Handle excessive data size for plotting. Artefacts may occur due to IIR decimation filter.
- decimate_factor = 0
- while data.shape[0] >= 20000:
- print(data.shape[0])
- decimate_factor += 2
- data = decimate(data, q=2, axis=0)
-
- if constraints.sample_timing == SampleTiming.RANDOM:
- x = np.arange(data.shape[0])
- x_label = 'Sample Index'
- elif constraints.sample_timing == SampleTiming.CONSTANT:
- if decimate_factor > 0:
- x = np.arange(data.shape[0]) / (self.sampling_rate / decimate_factor)
- else:
- x = np.arange(data.shape[0]) / self.sampling_rate
- x_label = 'Time (s)'
- else:
- x = data[:, 0] - data[0, 0]
- data = data[:, 1:]
- x_label = 'Time (s)'
- # Create figure and scale data
- max_abs_value = ScaledFloat(max(data.max(), np.abs(data.min())))
- if max_abs_value.scale:
- data = data / max_abs_value.scale_val
- y_label = f'Signal ({max_abs_value.scale}arb.u.)'
- else:
- y_label = 'Signal (arb.u.)'
-
- fig, ax = plt.subplots()
- ax.plot(x, data, linestyle='-', marker='', linewidth=0.5)
- ax.set_xlabel(x_label)
- ax.set_ylabel(y_label)
- return fig
-
- @QtCore.Slot()
- def save_trace_snapshot(self, name_tag: Optional[str] = '', save_figure: Optional[bool] = True):
- """ A snapshot of the current data trace window will be saved """
- try:
- timestamp = dt.datetime.now()
- constraints = self.streamer_constraints
- metadata = {
- 'Timestamp': timestamp.strftime('%d.%m.%Y, %H:%M:%S.%f'),
- 'Data rate (Hz)': self.data_rate,
- 'Oversampling factor (samples)': self.oversampling_factor,
- 'Sampling rate (Hz)': self.sampling_rate
- }
- column_headers = [
- f'{ch} ({constraints.channel_units[ch]})' for ch in self.active_channel_names
- ]
- nametag = f'trace_snapshot_{name_tag}' if name_tag else 'trace_snapshot'
-
- data_offset = self._trace_data.shape[0] - self._moving_average_width // 2
- data = self._trace_data[:data_offset, :]
- x = self._trace_times
- try:
- fig = self._draw_trace_snapshot_thumbnail(x, data) if save_figure else None
- finally:
- if constraints.sample_timing != SampleTiming.RANDOM:
- data = np.column_stack([x, data])
- column_headers.insert(0, 'Time (s)')
-
- storage = TextDataStorage(root_dir=self.module_default_data_dir)
- filepath, _, _ = storage.save_data(data,
- timestamp=timestamp,
- metadata=metadata,
- nametag=nametag,
- column_headers=column_headers)
- if fig is not None:
- storage.save_thumbnail(mpl_figure=fig, file_path=filepath)
- except:
- self.log.exception('Something went wrong while saving trace snapshot:')
- raise
-
- def _draw_trace_snapshot_thumbnail(self, x: np.ndarray, data: np.ndarray) -> plt.Figure:
- """ Draw figure to save with data file """
- if self.streamer_constraints.sample_timing == SampleTiming.RANDOM:
- x_label = 'Sample Index'
- else:
- x_label = 'Time (s)'
-
- # Create figure and scale data
- max_abs_value = ScaledFloat(max(data.max(), np.abs(data.min())))
- if max_abs_value.scale:
- data = data / max_abs_value.scale_val
- y_label = f'Signal ({max_abs_value.scale}arb.u.)'
- else:
- y_label = 'Signal (arb.u.)'
-
- fig, ax = plt.subplots()
- ax.plot(x, data, linestyle='-', marker='', linewidth=0.5)
- ax.set_xlabel(x_label)
- ax.set_ylabel(y_label)
- return fig
diff --git a/src/qudi/logic/common/fast_counter_logic.py b/src/qudi/logic/common/fast_counter_logic.py
index 2acb738..e64533e 100644
--- a/src/qudi/logic/common/fast_counter_logic.py
+++ b/src/qudi/logic/common/fast_counter_logic.py
@@ -83,6 +83,7 @@ def start_counter(self):
counter.start_measure()
self.__timer.start(int(self._record_length_s*1000))
self.sigScanStarted.emit()
+
@QtCore.Slot()
def stop_counter(self):
@@ -96,6 +97,6 @@ def stop_counter(self):
def __update_data(self):
with self._thread_lock:
counter = self._counter()
- self._last_data = counter.get_data_trace()
+ self._last_data = np.squeeze(counter.get_data_trace())
self.__timer.start(int(self._record_length_s*1000))
self.sigScanFinished.emit(self._last_data)
\ No newline at end of file
diff --git a/src/qudi/logic/common/wavemeter_logic.py b/src/qudi/logic/common/wavemeter_logic.py
new file mode 100644
index 0000000..febd9e9
--- /dev/null
+++ b/src/qudi/logic/common/wavemeter_logic.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+"""
+A module for controlling a scanning laser.
+
+Copyright (c) 2025, QuPIDC qudi developers.
+
+Qudi is free software: you can redistribute it and/or modify it under the terms of
+the GNU Lesser General Public License as published by the Free Software Foundation,
+either version 3 of the License, or (at your option) any later version.
+
+Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with qudi.
+If not, see .
+"""
+
+
+import numpy as np
+import matplotlib.pyplot as plt
+import matplotlib as mpl
+from PySide2 import QtCore
+from qudi.core.connector import Connector
+from qudi.core.configoption import ConfigOption
+from qudi.core.statusvariable import StatusVar
+from qudi.util.mutex import RecursiveMutex
+from qudi.core.module import LogicBase
+
+class WavemeterLogic(LogicBase):
+ """ Logic class for controlling a camera.
+
+ Example config for copy-paste:
+
+ wavemeter_logic:
+ module.Class: 'common.wavemeter_logic.WavemeterLogic'
+ connect:
+ wavemeter: wavemeter_dummy
+
+ """
+
+ # declare connectors
+ _wavemeter = Connector(name='wavemeter', interface='SimpleWavemeterInterface')
+
+ # signals
+ sigNewData = QtCore.Signal(np.ndarray)
+
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def on_activate(self):
+ """ Initialisation performed during activation of the module.
+ """
+ wavemeter = self._wavemeter()
+ self._wavelengths = wavemeter.get_wavelengths()
+
+ wavemeter.sigWavelengthUpdated.connect(self._update_data)
+
+
+ def on_deactivate(self):
+ """ Perform required deactivation. """
+ pass
+
+ @property
+ def wavelengths(self) -> np.ndarray:
+ return self._wavelengths
+
+ @QtCore.Slot(np.ndarray)
+ def _update_data(self, data: np.ndarray):
+ if self.module_state() == 'locked':
+ self._wavelengths = data
+ self.sigNewData.emit(data)
+
+ @QtCore.Slot()
+ def start_reading(self):
+ if self.module_state() == 'idle':
+ self.module_state.lock()
+
+ @QtCore.Slot()
+ def stop_reading(self):
+ if self.module_state() == 'locked':
+ self.module_state.unlock()
\ No newline at end of file
diff --git a/src/qudi/logic/terascan_logic.py b/src/qudi/logic/terascan_logic.py
index 16fff97..fb5ed0d 100644
--- a/src/qudi/logic/terascan_logic.py
+++ b/src/qudi/logic/terascan_logic.py
@@ -35,7 +35,7 @@ class TerascanLogic(LogicBase):
module.Class: 'terascan_logic.TerascanLogic'
connect:
laser: scanning_laser_logic
- wavemeter: time_series_reader_logic
+ wavemeter: wavemeter_logic
counter: fast_counter_logic
daq: daq_reader_logic # Note that this logic assumes there is exactly one (digital) input to the DAQ.
options:
@@ -45,14 +45,14 @@ class TerascanLogic(LogicBase):
# declare connectors
_laser = Connector(name='laser', interface='ScanningLaserLogic')
- _wavemeter = Connector(name='wavemeter', interface='DataInStreamLogic')
+ _wavemeter = Connector(name='wavemeter', interface='WavemeterLogic')
_counter = Connector(name='counter', interface='FastCounterLogic')
_daq = Connector(name='daq', interface='DAQReaderLogic')
# declare config options
_record_length_ms = ConfigOption(name='record_length_ms',
- default=1,
- missing='info')
+ default=1,
+ missing='info')
# status variables:
_start_wavelength = StatusVar('start_wavelength', default=0.75)
@@ -66,7 +66,9 @@ class TerascanLogic(LogicBase):
# Update signals, e.g. for GUI module
sigWavelengthUpdated = QtCore.Signal(float)
sigCountsUpdated = QtCore.Signal(object) # is a List[TerascanData]
+ sigLaserLocked = QtCore.Signal(bool)
+ # Update signals for other logics
sigConfigureCounter = QtCore.Signal(float, float)
sigConfigureLaser = QtCore.Signal(float, float)
@@ -107,7 +109,7 @@ def on_activate(self):
# Inputs:
laser.sigScanStarted.connect(self._laser_scan_started)
laser.sigScanFinished.connect(self._laser_scan_finished)
- wavemeter.sigNewRawData.connect(self._new_wavemeter_data)
+ wavemeter.sigNewData.connect(self._new_wavemeter_data)
counter.sigScanFinished.connect(self._process_counter_data)
daq.sigNewData.connect(self._new_daq_data)
@@ -119,6 +121,9 @@ def on_activate(self):
def on_deactivate(self):
pass
+ @property
+ def locked(self) -> bool:
+ return self._laser_locked
@QtCore.Slot()
def start_scan(self):
@@ -159,11 +164,11 @@ def _laser_scan_finished(self):
self.sigScanFinished.emit()
self.module_state.unlock()
- @QtCore.Slot(object, object)
- def _new_wavemeter_data(self, data: object, timestamps: object):
+ @QtCore.Slot(np.ndarray)
+ def _new_wavemeter_data(self, data: np.ndarray):
with self._thread_lock:
if self.module_state() == 'locked' and self._laser_locked:
- wave = np.average(data)
+ wave = data[0][0]
self._current_wavelength = wave # ?
self.sigWavelengthUpdated.emit(wave)
@@ -188,8 +193,10 @@ def _new_daq_data(self, data: List[ReaderVal]):
# We assume there is only one digital input for this measurement
if i.val and not self._laser_locked:
self._laser_locked = True
- self.sigStartCounting.emit()
+ # self.sigStartCounting.emit()
if not i.val and self._laser_locked:
self._laser_locked = False
- self.sigStopCounting.emit()
+ # self.sigStopCounting.emit()
+
+ self.sigLaserLocked.emit(self._laser_locked)