microscope.abc module

Abstract Base Classes for the different device types.

class microscope.abc.Camera(**kwargs)[source]

Bases: microscope.abc.DataDevice

Adds functionality to DataDevice to support cameras.

Defines the interface for cameras. Applies a transform to acquired data in the processing step.

ALLOWED_TRANSFORMS = [(False, False, False), (False, False, True), (False, True, False), (False, True, True), (True, False, False), (True, False, True), (True, True, False), (True, True, True)]
get_binning()[source]

Return a tuple of (horizontal, vertical) corrected for transform.

get_cycle_time()[source]

Return the cycle time, in seconds.

get_exposure_time()[source]

Return the current exposure time, in seconds.

get_meta_data()[source]

Return metadata.

get_roi()[source]

Return ROI as a rectangle (left, top, width, height).

Chosen this rectangle format as it completely defines the ROI without reference to the sensor geometry.

get_sensor_shape()[source]

Return a tuple of (width, height), corrected for transform.

get_sensor_temperature()[source]

Return the sensor temperature.

get_transform()[source]

Return the current transform without readout transform.

get_trigger_type()[source]

Return the current trigger mode.

One of

TRIGGER_AFTER, TRIGGER_BEFORE or TRIGGER_DURATION (bulb exposure.)

set_binning(binning)[source]

Set binning along both axes. Return True if successful.

abstract set_exposure_time(value)[source]

Set the exposure time on the device.

Parameters

value – exposure time in seconds

set_readout_mode(description)[source]

Set the readout mode and _readout_transform.

set_roi(roi)[source]

Set the ROI according to the provided rectangle. ROI is a tuple (left, right, width, height) Return True if ROI set correctly, False otherwise.

set_transform(transform)[source]

Combine provided transform with readout transform.

soft_trigger()[source]

Optional software trigger - implement if available.

class microscope.abc.Controller(index=None)[source]

Bases: microscope.abc.Device

Device that controls multiple devices.

Controller devices usually control multiple stage devices, typically a XY and Z stage, a filterwheel, and a light source. Controller devices also include multi light source engines.

Each of the controlled devices requires a name. The choice of name and its documentation is left to the concrete class.

Initialising and shutting down a controller device must initialise and shutdown the controlled devices. Concrete classes should be careful to prevent that the shutdown of a controlled device does not shutdown the controller and the other controlled devices. This might require that controlled devices do nothing as part of their shutdown and initialisation.

abstract property devices

Map of names to the controlled devices.

initialize()None[source]

Initialize the device.

class microscope.abc.DataDevice(buffer_length=0, **kwargs)[source]

Bases: microscope.abc.Device

A data capture device.

This class handles a thread to fetch data from a device and dispatch it to a client. The client is set using set_client(uri) or (legacy) receiveClient(uri).

Derived classed should implement::
  • abort(self) — required

  • _fetch_data(self) — required

  • _process_data(self, data) — optional

Derived classes may override __init__, enable and disable, but must ensure to call this class’s implementations as indicated in the docstrings.

abstract abort()[source]

Stop acquisition as soon as possible.

disable()[source]

Disable the data capture device.

Implement device-specific code in _on_disable .

enable()[source]

Enable the data capture device.

Ensures that a data handling threads are running. Implement device-specific code in _on_enable .

grab_next_data(soft_trigger=True)[source]

Returns results from next trigger via a direct call.

Parameters

soft_trigger – calls soft_trigger if True, waits for hardware trigger if False.

receiveClient(client_uri)[source]

A passthrough for compatibility.

receiveData(data, timestamp)[source]

Unblocks grab_next_frame so it can return.

set_client(new_client)[source]

Set up a connection to our client.

Clients now sit in a stack so that a single device may send different data to multiple clients in a single experiment. The usage is currently:

device.set_client(client) # Add client to top of stack
# do stuff, send triggers, receive data
device.set_client(None)   # Pop top client off stack.

There is a risk that some other client calls None before the current client is finished. Avoiding this will require rework here to identify the caller and remove only that caller from the client stack.

set_setting(*args, **kwargs)

Set a setting.

update_settings(*args, **kwargs)[source]

Update settings based on dict of settings and values.

class microscope.abc.DeformableMirror(**kwargs)[source]

Bases: microscope.abc.TriggerTargetMixin, microscope.abc.Device

Base class for Deformable Mirrors.

There is no method to reset or clear a deformable mirror. While different vendors provide functions to do that, it is unclear exactly what it does the actuators. Does it set all actuators back to something based on a calibration file? Does it apply a voltage of zero to each? Does it set the values to zero and what does that mean since different deformable mirrors expect values in a different range? For the sake of uniformity, it is better for python-microscope users to pass the pattern they want, probably a pattern that flattens the mirror.

It is also unclear what the use case for a reset. If it just to set the mirror to an initial state and not a specific shape, then destroying and re-constructing the DeformableMirror object provides the most obvious solution.

apply_pattern(pattern: numpy.ndarray)None[source]

Apply this pattern.

Raises
initialize()None[source]

Initialize the device.

abstract property n_actuators
next_pattern()None[source]

Apply the next pattern in the queue.

DEPRECATED: this is the same as calling trigger().

queue_patterns(patterns: numpy.ndarray)None[source]

Send values to the mirror.

Parameters
  • patterns (numpy.array) – An KxN elements array of values in the range [0 1], where N equals the number of actuators, and K is the number of patterns.

  • convenience fallback is provided for software triggering is (A) –

  • provided.

trigger()None[source]

Apply the next pattern in the queue.

class microscope.abc.Device(index=None)[source]

Bases: object

A base device class. All devices should subclass this class.

Parameters

index (int) – the index of the device on a shared library. This argument is added by the deviceserver.

add_setting(name, dtype, get_func, set_func, values, readonly=False)[source]

Add a setting definition.

Parameters
  • name – the setting’s name

  • dtype – a data type from (‘int’, ‘float’, ‘bool’, ‘enum’, ‘str’)

  • get_func – a function to get the current value

  • set_func – a function to set the value

  • values – a description of allowed values dependent on dtype, or function that returns a description

  • readonly – an optional flag to indicate a read-only setting.

A client needs some way of knowing a setting name and data type, retrieving the current value and, if settable, a way to retrieve allowable values, and set the value. We store this info in an OrderedDict. I considered having a Setting class with getter, setter, etc., and adding Setting instances as device attributes, but Pyro does not support dot notation to access the functions we need (e.g. Device.some_setting.set ), so I’d have to write access functions, anyway.

describe_setting(name)[source]

Return ordered setting descriptions as a list of dicts.

describe_settings()[source]

Return ordered setting descriptions as a list of dicts.

disable()[source]

Disable the device for a short period for inactivity.

enable()[source]

Enable the device.

get_all_settings()[source]

Return ordered settings as a list of dicts.

get_is_enabled()[source]
get_setting(name)[source]

Return the current value of a setting.

abstract initialize()[source]

Initialize the device.

make_safe()[source]

Put the device into a safe state.

set_setting(name, value)[source]

Set a setting.

shutdown()[source]

Shutdown the device for a prolonged period of inactivity.

update_settings(incoming, init=False)[source]

Update settings based on dict of settings and values.

class microscope.abc.FilterWheel(positions: int, **kwargs)[source]

Bases: microscope.abc.Device

get_num_positions()int[source]
get_position()int[source]
property n_positions

Number of wheel positions.

property position

Number of wheel positions (zero-based).

set_position(position: int)None[source]
class microscope.abc.FloatingDeviceMixin[source]

Bases: object

A mixin for devices that ‘float’.

Some SDKs handling multiple devices do not allow for explicit selection of a specific device: instead, a device must be initialized and then queried to determine its ID. This class is a mixin which identifies a subclass as floating, and enforces the implementation of a ‘get_id’ method.

abstract get_id()[source]

Return a unique hardware identifier, such as a serial number.

class microscope.abc.Laser(**kwargs)[source]

Bases: microscope.abc.Device

abstract get_is_on()[source]

Return True if the laser is currently able to produce light.

get_set_power()float[source]

Return the power set point.

abstract get_status()[source]

Query and return the laser status.

property power

Laser power in the [0, 1] interval.

class microscope.abc.SerialDeviceMixin(**kwargs)[source]

Bases: object

Mixin for devices that are controlled via serial.

Currently handles the flushing and locking of the comms channel until a command has finished, and the passthrough to the serial channel.

TODO: add more logic to handle the code duplication of serial devices.

abstract is_alive()[source]

Query if device is alive and we can send messages.

static lock_comms(func)[source]

Decorator to flush input buffer and lock communications.

There have been problems with the DeepStar lasers returning junk characters after the expected response, so it is advisable to flush the input buffer prior to running a command and subsequent readline. It also locks the comms channel so that a function must finish all its communications before another can run.

class microscope.abc.Stage(index=None)[source]

Bases: microscope.abc.Device

A stage device, composed of StageAxis instances.

A stage device can have any number of axes and dimensions. For a single StageDevice instance each axis has a name that uniquely identifies it. The names of the individual axes are hardware dependent and will be part of the concrete class documentation. They are typically strings such as “x” or “y”.

stage = SomeStageDevice()
stage.initialize()
stage.enable() # may trigger a stage move

# move operations
stage.move_to({'x': 42.0, 'y': -5.1})
stage.move_by({'x': -5.3, 'y': 14.6})

# Individual StageAxis can be controlled directly.
x_axis = stage.axes['x']
y_axis = stage.axes['y']
x_axis.move_to(42.0)
y_axis.move_by(-5.3)

Not all stage devices support simultaneous move of multiple axes. Because of this, there is no guarantee that move operations with multiple axes are done simultaneously. Refer to the concrete class documentation for hardware specific details.

If a move operation involves multiple axes and there is no support for simultaneous move, the order of the moves is undefined. If a specific order is required, one can either call the move functions multiple times in the expected order, or do so via the individual axes, like so:

# Move the x axis first, then mvoe the y axis:
stage.move_by({'x': 10})
stage.move_by({'y': 4})

# The same thing but via the individual axes:
stage.axes['x'].move_by(10)
stage.axes['y'].move_by(4)

Move operations will not attempt to move a stage beyond its limits. If a call to the move functions would require the stage to move beyond its limits the move operation is clipped to the axes limits. No exception is raised.

# Moves x axis to the its upper limit:
x_axis.move_to(x_axis.limits.upper)

# The same as above since the move operations are clipped to
# the axes limits automatically.
import math
x_axis.move_to(math.inf)
x_axis.move_by(math.inf)

Some stages need to find a reference position, home, before being able to be moved. If required, this happens automatically during enable().

abstract property axes

Map of axis names to the corresponding StageAxis.

for name, axis in stage.axes.items():
    print(f'moving axis named {name}')
    axis.move_by(1)

If an axis is not available then it is not included, i.e., given a stage with optional axes the missing axes will not appear on the returned dict with a value of None or some other special StageAxis instance.

property limits

Map of axis name to its upper and lower limits.

for name, limits in stage.limits.items():
    print(f'{name} axis lower limit is {limits.lower}')
    print(f'{name} axis upper limit is {limits.upper}')

These are the limits currently imposed by the device or underlying software and may change over the time of the StageDevice object.

The units of the limits is the same as the ones being currently used for the move operations.

abstract move_by(delta: Mapping[str, float])None[source]

Move axes by the corresponding amounts.

Parameters

delta – map of axis name to the amount to be moved.

# Move 'x' axis by 10.2 units and the y axis by -5 units:
stage.move_by({'x': 10.2, 'y': -5})

# The above is equivalent, but possibly faster than:
stage.axes['x'].move_by(10.2)
stage.axes['y'].move_by(-5)

The axes will not move beyond limits(). If delta would move an axis beyond it limit, no exception is raised. Instead, the stage will move until the axis limit.

abstract move_to(position: Mapping[str, float])None[source]

Move axes to the corresponding positions.

Parameters

position – map of axis name to the positions to move to.

# Move 'x' axis to position 8 and the y axis to position -5.3
stage.move_to({'x': 8, 'y': -5.3})

# The above is equivalent to
stage.axes['x'].move_to(8)
stage.axes['y'].move_to(-5.3)

The axes will not move beyond limits(). If positions is beyond the limits, no exception is raised. Instead, the stage will move until the axes limit.

property position

Map of axis name to their current position.

for name, position in stage.position.items():
    print(f'{name} axis is at position {position}')

The units of the position is the same as the ones being currently used for the absolute move (move_to()) operations.

class microscope.abc.StageAxis[source]

Bases: object

A single dimension axis for a StageDevice.

A StageAxis represents a single axis of a stage and is not a Device instance on itself. Even stages with a single axis, such as Z-axis piezos, are implemented as a StageDevice composed of a single StageAxis instance.

The interface for StageAxis maps to that of StageDevice so refer to its documentation.

abstract property limits

Upper and lower limits values for position.

abstract move_by(delta: float)None[source]

Move axis by given amount.

abstract move_to(pos: float)None[source]

Move axis to specified position.

abstract property position

Current axis position.

class microscope.abc.TriggerTargetMixin[source]

Bases: object

Mixin for a device that may be the target of a hardware trigger.

TODO: need some way to retrieve the supported trigger types and

modes. This is not just two lists, one for types and another for modes, because some modes can only be used with certain types and vice-versa.

abstract set_trigger(ttype: microscope.TriggerType, tmode: microscope.TriggerMode)None[source]

Set device for a specific trigger.

trigger()None[source]

Trigger device.

The actual effect is device type dependent. For example, on a Camera it triggers image acquisition while on a DeformableMirror it applies a queued pattern. See documentation for the devices implementing this interface for details.

Raises

microscope.IncompatibleStateError – if trigger type is not set to TriggerType.SOFTWARE.

abstract property trigger_mode
abstract property trigger_type
microscope.abc.keep_acquiring(func)[source]

Wrapper to preserve acquiring state of data capture devices.