microscope.abc module

Abstract Base Classes for the different device types.

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

Bases: microscope.abc.TriggerTargetMixin, 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()microscope.Binning[source]

Return the current binning corrected for transform.

get_cycle_time()float[source]

Return the cycle time in seconds.

get_exposure_time()float[source]

Return the current exposure time in seconds.

get_roi()microscope.ROI[source]

Return current ROI.

get_sensor_shape() → Tuple[int, int][source]

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

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: microscope.Binning)None[source]

Set binning along both axes. Return True if successful.

abstract set_exposure_time(value: float)None[source]

Set the exposure time on the device in seconds.

set_readout_mode(description)[source]

Set the readout mode and _readout_transform.

set_roi(roi: microscope.ROI)None[source]

Set the ROI according to the provided rectangle.

Return True if ROI set correctly, False otherwise.

set_transform(transform)[source]

Combine provided transform with readout transform.

class microscope.abc.Controller[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.

Shutting down a controller device must 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.

abstract property devices

Map of names to the controlled devices.

class microscope.abc.DataDevice(buffer_length: int = 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:

* :meth:`abort` (required)
* :meth:`_fetch_data` (required)
* :meth:`_process_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()None[source]

Stop acquisition as soon as possible.

disable()None[source]

Disable the data capture device.

Implement device-specific code in _do_disable.

enable()None[source]

Enable the data capture device.

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

grab_next_data(soft_trigger: bool = True)[source]

Returns results from next trigger via a direct call.

Parameters

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

receiveClient(client_uri: str)None[source]

A passthrough for compatibility.

receiveData(data, timestamp)None[source]

Unblocks grab_next_frame so it can return.

set_client(new_client)None[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.

The private properties _patterns and _pattern_idx are initialized to None to support the queueing of patterns and software triggering.

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

Apply this pattern.

Raises

microscope.IncompatibleStateError – if device trigger type is not set to software.

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 – 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.

A convenience fallback is provided for software triggering is provided.

trigger()None[source]

Apply the next pattern in the queue.

class microscope.abc.Device[source]

Bases: object

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

add_setting(name, dtype, get_func, set_func, values, readonly: Optional[Callable[], bool]] = None)None[source]

Add a setting definition.

Parameters
  • name – the setting’s name.

  • dtype – a data type from “int”, “float”, “bool”, “enum”, or “str” (see DTYPES).

  • 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 function to indicate if the setting is readonly. A setting may be readonly temporarily, so this function will return True or False to indicate its current state. If set to no None (default), then its value will be dependent on the value of set_func.

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 a dictionary. I considered having a Setting1 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: str)[source]

Return ordered setting descriptions as a list of dicts.

describe_settings()[source]

Return ordered setting descriptions as a list of dicts.

disable()None[source]

Disable the device for a short period for inactivity.

enable()None[source]

Enable the device.

get_all_settings()[source]

Return ordered settings as a list of dicts.

get_is_enabled()bool[source]
get_setting(name: str)[source]

Return the current value of a setting.

initialize()None[source]

Initialize the device.

If devices have this method (not required, and many don’t), then they should call it as part of the initialisation, i.e., they should call it on their __init__ method.

set_setting(name: str, value)None[source]

Set a setting.

shutdown()None[source]

Shutdown the device.

Disable and disconnect the device. This method should be called before destructing the device object, to ensure that the device is actually shutdown.

After shutdown, the device object is no longer usable and calling any other method is undefined behaviour. The only exception shutdown itself which can be called consecutively, and after the first time will have no effect.

A device object that has been shutdown can’t be reinitialised. Instead of reusing the object, a new one should be created instead. This means that shutdown will leave the device in a state that it can be reconnected.

device = SomeDevice()
device.shutdown()

# Multiple calls to shutdown are OK
device.shutdown()
device.shutdown()

# After shutdown, everything else is undefined behaviour.
device.enable()  # undefined behaviour
device.get_setting("speed")  # undefined behaviour

# To reinitialise the device, construct a new instance.
device = SomeDevice()

Note

While __del__ calls shutdown, one should not rely on it. Python does not guarante that __del__ will be called when the interpreter exits so if shutdown is not called explicitely, the devices might not be shutdown.

update_settings(incoming, init: bool = False)[source]

Update settings based on dict of settings and values.

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

Bases: microscope.abc.Device

ABC for filter wheels, cube turrets, and filter sliders.

FilterWheel devices are devices that have specific positions to hold different filters. Implementations will enable the change to any of those positions, including positions that may not hold a filter.

Parameters

positions – total number of filter positions on this device.

get_num_positions()int[source]

Deprecated, use the n_positions property.

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(index: int, **kwargs)[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, when the SDK is initialised it assigns an index to each device. However, this index is only unique until the program ends and next time the program runs the device might be assigned a different index. This means that it is not possible to request a specific device to the SDK. Instead, one connects to one of the available devices, then initialises it, and only then can one check which one we got.

Floating devices are a problem in systems where there are multiple devices of the same type but we only want to initialise a subset of them. Make sure that a device really is a floating device before making use of this class. Avoid it if possible.

This class is a mixin which enforces the implementation of a get_id method, which typically returns the device serial number.

Parameters

index – the index of the device on a shared library. This argument is added by the device_server program.

abstract get_id()str[source]

Return a unique hardware identifier such as a serial number.

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

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

Light source such as lasers or LEDs.

Light sources often, possibly always, only support the TriggerMode.BULB. In this context, the trigger type changes what happens when enable is called. TriggerType.SOFTWARE means that enable will make the device emit light immediately, and disable will make the device stop emit light.

TriggerType.HIGH or TriggerType.LOW means that enable will set and unset the laser such that it only emits light while receiving a high or low TTL, or digital, input signal.

abstract get_is_on()bool[source]

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

get_set_power()float[source]

Return the power set point.

abstract get_status() → List[str][source]

Query and return the light source status.

property power

Light source power in the [0, 1] interval.

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

Bases: object

Mixin for devices that are controlled via serial.

DEPRECATED: turns out that this was a bad idea. A device that has

a serial connection is not a serial connection. The “has a” and the not “is a” should have told us that we should have been using composition instead of subclassing, but there you go.

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

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[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.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.

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.