From 896256341e80f586da690eae2187f18013ef6881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A9la=C3=AFs=20Bruel?= Date: Fri, 21 Aug 2026 16:47:29 +0200 Subject: [PATCH 1/2] using quantities --- pyproject.toml | 2 +- .../extensions/ramping.py | 66 ++++++++++++------- .../utilities/ramp_generator.py | 15 +++-- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index adc16db..16926d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_ramping' name = "pymodaq_plugins_ramping" description = 'PyMoDAQ plugin implementing an extension to perform acquisition in the form of a ramp' dependencies = [ - "pymodaq>=5.3.0", + "pymodaq>=5.2.0", "pymodaq_utils>=5.2.0", "pymodaq_gui>=5.2.0", "pymodaq_data>=5.2.0", diff --git a/src/pymodaq_plugins_ramping/extensions/ramping.py b/src/pymodaq_plugins_ramping/extensions/ramping.py index 5938469..5253c1b 100644 --- a/src/pymodaq_plugins_ramping/extensions/ramping.py +++ b/src/pymodaq_plugins_ramping/extensions/ramping.py @@ -2,7 +2,7 @@ from pathlib import Path from time import perf_counter from typing import Iterable, TYPE_CHECKING, Union, Mapping - +import numpy as np from qtpy import QtWidgets, QtCore from pymodaq_data import Q_, DataDim, DataSource @@ -78,9 +78,10 @@ class RampExtension(CustomExt): {'title': 'Ramp:', 'name': 'ramp', 'type': 'group', 'children': [ {'title': 'Start:', 'name': 'start', 'type': 'float', 'value': 500.}, {'title': 'Stop:', 'name': 'stop', 'type': 'float', 'value': 560.}, - {'title': 'Duration:', 'name': 'duration', 'type': 'float', 'value': 40, 'suffix': 's', 'siPrefix': True}, - {'title': 'Velocity:', 'name': 'velocity', 'type': 'float', 'value': 0, 'suffix': '', 'siPrefix': True, - 'readonly': True}, + {'title': 'Duration:', 'name': 'duration', 'type': 'float', 'value': 40, 'suffix': 'min', 'siPrefix': True, + 'readonly' : True}, + {'title': 'Velocity:', 'name': 'velocity', 'type': 'float', 'value': 0, 'siPrefix': True, + 'readonly': False}, ]}, {'title': 'Use Steps:', 'name': 'use_steps', 'type': 'bool', 'value': True}, {'title': 'Steps:', 'name': 'steps', 'type': 'group', 'children': [ @@ -101,8 +102,8 @@ def __init__(self, parent: gutils.DockArea, dashboard): super().__init__(parent, dashboard, add_toolbar_break=False) - self._start_time: float = None - self._paused_time: float = None + self._start_time: Q_ = None + self._paused_time: Q_ = None self.ramp: RampGenerator = None self._n_emitted = 0 @@ -123,7 +124,7 @@ def __init__(self, parent: gutils.DockArea, dashboard): self.setup_ui() self.update_n_steps() - self.update_velocity() + self.update_duration() self.enable_runflow_actions(False) @@ -256,9 +257,13 @@ def start_ramp(self): pass if self.settings['use_steps']: - self.ramp_timer.setInterval(int(self.settings['steps', 'time_step'])) + self.ramp_timer.setInterval( + int(Q_(self.settings['steps', 'time_step'], + self.settings.child('steps', 'time_step').opts['suffix']).to('ms').magnitude)) - self.total_ramp_timer.setInterval(int(self.settings['ramp', 'duration'] * 1000)) + self.total_ramp_timer.setInterval( + int(Q_(self.settings['ramp', 'duration'], + self.settings.child('ramp', 'duration').opts['suffix']).to('ms').magnitude)) self.total_ramp_timer.setSingleShot(True) if self.is_action_checked('save'): @@ -348,7 +353,11 @@ def stop_ramp(self): self.ramp_timer.stop() self.total_ramp_timer.stop() #self.histogramer_timer.stop() - + self.wait_timer = QtCore.QTimer() + self.wait_timer.setInterval(3600000) + self.wait_timer.setSingleShot(True) + self.wait_timer.timeout.connect(self.go_home) + self.wait_timer.start() for detector in self.detectors: try: detector.grab_done_signal.disconnect(self.send_data) @@ -376,6 +385,9 @@ def stop_ramp(self): else: self._worker_done.connect(self.terminate_worker) + def go_home(self): + self.actuator.move_home() + def terminate_worker(self): self.exit_runner_thread() self.h5saver.flush() @@ -389,14 +401,14 @@ def pause_ramp(self, do_pause=True): if do_pause: self.ramp_timer.stop() #self.histogramer_timer.stop() - self._paused_time = perf_counter() + self._paused_time = Q_(perf_counter(), 's') for detector in self.detectors: detector.grab_done_signal.disconnect(self.send_data) for actuator in self.actuators: actuator.current_value_signal.disconnect(self.send_data) self.actuator.current_value_signal.disconnect(self.send_data) else: - self._start_time = perf_counter() - (self._paused_time - self._start_time) + self._start_time = Q_(perf_counter(),'s') - (self._paused_time - self._start_time) for detector in self.detectors: detector.grab_done_signal.connect(self.send_data) @@ -410,8 +422,8 @@ def pause_ramp(self, do_pause=True): def update_ramp(self): if self._start_time is None: - self._start_time = perf_counter() - elapsed_time = perf_counter() - self._start_time + self._start_time = Q_(perf_counter(), 's') + elapsed_time = Q_(perf_counter(), 's') - self._start_time step = self.ramp(elapsed_time) self.settings['steps', 'step'] = step @@ -465,23 +477,31 @@ def value_changed(self, param): self.update_ramp_settings() elif param.name() == 'use_steps': self.settings.child('steps').show(param.value()) - if param.name() in ('duration', 'start', 'stop'): - self.update_velocity() + if param.name() in ('velocity', 'start', 'stop'): + self.update_duration() def update_n_steps(self): - self.settings['steps', 'nsteps'] = (Q_(self.settings['ramp', 'duration'], 's') / - Q_(self.settings['steps', 'time_step'], 'ms').to('s')).magnitude + self.settings['steps', 'nsteps'] = (Q_(self.settings['ramp', 'duration'], + self.settings.child('ramp', 'duration').opts['suffix']) / + Q_(self.settings['steps', 'time_step'], + self.settings.child('steps', 'time_step').opts['suffix'])).to_reduced_units().magnitude - def update_velocity(self): + def update_duration(self): if self.actuator is not None: - self.settings['ramp', 'velocity'] = ( - (self.settings['ramp', 'stop'] - self.settings['ramp', 'start']) / self.settings['ramp', 'duration']) - self.settings.child('ramp', 'velocity').setOpts(suffix=f'{self.actuator.units}/s') + if not np.allclose(self.settings['ramp','velocity'], 0): + self.settings.child('ramp', 'velocity').setOpts(suffix=f'{self.actuator.units}/min') + self.settings['ramp', 'duration'] = ( + (self.settings['ramp', 'stop'] - self.settings['ramp', 'start']) / self.settings['ramp', 'velocity']) + self.settings.child('ramp', 'duration').setOpts(suffix='min') + else : + self.settings['ramp', 'duration'] = 0 + def get_ramp(self) -> RampGenerator: return RampGenerator(self.settings['ramp', 'start'], self.settings['ramp', 'stop'], - self.settings['ramp', 'duration'],) + Q_(self.settings['ramp', 'duration'], + self.settings.child('ramp', 'duration').opts['suffix'])) def send_data(self, dte: DataToExport | DataActuator): if self.is_action_checked('save'): diff --git a/src/pymodaq_plugins_ramping/utilities/ramp_generator.py b/src/pymodaq_plugins_ramping/utilities/ramp_generator.py index 4f3a3a3..2ec32f2 100644 --- a/src/pymodaq_plugins_ramping/utilities/ramp_generator.py +++ b/src/pymodaq_plugins_ramping/utilities/ramp_generator.py @@ -1,22 +1,25 @@ import numpy as np - +from pymodaq_data import Q_ class RampGenerator: - def __init__(self, start=0.0, end=10.0, duration=5.0): + def __init__(self, + start: float, + end: float, + duration=Q_(5.0,'s')): self.start = start self.end = end self.duration = duration - def __call__(self, elapsed_time: float) -> float: + def __call__(self, elapsed_time: Q_) -> float: return self.update(elapsed_time) - def update(self, elapsed_time): + def update(self, elapsed_time: Q_): # Clamp elapsed time to the valid duration window t_clamped = np.clip(elapsed_time, 0.0, self.duration) # Linear interpolation for the ramp output - if self.duration > 0: - fraction = t_clamped / self.duration + if self.duration.to_reduced_units().magnitude > 0: + fraction = (t_clamped / self.duration).to_reduced_units().magnitude value = self.start + (self.end - self.start) * fraction else: value = self.end From e51372d143ab4099b3106681f2f678afa3fd2d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A9la=C3=AFs=20Bruel?= Date: Fri, 28 Aug 2026 14:40:14 +0200 Subject: [PATCH 2/2] Correcting dwell time --- src/pymodaq_plugins_ramping/extensions/ramping.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pymodaq_plugins_ramping/extensions/ramping.py b/src/pymodaq_plugins_ramping/extensions/ramping.py index 5253c1b..67630c3 100644 --- a/src/pymodaq_plugins_ramping/extensions/ramping.py +++ b/src/pymodaq_plugins_ramping/extensions/ramping.py @@ -111,6 +111,10 @@ def __init__(self, parent: gutils.DockArea, dashboard): self.ramp_timer = QtCore.QTimer() self.ramp_timer.timeout.connect(self.update_ramp) + self.wait_after_stop_timer = QtCore.QTimer() + self.wait_after_stop_timer.setSingleShot(True) + self.wait_after_stop_timer.timeout.connect(self.go_home) + self.total_ramp_timer = QtCore.QTimer() self.total_ramp_timer.timeout.connect(self.stop_ramp) @@ -246,6 +250,7 @@ def update_histogramer(self): self.histogramer.compute_plot_histogram(self.settings['actuator']) def start_ramp(self): + self.wait_after_stop_timer.stop() try: self.actuator.move_done_signal.disconnect(self.start_ramp) except TypeError: @@ -353,11 +358,8 @@ def stop_ramp(self): self.ramp_timer.stop() self.total_ramp_timer.stop() #self.histogramer_timer.stop() - self.wait_timer = QtCore.QTimer() - self.wait_timer.setInterval(3600000) - self.wait_timer.setSingleShot(True) - self.wait_timer.timeout.connect(self.go_home) - self.wait_timer.start() + self.wait_after_stop_timer.setInterval(3600000) + self.wait_after_stop_timer.start() for detector in self.detectors: try: detector.grab_done_signal.disconnect(self.send_data)