diff --git a/cfg/terascan.cfg b/cfg/terascan.cfg index 275a27b..c842f76 100644 --- a/cfg/terascan.cfg +++ b/cfg/terascan.cfg @@ -48,6 +48,7 @@ logic: daq: nidaq # Note that this logic assumes there is exactly one (digital) input to the DAQ. options: record_length_ms: 1 # Length of data (in ms) to take at each wavelength. Only integers. + laser_timeout_s: 10 # time in seconds before we automatically restart the laser scan # daq_reader_logic: diff --git a/src/qudi/hardware/laser/solstis_laser.py b/src/qudi/hardware/laser/solstis_laser.py index 5bbc200..795fefa 100644 --- a/src/qudi/hardware/laser/solstis_laser.py +++ b/src/qudi/hardware/laser/solstis_laser.py @@ -18,6 +18,7 @@ from typing import List from PySide2 import QtCore +from time import time from qudi.core.configoption import ConfigOption from qudi.core.statusvariable import StatusVar @@ -79,6 +80,8 @@ def on_activate(self): if (self._scan_type == -1): self._scan_type = self.get_default_scan_type() self._scan_rate = self.get_default_scan_rate() + + self._scan_started = time() def on_deactivate(self): """ Deactivate module. @@ -237,6 +240,7 @@ def start_scan(self) -> bool: pause=True) solstis.scan_stitch_op(self.socket, self._scan_type, "start") self.sigScanStarted.emit() + self._scan_started = time() self.module_state.lock() return True @@ -257,6 +261,19 @@ def stop_scan(self) -> bool: except solstis.SolstisError as e: self.log.exception(f'Scan stop failure: {e.message}') return False + + @QtCore.Slot(float) + def restart_scan(self, wavelength: float) -> bool: + """Restart a scan from a specified wavelength""" + + if self.module_state() == 'locked': + self.module_state.unlock() + self._start_wavelength = wavelength + self._scan_started = time() + return self.start_scan() + else: + self.log.warning('Restart scan called when not running') + return False def pause_scan(self): """Pause a running scan (unimplemented)""" @@ -358,7 +375,8 @@ def set_scan_rate(self, scan_rate: int): def __status_update(self): - if self.module_state() == 'locked': + if self.module_state() == 'locked' \ + and self._scan_started + 3 < time(): # if we check for status too soon, we might get a false positive status = self.get_laser_state() if status['in_progress'] == False: self.sigScanFinished.emit() diff --git a/src/qudi/logic/terascan_logic.py b/src/qudi/logic/terascan_logic.py index 1fefc9b..b830f42 100644 --- a/src/qudi/logic/terascan_logic.py +++ b/src/qudi/logic/terascan_logic.py @@ -40,7 +40,7 @@ class TerascanLogic(LogicBase): daq: nidaq # Note that this logic assumes there is exactly one (digital) input to the DAQ. options: record_length_ms: 1 # Length of data (in ms) to take at each wavelength. Only integers. - + laser_timeout_s: 10 # Time in seconds to wait for the laser to lock before automatically restarting the scan """ # declare connectors @@ -53,6 +53,8 @@ class TerascanLogic(LogicBase): _record_length_ms = ConfigOption(name='record_length_ms', default=1, missing='info') + + _laser_timeout_s = ConfigOption(name='laser_timeout_s', default=10) # status variables: _start_wavelength = StatusVar('start_wavelength', default=0.75) @@ -65,6 +67,7 @@ class TerascanLogic(LogicBase): _laser_locked = StatusVar('laser_locked', default=False) _current_data = StatusVar('current_data', default=[]) # list of TerascanData + _last_locked: float = 0 # Update signals, e.g. for GUI module sigWavelengthUpdated = QtCore.Signal(float) @@ -76,6 +79,7 @@ class TerascanLogic(LogicBase): sigSetLaserWavelengths = QtCore.Signal(float, float) sigSetLaserScanRate = QtCore.Signal(int) sigSetLaserScanType = QtCore.Signal(int) + sigRestartLaser = QtCore.Signal(float) # used when we need to restart the laser, as everything else is already running sigStartScan = QtCore.Signal() sigStopScan = QtCore.Signal() @@ -87,7 +91,7 @@ class TerascanLogic(LogicBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - + self.__timer = None self._thread_lock = RecursiveMutex() def on_activate(self): @@ -101,6 +105,7 @@ def on_activate(self): self.sigSetLaserWavelengths.connect(laser.set_wavelengths, QtCore.Qt.QueuedConnection) self.sigSetLaserScanRate.connect(laser.set_scan_rate, QtCore.Qt.QueuedConnection) self.sigSetLaserScanType.connect(laser.set_scan_type, QtCore.Qt.DirectConnection) # Is direct on purpose + self.sigRestartLaser.connect(laser.restart_scan, QtCore.Qt.QueuedConnection) self.sigStartScan.connect(counter.start_measure, QtCore.Qt.QueuedConnection) self.sigStartScan.connect(laser.start_scan, QtCore.Qt.QueuedConnection) @@ -124,9 +129,17 @@ def on_activate(self): self._record_length_s = self._record_length_ms * 1e-3 self._bin_width_s = self._record_length_s self.sigConfigureCounter.emit(self._bin_width_s, self._record_length_s) + + # Add watchdog timer + self.__timer = QtCore.QTimer() + self.__timer.setSingleShot(False) + self.__timer.timeout.connect(self.__watchdog) + self.__timer.start(500) def on_deactivate(self): - pass + self.__timer.stop() + self.__timer.timeout.disconnect() + self.__timer = None @property def locked(self) -> bool: @@ -146,6 +159,8 @@ def start_scan(self): if self.module_state() == 'idle': self.module_state.lock() self._current_data = [] + self._last_locked = time.time() + self.sigSetLaserWavelengths.emit(self._start_wavelength, self._end_wavelength) self.sigStartScan.emit() @QtCore.Slot() @@ -243,3 +258,17 @@ def _new_daq_data(self, data: List[ReaderVal]): # self.sigStopCounting.emit() self.sigLaserLocked.emit(self._laser_locked) + + + #### Watchdog Timer #### + def __watchdog(self): + with self._thread_lock: + if self.module_state() == 'locked': + if self._laser_locked: + self._last_locked = time.time() + elif time.time() - self._last_locked > self._laser_timeout_s: + self.log.info('Laser did not lock in time. Restarting scan.') + new_start = self._current_wavelength*1e-3 + self._last_locked = time.time() + self.sigRestartLaser.emit(new_start) +