Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies = [
"pymodaq_utils>=5.3.1",
"pymodaq_gui>=5.3.0",
"pymodaq_data>=5.3.0",
"pymodaq_plugins_utils>=5.3.4",
"slmsuite>=0.5.0",
]

Expand Down
12 changes: 12 additions & 0 deletions src/pymodaq_plugins_slmsuite/hardware/slm_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ def shape(self) -> tuple[int, int]:
"""The shape (height, width) of the SLM in pixels"""
return tuple(int(s) for s in self.slm.shape)

@property
def pitch_um(self) -> tuple[float, float]:
"""The size (x, y) of the pixels in micrometer, as known by slmsuite (the driver when it can tell, else 8 um)"""
pitch = np.broadcast_to(np.asarray(self.slm.pitch_um, dtype=float), (2,))
return float(pitch[0]), float(pitch[1])

@property
def has_internal_calibration(self) -> bool:
"""True if the SLM converts a phase to grey levels with a calibration of its own: a lookup table known by
slmsuite, or the one the Meadowlark SDK loads from the hardware"""
return self.slm_type == 'Meadowlark' or getattr(self.slm, 'lut', None) is not None

@property
def bitresolution(self) -> int:
"""The number of grey levels of the SLM"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from pymodaq_utils.utils import ThreadCommand
from pymodaq_utils.logger import set_logger, get_module_name

from pymodaq_plugins_utils.hardware.shaper import ModulatorType, shaper_params, fill_shaper_settings

from pymodaq_plugins_slmsuite import config
from pymodaq_plugins_slmsuite.hardware.slm_controller import SlmController

Expand Down Expand Up @@ -50,7 +52,7 @@ class DAQ_Move_SlmsuiteBase(DAQ_Move_base):
'tip': 'Add the wavefront correction loaded in slmsuite (if any) to the displayed phases'},
{'title': 'Input as grey levels:', 'name': 'input_as_grey_levels', 'type': 'bool', 'value': False,
'tip': 'Interpret the received arrays as grey levels instead of phases in radians'},
]
] + shaper_params()
params = common_slm_params + comon_parameters_fun(is_multiaxes, _axis_names, epsilon=_epsilons)

def ini_attributes(self):
Expand All @@ -74,6 +76,9 @@ def ini_stage(self, controller=None):
height, width = self.controller.shape
self.settings.child('info', 'width').setValue(width)
self.settings.child('info', 'height').setValue(height)
fill_shaper_settings(self.settings, self.get_slm_type(), width, height, *self.controller.pitch_um,
modulator_type=ModulatorType.PHASE,
has_internal_calibration=self.controller.has_internal_calibration)
self.settings.child('info', 'levels').setValue(self.controller.bitresolution)

self.current_value = DataActuator(self._title, data=[np.zeros(self.controller.shape)])
Expand Down
45 changes: 45 additions & 0 deletions tests/test_slmsuite_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,49 @@ def test_not_a_dict(self):
parse_kwargs('[1, 2]')


def _geometry(plugin):
group = plugin.settings.child('shaper_geometry')
return [group[name] for name in ('width', 'height', 'pixel_size_x', 'pixel_size_y')]


def _properties(plugin):
group = plugin.settings.child('shaper_properties')
return [group[name] for name in ('name', 'has_internal_calibration', 'use_internal_calibration')]


class TestShaperGeometry:
def test_simulated(self, slm):
width, height, size_x, size_y = _geometry(slm)
assert (width, height) == (WIDTH, HEIGHT)
assert size_x > 0 and size_y > 0

def test_pitch_from_slmsuite(self):
controller = SlmController('SimulatedSLM', resolution=(WIDTH, HEIGHT), pitch_um=(5., 6.))
assert controller.pitch_um == (5., 6.)
controller.close()

def test_pitch_given_once(self):
controller = SlmController('SimulatedSLM', resolution=(WIDTH, HEIGHT), pitch_um=7.)
assert controller.pitch_um == (7., 7.)
controller.close()

def test_fake_controller(self, qapp, fake_controller):
plugin = make_actuator(DAQ_Move_SlmsuiteMeadowlark)
assert _geometry(plugin) == [6, 4, 8., 9.]
assert _properties(plugin) == ['Meadowlark', True, True]
plugin.close()

def test_no_internal_calibration_by_default(self, slm):
assert _properties(slm) == ['SimulatedSLM', False, False]

def test_lut_is_an_internal_calibration(self):
controller = SlmController('SimulatedSLM', resolution=(WIDTH, HEIGHT))
assert not controller.has_internal_calibration
controller.slm.lut = np.arange(256)
assert controller.has_internal_calibration
controller.close()


class TestSimulatedActuator:
def test_infos(self, slm):
assert slm.settings['info', 'width'] == WIDTH
Expand Down Expand Up @@ -127,6 +170,8 @@ class FakeController:
def __init__(self, slm_type, **kwargs):
self.slm_type, self.kwargs = slm_type, kwargs
self.shape = (4, 6)
self.pitch_um = (8., 9.)
self.has_internal_calibration = slm_type == 'Meadowlark'
self.bitresolution = 256
FakeController.instances.append(self)

Expand Down
Loading