This wiki describes why and how one subclasses the class
dabs.interfaces.LinearStageController. From now on we will refer to this class or an instance of it as (an) "LSC".
An LSC represents a stage controller device in MATLAB. By creating an LSC and interacting with it, you can control a stage controller interactively from the MATLAB command-line, or programmatically in MATLAB scripts/programs.
LSC is an abstract MATLAB class. The word abstract indicates that, by design, the class has some missing elements (abstract methods and properties) that must be filled in in order to make the class complete and usable. To create and use an LSC, one must first create (or already have) a concrete subclass of LSC that fills in those missing blanks.
The reason that LSC is an abstract class is that there are a variety of stage controller devices available. On the one hand, these stage controllers all share some common elements: for example, all have a notion of the stage position. On the other hand, different stage controllers differ in the details: they have different programming interfaces, represent data in different formats, and so on. It would not be possible to create a single class that controls an arbitrary stage controller. By making LSC abstract, it can contain the elements common to all stage controllers, while leaving out device-specific details. Creating a subclass of LSC fills in those details for a particular type of device.
The LSC interface
Broadly speaking, the LSC interface enables the following functionality:
- It has properties for the current stage position (
.positionRelative) and whether the stage is currently moving (
- It performs unit conversion, and can map the stage coordinate system onto a differing external coordinate system.
- It has methods for nonblocking and blocking stage moves (see below).
- It maintains an origin to enable zeroing the position coordinate system (within software).
- It has properties for device metadata, such as
velocity, and so on.
Blocking vs nonblocking moves
The LSC interface provides two types of moves: blocking, and nonblocking. When started, a blocking move blocks MATLAB execution until the move is complete. When a nonblocking move is started, control is returned to MATLAB immediately after the move is initiated, so that MATLAB execution can continue. For nonblocking moves, the LSC interface allows a callback to be supplied, which fires when the move is complete.
How LSC works
LSC properties and methods are roughly split into two groups: those intended for the subclass writer (you), and those intended for the LSC user (whoever or whatever interacts with the class; most often, this will be the ScanImage application).
The properties you should focus on are the abstract properties, many of which have "Raw" in their names:
velocityRaw, etc. You will implement these properties appropriately for your device, within a coordinate and unit system of your choosing.
On the other hand, users of your LSC class will interact with the "non-Raw" properties:
velocity, and so on. These properties provide access to the Raw properties, but with an additional layer of code that performs unit conversion and coordinate transformations. In general, LSC users do not interact with the Raw properties.
The situation with methods is the same. As an LSC class author, you should focus on the abstract methods which have "Hook" in their names, such as
moveStartHook. Meanwhile, users of your class will use the corresponding "non-Hook" methods, such as
moveStartAbsolute. The code for
moveStartAbsolute calls your
moveStartHook method, but has an additional layer of code.
We will assume you have a particular stage controller device you wish to control from MATLAB. To be controlled by MATLAB, the device must have an interface to your computer, such as a serial port interface. You will need the documentation for this interface (the available commands and their responses, etc). Note that your stage will often have manual input/control in addition to control via its computer interface.
Before actually writing any code, you should consider the following questions/decisions about your particular device:
Is the device controlled via serial port?
If so, you may want to subclass from
dabs.interfaces.LSCSerial rather than LinearStageController. LSCSerial is itself a subclass of LSC and provides initialization of the MATLAB serial port, along with an instance of an RS232DeviceBasic object which is a serial port interface with some built-in convenience functionality.
How many physical dimensions does my device control?
The LSC interface currently uses three dimensions (X, Y, and Z) for input and output of position values. However, some devices only control one or two physical dimensions. In this case, your subclass will need to specify a nondefault value of
numDeviceDimensions when calling the LSC constructor. This value specifies the number of dimensions controlled by your device.
Note that the users of your LSC class may set the property
lsc2DeviceDims to specify how the device coordinate system is mapped to the external, three-dimensional coordinate system. This functionality is built in to LSC; you, the subclass writer, do not have to do anything to make this work.
What units of measurement does my device operate in?
In particular, what units of length are used for values of position, velocity, and acceleration (as applicable)?
LSC contains two unit systems. The first set of units,
accelerationDeviceUnits, is intended for you, the class writer. You are free to specify any units you like for these properties, so long as you then provide the "raw" device quantities/properties in those units. For example, you must implement
positionAbsoluteRaw to report its values in
positionDeviceUnits. Typically, you would set this unit system to agree with the unit system used by the device hardware.
The second unit system is intended for the class user, who is interacting with an instance of your class to control a particular stage. This set of units is represented by the properties
accelerationUnits. The user can set these units as is convenient for his or her application. For example, ScanImage works in units of microns; when ScanImage uses LSC objects, it makes sure to have
positionUnits set to be
1e-6, and so on.
Important: Since LSC performs unit conversion when setting/reporting quantities like
velocity, failure to properly specify the device unit system could lead to wild/unpredictable stage moves.
Does my device have two or more "modes"?
Some devices have various move-related modes. For example, the
sutter.MP285 has a "coarse" resolution for faster, less accurate moves, and a "fine" resolution for slower, more accurate moves.
The LSC interface does not include the notion of a mode. However, as the creator of a concrete LSC subclass, you are free to include in your subclass any properties and methods specific to your device. For example, you might include in your class a
moveMode property which takes one of several values. You can then refer to this property when implementing LSC properties which may be mode-dependent, such as
Does my device provide a response when a move completes?
Some devices, when given a move command, complete the move, and then return a response code (eg to the serial port). Others simply complete the move silently and do not provide any signal on completion.
If your device responds when a move is complete, then you should probably set
nonblockingMoveCompletedDetectionStrategy to 'callback'. Otherwise, set it to 'poll'. Specification of this property is necessary for LSC to implement its move interface, which supports both blocking and nonblocking moves (see above).
Implementing Abstract Properties
dabs.sutter.MP285 as an example of a concrete LSC subclass. Since the Sutter MP285 device is controlled by serial port,
MP285 implements LSC abstract properties in a typical way:
- Some properties are known in advance and will not change at run-time:
velocityUnits, for example. These properties are implemented as regular (non-Dependent) properties, and are given default values directly in the properties block. In some cases, it may make sense to allow these properties to be optionally constructor-initialized, eg if their value depends on a firmware version which can vary from device to device. For the MP285, the
positionUnitsfalls into this "set-it-and-forget-it" category.
- Some properties change at runtime, and involve read-only queries to the hardware. An example is
positionAbsoluteRaw. These properties are implemented as Dependent properties, with get-methods that send the appropriate query commands to the hardware.
- Some properties not only change at runtime, but can be set to new values, as well as queried. An example of such a property is
velocity, implemented as a Dependent property with both a set-method and a get-method. The set-method sends the appropriate set-command (and value-to-be-set) to the hardware to set the value on the device.
- In some cases, a property "does not apply" to a device.
accelerationRaw, for example, does not apply to the MP285. To make MP285 a concrete class,
accelerationRawmust be defined somewhere in a properties block; however, this property doesn't do anything (its get-method always returns NaN, and there is no set-method). Your LSC subclass may similarly "omit" inapplicable properties. Note however that ScanImage will expect certain properties to be present and working. See #Minimal LSC Implementation for ScanImage below.
- The MP285 has two "resolutionModes", 'fine' and 'coarse'. Each mode has its own velocity setting, so that the
resolutionModeis queried when setting/getting the
velocityproperty. The details related to
resolutionModeare specific to MP285 and may not apply to your device; however they do illustrate the general pattern of having more than one mode.
- The MP285 is a serial port device, and the
MP285class subclasses from
LSCSerial. To implement its send/receive commands, it uses the
LSCSerialproperty , which holds an
hRS232object. This object adds some convenience functionality to the MATLAB
serialobject. See the documentation for
Implementing Abstract Methods
LSC has only one pure abstract method,
moveStartHook, that must be implemented by a subclass. This method is called when a nonblocking move is initiated. A concrete subclass implements this method by issuing the appropriate command to the hardware device. Note that LSC imposes certain requirements that a subclass must adhere to during a nonblocking move; for example, the
isMoving property must be implemented to properly reflect the state of the move. Another requirement relates to the property
nonblockingMoveCompletedDetectionStrategy. See the documentation for
moveStartHook for more information.
LSC has a number of other methods with
Hook in their names, such as
interruptMoveHook, etc. Concrete subclasses are encouraged to override these methods whenever possible. The default implementation of
interruptMoveHook, for example, simply throws an error indicating that move-interruption is not supported. LSC subclasses representing stages that can be interrupted during moves should implement an override for this method to enable such interruption.
Minimal LSC Implementation for ScanImage
The LSC interface includes more than is absolutely required by ScanImage, because it was designed to be generally useful, independent of any single application.
That said, you are probably reading this page because you are a ScanImage user with a custom stage. At the moment, ScanImage requires the following properties and methods of LSC to be implemented/functional:
getResolutionBestHook(This method has a default implementation in LSC, but if the default method doesn't apply to your device then you should provide an override.)
When your subclass is complete, it is critical to test its operation with real hardware in a "safe" setting before live use. Implementing an LSC subclass involves a fair amount of complexity and some amount of testing/debugging time will likely be necessary to ensure reliable operation. Using an LSC without proper testing could lead to wild stage moves and damage to your rig.