diff --git a/pyproject.toml b/pyproject.toml index ee7e8e9..8c94a2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ instruments = true # true if plugin contains instrument classes (else false, notice the lowercase for toml files) extensions = true # true if plugins contains dashboard extensions models = true # true if plugins contains pid models -h5exporters = false # true if plugin contains custom h5 file exporters +h5exporters = true # true if plugin contains custom h5 file exporters scanners = false # true if plugin contains custom scan layout (daq_scan extensions) [urls] @@ -19,12 +19,12 @@ dependencies = [ "pillow", ] + authors = [ {name = "Weber Sebastien", email = "sebastien.weber@cemes.fr"}, ] maintainers = [ {name = "Weber Sebastien", email = "sebastien.weber@cemes.fr"}, - #todo: list here all maintainers of your plugin ] @@ -33,7 +33,7 @@ maintainers = [ dynamic = ["version", "urls", "entry-points"] readme = "README.rst" license = { file="LICENSE" } -requires-python = ">=3.11" +requires-python = ">=3.10" classifiers = [ "Development Status :: 5 - Production/Stable", diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirror.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirror.py index d13fe2e..9030537 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirror.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirror.py @@ -16,14 +16,14 @@ class DAQ_Move_BSPiezoMirror(DAQ_Move_base): _controller_units = '' is_multiaxes = True - stage_names = BeamSteeringActuators.axes[:2] - _epsilon = 1 + _axis_names = BeamSteeringActuators.axes[:2] + _epsilons = 1 data_actuator_type = DataActuatorType.DataActuator params = [ {'title': 'Tau (ms):', 'name': 'tau', 'type': 'int', 'value': BeamSteering._tau * 1000, 'tip': 'Characteristic evolution time'}, - ] + comon_parameters_fun(is_multiaxes, stage_names, epsilon=_epsilon) + ] + comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilons) def ini_attributes(self): self.controller: BeamSteering = None diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirrorRunTimeError.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirrorRunTimeError.py index 7a7b755..9d6d9dc 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirrorRunTimeError.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirrorRunTimeError.py @@ -8,11 +8,7 @@ class DAQ_Move_BSPiezoMirrorRunTimeError(DAQ_Move_BSPiezoMirror): """ def get_actuator_value(self): - raise RuntimeError('Testing Purpose') - axis = self.settings['multiaxes', 'axis'] - pos = self.controller.get_value(axis) - pos = self.get_position_with_scaling(pos) - return pos + raise RuntimeError('Testing Purpose') #noqa PMQ202 if __name__ == '__main__': diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirrorUninitializing.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirrorUninitializing.py index e8294e4..6e5dfa1 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirrorUninitializing.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirrorUninitializing.py @@ -12,7 +12,10 @@ def ini_stage(self, controller=None): """ """ - self.ini_stage_init(controller, BeamSteering()) + if self.is_master: + self.controller = BeamSteering() + else: + self.controller = controller self.controller.tau = self.settings['tau'] / 1000 raise IOError('Testing Purpose') # simulating an error in initialization !!! info = "" diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPowerMeter.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPowerMeter.py index 4519b66..eb27239 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPowerMeter.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPowerMeter.py @@ -1,4 +1,4 @@ -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main # base class +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main, DataActuatorType, DataActuator # base class from pymodaq.control_modules.move_utility_classes import comon_parameters_fun # common set of parameters for all actuators from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo # object used to send info back to the main thread @@ -14,20 +14,23 @@ class DAQ_Move_BSPowerMeter(DAQ_Move_base): _controller_units = 'W' is_multiaxes = True - stage_names = [BeamSteeringActuators.axes[-1]] - _epsilon = 0.5 + _axis_names = [BeamSteeringActuators.axes[-1]] + _epsilons = 0.5 params = [ {'title': 'Tau (ms):', 'name': 'tau', 'type': 'int', 'value': BeamSteering._tau * 1000, 'tip': 'Characteristic evolution time'}, - ] + comon_parameters_fun(is_multiaxes, stage_names, epsilon=_epsilon) + ] + comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilons) + + data_actuator_type = DataActuatorType.DataActuator def ini_attributes(self): self.controller: BeamSteering = None - def get_actuator_value(self): - axis = self.settings['multiaxes', 'axis'] - pos = self.controller.get_value(axis) + def get_actuator_value(self) -> DataActuator: + pos = DataActuator(self._title, + data=self.controller.get_value(self.axis_name), + units=self.axis_unit) pos = self.get_position_with_scaling(pos) return pos @@ -61,25 +64,28 @@ def ini_stage(self, controller=None): -------- daq_utils.ThreadCommand """ - self.ini_stage_init(controller, BeamSteering()) + if self.is_master: + self.controller = BeamSteering() + else: + self.controller = controller info = "" initialized = True return info, initialized - def move_abs(self, position): + def move_abs(self, position: DataActuator): position = self.check_bound(position) #if user checked bounds, the defined bounds are applied here self.target_value = position position = self.set_position_with_scaling(position) - axis = self.settings['multiaxes', 'axis'] - pos = self.controller.move_at(position, axis) - def move_rel(self, position): - position = self.check_bound(self.current_position + position) - self.current_position - self.target_value = position + self.current_position + self.controller.move_at(position.value(self.axis_unit), self.axis_name) + + def move_rel(self, position: DataActuator): + position = self.check_bound(self.current_value + position) - self.current_value + self.target_value = position + self.current_value position = self.set_position_with_scaling(self.target_value) - axis = self.settings['multiaxes', 'axis'] - pos = self.controller.move_at(position, axis) + + self.controller.move_at(position.value(self.axis_unit), self.axis_name) def move_home(self): """ @@ -98,7 +104,7 @@ def stop_motion(self): -------- move_done """ - self.controller.stop(self.settings['multiaxes', 'axis']) + self.controller.stop(self.axis_name) self.move_done() diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Harmonics.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Harmonics.py index 26a0c49..176e381 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Harmonics.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Harmonics.py @@ -39,7 +39,9 @@ def get_actuator_value(self): ------- float: The position obtained after scaling conversion. """ - pos = DataActuator(data=self.controller.amplitude) # when writing your own plugin replace this line + pos = DataActuator(self._title, + data=self.controller.amplitude, + units=self.axis_unit) pos = self.get_position_with_scaling(pos) return pos @@ -71,10 +73,10 @@ def ini_stage(self, controller=None): initialized: bool False if initialization failed otherwise True """ - self.controller = self.ini_stage_init(old_controller=controller, - new_controller=None) if self.is_master: self.controller = Harmonics() + else: + self.controller = controller info = "Whatever info you want to log" initialized = True @@ -100,8 +102,8 @@ def move_rel(self, value: DataActuator): ---------- value: (float) value of the relative target positioning """ - value = self.check_bound(self.current_position + value) - self.current_position - self.target_value = value + self.current_position + value = self.check_bound(self.current_value + value) - self.current_value + self.target_value = value + self.current_value value = self.set_position_relative_with_scaling(value) self.controller.amplitude = self.target_value.value() diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockBinary.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockBinary.py index 05826d0..395edcd 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockBinary.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockBinary.py @@ -13,10 +13,9 @@ class DAQ_Move_MockBinary(DAQ_Move_base): """ _controller_units = '' - is_multiaxes = True # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) - axes_names = [''] - _epsilon = 0.01 - params = comon_parameters_fun(is_multiaxes, axes_names, epsilon=_epsilon) + _axis_names = [''] + _epsilons = 0.01 + params = comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilons) data_actuator_type = DataActuatorType.DataActuator ui_type = UiType.BINARY #for pymodaq >= 5.3.0 @@ -32,7 +31,10 @@ def internal_state(self, value: int): self._internal_state = int(value) def get_actuator_value(self): - pos = DataActuator(data=self.internal_state) + pos = DataActuator(self._title, + data=self.internal_state, + units=self.axis_unit + ) pos = self.get_position_with_scaling(pos) return pos diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockCamera.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockCamera.py index ff19dd8..ba0b1b0 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockCamera.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockCamera.py @@ -1,8 +1,10 @@ -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main # base class +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main, DataActuatorType # base class from pymodaq.control_modules.move_utility_classes import comon_parameters_fun # common set of parameters for all actuators from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo # object used to send info back to the main thread from easydict import EasyDict as edict # type of dict + +from pymodaq.utils.data import DataActuator from ..hardware.camera_wrapper import Camera from pymodaq_plugins_mockexamples import config @@ -19,17 +21,19 @@ class DAQ_Move_MockCamera(DAQ_Move_base): """ _controller_units = '' is_multiaxes = True - stage_names = Camera.axes - _epsilon = 0.01 + _axis_names = Camera.axes + _epsilons = 0.01 - params = comon_parameters_fun(is_multiaxes, stage_names) + params = comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilons) + data_actuator_type = DataActuatorType.DataActuator def ini_attributes(self): self.controller: Camera = None - def get_actuator_value(self): - axis = self.settings['multiaxes', 'axis'] - pos = self.controller.get_value(axis) + def get_actuator_value(self) -> DataActuator: + pos = DataActuator(self._title, + data=self.controller.get_value(self.axis_name), + units=self.axis_unit) pos = self.get_position_with_scaling(pos) return pos @@ -62,25 +66,27 @@ def ini_stage(self, controller=None): -------- daq_utils.ThreadCommand """ - self.ini_stage_init(controller, Camera()) + if self.is_master: + self.controller = Camera() + else: + self.controller = controller + info = "Mock Camera" initialized = True return info, initialized - def move_abs(self, position): + def move_abs(self, position: DataActuator): position = self.check_bound(position) #if user checked bounds, the defined bounds are applied here self.target_value = position position = self.set_position_with_scaling(position) - axis = self.settings['multiaxes', 'axis'] - pos = self.controller.set_value(axis, position) + pos = self.controller.set_value(self.axis_name, position.value(self.axis_unit)) - def move_rel(self, position): - position = self.check_bound(self.current_position + position) - self.current_position - self.target_value = position + self.current_position + def move_rel(self, position: DataActuator): + position = self.check_bound(self.current_value + position) - self.current_value + self.target_value = position + self.current_value position = self.set_position_with_scaling(self.target_value) - axis = self.settings['multiaxes', 'axis'] - pos = self.controller.set_value(axis, position) + pos = self.controller.set_value(self.axis_name, position.value(self.axis_unit)) def move_home(self): """ diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockComplexSignal.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockComplexSignal.py index 46a553f..10a6317 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockComplexSignal.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockComplexSignal.py @@ -1,4 +1,4 @@ -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main # base class +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main, DataActuatorType, DataActuator # base class from pymodaq.control_modules.move_utility_classes import comon_parameters_fun # common set of parameters for all actuators from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo # object used to send info back to the main thread @@ -19,17 +19,20 @@ class DAQ_Move_MockComplexSignal(DAQ_Move_base): _controller_units = '' is_multiaxes = True _axis_names = dict(zip(DataSignal.axes, list(range(len(DataSignal.axes))))) - _epsilon = 0.01 + _epsilons = 0.01 params = [{'title': 'Tau (ms):', 'name': 'tau', 'type': 'int', 'value': DataSignal._tau * 1000, 'tip': 'Characteristic evolution time'},] + \ - comon_parameters_fun(is_multiaxes, axes_names=_axis_names, epsilon=_epsilon) + comon_parameters_fun(axes_names=_axis_names, epsilon=_epsilons) + data_actuator_type = DataActuatorType.DataActuator def ini_attributes(self): self.controller: DataSignal = None - def get_actuator_value(self): - pos = self.controller.get_value(self.axis_name) + def get_actuator_value(self) -> DataActuator: + pos = DataActuator(self._title, + data=self.controller.get_value(self.axis_name), + units = self.axis_unit) pos = self.get_position_with_scaling(pos) return pos @@ -65,25 +68,28 @@ def ini_stage(self, controller=None): -------- daq_utils.ThreadCommand """ - self.ini_stage_init(controller, DataSignal()) - if self.settings['multiaxes', 'multi_status'] == "Master": + if self.is_master: + self.controller = DataSignal() self.controller.ini_random_structures() + else: + self.controller = controller + info = "Mock MultiAxis" initialized = True return info, initialized - def move_abs(self, position): + def move_abs(self, position: DataActuator): position = self.check_bound(position) #if user checked bounds, the defined bounds are applied here self.target_value = position position = self.set_position_with_scaling(position) - pos = self.controller.move_at(position, self.axis_name) + pos = self.controller.move_at(position.value(self.axis_unit), self.axis_name) def move_rel(self, position): - position = self.check_bound(self.current_position + position) - self.current_position - self.target_value = position + self.current_position + position = self.check_bound(self.current_value + position) - self.current_value + self.target_value = position + self.current_value position = self.set_position_with_scaling(self.target_value) - pos = self.controller.move_at(position, self.axis_name) + pos = self.controller.move_at(position.value(self.axis_unit), self.axis_name) def move_home(self): """ diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockHeater.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockHeater.py index 1445635..19f07e9 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockHeater.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockHeater.py @@ -17,21 +17,22 @@ class DAQ_Move_MockHeater(DAQ_Move_base): """ _controller_units = 'W' is_multiaxes = True - stage_names = ['Heater'] - _epsilon = 0.01 + _axis_names = ['Heater'] + _epsilons = 0.01 - params = [] + comon_parameters_fun(is_multiaxes, stage_names, epsilon=_epsilon) + params = [] + comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilons) data_actuator_type = DataActuatorType.DataActuator def ini_attributes(self): self.controller: HeaterController = None - def get_actuator_value(self) -> DataActuator: """ """ - pos = DataActuator(self._title, data=self.controller.check_position()) + pos = DataActuator(self._title, + data=self.controller.check_position(), + units=self.axis_unit) pos = self.get_position_with_scaling(pos) return pos diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockNamedAxes.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockNamedAxes.py index f403bf4..088c0dd 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockNamedAxes.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockNamedAxes.py @@ -1,4 +1,5 @@ -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main # base class + +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main, DataActuatorType, DataActuator # base class from pymodaq.control_modules.move_utility_classes import comon_parameters_fun # common set of parameters for all actuators from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo # object used to send info back to the main thread @@ -20,15 +21,18 @@ class DAQ_Move_MockNamedAxes(DAQ_Move_base): _controller_units = '' is_multiaxes = True _axis_names = dict(zip(['Xaxis', 'Yaxis', 'Zaxis'], MultiAxis.axes_indexes)) - _epsilon = 0.01 + _epsilons = 0.01 - params = comon_parameters_fun(is_multiaxes, axes_names=_axis_names) + params = comon_parameters_fun(axes_names=_axis_names, epsilon=_epsilons) + data_actuator_type = DataActuatorType.DataActuator def ini_attributes(self): self.controller: MultiAxis = None - def get_actuator_value(self): - pos = self.controller.get_value(self.axis_value) + def get_actuator_value(self) -> DataActuator: + pos = DataActuator(self._title, + data=self.controller.get_value(self.axis_value), + units = self.axis_unit) pos = self.get_position_with_scaling(pos) return pos @@ -61,23 +65,26 @@ def ini_stage(self, controller=None): -------- daq_utils.ThreadCommand """ - self.ini_stage_init(controller, MultiAxis()) + if self.is_master: + self.controller = MultiAxis() + else: + self.controller = controller info = "Mock MultiAxis" initialized = True return info, initialized - def move_abs(self, position): + def move_abs(self, position: DataActuator): position = self.check_bound(position) #if user checked bounds, the defined bounds are applied here self.target_value = position position = self.set_position_with_scaling(position) - pos = self.controller.set_value(self.axis_value, position) + pos = self.controller.set_value(self.axis_value, position.value(self.axis_unit)) - def move_rel(self, position): - position = self.check_bound(self.current_position + position) - self.current_position - self.target_value = position + self.current_position + def move_rel(self, position: DataActuator): + position = self.check_bound(self.current_value + position) - self.current_value + self.target_value = position + self.current_value position = self.set_position_with_scaling(self.target_value) - pos = self.controller.set_value(self.axis_value, position) + pos = self.controller.set_value(self.axis_value, position.value(self.axis_unit)) def move_home(self): """ diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockRandom.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockRandom.py index fe1907d..c3e6ad3 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockRandom.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockRandom.py @@ -1,8 +1,10 @@ -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main # base class +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main, DataActuatorType # base class from pymodaq.control_modules.move_utility_classes import comon_parameters_fun # common set of parameters for all actuators from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo # object used to send info back to the main thread from easydict import EasyDict as edict # type of dict + +from pymodaq.utils.data import DataActuator from ..hardware.random_wrapper import RandomWrapper from pymodaq_plugins_mockexamples import config @@ -17,17 +19,19 @@ class DAQ_Move_MockRandom(DAQ_Move_base): *params* dictionnary =============== ============== """ - _controller_units = '' + _controller_units = [''] is_multiaxes = False - _epsilon = 0.01 - - params = comon_parameters_fun(is_multiaxes) + _epsilons = 0.01 + _axis_names = [''] + params = comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilons) + data_actuator_type = DataActuatorType.DataActuator def ini_attributes(self): self.controller: RandomWrapper = None - def get_actuator_value(self): - pos = self.controller.get_value() + def get_actuator_value(self) -> DataActuator: + pos = DataActuator(self._title, data=self.controller.get_value(), + units=self.axis_unit) pos = self.get_position_with_scaling(pos) return pos @@ -60,23 +64,27 @@ def ini_stage(self, controller=None): -------- daq_utils.ThreadCommand """ - self.ini_stage_init(controller, RandomWrapper()) + if self.is_master: + self.controller = controller + else: + self.controller = controller + info = "Mock Random Wrapper" initialized = True return info, initialized - def move_abs(self, position): + def move_abs(self, position: DataActuator): position = self.check_bound(position) #if user checked bounds, the defined bounds are applied here self.target_value = position position = self.set_position_with_scaling(position) - pos = self.controller.set_value(position) + pos = self.controller.set_value(position.value(self.axis_unit)) - def move_rel(self, position): - position = self.check_bound(self.current_position + position) - self.current_position - self.target_value = position + self.current_position + def move_rel(self, position: DataActuator): + position = self.check_bound(self.current_value + position) - self.current_value + self.target_value = position + self.current_value position = self.set_position_with_scaling(self.target_value) - pos = self.controller.set_value(position) + pos = self.controller.set_value(position.value()) def move_home(self): """ diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockRelative.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockRelative.py index aee6008..a620bac 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockRelative.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockRelative.py @@ -1,3 +1,5 @@ +import numpy as np + from pymodaq.control_modules.move_utility_classes import (DAQ_Move_base, comon_parameters_fun, DataActuatorType, DataActuator, main) from pymodaq.control_modules.daq_move_ui.utils import UiType @@ -12,9 +14,9 @@ class DAQ_Move_MockRelative(DAQ_Move_base): _controller_units = '' is_multiaxes = True # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) - axes_names = [''] - _epsilon = 0.01 - params = comon_parameters_fun(is_multiaxes, axes_names, epsilon=_epsilon) + _axis_names = [''] + _epsilons = 0.01 + params = comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilons) data_actuator_type = DataActuatorType.DataActuator ui_type = UiType.RELATIVE #for pymodaq >= 5.3.0 @@ -24,7 +26,7 @@ def ini_attributes(self): self._internal_value = 0. def get_actuator_value(self) -> DataActuator: - raise NotImplementedError + raise NotImplementedError # noqa: PMQ202 should never be called as actuator is binary but this ssay the checker it is not an error def close(self): """ @@ -61,7 +63,7 @@ def move_abs(self, value: DataActuator): value: (DataActuator) value of the absolute target positioning """ - raise NotImplementedError + raise NotImplementedError # noqa PMQ202 # should never be called as actuator is binary but this ssay the checker it is not an error def move_rel(self, value: DataActuator): """ Move the actuator to the relative target actuator value defined by position diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockTempController.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockTempController.py index bb6e551..156b306 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockTempController.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockTempController.py @@ -17,7 +17,7 @@ class DAQ_Move_MockTempController(DAQ_Move_base): _controller_units = ['K', 'W'] is_multiaxes = True _axis_names = ['Temperature', 'Power'] - _epsilon = [0.01, 0.01] + _epsilons = [0.01, 0.01] params = [ {'title': 'PID Constants', 'name': 'constants', 'type': 'group', 'children': [ @@ -29,7 +29,7 @@ class DAQ_Move_MockTempController(DAQ_Move_base): {'title': 'HasCooling:', 'name': 'has_cooling', 'type': 'led', 'value': TempController.has_cooling}, {'title': 'Enabled:', 'name': 'enabled', 'type': 'led', 'value': False}, {'title': 'Reset', 'name': 'reset', 'type': 'bool_push', 'value': False, 'label': 'Reset'}, - ] + comon_parameters_fun(is_multiaxes, _axis_names, epsilon=_epsilon) + ] + comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilons) data_actuator_type = DataActuatorType.DataActuator def ini_attributes(self): diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Pinem.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Pinem.py deleted file mode 100644 index ba209b4..0000000 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Pinem.py +++ /dev/null @@ -1,115 +0,0 @@ -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main # base class -from pymodaq.control_modules.move_utility_classes import comon_parameters_fun # common set of parameters for all actuators - -from pymodaq_utils.utils import ThreadCommand, getLineInfo # object used to send info back to the main thread -from pymodaq_utils import math_utils as mutils - - -from pymodaq_plugins_mockexamples import config -from pymodaq_plugins_mockexamples.hardware.pinem_simulator import PinemGenerator - - -class DAQ_Move_Pinem(DAQ_Move_base): - """ - Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. - - =============== ============== - **Attributes** **Type** - *params* dictionnary - =============== ============== - """ - _controller_units = '' - is_multiaxes = True - axes_names = ['omg', 'g'] - _epsilon = 0.01 - params = \ - [ - {'title': 'Tau (ms):', 'name': 'tau', 'type': 'int', 'value': 500, - 'tip': 'Characteristic evolution time'}, - ] + comon_parameters_fun(is_multiaxes, axes_names, epsilon=_epsilon) - - def ini_attributes(self): - self.controller: PinemGenerator = None - - def get_actuator_value(self): - pos = self.controller.get_value(self.axis_name) - pos = self.get_position_with_scaling(pos) - return pos - - def close(self): - pass - - def commit_settings(self, param): - pass - - def ini_stage(self, controller=None): - """ - Initialize the controller and stages (axes) with given parameters. - - ============== ================================================ ========================================================================================== - **Parameters** **Type** **Description** - - *controller* instance of the specific controller object If defined this hardware will use it and will not initialize its own controller instance - ============== ================================================ ========================================================================================== - - Returns - ------- - Easydict - dictionnary containing keys: - * *info* : string displaying various info - * *controller*: instance of the controller object in order to control other axes without the need to init the same controller twice - * *stage*: instance of the stage (axis or whatever) object - * *initialized*: boolean indicating if initialization has been done corretly - - See Also - -------- - daq_utils.ThreadCommand - """ - - self.ini_stage_init(controller, PinemGenerator(1024, 0.05, 'Gaussian')) - - self.g_omega = lambda x: 5 * mutils.gauss1D(x, 1.5, 1.) - - info = "Pinem Simulator" - initialized = True - return info, initialized - - def move_abs(self, position): - position = self.check_bound(position) #if user checked bounds, the defined bounds are applied here - self.target_value = position - position = self.set_position_with_scaling(position) - pos = self.controller.set_value(self.axis_name, position) - if self.axis_name == 'omg': - self.controller.g = self.g_omega(self.controller.omg) - - def move_rel(self, position): - position = self.check_bound(self.current_position + position) - self.current_position - self.target_value = position + self.current_position - position = self.set_position_with_scaling(self.target_value) - - pos = self.controller.set_value(position) - if self.axis_name == 'omg': - self.controller.g = self.g_omega(self.controller.omg) - - def move_home(self): - """ - Send the update status thread command. - See Also - -------- - daq_utils.ThreadCommand - """ - self.emit_status(ThreadCommand('Update_Status', ['Move Home not implemented'])) - - def stop_motion(self): - """ - Call the specific move_done function (depending on the hardware). - - See Also - -------- - move_done - """ - self.move_done() - - -if __name__ == '__main__': - main(__file__) \ No newline at end of file diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_BSPhotodiode.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_BSPhotodiode.py index 32164a4..235ae73 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_BSPhotodiode.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_BSPhotodiode.py @@ -11,6 +11,7 @@ class DAQ_0DViewer_BSPhotodiode(DAQ_2DViewer_BSCamera): + """ Simulate the overall power send on to the Camera""" def average_data(self, Naverage, init=False): data_tmp = np.zeros_like(self.controller.get_photodiode_data()) diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockAdaptive.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockAdaptive.py index cee6ff7..8170f2f 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockAdaptive.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockAdaptive.py @@ -1,10 +1,8 @@ -from qtpy import QtWidgets -from qtpy.QtCore import Signal, QThread, Slot + from pymodaq_utils import utils as utils from pymodaq.utils.data import DataFromPlugins, DataToExport import numpy as np -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base -from easydict import EasyDict as edict +from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, main from pymodaq.control_modules.viewer_utility_classes import comon_parameters from PIL import Image from pathlib import Path @@ -150,9 +148,8 @@ def ini_detector(self, controller=None): data=[DataFromPlugins(name='Mock1', data=[np.array(0)], dim='Data0D', labels=['RandomGaussians'])])) - self.status.initialized = True - self.status.controller = self.controller - return self.status + initialized = True + return 'Init', initialized def close(self): """ @@ -205,3 +202,7 @@ def stop(self): """ return "" + + +if __name__ == '__main__': + main(__file__) diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockErrors.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockErrors.py index 368f058..d888e6f 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockErrors.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockErrors.py @@ -12,7 +12,7 @@ class DAQ_0DViewer_MockErrors(DAQ_0DViewer_Mock): - + """ To test the inclusion of error on 0D measurements""" hardware_averaging = True diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockRandom.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockRandom.py index e2596ff..23256fe 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockRandom.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockRandom.py @@ -11,7 +11,7 @@ class DAQ_0DViewer_MockRandom(DAQ_Viewer_base): - + """ Generate a HyperGaussian but on random points""" live_mode_available = False hardware_averaging = False diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_MockRandom.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_MockRandom.py index 8243f5c..5072913 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_MockRandom.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_MockRandom.py @@ -12,7 +12,7 @@ class DAQ_1DViewer_MockRandom(DAQ_Viewer_base): - + """ Generate shuffled 1D gaussian """ live_mode_available = False hardware_averaging = False diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_Mock_spectro.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_Mock_spectro.py index 0623ac4..29e0111 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_Mock_spectro.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_Mock_spectro.py @@ -199,10 +199,8 @@ def ini_detector(self, controller=None): axes=[self.x_axis], labels=['Mock1', 'label2']),])) - self.status.initialized = True - self.status.controller = self.controller - self.status.x_axis = self.x_axis - return self.status + initialized = True + return 'Initialized', initialized def close(self): """ diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_Pinem.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_Pinem.py deleted file mode 100644 index d091662..0000000 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_Pinem.py +++ /dev/null @@ -1,130 +0,0 @@ -import numpy as np -from pymodaq.utils.data import DataFromPlugins, Axis, DataToExport -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main -from pymodaq_gui.parameter import Parameter - -from pymodaq_plugins_mockexamples.hardware.pinem_simulator import PinemGenerator - - -class DAQ_1DViewer_Pinem(DAQ_Viewer_base): - """ Instrument plugin class for a 1D viewer. - - This object inherits all functionalities to communicate with PyMoDAQ’s DAQ_Viewer module through inheritance via - DAQ_Viewer_base. It makes a bridge between the DAQ_Viewer module and the Python wrapper of a particular instrument. - - Attributes: - ----------- - controller: object - The particular object that allow the communication with the hardware, in general a python wrapper around the - hardware library. - - """ - params = comon_parameters + [ - {'title': 'g', 'name': 'g', 'type': 'slide', 'value': 1, 'default': 1, 'min': 0, - 'max': 5, 'subtype': 'linear'}, - # the strength of the Signal to noise ratio is solely dependent on the amplitude of the signal - {'title': 'amp', 'name': 'amplitude', 'type': 'slide', 'value': 20, 'default': 20, 'min': 5, - 'max': 500, 'subtype': 'linear'}, - {'title': 'omg', 'name': 'omega', 'type': 'slide', 'value': 1.5, 'default': 1.5, 'min': 0.1, - 'max': 4.5, 'subtype': 'linear'}, - {'title': 'offset', 'name': 'offset', 'type': 'slide', 'value': 0.5, 'default': 0.5, - 'min': 0.0, - 'max': 5.0, 'subtype': 'linear'}, - {'title': 'noise', 'name': 'noise', 'type': 'bool', 'value': False, 'default': False}, - {'title': 'background', 'name': 'background', 'type': 'slide', 'value': 0.1, 'default': 0.1, - 'min': 0.0, - 'max': 1.0, 'subtype': 'linear'}, - {'title': 'remove_background', 'name': 'remove_background', 'type': 'bool', 'value': True, - 'default': True} - # elements to be added here as dicts in order to control your custom stage - ############ - ] - - def ini_attributes(self): - self.controller: PinemGenerator = None - self.x_axis = None - - def commit_settings(self, param: Parameter): - """Apply the consequences of a change of value in the detector settings - - Parameters - ---------- - param: Parameter - A given parameter (within detector_settings) whose value has been changed by the user - """ - if param.name() == "g": - self.controller.g = param.value() - - elif param.name() == 'amplitude': - self.controller.amplitude = param.value() - - elif param.name() == 'offset': - self.controller.offset = param.value() - - elif param.name() == 'noise': - self.controller.noise = param.value() - - elif param.name() == 'background': - self.controller.background = param.value() - - elif param.name() == 'remove_background': - self.controller.remove_background = param.value() - - elif param.name() == 'omega': - self.controller.omg = param.value() - - def ini_detector(self, controller=None): - """Detector communication initialization - - Parameters - ---------- - controller: (object) - custom object of a PyMoDAQ plugin (Slave case). None if only one actuator/detector by controller - (Master case) - - Returns - ------- - info: str - initialized: bool - False if initialization failed otherwise True - """ - if self.is_master: - self.controller = PinemGenerator(1024, 0.05, 'Gaussian', - amplitude=self.settings['amplitude']) - else: - self.controller = comon_parameters - - info = "Whatever info you want to log" - initialized = True - return info, initialized - - def close(self): - """Terminate the communication protocol""" - ... - - def grab_data(self, Naverage=1, **kwargs): - """Start a grab from the detector - - Parameters - ---------- - Naverage: int - Number of hardware averaging (if hardware averaging is possible, self.hardware_averaging should be set to - True in class preamble and you should code this implementation) - kwargs: dict - others optionals arguments - """ - data_array = self.controller.gen_data() - axis = Axis('energy', data=self.controller.x) - self.dte_signal.emit(DataToExport('Pinem', - data=[DataFromPlugins(name='Spectrum', data=[data_array], - dim='Data1D', labels=['Spectrum'], - axes=[axis]), - ])) - - def stop(self): - """Stop the current grab hardware wise if necessary""" - return '' - - -if __name__ == '__main__': - main(__file__) diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_PinemAnalysis.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_PinemAnalysis.py deleted file mode 100644 index 33b54d0..0000000 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_1D/daq_1Dviewer_PinemAnalysis.py +++ /dev/null @@ -1,69 +0,0 @@ -import numpy as np -from pymodaq.utils.data import DataFromPlugins, Axis, DataToExport -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main -from pymodaq_plugins_mockexamples.hardware.pinem_simulator import PinemGenerator -from pymodaq_plugins_mockexamples.daq_viewer_plugins.plugins_1D.daq_1Dviewer_Pinem import DAQ_1DViewer_Pinem - -from pymodaq_plugins_mockexamples.hardware.pinem_analysis import PinemAnalysis - -import os - -file_path = os.path.dirname(os.path.abspath(__file__)) - - - - -class DAQ_1DViewer_PinemAnalysis(DAQ_1DViewer_Pinem): - """ Instrument plugin class for a 1D viewer. - - This object inherits all functionalities to communicate with PyMoDAQ’s DAQ_Viewer module through inheritance via - DAQ_Viewer_base. It makes a bridge between the DAQ_Viewer module and the Python wrapper of a particular instrument. - - Attributes: - ----------- - controller: object - The particular object that allow the communication with the hardware, in general a python wrapper around the - hardware library. - - """ - - def ini_attributes(self): - self.controller: PinemGenerator = None - self.x_axis = None - self.pinem_model = PinemAnalysis(file_path + '/plasmon_cnn_Kalinin_div2_nobkgd.h5') - - def grab_data(self, Naverage=1, **kwargs): - """Start a grab from the detector - - Parameters - ---------- - Naverage: int - Number of hardware averaging (if hardware averaging is possible, self.hardware_averaging should be set to - True in class preamble and you should code this implementation) - kwargs: dict - others optionals arguments - """ - data_array = self.controller.gen_data() - axis = Axis('energy', data=self.controller.x) - # pinem_model = PinemAnalysis(file_path + '/cnn_3layers_32_v1.h5') - - # TODO : For now we'll just predict g, but ideally we should be flexible depending on the loaded neural network. - # While I think it should be possible to get input and output shape from the h5 file, we could use a json file to store that info. - # This way we could also store parameter names. - g = self.pinem_model.predict(data_array, self.controller.remove_background) - # We compare true g vs predicted g - self.dte_signal.emit(DataToExport('Pinem', - data=[ - DataFromPlugins(name='Constants', - data=[np.array([self.controller.g]), - np.array([g[0][0]])], - dim='Data0D', labels=['true g', 'g pred']), - DataFromPlugins(name='Spectrum', data=[data_array], - dim='Data1D', labels=['Spectrum'], - axes=[axis]), - ])) - - - -if __name__ == '__main__': - main(__file__) diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_BSCamera.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_BSCamera.py index d5545c2..d257d77 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_BSCamera.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_BSCamera.py @@ -11,7 +11,8 @@ class DAQ_2DViewer_BSCamera(DAQ_Viewer_base): - + """ Generate a 2D image with a spot on it whose position can be set + using DAQ_Move_BSPiezoMirror""" live_mode_available = True hardware_averaging = True diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_MockCamera.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_MockCamera.py index a65f26f..cd22907 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_MockCamera.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_MockCamera.py @@ -13,7 +13,7 @@ class DAQ_2DViewer_MockCamera(DAQ_Viewer_base): - + """ Generate various 2D images""" live_mode_available = True hardware_averaging = True diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_RoiStuff.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_RoiStuff.py index 735efdc..fdacf6f 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_RoiStuff.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_2D/daq_2Dviewer_RoiStuff.py @@ -14,7 +14,7 @@ class DAQ_2DViewer_RoiStuff(DAQ_2DViewer_MockCamera): - + """ Generate 2D image that can be croped using the ROI select on the DAQViewer""" params = DAQ_2DViewer_MockCamera.params + \ [{'title': 'Use Roi:', 'name': 'use_roi', 'type': 'bool'}] diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_ND/daq_NDviewer_MockEvents.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_ND/daq_NDviewer_MockEvents.py index ad6bd8c..7c52046 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_ND/daq_NDviewer_MockEvents.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_ND/daq_NDviewer_MockEvents.py @@ -127,7 +127,6 @@ def commit_settings(self, param: Parameter): param: Parameter A given parameter (within detector_settings) whose value has been changed by the user """ - # TODO for your custom plugin if param.name() in iter_children(self.settings.child('histogram'), []): if self._loader is not None: self.process_events() @@ -164,9 +163,10 @@ def ini_detector(self, controller=None): initialized: bool False if initialization failed otherwise True """ - - self.ini_detector_init(old_controller=controller, - new_controller=PhotonYielder()) + if self.is_master: + self.controller = PhotonYielder() + else: + self.controller = controller callback = PhotonCallback(self.controller, self._queue) self.callback_thread = QtCore.QThread(self) # creation of a Qt5 thread @@ -272,7 +272,11 @@ def show_nd(self): def close(self): """Terminate the communication protocol""" - self.saver.close() + if self.saver is not None: + self.saver.close() + if hasattr(self, 'callback_thread'): + self.callback_thread.terminate() + self.callback_thread.wait() def grab_data(self, Naverage=1, **kwargs): """Start a grab from the detector diff --git a/src/pymodaq_plugins_mockexamples/hardware/photon_yielder.py b/src/pymodaq_plugins_mockexamples/hardware/photon_yielder.py index 313c73f..7503947 100644 --- a/src/pymodaq_plugins_mockexamples/hardware/photon_yielder.py +++ b/src/pymodaq_plugins_mockexamples/hardware/photon_yielder.py @@ -74,7 +74,7 @@ def grab(self) -> Photons: from pymodaq_data.data import Axis, DataToExport, DataRaw, DataCalculated, DataWithAxes from pymodaq_data.h5modules.data_saving import DataSaverLoader - here = Path(r'C:\Users\weber\Labo\Programmes Python\PyMoDAQ_Git\pymodaq_plugins_folder\pymodaq_plugins_mockexamples\src\pymodaq_plugins_mockexamples\resources') + here = Path(__file__).parent.joinpath('resources') addhoc_file_path = here.joinpath('photons.h5') h5temp = H5SaverLowLevel() diff --git a/src/pymodaq_plugins_mockexamples/hardware/pinem_analysis.py b/src/pymodaq_plugins_mockexamples/hardware/pinem_analysis.py deleted file mode 100644 index 3130d0e..0000000 --- a/src/pymodaq_plugins_mockexamples/hardware/pinem_analysis.py +++ /dev/null @@ -1,101 +0,0 @@ -from tensorflow.keras.models import load_model -from pathlib import Path -import numpy as np - -# TODO : Implement a way for the user to chose what model to load. -# TODO : Implement some utility function that adapts the code the expected shape from the neural network. (For example) - - -class PinemAnalysis : - """ - Class to use the neural network and perform the data analysis in live. - - Args: - model_file : str - Path to the model file (hdf5 format) - - It can be any keras-usable model file. - """ - def __init__(self,model_file) : - self.model = load_model(model_file, compile = False) - print('Loaded model from', model_file) - - # TODO : Implement scaling along with the normalization - - def eval_background(self, data) : - """ - Evaluate the background of the spectrum. - - Args: - data : np.ndarray - Data to evaluate the background it should be a 1D array. - """ - first10 = data[:int(data.shape[0] * 0.1)] - last10 = data[int(data.shape[0] * 0.9):] - return np.concatenate((first10, last10)).mean() - - def normalize(self, data) : - """ - Normalize the data between 0 and 1. - - Args: - data : np.ndarray - Data to normalize it should be a 1D array. - """ - M = np.max(data) - m = np.min(data) - return (data-m)/(M-m) - - def predict(self, data, remove_background = True) : - """ - Predict the shape of the spectrum (i.e. its underlying parameters) using the neural network. - - Args: - data : np.ndarray - Data to predict it should be a 1D array. - """ - if remove_background : - data = data - self.eval_background(data).clip(min=0) - ndata = self.normalize(data) - cdata = correct_center_of_mass(ndata) - cdata = cdata[np.newaxis, :, np.newaxis] - g = self.model.predict(cdata) - return g - -def center_of_mass(data : np.ndarray) : - """ - Compute the center of mass of a collection of 1D array - - Parameters - ---------- - data : 2D array - The data to compute the center of mass with shape (number of points in the spectra,) - """ - coords = np.arange(data.shape[0]) - com = np.sum(data*coords)/np.sum(data) - return com - - -def eval_center_of_mass(data) : - """ - Evaluate the center of mass of a set of spectra. The spectra should have the shape (number of spectra, spectra length). - - Args: - - data (np.ndarray): The set of spectra to evaluate the center of mass of. - """ - cdata = data.copy() - hM = cdata.max()/1.3 - mask = cdata < hM - cdata[mask] = 0 - return center_of_mass(cdata) - -def correct_center_of_mass(data) : - """ - Correct the center of mass of a set of spectra. The spectra should have the shape (number of spectra, spectra length). - - Args: - - data (np.ndarray): The set of spectra to correct the center of mass of. - """ - com = eval_center_of_mass(data) - out = np.roll(data, int(np.round(data.shape[0]/2 - com))) - return out \ No newline at end of file diff --git a/src/pymodaq_plugins_mockexamples/hardware/pinem_simulator.py b/src/pymodaq_plugins_mockexamples/hardware/pinem_simulator.py deleted file mode 100644 index 4967c70..0000000 --- a/src/pymodaq_plugins_mockexamples/hardware/pinem_simulator.py +++ /dev/null @@ -1,376 +0,0 @@ -import numpy as np -from scipy.special import jv, voigt_profile - - -# We use this implementation so that the shape is not always the same at the cost of some accuracy on fwhm -def voigt_fwhm(x, fwhm, seed=0): - """ - Generate a pseudo-voigt profile. Usually, the fwhm is defined by sigma and gamma. - Here we use random values of sigma and gamma that respect the relationship found in https://doi.org/10.1364/JOSA.63.000987, to preserve the desired fwhm. - We use this method to generate pseudo-voigt with a different shape each time. The seed is required to control the randomness of the shape. - - Args: - - x (np.ndarray): The energy values - - fwhm (float): The desired full width at half maximum of the pseudo-voigt profile. The actual fwhm will be slightly different (10% accuracy). - - seed (int): The seed to control the randomness of the shape of the pseudo-voigt profile. If 0, a random seed will be generated. - """ - if seed == 0: - seed = np.random.randint(100000) - np.random.seed(seed) - ratio = 3.0 * np.random.rand(1) - gamma = fwhm / (0.5346 + np.sqrt( - 0.2166 + ratio ** 2)) # inverting the approximation of https://doi.org/10.1364/JOSA.63.000987 - # it leads to roughly 10% error in the fwhm - sigma = gamma * ratio - profile = voigt_profile(x, sigma, gamma) - return profile / np.max(profile) - - -def my_gaussian(x, mu, sig): - """ - Generate a gaussian profile. - - Args: - - x (np.ndarray): The energy values - - mu (float): The mean of the gaussian profile - - sig (float): The standard deviation of the gaussian profile - """ - gauss = np.exp(-np.power(x - mu, 2.0) / (2 * np.power(sig, 2.0))) * ( - 1 / (sig * np.sqrt(2 * np.pi))) - return gauss - - -def g_distrib_temp_averaged(g, g0, ratio): - """ - Function to take into account g-averaging in the calculation of PINEM spectra. - It assumes that the electron pulse very broad compared to the photon pulse in the temporal domain. - A high ratio (above 1.0) will lead to almost no g-averaging, while a low ratio (below 1.0) will lead to a strong g-averaging. - - Args: - - g (np.ndarray): The range of g values. It is assumed to be normalized between 0 and g0. - - g0 (float): The maximum value of g - - ratio (float): The ratio of the temporal width of the electron pulse to the photon pulse (To be checked) - """ - return ((1. / np.sqrt(np.pi) * ratio) * (g / g0) ** (ratio * ratio)) * 1. / ( - g * np.sqrt(np.log(g0 / g))) - - -# TODO : externalise the simulation to another package so that it is more flexible. - -class Pinem: - """ - PINEM spectrum simulation class. Currently the simulation takes into account one interaction only, with g averaging. - - Args: - - x: 1D array of energy values in eV - - kernel (np.ndarray or str): - np.ndarray : 1D array of the zero-loss peak in the same energy units as x and same length as x. The kernel is assumed to be centered at x=0. - str : The type of kernel to use. Currently, only 'Voigt' and 'Gaussian' are supported. - - amplitude: float to scale the intensity of the peaks - - pump: float describing the energy of the subbands in eV - - n_cutoff: int describing the number of subbands to consider - """ - - def __init__(self, x, amplitude, n_cutoff, kernel=None, plasmon=True): - self.x = x - # delta g is used to produce a linear distribution of g values between 0.0001*g0 and 0.9999*g0 - self.delta_g = np.linspace(0.0001, 1.0 - 0.0001, 1000) - # number of possible g values taking into account the g averaging effect - self.num_g = self.delta_g.shape[0] - self.amplitude = amplitude - self.kernel = kernel - self.n_cutoff = n_cutoff - self.scale = self.x[1] - self.x[0] - self.ns, self.fns = self.gen_indices() - self.plasmon = plasmon - - def gen_indices(self): - """ - Generate all the reachable subband indices inside the pre-determined cutoff. - - Returns: - - tns (np.ndarray): The subband indices inside cutoff, both positive and negative - - ns (np.ndarray): expanded indices for all g values. It has shape (2*self.n_cutoff+1, self.num_g) - """ - tns = np.arange(-self.n_cutoff, self.n_cutoff + 1) - ns = tns[:, np.newaxis] * np.ones((self.num_g,)) - return ns, tns - - def gen_kernel_mattrix(self, omega, offset=0.0, fwhm=0.3, seed=0): - """ - Generate a matrix of 'kernels' (e.g. gaussians), each 'kernel' being shifted by a different amount of energy (i.e. omega). - There is one 'kernel' for each allowed subband index. - - Args: - - omega (float): The energy of the laser pulse in eV - - offset (float): The offset of the energy scale in eV (To simulate energy shift during the experiment) - - fwhm (float): The full width at half maximum of the kernel (gaussian or pseudo-voigt). Ignore for other kernel types. - - seed (int): The seed to control the randomness of the shape of the pseudo-voigt profile. If 0, a random seed will be generated. Not applicable to other kernel types. - - Returns: - - t (np.ndarray): The matrix of shifted 'kernels' (e.g. gaussians) for each subband index - - mask_mus (np.ndarray): A mask to keep only the indices that are within the energy range of the x values - """ - # Scaling to get the indices that are in the range of the x values (while taking into account the offset) - fmus = self.fns * omega / self.scale + offset / self.scale - mus = np.round(fmus).astype(int) - # Masking out the indices that are outside the range of the x values - mask_mus = np.abs(mus) < self.x.shape[0] / 2 - len_mask = np.sum(mask_mus) - # Build or set the kernel - if self.kernel == 'Voigt': - kernel1D = voigt_fwhm(self.x, fwhm, seed=seed) - elif self.kernel == 'Gaussian': - kernel1D = my_gaussian(self.x, 0, fwhm / 2.355) - else: - kernel1D = np.roll(self.kernel) - # Generata a matrix of kernels. One kernel for each subband index that is in the range of the x values (i.e. kept in by the mask) - kernels = np.repeat(kernel1D[:, np.newaxis], len_mask, axis=1) - # Shift the kernels to the right position thus building the matrix of shifted kernels - t = np.array([np.roll(kernels[:, i], mus[mask_mus][i]) for i in range(len(mus[mask_mus]))]) - return t, mask_mus - - def calc(self, omega, g, offset=0.0, fwhm=0.3, rt=0.7, seed=0): - """ - Calculate the complex valued PINEM spectrum. - - Args: - - omega (float): The energy of the laser pulse in eV - - g (float): The g factor - - offset (float): The offset of the energy scale in eV (To simulate energy shift during the experiment) - - fwhm (float): The full width at half maximum of the kernel (gaussian or pseudo-voigt) - - rt (float): The ratio of the temporal width of the electron pulse to the photon pulse (To be checked) - - seed (int): The seed to control the randomness of the shape of the pseudo-voigt profile. If 0, a random seed will be generated. Not applicable to other kernel types. - - Returns: - - wave (np.ndarray): The complex valued PINEM spectrum - """ - # TODO : Check the calculation speed. It seems a bit slow. - - # Generate the g distribution, i.e. g values between 0.0001*g and 0.9999*g - vg1 = np.tile((g * self.delta_g)[:, np.newaxis], self.ns.shape[0]).T - g_dist = g_distrib_temp_averaged(vg1, g, rt) - # Check if the g values are out of bounds - assert np.isnan(g_dist).sum() == 0, 'Il y a un souci vg1 max {}, vg1 min {}, g {}'.format( - np.max(vg1), np.min(vg1), g) - # Generate the shifted kernels and the accessible subband indices - kern_mat, mask_n = self.gen_kernel_mattrix(omega, offset=offset, fwhm=fwhm, seed=seed) - # Generation of all the weights for each subband and g value - j = jv(self.ns, 2 * vg1) - # Summing the contribution of all the g values for each accessible subband - js = np.sum((g_dist * j ** 2)[mask_n, :], axis=1)[:, np.newaxis] - # Apply the weights to the shifted kernels and sum the contributions of all the accessible subbands to create the PINEM spectrum - wave = np.sum(kern_mat * js, axis=0) - - self.kernel_matrix = kern_mat - - return wave - - def calc_sq_modulus(self, omega, g, offset=0.0, fwhm=0.3, rt=0.7, seed=0): - """ - Function to be called to get real PINEM spectra. It returns the squared modulus of the complex PINEM simulation. - It is first normalized to the range [0,1] and then scaled to the desired amplitude. We do it that way for a better control of the intensity of the peaks. - - Args: - - omega (float): The energy of the laser pulse in eV - - g (float): The g factor - - offset (float): The offset of the energy scale in eV (To simulate energy shift during the experiment) - - fwhm (float): The full width at half maximum of the kernel (gaussian or pseudo-voigt) - - rt (float): The ratio of the temporal width of the electron pulse to the photon pulse (To be checked) - - seed (int): The seed to control the randomness of the shape of the pseudo-voigt profile. If 0, a random seed will be generated. Not applicable to other kernel types. - """ - wave = self.calc(omega, g, offset=offset, fwhm=fwhm, rt=rt, seed=seed) - mod_wave = np.real(wave) ** 2 + np.imag(wave) ** 2 - Mwave, mwave = np.max(mod_wave), np.min(mod_wave) - nwave = (mod_wave - mwave) / (Mwave - mwave) - fwave = self.amplitude * nwave - if self.plasmon: - fwave += self.add_plasmon(self.amplitude, fwhm) - return fwave - - def add_plasmon(self, amplitude, fwhm): - """ - Add a plasmon peak to the PINEM spectrum. The plasmon peak is a gaussian profile. - - Args: - - center (float): The energy of the plasmon peak in eV - - sigma (float): The standard deviation of the gaussian profile - - amplitude (float): The amplitude of the plasmon peak - """ - peak_num = np.random.randint(1, 4) - plasmon = np.zeros(self.x.shape[0]) - for i in range(peak_num): - center = np.random.uniform(1.5, 15.0) - sigma = fwhm * 2 * np.random.uniform(1.0, 5.0) - amp = amplitude * np.random.uniform(0.01, 0.2) - plasmon += amp * my_gaussian(self.x, center, sigma) - - return plasmon - - -class PinemGenerator(): - """ - Object managing the PINEM interfacing for live modifications of parameters in the GUI. - - Args : - - n (int): The number of energy values in the x axis - - scale (float): The scale of the x axis - - kernel (np.ndarray or str): - np.ndarray : 1D array of the zero-loss peak in the same energy units as x and same length as x. The kernel is assumed to be centered at x=0. - str : The type of kernel to use. Currently, only 'Voigt' and 'Gaussian' are supported. - - n_cutoff (int): The number of subbands to consider - - amplitude (int): The amplitude of the PINEM spectra - """ - - def __init__( - self, - n: int, - scale: float, - kernel, - n_cutoff: int = 20, - k_cutoff: int = 100, - amplitude: int = 50 - ): - self.n = n - self.pinem: Pinem = None - self._rt = None - self.x = np.linspace(-n / 2 * scale, n / 2 * scale, n) - self.kernel = kernel - self.n_cutoff = n_cutoff - self._amplitude = amplitude - - self._omg = 1.5 - self._g = None - self._fwhm = 0.7 - self._offset = None - self.scale = scale - self._noise = False - self._background = 0.1 - self._remove_background = True - self._counter = 0 - self._angles = np.linspace(0, 2 * np.pi, 360) - - - ## Getters and setters ## - - # We use properties to control the values of the parameters from the GUI - @property - def scale(self): - return self._scale - - @scale.setter - def scale(self, value): - self._scale = value - # Changing the scale requires a change in the x values of the PINEM object - self.x = np.linspace(-self.n / 2 * self._scale, self.n / 2 * self._scale, self.n) - self.pinem = Pinem(x=self.x, amplitude=self._amplitude, kernel=self.kernel, - n_cutoff=self.n_cutoff) - - @property - def rt(self): - if self._rt is None: - return 0.7 - else: - return self._rt - - @rt.setter - def rt(self, value): - self._rt = value - - @property - def amplitude(self): - return self._amplitude - - @amplitude.setter - def amplitude(self, value): - self._amplitude = value - #  It is the same as for the scale setter - self.pinem = Pinem(x=self.x, amplitude=self._amplitude, kernel=self.kernel, - n_cutoff=self.n_cutoff) - - @property - def omg(self): - return self._omg - - @omg.setter - def omg(self, value): - self._omg = value - - @property - def g(self): - #  Currently, the g value is randomly generated if not set - self._counter += 1 - if self._g is None: - return 3 * (np.sin(self._angles[self._counter % 360]) ** 2) - else: - return self._g - - @g.setter - def g(self, value): - self._g = value - - @property - def offset(self): - # Currently, the offset is randomly generated if not set - if self._offset is None: - return np.random.uniform(-0.5, 0.5) - else: - return self._offset - - @offset.setter - def offset(self, value): - self._offset = value - - @property - def fwhm(self): - return self._fwhm - - @fwhm.setter - def fwhm(self, value): - self._fwhm = value - - @property - def noise(self): - return self._noise - - @noise.setter - def noise(self, value): - self._noise = value - - @property - def background(self): - return self._background - - @background.setter - def background(self, value): - self._background = value - - @property - def remove_background(self): - return self._remove_background - - @remove_background.setter - def remove_background(self, value): - self._remove_background = value - - ## Data generation ## - - def gen_data(self): - spectre = self.pinem.calc_sq_modulus(omega=self.omg, g=self.g, offset=self.offset, - fwhm=self.fwhm, rt=self.rt) - # The spectra are already scaled to [0, self.amplitude] in the PINEM class - # Then we add the background and the noise if required. The background is expected to be expressed in percentage of the amplitude. - if not (self.noise): - noiseless_spectre = spectre + self.amplitude * self.background - return noiseless_spectre - else: - noisy_spectre = np.random.poisson(spectre + self.background * self.amplitude) - return noisy_spectre - - def get_value(self, prop: str): - if hasattr(self, prop): - return getattr(self, prop) - - def set_value(self, prop: str, value: float): - if hasattr(self, prop): - setattr(self, prop, value) \ No newline at end of file