You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I implemented a draft of an abc.SLM in here. Very much like abc.DM
Code below. Any comments or feedback?
classSpatialLightModulator(TriggerTargetMixin, Device, metaclass=abc.ABCMeta):
"""Base class for Spatial Light Modulators (SLM). This class is very similar to the Deformable Mirrors abc. We are trying to keep nomenclature consistent. The main differences are that the shape of the patterns are different and that we need to provide wavelengths along the patterns. Similarly to deformable mirrors, there is no method to reset or clear a deformable mirror. For the sake of uniformity, it is better for python-microscope users to pass the pattern they want, probably a pattern that flattens the SLM. The private properties `_patterns` and `_pattern_idx` are initialized to `None` to support the queueing of patterns and software triggering. """@abc.abstractmethoddef__init__(self, **kwargs) ->None:
super().__init__(**kwargs)
self._patterns: typing.Optional[numpy.ndarray] =Noneself._pattern_idx: int=-1self._wavelengths: typing.Optional[typing.List[int]] =None@abc.abstractmethoddef_get_shape(self) ->typing.Tuple[int, int]:
"""Get the shape of the SLM in pixels as a (width, height) tuple."""raiseNotImplementedErrordefget_shape(self) ->typing.Tuple[int, int]:
"""Return a tuple of `(width, height)` corresponding to the shape of the SLM"""returnself._get_shape()
def_validate_patterns(self, patterns: numpy.ndarray, wavelengths: typing.Union[list[int], int]) ->None:
"""Validate the shape of a series of patterns. Only validates the shape of the patterns, not if the values are actually in the [0 1] range. If some hardware is unable to handle values outside their defined range (most will simply clip them), then it's the responsibility of the subclass to do the clipping before sending the values. """if2>patterns.ndim>3:
raiseValueError(
"PATTERNS has %d dimensions (must be 2 or 3)"%patterns.ndim
)
ifpatterns.ndim==3:
ifnotisinstance(wavelengths, list) orlen(wavelengths) !=patterns.shape[0]:
raiseValueError(
"The length of the wavelengths list %d does not match the number of patterns to load %d"% (len(wavelengths), patterns.shape[0],)
)
elifnotisinstance(wavelengths, int):
raiseValueError(
"The wavelength should be an integer when loading a single pattern"
)
if (patterns.shape[-2], patterns.shape[-1]) !=self.get_shape():
raiseValueError(
"PATTERNS shape %s does not match the SLM's shape %s"% ((patterns.shape[-2], patterns.shape[-1],), self.get_shape(),)
)
@abc.abstractmethoddef_do_apply_pattern(self, pattern: numpy.ndarray, wavelength: int) ->None:
raiseNotImplementedError()
defapply_pattern(self, pattern: numpy.ndarray, wavelength: int) ->None:
"""Apply this pattern. Args: pattern: A 'XY' ndarray with the phases to be loaded into the SLM. The phases have to be in the range [0, 1]. 0=0pi and 1=2pi wavelength: The wavelength to which the SLM has to be calibrated for that pattern Raises: microscope.IncompatibleStateError: if device trigger type is not set to software. """ifself.trigger_typeisnotmicroscope.TriggerType.SOFTWARE:
# An alternative to error is to change the trigger type,# apply the pattern, then restore the trigger type, but# that would clear the queue on the device. It's better# to have the user specifically do it. See issue #61.raisemicroscope.IncompatibleStateError(
"apply_pattern requires software trigger type"
)
self._validate_patterns(pattern, [wavelength])
self._do_apply_pattern(pattern, wavelength)
defqueue_patterns(self, patterns: numpy.ndarray, wavelengths: typing.List[int]) ->None:
"""Send a set of patterns to the SLM. Args: patterns: An `NXY` elements array of phase values in the range [0, 1]. 0=0pi and 1=2pi. N is the number of phases to add the queue wavelengths: A list of wavelengths (in nm) of length N A convenience fallback is provided for software triggering is provided. """self._validate_patterns(patterns, wavelengths)
self._patterns=patternsself._wavelengths=wavelengthsself._pattern_idx=-1# none is applied yet# TODO: What is the function to run the patterns in the queue? enable?def_do_trigger(self) ->None:
"""Convenience fallback. This only provides a convenience fallback for devices that don't support queuing multiple patterns and software trigger, i.e., devices that take only one pattern at a time. This is not the case of most devices. Devices that support queuing patterns, should override this method. .. todo:: Instead of a convenience fallback, we should have a separate mixin for this. """ifself._patternsisNone:
raisemicroscope.DeviceError("no pattern queued to apply")
self._pattern_idx+=1self.apply_pattern(self._patterns[self._pattern_idx, :], self._wavelengths[self._pattern_idx])
deftrigger(self) ->None:
"""Apply the next pattern in the queue."""# This is just a passthrough to the TriggerTargetMixin class# and only exists for the docstring.returnsuper().trigger()
The text was updated successfully, but these errors were encountered:
I implemented a draft of an abc.SLM in here. Very much like abc.DM
Code below. Any comments or feedback?
The text was updated successfully, but these errors were encountered: