12.3. openwfs.devices

class Axis(channel, v_min, v_max, scale, maximum_acceleration, terminal_configuration=TerminalConfiguration.DEFAULT)[source]

Bases: object

Specification of a NIDAQ output channel for controlling a scan axis.

Output voltages are clipped to the safety limit indicated by [v_min, v_max] Note that the actual field of view only covers part of this range (see reference_zoom in ScanningMicroscope.).

channel

The name of the channel, e.g. ‘Dev4/ao0’

v_min

The minimum voltage that can safely be sent to the output.

v_max

The maximum voltage that can safely be sent to the output.

scale

Conversion factor between voltage at the NI-DAQ card and displacement of the focus in the object plane. This may be different for different axes.

maximum_acceleration

The maximum acceleration of this axis in V per s². The output signal will be constructed to ensure that the mirror does not exceed this acceleration, except in special cases such as when turning on the system. This acceleration is also used to compute how far to overshoot the scan mirror to ensure that it reaches a linear speed over the scan range. See scan for details.

terminal_configuration

The terminal configuration of the channel, defaults to TerminalConfiguration.DEFAULT

channel: str
static compute_acceleration(*, optical_deflection, torque_constant, rotor_inertia, maximum_current)[source]

Computes the angular acceleration of the focus of the galvo mirror.

The result is returned in the unit V / second², where the voltage can be converted to displacement using the scale factor.

Parameters:
  • optical_deflection (Quantity[u.deg/u.V]) –

    The optical deflection (i.e. twice the mechanical angle) of the mirror

    as a function of applied voltage.

  • torque_constant (Quantity[u.N*u.m/u.A]) – The torque constant of the galvo mirror driving coil. May also be given in the equivalent unit of dyne·cm/A.

  • rotor_inertia (Quantity[u.kg*u.m**2]) – The moment of inertia of the rotor. May also be given in the equivalent unit of g·cm².

  • maximum_current (Quantity[u.A]) – The maximum current that can be applied to the galvo mirror.

static compute_scale(*, optical_deflection, galvo_to_pupil_magnification, objective_magnification, reference_tube_lens)[source]

Computes the conversion factor between voltage and displacement in the object plane.

Parameters:
  • optical_deflection (Quantity[u.deg/u.V]) –

    The optical deflection (i.e. twice the mechanical angle) of the mirror

    as a function of applied voltage.

  • galvo_to_pupil_magnification (float) – The magnification of the relay system between the galvo mirrors and the pupil.

  • objective_magnification (Quantity[u.mm]) – The magnification of the microscope objective.

  • reference_tube_lens (Quantity[u.mm]) – The tube lens focal length on which the objective magnification is based. This value is manufacturer-specific. Typical values are: - 200 mm for Thorlabs, Nikon, Leica, and Mitutoyo - 180 mm for Olympus/Evident - 165 mm for Zeiss

Returns:

Quantity[u.um/u.V] – The conversion factor between voltage and displacement in the object plane.

maximum_acceleration: Annotated[Quantity]
maximum_scan_speed(linear_range)[source]

Computes the maximum scan speed in V per sample

It is assumed that the mirror accelerates and decelerates at the maximum acceleration, and scans with a constant velocity over the linear range. There are two limits to the scan speed:

  • A practical limit: if it takes longer to perform the acceleration + deceleration than it does to traverse the linear range, it does not make sense to set the scan speed so high. The speed at which acceleration + deceleration takes as long as the linear range is the maximum speed.

  • A hardware limit: when accelerating with the maximum acceleration over a distance 0.5 · (V_max-V_min) · (1-linear_range), the mirror will reach the maximum possible speed.

Parameters:

linear_range (float) – fraction of the full range that is used for the linear part of the scan

Returns:

Quantity[u.V / u.s] – maximum scan speed

scale: Annotated[Quantity]
scan(start, stop, sample_count, sample_rate)[source]

Generate a voltage sequence to scan with a constant velocity from start to stop, including acceleration and deceleration.

Before starting this sequence, the mirror is assumed to be standing still at the launch point, which is some distance _before_ start. After the scan sequence, the mirror is stopped at the landing point, which is some distance _after_ stop. The launch point and landing point are returned along with the scan sequence.

This function also returns a slice object, which represents the part of the sequence that corresponds to a linear movement from start to stop. slice.stop - slice.start = sample_count.

The scan follows the coordinate convention used throughout OpenWFS and Astropy, where the coordinates correspond to the centers of the pixels. Therefore, while the linear part of the scan starts at start and ends at stop, the sample points in this range correspond to the centers of the pixels, so the sample at slice.start lies half a pixel _after_ start, and the sample at slice.stop - 1 lies half a pixel _before_ stop.

Returns:

(Quantity[u.V], float, float, slice) – voltage sequence, launch point, landing point, slice object

step(start, stop, sample_rate)[source]

Generate a voltage sequence to move from start to stop in the fastest way possible.

This function assumes that the mirror is standing still at v_start, and generates a voltage ramp to move the mirror and stop it exactly at v_end, using the maximum acceleration allowed by the mirror.

The voltage curve is given by: v_start + 1/2 a·t² for t < t_total /2 v_end - 1/2 a·(t_total-t)² for t >= t_total

using continuity at t=t_total/2, we can solve this equation to get: t_total = 2·sqrt((v_end - v_start) / a)

Returns:

Quantity[u.V] – voltage sequence

terminal_configuration: TerminalConfiguration = -1
to_pos(volt)[source]

Converts voltage [V_min .. V_max] to relative position [0.0 .. 1.0]

to_volt(pos)[source]

Converts relative position [0.0 … 1.0] to voltage [V_min … V_max]

Currently, this is just a linear conversion, but a lookup table may be used in the future.

v_max: Annotated[Quantity]
v_min: Annotated[Quantity]
class Camera(cti_file, serial_number=None, multi_threaded=True, **kwargs)[source]

Bases: Detector

Adapter for GenICam/GenTL cameras.

_nodes

The GenICam node map of the camera. This map can be used to access camera properties, see the GenICam/GenAPI documentation and the Standard Features Naming Convention <https://www.emva.org/wp-content/uploads/GenICam_SFNC_2_3.pdf> for more details.

The node map should not be used to set properties that are available as properties in the Camera object, such as exposure, width`, ``height, binning, etc.

Also, the node map should not be used to set properties while the camera is fetching a frame (i.e., between trigger() and calling result() on the returned concurrent.futures.Future object).

Note

This class is a thin wrapper around the Harvesters module, which is a generic adapter for GenICam/GenTL cameras.

Example

>>> camera = Camera(cti_file=R"C:\Program Files\Basler\pylon 7\Runtime\x64\ProducerU3V.cti")
>>> camera.exposure_time = 10 * u.ms
>>> frame = camera.read()
property binning: int

Pixel binning factor

Note

setting horizontal and vertical binning separately is not supported.

property data_shape

The shape of the data array that read() will return.

For some detectors this property may be mutable, for example, for a camera it represents the height and width of the ROI, which can be changed.

property duration: Annotated[Quantity, Unit('ms')]

The duration between the trigger and the end of the exposure.

Returns ∞ · ms if hardware triggering is used.

static enumerate_cameras(cti_file)[source]
property exposure: Annotated[Quantity, Unit('ms')]

Exposure time of the camera

property height: int

Height of the camera frame, in pixels.

Note: the camera may round up this value to multiples of some power of 2.

property left: int

The horizontal start position of the region of interest (in pixels).

Note: the camera may round up this value to multiples of some power of 2.

paused()[source]

Returns a context manager for pausing the camera.

Usage ::
>>> with camera.paused():
>>>     camera.nodes.SomeNode.value = 10
property pixel_size: Annotated[Quantity, Unit('um')] | None

Physical pixel size of the camera sensor.

property top: int

The vertical start position of the region of interest (in pixels).

Note: the camera may round up this value to multiples of some power of 2.

property width: int

Width of the camera frame, in pixels.

Note: the camera may round up this value to multiples of some power of 2.

class SLM(monitor_id=WINDOWED, shape=None, pos=(0, 0), refresh_rate=None, latency=2, duration=1, coordinate_system='short', transform=None)[source]

Bases: Actuator, PhaseSLM

An OpenGL object to control a spatial light modulator connected to a graphics card.

See Section 7 for more information.

WINDOWED = 0
clone(monitor_id=WINDOWED, shape=None, pos=(0, 0))[source]

Creates a new SLM window that mirrors the content of this SLM window.

This is useful for demonstration and debugging purposes. The image in the clone window is updated automatically when the SLM is updated.

Parameters:
  • monitor_id (int) – ID of the monitor to display the window on. Defaults to WINDOWED (0) mode

  • shape (Optional[tuple[int, int]]) – shape (height, width) of the window.

  • pos (tuple[int, int]) – position (y, x) of the window.

Returns:

Returns an object that should be stored in a variable to keep the SLM window open. When the variable is cleared or leaves the current scope, the SLM window is closed. See slm_demo.py for an example.

property coordinate_system: str

Specifies the base coordinate system that is used to map vertex coordinates to the SLM window.

Possible values are ‘full’, ‘short’ and ‘long’.

‘full’ means that the coordinate range (-1,-1) to (1,1) is mapped to the entire SLM window. If the window is not square, this means that the coordinates are anisotropic.

‘short’ and ‘long’ map the coordinate range (-1,-1) to (1,1) to a square. ‘short’ means that the square is scaled to fill the short side of the SLM (introducing zero-padding at the edges).

‘long’ means that the square is scaled to fill the long side of the SLM (causing part of the coordinate range to be cropped because these coordinates correspond to points outside the SLM window).

For a square SLM, ‘full’, ‘short’ and ‘long’ are all equivalent.

In all three cases, (-1,-1) corresponds to the top-left corner of the screen, and (1,-1) to the bottom-left corner. This convention is consistent with that used in numpy/matplotlib

To further modify the mapping system, use the transform property.

property duration: Annotated[Quantity, Unit('ms')]

maximum amount of time it takes to perform the measurement or for the actuator to stabilize. This value does not include the latency.

For a detector, this is the maximum amount of time that elapses between returning from trigger() and the end of the measurement. For an actuator, this is the maximum amount of time that elapses from returning from a command like update( ) and the stabilization of the device.

If the duration of an operation is not known in advance, (e.g., when waiting for a hardware trigger), this function should return np.inf * u.ms.

Note: A device may update the duration dynamically. For example, a stage may compute the required time to move to the target position and update the duration accordingly.

Note: If latency is a (lower) estimate, the duration should be high enough to guarantee that latency + duration is at least as large as the time between starting the operation and finishing it.

Type:

duration (Quantity[u.ms])

property field: Detector

Returns a ‘camera’ to monitor the current field coming from the SLM.

Returns:

a detector that returns self.amplitude * exp(1.0j * self.phases.read()

property latency: Annotated[Quantity, Unit('ms')]

minimum amount of time between sending a command or trigger to the device and the moment the device starts responding.

The default value is 0.0 ms.

Note

The latency is used to compute when it is safe to switch the global state from ‘moving’ to ‘measuring’ or vice versa. Devices that report a non-zero latency promise not to do anything before the latency has passed. This allows making the state switch even before the devices of the other type have all finished.

This construct is used for spatial light modulators, which typically have a long latency (1-2 frames). Due to this latency, we may send an update command to the SLM even before the camera has finished reading the previous frame.

Note

A device is allowed to report a different latency every time latency is called. For example, for a spatial light modulator that only refreshes at a fixed rate, we can add the remaining time until the next refresh to the latency.

Type:

latency (Quantity[u.ms])

property lookup_table: Sequence[int]

Lookup table that is used to map the wrapped phase range of 0-2pi to gray values

The gray values are represented in the range from 0 to 2**bit_depth - 1). For an 8-bit video mode, this is 0-255. By default, a linear lookup table is set: range(2**bit_depth - 1).

Note: lookup table need not contain 2**bit_depth elements. A typical scenario is to use something like slm.lookup_table=range(142) to map the 0-2pi range to only the first 142 gray values.

property monitor_id: int

Number of the monitor (1 for primary screen, 2 for secondary screen, etc.) for the SLM.

Each monitor can only hold a single full-screen window. Use monitor_id=SLM.WINDOWED to show a windowed SLM on the primary screen (for debugging and monitoring purposes). There can be multiple windowed SLMs on the primary screen, but there cannot also be a fullscreen SLM on the primary screen at the same time.

monitor_id can be modified at run time, in which case the current SLM window is replaced by a new window on a different monitor. When moving the SLM to a different window, width, height and refresh_rate are set

patches: list[Patch]
property period: Annotated[Quantity, Unit('ms')]

The period of the refresh rate in milliseconds (read only).

property phases: Detector

Returns an object to monitor the phase pattern last sent to the SLM.

This is the pattern before applying the lookup table.

property pixels: Detector

Returns a ‘camera’ to monitor the current value of the pixels displayed on the SLM.

property position: tuple[int, int]

The position of the top-left corner of the SLM window as (y, x) screen coordinates.

Note

This property is ignored for full-screen SLMs.

primary_patch
property refresh_rate: Annotated[Quantity, Unit('Hz')]

Refresh rate of the SLM in Hz (read only).

Note

The refresh rate cannot be modified after the SLM is created. When moving the SLM to a different monitor (see monitor_id), the refresh rate is changed to the current video mode on that monitor. Note that OpenGL specifies this value as an integer, whereas some SLMs support non-integer refresh rates. It is always best to not specify the refresh rate and set the video mode in the operating system before creating the SLM object.

set_phases(values, update=True)[source]

Sets the phase pattern on the SLM.

Parameters:
  • values (ArrayLike) – phase pattern, in radians. The pattern is automatically stretched to fill the full SLM.

  • update – when True, calls update after setting the phase pattern. Set to False to suppress the call to update. This is useful in advanced scenarios where multiple parameters of the SLM need to be changed before updating the displayed image.

property shape: tuple[int, int]

Shape (height × width) of the window in pixels.

Limitations :
  • For windowed-mode SLMs, the size cannot be modified.

  • When moving the SLM to a different monitor (see monitor_id), the SLM is sized to match the current

    resolution on that monitor. Note that this value may differ from the value passed as input, because the input value is specified in screen coordinates, whereas the reported width is in pixels. In this case, the original value of shape will be lost.

  • The transform property is not updated automatically, so if the aspect ratio changes

    the transform needs to be set again.

property transform: Transform

Global transformation matrix

The transform determines how the vertex coordinates that make up the shape of a Patch (see Patch) are mapped to the standard coordinate system. In turn, the coordinate_system property determines how this coordinate system is mapped to the SLM window. By default, this value is just the identity transformation Transform().

update()[source]

Sends the new phase pattern to be displayed on the SLM.

This function waits for the vsync, and returns directly after it. Therefore, it can be used as software synchronization to the SLM.

Note

This function does not wait for the image to appear on the SLM. To wait for the image stabilization explicitly, use ‘wait()’. However, this should rarely be needed since all Detectors already call wait_finished before starting a measurement.

Note

At the moment, update() blocks until all OpenGL commands are processed, and a vertical retrace occurs (i.e., the hardware signals the start of a new frame). This behavior may change in the future and should not be relied on. Instead, use the automatic synchronization mechanism to synchronize detectors with the SLM hardware.

class ScanningMicroscope(input, y_axis, x_axis, sample_rate, resolution, reference_zoom, *, delay=<Quantity 0. us>, bidirectional=True, multi_threaded=True, preprocessor=None, test_pattern=TestPatternType.NONE, test_image=None)[source]

Bases: Detector

Laser scanning microscope with galvo mirrors controlled by a National Instruments data acquisition card (nidaq).

Effectively, a ScanningMicroscope works like a camera, which can be triggered and returns 2-D images. These images are obtained by raster-scanning a focus using two galvo mirrors, controlled by a nidaq card, and recording a detector signal (typically from a photon multiplier tube (PMT)) with the same card.

Region of Interest (ROI):

Upon construction, the maximum voltage range is set for both axes. To avoid possible damage to the hardware, this range cannot be exceeded during scanning, and the value cannot be changed after construction of the ScanningMicroscope. It is recommended to set this voltage range slightly larger than the maximum field of view of the microscope (but still within the limits that may cause damage to the hardware), so that the mirror has some room to accelerate and decelerate at the edges of the scan range.

The scan region is defined by the following equation:

V_start = V_min + (center - 0.5 / (reference_zoom * zoom)) * (V_max - V_min) V_stop = V_min + (center + 0.5 / (reference_zoom * zoom)) * (V_max - V_min)

or, in relative coordinates:

start_full = center - 0.5 / (reference_zoom * zoom) stop_full = center + 0.5 / (reference_zoom * zoom)

with * V_min, V_max: The voltage ranges set for both axes: * zoom, reference_zoom: The zoom factors * center: The center of the image relative to the full voltage range, default value is 0.5

The scan region is divided into resolution × resolution pixels. Within this scan region, a smaller region of interest (ROI) can be defined by setting top, left, height, and width properties. The ROI is defined in pixels, with (0,0,resolution, resolution) corresponding to the full field of view.

start = center + (left / resolution - 0.5) / (reference_zoom * zoom) stop = center + ((left + width) / resolution - 0.5) / (reference_zoom * zoom)

Scan pattern:

The scanner performs a raster scan, with y being the slow axis and x the fast axis. The pattern is computed such that the mirrors have a constant velocity during the scan. The start and end of each scan line, where the mirror accelerates or decelerates are discarded from the data. By default, the scanner uses bidirectional scanning along the fast axis, which reduces the time needed for a full scan. Especially for bidirectional scanning, the synchronization between output and input is crucial, otherwise the image will appear teared (even and odd scan lines not aligning). To fine-tune this synchronization, the delay parameter can be used.

property bidirectional: bool

Whether scanning is bidirectional along the fast axis.

property binning: int

Undersampling factor.

Increasing the binning reduces the number of pixels in the image while keeping dwell time the same. As a result, the total duration of a scan decreases.

Note

This behavior is different from that of a real camera. No actual binning is performed, the scanner just takes fewer steps in x and y

Note: the ROI is kept the same as much as possible.

However, due to rounding, it may vary slightly.

close()[source]

Close connection to the NI-DAQ.

property delay: Annotated[Quantity, Unit('us')]

Delay between the control signal to the mirrors and the start of data acquisition.

property duration: Annotated[Quantity, Unit('ms')]

Total duration of scanning for one frame.

property dwell_time: Annotated[Quantity, Unit('us')]

The time spent on each pixel during scanning.

property exposure: Annotated[Quantity, Unit('ms')]

The required time to scan a frame.

property height: int

The number of pixels in the vertical dimension of the ROI.

property left: int

The leftmost pixel of the Region of Interest (ROI) in the scan range.

static list_devices()[source]

Returns a list of all nidaq devices available on the system.

property offset_x: float

The center of the full field of view in the horizontal direction.

The offset is relative to the full voltage range specified in the Axis objects, with 0.0 corresponding to the center of the voltage range, and -0.5 and +0.5 to the edges of the voltage range.

Note that changing the offset may cause the ROI to move outside the original field of view. Also, it may cause the scan speed to change, as the mirror has a shorter distance to accelerate or decelerate.

property offset_y: float

The center of the full field of view in the vertical direction.

The offset is relative to the full voltage range specified in the Axis objects, with 0.0 corresponding to the center of the voltage range, and -0.5 and +0.5 to the edges of the voltage range.

Note that changing the offset may cause the ROI to move outside the original field of view. Also, it may cause the scan speed to change, as the mirror has a shorter distance to accelerate or decelerate.

property pixel_size: Quantity

The size of a pixel in the object plane.

property preprocessor

An optional function to preprocess raw data before cropping.

The function takes a linear array of raw data as required arguments,
and a list of keyword arguments. Currently, the following arguments are passed:
  • sample_rate (Quantity[u.MHz]): the sample rate of the NI-DAQ input channel

reset_roi()[source]

Reset the ROI to span the original left, top, width and height.

property resolution: int
property scan_speed: Annotated[float, Ge(ge=0.05), Le(le=1.0)]

The scan speed relative to the maximum scan speed.

property test_pattern: TestPatternType
property top: int

The topmost pixel of the ROI in the scan range.

property width: int

The number of pixels in the horizontal dimension of the ROI.

Depending on the scan speed and sample rate, the scanner may acquire multiple data points along a scan line, and return the averaged value. A value of 1 is treated as a special case, where the beam does not move horizontally.at all (i.e. it does not scan back and forth over the size of this single pixel).

property zoom: float

Zoom factor. The zoom factor determines the pixel size relative to the original pixel size. When zooming in or out, the center of the region of interest is kept constant. Note that this may cause the field of view to get extended to outside the original FOV

safe_import(module_name, extra_name)[source]

12.3.1. openwfs.devices.slm

class Geometry(vertices, indices)[source]

Bases: object

Class that represents the shape of a Patch object that can be drawn on the screen.

The geometry is defined by a set of vertices and a set of indices that define the order in which the vertices are drawn.

The vertices are stored in a numpy array of shape (N, 4) where N is the number of vertices. Each vertex is represented by an array of four values [x, y, tx, ty]. Here, (x, y) are the coordinates that determine where the vertex is drawn on the screen (after the applying the transform of the SLM that contains this patch).

The vertices and indices define a triangle strip (see OpenGL specification) that is drawn on the screen. A triangle strip is a sequence of connected triangles, where each triangle shares two vertices with the previous triangle. The indices are used to determine the order in which the vertices are connected to form the triangles. To start a new triangle strip, insert the special index 0xFFFF into the index array.

(tx, ty) are the texture coordinates that determine which pixel of the texture (e.g. the array passed to set_phases) is drawn at each vertex. For each triangle, the screen coordinates (x,y) define a triangle on the screen, whereas the texture coordinates (tx, ty) define a triangle in the texture. OpenGL maps the texture triangle onto the screen triangle, using linear interpolation of the coordinates between the vertex points.

The vertex data is uploaded to the GPU when the Geometry object is assigned to the geometry property of a Patch.

static compute_indices_for_grid(shape)[source]

Computes indices for a rectangular grid of vertices.

Assuming the vertices represent the corner points of a rectangular grid, this function computes the indices needed to connect these vertices into triangle strips.

Parameters:

shape (Sequence[int]) – The shape of the grid (nr, nc). nr is the number of grid rows, nc is the number of grid columns. Note that the number of vertices should equal (nr + 1) · (nc+1).

property indices

The indices of the geometry.

property vertices

The vertices of the geometry.

class Patch(slm, geometry=None, vertex_shader=default_vertex_shader, fragment_shader=default_fragment_shader)[source]

Bases: PhaseSLM

property geometry

Vertices that define the shape of the patch on the screen. Currently, this should be a NxMx4 numpy array of float32 values. Each 4 values define a vector: x,y position and tx, ty texture coordinate. See geometry.py for examples of geometry specifications. The vertices are drawn as a NxM ‘rectangular’ grid of quadrilaterals.

set_phases(values, update=True)[source]
Parameters:
  • values (ArrayLike) – 1-D or 2-D array holding phase values to display on the SLM. Phases are in radians, and stored as float32. There is no need to wrap the phase to a 0-2pi range.

  • update (bool) – when True, the SLM in which this patch is contained is updated immediately. When False, the SLM is not updated, and the caller is responsible for calling slm.update() to update the SLM.

update()[source]

Sends the new phase pattern to be displayed on the SLM.

Implementations should call _start() before triggering the SLM.

Note

This function does not wait for the image to appear on the SLM. To wait for the image stabilization explicitly, use ‘wait()’. However, this should rarely be needed since all Detectors already wait for the image to stabilize before starting a measurement.

class SLM(monitor_id=WINDOWED, shape=None, pos=(0, 0), refresh_rate=None, latency=2, duration=1, coordinate_system='short', transform=None)[source]

Bases: Actuator, PhaseSLM

An OpenGL object to control a spatial light modulator connected to a graphics card.

See Section 7 for more information.

WINDOWED = 0
clone(monitor_id=WINDOWED, shape=None, pos=(0, 0))[source]

Creates a new SLM window that mirrors the content of this SLM window.

This is useful for demonstration and debugging purposes. The image in the clone window is updated automatically when the SLM is updated.

Parameters:
  • monitor_id (int) – ID of the monitor to display the window on. Defaults to WINDOWED (0) mode

  • shape (Optional[tuple[int, int]]) – shape (height, width) of the window.

  • pos (tuple[int, int]) – position (y, x) of the window.

Returns:

Returns an object that should be stored in a variable to keep the SLM window open. When the variable is cleared or leaves the current scope, the SLM window is closed. See slm_demo.py for an example.

property coordinate_system: str

Specifies the base coordinate system that is used to map vertex coordinates to the SLM window.

Possible values are ‘full’, ‘short’ and ‘long’.

‘full’ means that the coordinate range (-1,-1) to (1,1) is mapped to the entire SLM window. If the window is not square, this means that the coordinates are anisotropic.

‘short’ and ‘long’ map the coordinate range (-1,-1) to (1,1) to a square. ‘short’ means that the square is scaled to fill the short side of the SLM (introducing zero-padding at the edges).

‘long’ means that the square is scaled to fill the long side of the SLM (causing part of the coordinate range to be cropped because these coordinates correspond to points outside the SLM window).

For a square SLM, ‘full’, ‘short’ and ‘long’ are all equivalent.

In all three cases, (-1,-1) corresponds to the top-left corner of the screen, and (1,-1) to the bottom-left corner. This convention is consistent with that used in numpy/matplotlib

To further modify the mapping system, use the transform property.

property duration: Annotated[Quantity, Unit('ms')]

maximum amount of time it takes to perform the measurement or for the actuator to stabilize. This value does not include the latency.

For a detector, this is the maximum amount of time that elapses between returning from trigger() and the end of the measurement. For an actuator, this is the maximum amount of time that elapses from returning from a command like update( ) and the stabilization of the device.

If the duration of an operation is not known in advance, (e.g., when waiting for a hardware trigger), this function should return np.inf * u.ms.

Note: A device may update the duration dynamically. For example, a stage may compute the required time to move to the target position and update the duration accordingly.

Note: If latency is a (lower) estimate, the duration should be high enough to guarantee that latency + duration is at least as large as the time between starting the operation and finishing it.

Type:

duration (Quantity[u.ms])

property field: Detector

Returns a ‘camera’ to monitor the current field coming from the SLM.

Returns:

a detector that returns self.amplitude * exp(1.0j * self.phases.read()

property latency: Annotated[Quantity, Unit('ms')]

minimum amount of time between sending a command or trigger to the device and the moment the device starts responding.

The default value is 0.0 ms.

Note

The latency is used to compute when it is safe to switch the global state from ‘moving’ to ‘measuring’ or vice versa. Devices that report a non-zero latency promise not to do anything before the latency has passed. This allows making the state switch even before the devices of the other type have all finished.

This construct is used for spatial light modulators, which typically have a long latency (1-2 frames). Due to this latency, we may send an update command to the SLM even before the camera has finished reading the previous frame.

Note

A device is allowed to report a different latency every time latency is called. For example, for a spatial light modulator that only refreshes at a fixed rate, we can add the remaining time until the next refresh to the latency.

Type:

latency (Quantity[u.ms])

property lookup_table: Sequence[int]

Lookup table that is used to map the wrapped phase range of 0-2pi to gray values

The gray values are represented in the range from 0 to 2**bit_depth - 1). For an 8-bit video mode, this is 0-255. By default, a linear lookup table is set: range(2**bit_depth - 1).

Note: lookup table need not contain 2**bit_depth elements. A typical scenario is to use something like slm.lookup_table=range(142) to map the 0-2pi range to only the first 142 gray values.

property monitor_id: int

Number of the monitor (1 for primary screen, 2 for secondary screen, etc.) for the SLM.

Each monitor can only hold a single full-screen window. Use monitor_id=SLM.WINDOWED to show a windowed SLM on the primary screen (for debugging and monitoring purposes). There can be multiple windowed SLMs on the primary screen, but there cannot also be a fullscreen SLM on the primary screen at the same time.

monitor_id can be modified at run time, in which case the current SLM window is replaced by a new window on a different monitor. When moving the SLM to a different window, width, height and refresh_rate are set

patches: list[Patch]
property period: Annotated[Quantity, Unit('ms')]

The period of the refresh rate in milliseconds (read only).

property phases: Detector

Returns an object to monitor the phase pattern last sent to the SLM.

This is the pattern before applying the lookup table.

property pixels: Detector

Returns a ‘camera’ to monitor the current value of the pixels displayed on the SLM.

property position: tuple[int, int]

The position of the top-left corner of the SLM window as (y, x) screen coordinates.

Note

This property is ignored for full-screen SLMs.

primary_patch
property refresh_rate: Annotated[Quantity, Unit('Hz')]

Refresh rate of the SLM in Hz (read only).

Note

The refresh rate cannot be modified after the SLM is created. When moving the SLM to a different monitor (see monitor_id), the refresh rate is changed to the current video mode on that monitor. Note that OpenGL specifies this value as an integer, whereas some SLMs support non-integer refresh rates. It is always best to not specify the refresh rate and set the video mode in the operating system before creating the SLM object.

set_phases(values, update=True)[source]

Sets the phase pattern on the SLM.

Parameters:
  • values (ArrayLike) – phase pattern, in radians. The pattern is automatically stretched to fill the full SLM.

  • update – when True, calls update after setting the phase pattern. Set to False to suppress the call to update. This is useful in advanced scenarios where multiple parameters of the SLM need to be changed before updating the displayed image.

property shape: tuple[int, int]

Shape (height × width) of the window in pixels.

Limitations :
  • For windowed-mode SLMs, the size cannot be modified.

  • When moving the SLM to a different monitor (see monitor_id), the SLM is sized to match the current

    resolution on that monitor. Note that this value may differ from the value passed as input, because the input value is specified in screen coordinates, whereas the reported width is in pixels. In this case, the original value of shape will be lost.

  • The transform property is not updated automatically, so if the aspect ratio changes

    the transform needs to be set again.

property transform: Transform

Global transformation matrix

The transform determines how the vertex coordinates that make up the shape of a Patch (see Patch) are mapped to the standard coordinate system. In turn, the coordinate_system property determines how this coordinate system is mapped to the SLM window. By default, this value is just the identity transformation Transform().

update()[source]

Sends the new phase pattern to be displayed on the SLM.

This function waits for the vsync, and returns directly after it. Therefore, it can be used as software synchronization to the SLM.

Note

This function does not wait for the image to appear on the SLM. To wait for the image stabilization explicitly, use ‘wait()’. However, this should rarely be needed since all Detectors already call wait_finished before starting a measurement.

Note

At the moment, update() blocks until all OpenGL commands are processed, and a vertical retrace occurs (i.e., the hardware signals the start of a new frame). This behavior may change in the future and should not be relied on. Instead, use the automatic synchronization mechanism to synchronize detectors with the SLM hardware.

circular(radii, segments_per_ring, edge_count=256, center=(0, 0))[source]

Creates a circular geometry with the specified extent.

This geometry maps a texture to a disk or a ring. It can be used for feedback-based wavefront shaping to match the segment size to the illumination profile of the SLM. [1]

When used with a linear texture of shape (1, np.sum(segments_per_ring) (see set_phases), this geometry maps the pixels of the texture to concentric rings, starting with the innermost ring, and distributing the phase values counter-clockwise, starting at the positive x-axis. Each ring will hold the specified segments_per_ring.

Notes

When radii[0] == 0 , a disk is created. When radii[0] > 0, the center is left open, creating a ring

Parameters:
  • radii (Sequence[float]) – The radii of the rings in the circular geometry. The first value is the inner radius of the inner ring, the last value is the outer radius of the outer ring.

  • center (CoordinateType) – The center of the circle with respect to the origin, specified in (y, x) coordinates. Default value is (0, 0).

  • segments_per_ring (Sequence[int]) – The number of segments for each ring. The number of segments is one less than the number of rings.

  • edge_count (int) – The number of edge points to approximate the full circle. The more edges, the closer the geometry will approximate a circle. Default value is 256.

[1]: Mastiani, Bahareh, and Ivo M. Vellekoop. “Noise-tolerant wavefront shaping in a Hadamard basis.” Optics express 29.11 (2021): 17534-17541.

rectangle(extent, center=(0, 0))[source]

Creates a rectangle geometry with the specified extent.

Parameters:
  • extent (ExtentType) – The extent (height, width) of the rectangle. Default value is (2, 2). If a single value is specified, the same value is used for both axes.

  • center (CoordinateType) – The center of the rectangle with respect to the origin, specified in (y, x) coordinates. Default value is (0, 0).