Skip to content

Motion¤

ThorlabsMotion (Interface) ¤

ThorlabsMotion(equipment: Equipment)

              flowchart LR
              msl.equipment_resources.thorlabs.motion.ThorlabsMotion[ThorlabsMotion]
              msl.equipment.schema.Interface[Interface]

                              msl.equipment.schema.Interface --> msl.equipment_resources.thorlabs.motion.ThorlabsMotion
                


              click msl.equipment_resources.thorlabs.motion.ThorlabsMotion href "" "msl.equipment_resources.thorlabs.motion.ThorlabsMotion"
              click msl.equipment.schema.Interface href "" "msl.equipment.schema.Interface"
            

Thorlabs Motion Controller.

Implements the Host-Controller Communication Protocol.

Parameters:

Name Type Description Default
equipment Equipment

An Equipment instance.

required

A Connection instance supports the following properties for the ThorlabsMotion class, as well as the properties defined in FTDI. The default baud rate is 115200 and RTS/CTS flow control is enabled.

Connection Properties:

Name Type Description
init bool

Whether to initialise the motion controller to default parameters. These parameters are specific to each motion controller. If False, the parameters that are persisted in the firmware of the controller are kept. You may change these parameters at runtime, init just changes the initialisation procedure. Default: False

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def __init__(self, equipment: Equipment) -> None:
    """Thorlabs Motion Controller.

    Implements the Host-Controller Communication Protocol.

    Args:
        equipment: An [Equipment][] instance.

    A [Connection][msl.equipment.schema.Connection] instance supports the following
    _properties_ for the `ThorlabsMotion` class, as well as the _properties_ defined
    in [FTDI][msl.equipment.interfaces.ftdi.FTDI]. The default baud rate is 115200
    and RTS/CTS flow control is enabled.

    Attributes: Connection Properties:
        init (bool): Whether to initialise the motion controller to default parameters.
            These parameters are specific to each motion controller. If `False`, the
            parameters that are persisted in the firmware of the controller are kept.
            You may change these parameters at runtime, `init` just changes the
            initialisation procedure. _Default: `False`_
    """
    self._is_connected: bool = False
    super().__init__(equipment)

    # These should be redefined in the subclass (if necessary)
    self._position: Convert = Convert(1)
    self._velocity: Convert = Convert(1)
    self._acceleration: Convert = Convert(1)
    self._is_slot_system: bool = False
    self._has_encoder: bool = False
    # Some controllers (e.g., BSC201 firmware_version='3.1.5') have a firmware crash when
    # MGMSG_HW_START_UPDATEMSGS is written such that the controller becomes unusable after a few seconds
    # of emitting update messages. The BSC20x Series supports MGMSG_MOT_REQ_STATUSUPDATE so use
    # MGMSG_MOT_REQ_STATUSUPDATE instead of MGMSG_HW_START_UPDATEMSGS while waiting for a move to complete
    self._start_update_msgs_while_waiting: bool = True

    assert equipment.connection is not None  # noqa: S101
    equipment.connection.properties.setdefault("baud_rate", 115200)

    try:
        ftdi = FTDI(equipment)
    except MSLConnectionError as e:
        if str(e).endswith(("FT_DEVICE_NOT_FOUND", "not found")):
            raise

        if sys.maxsize > (1 << 32):
            path = r"C:\Program Files\Thorlabs\Kinesis\ftd2xx.dll"
        else:
            path = r"C:\Program Files (x86)\Thorlabs\Kinesis\ftd2xx.dll"

        if not os.path.isfile(path):  # noqa: PTH113
            raise

        os.environ["D2XX_LIBRARY"] = path
        ftdi = FTDI(equipment)

    self._ftdi: FTDI = ftdi
    self._is_connected = True
    self._callback: Callable[[float, int, int], None] | None = None
    self._auto_updates_running: bool = False
    self._init_defaults: bool = equipment.connection.properties.get("init", False)

    # The following mimics what is recommended in the
    # Host-Controller Communications Protocol Issue 44.1
    # Section 2.1: USB Interface

    # === Start mimic ===
    # Pre purge dwell 50ms
    sleep(0.05)

    # Purge the device
    self._ftdi.purge_buffers()

    # Post purge dwell 50ms
    sleep(0.05)

    # Reset device
    self._ftdi.reset_device()

    # Set RTS
    self._ftdi.set_rts(state=equipment.connection.properties.setdefault("rts_cts", True))
    # === End mimic ===

    _ = self.write(0x0018)  # MGMSG_HW_NO_FLASH_PROGRAMMING
    self.stop_auto_updates()

equipment property ¤

equipment: Equipment

The Equipment associated with the interface.

ftdi property ¤

ftdi: FTDI

Returns the underlying interface instance.

timeout property writable ¤

timeout: float | None

The timeout, in seconds, for read and write operations.

A value <0 will set the timeout to be None (blocking mode).

unit class-attribute instance-attribute ¤

unit: str = 'mm'

The physical unit.

disable ¤

disable(channel: int = 1) -> None

Disable a channel.

When disabled, power is removed from the motor and it can be freely moved by hand.

Parameters:

Name Type Description Default
channel int

The channel to disable.

1
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
171
172
173
174
175
176
177
178
179
def disable(self, channel: int = 1) -> None:
    """Disable a channel.

    When disabled, power is removed from the motor and it can be freely moved by hand.

    Args:
        channel: The channel to disable.
    """
    _ = self.write(0x0210, param1=channel, param2=0x02)  # MGMSG_MOD_SET_CHANENABLESTATE

disconnect ¤

disconnect() -> None

Disconnect from the Thorlabs motion controller.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
181
182
183
184
185
186
187
188
189
190
191
192
def disconnect(self) -> None:  # pyright: ignore[reportImplicitOverride]
    """Disconnect from the Thorlabs motion controller."""
    if self._is_connected:
        dest = 0x11 if self._is_slot_system else 0x50
        with contextlib.suppress(MSLConnectionError):
            if self._auto_updates_running:
                self.stop_auto_updates()
            _ = self.write(0x0002, dest=dest)  # MGMSG_HW_DISCONNECT

        self._ftdi.disconnect()
        super().disconnect()
        self._is_connected = False

enable ¤

enable(channel: int = 1) -> None

Enable a channel.

When enabled, power is applied to the motor so it is fixed in position.

Parameters:

Name Type Description Default
channel int

The channel to enable.

1
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
194
195
196
197
198
199
200
201
202
def enable(self, channel: int = 1) -> None:
    """Enable a channel.

    When enabled, power is applied to the motor so it is fixed in position.

    Args:
        channel: The channel to enable.
    """
    _ = self.write(0x0210, param1=channel, param2=0x01)  # MGMSG_MOD_SET_CHANENABLESTATE

encoder ¤

encoder(channel: int = 1) -> int

Get the position of the actuator or stage in encoder counts.

Parameters:

Name Type Description Default
channel int

The channel to get the encoder counts of.

1

Returns:

Type Description
int

Encoder counts (number of pulses).

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
204
205
206
207
208
209
210
211
212
213
214
215
216
def encoder(self, channel: int = 1) -> int:
    """Get the position of the actuator or stage in encoder counts.

    Args:
        channel: The channel to get the encoder counts of.

    Returns:
        Encoder counts (number of pulses).
    """
    # MGMSG_MOT_REQ_ENCCOUNTER or MGMSG_MOT_REQ_POSCOUNTER
    msg_id = 0x040A if self._has_encoder else 0x0411
    _, counts = unpack("<Hi", self.query(msg_id, param1=channel))
    return counts

get_backlash ¤

get_backlash(channel: int = 1) -> float

Get the backlash value (to control hysteresis).

Parameters:

Name Type Description Default
channel int

The channel to get the backlash of.

1

Returns:

Type Description
float

The backlash value (in millimetres or degrees).

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
223
224
225
226
227
228
229
230
231
232
233
234
def get_backlash(self, channel: int = 1) -> float:
    """Get the backlash value (to control hysteresis).

    Args:
        channel: The channel to get the backlash of.

    Returns:
        The backlash value (in millimetres or degrees).
    """
    data = self.query(0x043B, param1=channel)  # MGMSG_MOT_REQ_GENMOVEPARAMS
    _, encoder = unpack("<Hi", data)
    return self._position.to_mm_or_degree(encoder)

get_home_parameters ¤

get_home_parameters(channel: int = 1) -> ThorlabsHomeParameters

Get the parameters that are used to home the actuator or stage.

Parameters:

Name Type Description Default
channel int

The channel to get the home parameters of.

1

Returns:

Type Description
ThorlabsHomeParameters

The homing parameters.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
def get_home_parameters(self, channel: int = 1) -> ThorlabsHomeParameters:
    """Get the parameters that are used to home the actuator or stage.

    Args:
        channel: The channel to get the home parameters of.

    Returns:
        The homing parameters.
    """
    reply = self.query(0x0441, param1=channel)  # MGMSG_MOT_REQ_HOMEPARAMS
    ch, direction, limit_switch, velocity, offset = unpack("<HHHii", reply)
    return ThorlabsHomeParameters(
        channel=ch,
        direction="forward" if direction == 1 else "reverse",
        limit_switch="forward" if limit_switch == 4 else "reverse",  # noqa: PLR2004
        velocity=self._velocity.to_mm_or_degree(velocity),
        offset=self._position.to_mm_or_degree(offset),
    )

get_limit_parameters ¤

get_limit_parameters(channel: int = 1) -> ThorlabsLimitParameters

Get the limit-switch parameters that are used for the actuator or stage.

Parameters:

Name Type Description Default
channel int

The channel to get the limit-switch parameters of.

1

Returns:

Type Description
ThorlabsLimitParameters

The limit-switch parameters.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
def get_limit_parameters(self, channel: int = 1) -> ThorlabsLimitParameters:
    """Get the limit-switch parameters that are used for the actuator or stage.

    Args:
        channel: The channel to get the limit-switch parameters of.

    Returns:
        The limit-switch parameters.
    """
    reply = self.query(0x0424, param1=channel)  # MGMSG_MOT_REQ_LIMSWITCHPARAMS
    ch, cw_hard, ccw_hard, cw_soft, ccw_soft, mode = unpack("<HHHiiH", reply)
    return ThorlabsLimitParameters(
        channel=ch,
        cw_hardware=cw_hard,
        ccw_hardware=ccw_hard,
        cw_software=self._position.to_mm_or_degree(cw_soft),
        ccw_software=self._position.to_mm_or_degree(ccw_soft),
        mode=mode,
    )

get_move_parameters ¤

get_move_parameters(channel: int = 1) -> ThorlabsMoveParameters

Get the parameters that are used to move the actuator or stage.

Parameters:

Name Type Description Default
channel int

The channel to get the move parameters of.

1

Returns:

Type Description
ThorlabsMoveParameters

The move parameters.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
def get_move_parameters(self, channel: int = 1) -> ThorlabsMoveParameters:
    """Get the parameters that are used to move the actuator or stage.

    Args:
        channel: The channel to get the move parameters of.

    Returns:
        The move parameters.
    """
    reply = self.query(0x0414, param1=channel)  # MGMSG_MOT_REQ_VELPARAMS
    ch, minimum, acceleration, maximum = unpack("<Hiii", reply)
    return ThorlabsMoveParameters(
        channel=ch,
        min_velocity=self._velocity.to_mm_or_degree(minimum),
        max_velocity=self._velocity.to_mm_or_degree(maximum),
        acceleration=self._acceleration.to_mm_or_degree(acceleration),
    )

hardware_info ¤

hardware_info() -> ThorlabsHardwareInfo

Get the hardware information about the motion controller.

Returns:

Type Description
ThorlabsHardwareInfo

The hardware information.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
def hardware_info(self) -> ThorlabsHardwareInfo:
    """Get the hardware information about the motion controller.

    Returns:
        The hardware information.
    """
    serial, model, typ, fw, notes, data, hw, state, n = unpack("<I8sH4s48s12sHHH", self.query(0x0005))

    # "notes" sometimes has non \x00 characters at the end, so cannot reliably use rstrip(b"\x00")
    index = notes.find(b"\x00")
    if index > 0:
        notes = notes[:index]

    return ThorlabsHardwareInfo(
        serial=str(serial),
        model=model.rstrip(b"\x00").decode(),
        type=typ,
        firmware_version=f"{fw[2]}.{fw[1]}.{fw[0]}",
        notes=notes.decode(),
        data=data.strip(b"\x00").decode().rstrip(),
        hardware_version=hw,
        modification_state=state,
        num_channels=n,
    )

home ¤

home(*, channel: int = 1, wait: bool = True) -> None

Move the actuator or stage to the home position.

Parameters:

Name Type Description Default
channel int

The channel to home.

1
wait bool

Whether to wait for homing to complete before returning to the calling program.

True
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
318
319
320
321
322
323
324
325
326
327
def home(self, *, channel: int = 1, wait: bool = True) -> None:
    """Move the actuator or stage to the home position.

    Args:
        channel: The channel to home.
        wait: Whether to wait for homing to complete before returning to the calling program.
    """
    _ = self.write(0x0443, param1=channel)  # MGMSG_MOT_MOVE_HOME
    if wait:
        self._wait(channel)

identify ¤

identify(channel: int = 1) -> None

Instruct the motion controller to identify itself by flashing its front panel LEDs.

Parameters:

Name Type Description Default
channel int

The channel to identify.

1
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
329
330
331
332
333
334
335
336
def identify(self, channel: int = 1) -> None:
    """Instruct the motion controller to identify itself by flashing its front panel LEDs.

    Args:
        channel: The channel to identify.
    """
    dest = 0x11 if self._is_slot_system else 0x50
    _ = self.write(0x0223, param1=channel, dest=dest)

is_enabled ¤

is_enabled(channel: int = 1) -> bool

Check if a motor is enabled.

If enabled, power is applied to the motor so it is fixed in position.

Parameters:

Name Type Description Default
channel int

The channel to check.

1

Returns:

Type Description
bool

Whether the motor for the channel is enabled or disabled.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
338
339
340
341
342
343
344
345
346
347
348
349
350
def is_enabled(self, channel: int = 1) -> bool:
    """Check if a motor is enabled.

    If enabled, power is applied to the motor so it is fixed in position.

    Args:
        channel: The channel to check.

    Returns:
        Whether the motor for the channel is enabled or disabled.
    """
    reply = self.query(0x0211, param1=channel)
    return reply[1] == 0x01

is_homed ¤

is_homed(channel: int = 1) -> bool

Check if the actuator or stage has been homed.

Parameters:

Name Type Description Default
channel int

The channel to check.

1

Returns:

Type Description
bool

Whether the actuator or stage has been homed.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
352
353
354
355
356
357
358
359
360
361
def is_homed(self, channel: int = 1) -> bool:
    """Check if the actuator or stage has been homed.

    Args:
        channel: The channel to check.

    Returns:
        Whether the actuator or stage has been homed.
    """
    return bool(self.status(channel) & HOMED)

is_moving ¤

is_moving(channel: int = 1) -> bool

Check if the actuator or stage is moving.

Parameters:

Name Type Description Default
channel int

The channel to check.

1

Returns:

Type Description
bool

Whether the actuator or stage is moving.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
363
364
365
366
367
368
369
370
371
372
def is_moving(self, channel: int = 1) -> bool:
    """Check if the actuator or stage is moving.

    Args:
        channel: The channel to check.

    Returns:
        Whether the actuator or stage is moving.
    """
    return bool(self.status(channel=channel) & MOVING)

move_by ¤

move_by(
    distance: float,
    *,
    channel: int = 1,
    convert: bool = True,
    wait: bool = True
) -> None

Move the actuator or stage by a relative distance.

Parameters:

Name Type Description Default
distance float

The distance to move by. Can be a negative or a positive value. The unit of the value depends on the whether convert is True or False. If True, the unit must be in millimetres (for a translation) or degrees (for a rotation), otherwise the distance must be specified as encoder counts.

required
channel int

The channel to move.

1
convert bool

Whether to convert distance to encoder counts.

True
wait bool

Whether to wait for the move to complete before returning to the calling program.

True
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
def move_by(self, distance: float, *, channel: int = 1, convert: bool = True, wait: bool = True) -> None:
    """Move the actuator or stage by a relative distance.

    Args:
        distance: The distance to move by. Can be a negative or a positive value. The unit of the
            value depends on the whether `convert` is `True` or `False`. If `True`, the unit must
            be in millimetres (for a translation) or degrees (for a rotation), otherwise the
            distance must be specified as encoder counts.
        channel: The channel to move.
        convert: Whether to convert `distance` to encoder counts.
        wait: Whether to wait for the move to complete before returning to the calling program.
    """
    counts = self._position.to_encoder(distance) if convert else int(distance)
    _ = self.write(0x0448, data=pack("<Hi", channel, counts))  # MGMSG_MOT_MOVE_RELATIVE
    if wait:
        self._wait(channel)

move_to ¤

move_to(
    position: float,
    *,
    channel: int = 1,
    convert: bool = True,
    wait: bool = True
) -> None

Move the actuator or stage to an absolute position.

Parameters:

Name Type Description Default
position float

The position to move to. The unit of the value depends on the whether convert is True or False. If True, the unit must be in millimetres (for a translation) or degrees (for a rotation), otherwise the position must be specified as encoder counts.

required
channel int

The channel to move.

1
convert bool

Whether to convert position to encoder counts.

True
wait bool

Whether to wait for the move to complete before returning to the calling program.

True
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
def move_to(self, position: float, *, channel: int = 1, convert: bool = True, wait: bool = True) -> None:
    """Move the actuator or stage to an absolute position.

    Args:
        position: The position to move to. The unit of the value depends on the whether `convert` is
            `True` or `False`. If `True`, the unit must be in millimetres (for a translation) or degrees
            (for a rotation), otherwise the position must be specified as encoder counts.
        channel: The channel to move.
        convert: Whether to convert `position` to encoder counts.
        wait: Whether to wait for the move to complete before returning to the calling program.
    """
    counts = self._position.to_encoder(position) if convert else int(position)
    data = pack("<Hi", channel, counts)
    _ = self.write(0x0453, data=data)  # MGMSG_MOT_MOVE_ABSOLUTE
    if wait:
        self._wait(channel)

position ¤

position(channel: int = 1) -> float

Get the position of the actuator or stage.

Parameters:

Name Type Description Default
channel int

The channel to get the position of.

1

Returns:

Type Description
float

The position (in millimetres or degrees).

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
408
409
410
411
412
413
414
415
416
417
def position(self, channel: int = 1) -> float:
    """Get the position of the actuator or stage.

    Args:
        channel: The channel to get the position of.

    Returns:
        The position (in millimetres or degrees).
    """
    return self._position.to_mm_or_degree(self.encoder(channel=channel))

query ¤

query(
    message_id: int,
    *,
    param1: int = 0,
    param2: int = 0,
    data: bytes | None = None,
    dest: int | None = None,
    delay: float = 0
) -> bytes

Query data from the motion controller.

Parameters:

Name Type Description Default
message_id int

The message ID of the request.

required
param1 int

First parameter required for the query.

0
param2 int

Second parameter required for the query.

0
data bytes | None

The optional data to include with the message. If specified, param1 and param2 are not used.

None
dest int | None

Destination module that the query is for, e.g., 0x50 for a generic controller, 0x11 for a rack controller, motherboard in a card-slot system or a router board, 0x21 for Bay 0 in a card-slot system, 0x22 for Bay 1 in a card-slot system, etc. If not specified, the destination module is automatically determined.

None
delay float

The number of seconds to wait between write and read operations.

0

Returns:

Type Description
bytes

The data of the response.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
def query(
    self,
    message_id: int,
    *,
    param1: int = 0,
    param2: int = 0,
    data: bytes | None = None,
    dest: int | None = None,
    delay: float = 0,
) -> bytes:
    """Query data from the motion controller.

    Args:
        message_id: The message ID of the request.
        param1: First parameter required for the query.
        param2: Second parameter required for the query.
        data: The optional data to include with the message. If specified, `param1` and `param2` are not used.
        dest: Destination module that the query is for, e.g., `0x50` for a generic controller,
            `0x11` for a rack controller, motherboard in a card-slot system or a router board,
            `0x21` for Bay 0 in a card-slot system, `0x22` for Bay 1 in a card-slot system, etc.
            If not specified, the destination module is automatically determined.
        delay: The number of seconds to wait between
            [write][msl.equipment_resources.thorlabs.motion.ThorlabsMotion.write]
            and [read][msl.equipment_resources.thorlabs.motion.ThorlabsMotion.read] operations.

    Returns:
        The data of the response.
    """
    _ = self.write(message_id, param1=param1, param2=param2, data=data, dest=dest)
    if delay > 0:
        sleep(delay)

    # Could get automatic update messages in between requested data
    while True:
        response = self.read()
        if response.message_id == message_id + 1:
            return response.data

        if self._maybe_handle_auto_update(response):
            continue

        msg = f"Expected a response with message ID 0x{message_id + 1:04X}, got {response!r}"
        raise MSLConnectionError(self, msg)

read ¤

read() -> ThorlabsResponse

Read a response from the motion controller.

Returns:

Type Description
ThorlabsResponse

A response instance.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
463
464
465
466
467
468
469
470
471
def read(self) -> ThorlabsResponse:
    """Read a response from the motion controller.

    Returns:
        A response instance.
    """
    msg_id, p1, p2, d, s = unpack("<HBBBB", self._ftdi.read(size=6, decode=False))
    data = self._ftdi.read(size=p1 | (p2 << 8), decode=False) if d & 0x80 else bytes([p1, p2])
    return ThorlabsResponse(message_id=msg_id, module=s, data=data)

set_backlash ¤

set_backlash(
    backlash: float, *, channel: int = 1, convert: bool = True
) -> None

Set the backlash value (to control hysteresis) of the actuator or stage.

Parameters:

Name Type Description Default
backlash float

The backlash value. The unit of the value depends on the whether convert is True or False. If True, the unit must be in millimetres (for a translation) or degrees (for a rotation), otherwise the backlash must be specified as encoder counts.

required
channel int

The channel to set the backlash of.

1
convert bool

Whether to convert backlash to encoder counts.

True
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
473
474
475
476
477
478
479
480
481
482
483
484
def set_backlash(self, backlash: float, *, channel: int = 1, convert: bool = True) -> None:
    """Set the backlash value (to control hysteresis) of the actuator or stage.

    Args:
        backlash: The backlash value. The unit of the value depends on the whether `convert` is
            `True` or `False`. If `True`, the unit must be in millimetres (for a translation) or
            degrees (for a rotation), otherwise the backlash must be specified as encoder counts.
        channel: The channel to set the backlash of.
        convert: Whether to convert `backlash` to encoder counts.
    """
    counts = self._position.to_encoder(backlash) if convert else int(backlash)
    _ = self.write(0x043A, data=pack("<Hi", channel, counts))  # MGMSG_MOT_SET_GENMOVEPARAMS

set_callback ¤

set_callback(callback: Callable[[float, int, int], None] | None) -> None

Set a callback function to receive position, encoder counts and status information.

The callback function is called while waiting for an actuator or a stage to stop moving.

Parameters:

Name Type Description Default
callback Callable[[float, int, int], None] | None

A callback function. Set to None to disable callbacks.

The callback function receives three arguments:

  • Position (in millimetres or degrees)
  • Encoder counts (as an integer)
  • Status of the motion controller. A 32-bit integer that represents the current status of the motion controller. Each of the 32 bits acts as a flag (0 or 1), simultaneously indicating 32 distinct operating conditions of the motion controller.
required
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
def set_callback(self, callback: Callable[[float, int, int], None] | None) -> None:
    """Set a callback function to receive position, encoder counts and status information.

    The callback function is called while waiting for an actuator or a stage to stop moving.

    Args:
        callback: A callback function. Set to `None` to disable callbacks.

            The callback function receives three arguments:

            * Position (in millimetres or degrees)
            * Encoder counts (as an integer)
            * Status of the motion controller. A 32-bit integer that represents the
                current status of the motion controller. Each of the 32 bits acts as
                a flag (0 or 1), simultaneously indicating 32 distinct operating
                conditions of the motion controller.
    """
    self._callback = callback

set_home_parameters ¤

set_home_parameters(parameters: ThorlabsHomeParameters) -> None

Set the parameters that are used to home the actuator or stage.

Parameters:

Name Type Description Default
parameters ThorlabsHomeParameters

Homing parameters. It is recommended to call get_home_parameters first and then update the appropriate attributes.

required
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
def set_home_parameters(self, parameters: ThorlabsHomeParameters) -> None:
    """Set the parameters that are used to home the actuator or stage.

    Args:
        parameters: Homing parameters. It is recommended to call
            [get_home_parameters][msl.equipment_resources.thorlabs.motion.ThorlabsMotion.get_home_parameters]
            first and then update the appropriate attributes.
    """
    direction = 1 if parameters.direction == "forward" else 2
    limit_switch = 4 if parameters.limit_switch == "forward" else 1
    velocity = self._velocity.to_encoder(parameters.velocity)
    offset = self._position.to_encoder(parameters.offset)
    data = pack("<HHHii", parameters.channel, direction, limit_switch, velocity, offset)
    _ = self.write(0x0440, data=data)  # MGMSG_MOT_SET_HOMEPARAMS

set_limit_parameters ¤

set_limit_parameters(parameters: ThorlabsLimitParameters) -> None

Set the limit-switch parameters for the actuator or stage.

Parameters:

Name Type Description Default
parameters ThorlabsLimitParameters

Limit-switch parameters. It is recommended to call get_limit_parameters first and then update the appropriate attributes.

required
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
def set_limit_parameters(self, parameters: ThorlabsLimitParameters) -> None:
    """Set the limit-switch parameters for the actuator or stage.

    Args:
        parameters: Limit-switch parameters. It is recommended to call
            [get_limit_parameters][msl.equipment_resources.thorlabs.motion.ThorlabsMotion.get_limit_parameters]
            first and then update the appropriate attributes.
    """
    cw_soft = self._position.to_encoder(parameters.cw_software)
    ccw_soft = self._position.to_encoder(parameters.ccw_software)
    data = pack(
        "<HHHiiH",
        parameters.channel,
        parameters.cw_hardware,
        parameters.ccw_hardware,
        cw_soft,
        ccw_soft,
        parameters.mode,
    )
    _ = self.write(0x0423, data=data)  # MGMSG_MOT_SET_LIMSWITCHPARAMS

set_move_parameters ¤

set_move_parameters(parameters: ThorlabsMoveParameters) -> None

Set the parameters that are used to move the actuator or stage.

Parameters:

Name Type Description Default
parameters ThorlabsMoveParameters

Move parameters. It is recommended to call get_move_parameters first and then update the appropriate attributes.

required
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
541
542
543
544
545
546
547
548
549
550
551
552
553
def set_move_parameters(self, parameters: ThorlabsMoveParameters) -> None:
    """Set the parameters that are used to move the actuator or stage.

    Args:
        parameters: Move parameters. It is recommended to call
            [get_move_parameters][msl.equipment_resources.thorlabs.motion.ThorlabsMotion.get_move_parameters]
            first and then update the appropriate attributes.
    """
    minimum = self._velocity.to_encoder(parameters.min_velocity)
    maximum = self._velocity.to_encoder(parameters.max_velocity)
    acceleration = self._acceleration.to_encoder(parameters.acceleration)
    data = pack("<Hiii", parameters.channel, minimum, acceleration, maximum)
    _ = self.write(0x0413, data=data)  # MGMSG_MOT_SET_VELPARAMS

start_auto_updates ¤

start_auto_updates() -> None

Start automatic updates from the motion controller.

Update messages contain information about the position and status of the controller. The messages will be sent by the controller approximately every 100 milliseconds until stop_auto_updates is called.

If you want to receive position, encoder counts and status updates from the controller, call set_callback with a function to handle the updates.

Automatic updates are temporarily enabled while waiting for an actuator or a stage to stop moving.

You must periodically call read to handle the automatic updates if you explicitly call this method, otherwise the read buffer may overflow.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
def start_auto_updates(self) -> None:
    """Start automatic updates from the motion controller.

    Update messages contain information about the position and status of the controller.
    The messages will be sent by the controller approximately every 100 milliseconds until
    [stop_auto_updates][msl.equipment_resources.thorlabs.motion.ThorlabsMotion.stop_auto_updates] is called.

    If you want to receive position, encoder counts and status updates from the controller, call
    [set_callback][msl.equipment_resources.thorlabs.motion.ThorlabsMotion.set_callback] with a function
    to handle the updates.

    Automatic updates are temporarily enabled while waiting for an actuator or a stage to stop moving.

    You must periodically call [read][msl.equipment_resources.thorlabs.motion.ThorlabsMotion.read] to handle
    the automatic updates if you explicitly call this method, otherwise the read buffer may overflow.
    """
    self._auto_updates_running = True
    _ = self.write(0x0011)  # MGMSG_HW_START_UPDATEMSGS

status ¤

status(channel: int = 1) -> int

Get the status of the motion controller.

Parameters:

Name Type Description Default
channel int

The channel to get the status of.

1

Returns:

Type Description
int

The status. A 32-bit value that represents the current status of the motion controller. Each of the 32 bits acts as a flag (0 or 1), simultaneously indicating 32 distinct operating conditions of the motion controller.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
574
575
576
577
578
579
580
581
582
583
584
585
586
587
def status(self, channel: int = 1) -> int:
    """Get the status of the motion controller.

    Args:
        channel: The channel to get the status of.

    Returns:
        The status. A 32-bit value that represents the current status of the motion controller.
            Each of the 32 bits acts as a flag (0 or 1), simultaneously indicating 32 distinct
            operating conditions of the motion controller.
    """
    status: int
    _, status = unpack("<HI", self.query(0x0429, param1=channel))  # MGMSG_MOT_REQ_STATUSBITS
    return status

stop ¤

stop(*, channel: int = 1, immediate: bool = False) -> None

Stop the actuator or stage from moving.

Parameters:

Name Type Description Default
channel int

The channel of the motion controller to stop.

1
immediate bool

Whether to stop immediately (True) or use a gradual stop (False). Stopping immediately may risk losing track of the position.

False
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
589
590
591
592
593
594
595
596
597
598
599
def stop(self, *, channel: int = 1, immediate: bool = False) -> None:
    """Stop the actuator or stage from moving.

    Args:
        channel: The channel of the motion controller to stop.
        immediate: Whether to stop immediately (`True`) or use a gradual stop (`False`).
            Stopping immediately may risk losing track of the position.
    """
    mode = 0x01 if immediate else 0x02
    _ = self.write(0x0465, param1=channel, param2=mode)  # MGMSG_MOT_MOVE_STOP
    self._wait(channel)

stop_auto_updates ¤

stop_auto_updates() -> None

Stop automatic updates from the motion controller.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
601
602
603
604
def stop_auto_updates(self) -> None:
    """Stop automatic updates from the motion controller."""
    self._auto_updates_running = False
    _ = self.write(0x0012)  # MGMSG_HW_STOP_UPDATEMSGS

wait_until_moved ¤

wait_until_moved(channel: int = 1) -> None

Wait until the motion controller indicates that a move is complete.

This method will block forever if the actuator or stage is not moving.

Warning

Some motion controllers indicate that a move is complete but upon reading the position of the actuator (or stage) the returned value indicates that it is still a few encoder counts away from the target position. It has been observed that it could take up to 600 ms for the indicated position to settle at the target position. However, the change in position due to this settling is typically outside of the accuracy and repeatability specifications of the actuator (or stage).

Parameters:

Name Type Description Default
channel int

The channel to wait for.

1
Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
def wait_until_moved(self, channel: int = 1) -> None:
    """Wait until the motion controller indicates that a move is complete.

    This method will block forever if the actuator or stage is not moving.

    !!! warning
        Some motion controllers indicate that a move is complete but upon reading the position
        of the actuator (or stage) the returned value indicates that it is still a few
        _encoder counts_ away from the target position. It has been observed that it could take
        up to 600 ms for the indicated position to settle at the target position. However, the
        change in position due to this settling is typically outside of the accuracy and
        repeatability specifications of the actuator (or stage).

    Args:
        channel: The channel to wait for.
    """
    self._wait(channel)

write ¤

write(
    message_id: int,
    *,
    param1: int = 0,
    param2: int = 0,
    data: bytes | None = None,
    dest: int | None = None
) -> int

Write a message to the motion controller.

Parameters:

Name Type Description Default
message_id int

Message ID.

required
param1 int

First parameter required for the message.

0
param2 int

Second parameter required for the message.

0
data bytes | None

The optional data to include with the message. If specified, param1 and param2 are not used.

None
dest int | None

Destination module that the message is for, e.g., 0x50 for a single-channel controller, 0x11 for a rack controller, motherboard in a card slot system or a router board, 0x21 for Bay 0 in a card slot system, 0x22 for Bay 1 in a card slot system, etc. If not specified, the destination module is automatically determined.

None

Returns:

Type Description
int

The number of bytes written.

Source code in packages/resources/src/msl/equipment_resources/thorlabs/motion.py
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
def write(
    self, message_id: int, *, param1: int = 0, param2: int = 0, data: bytes | None = None, dest: int | None = None
) -> int:
    """Write a message to the motion controller.

    Args:
        message_id: Message ID.
        param1: First parameter required for the message.
        param2: Second parameter required for the message.
        data: The optional data to include with the message. If specified, `param1` and `param2` are not used.
        dest: Destination module that the message is for, e.g., `0x50` for a single-channel controller,
            `0x11` for a rack controller, motherboard in a card slot system or a router board,
            `0x21` for Bay 0 in a card slot system, `0x22` for Bay 1 in a card slot system, etc.
            If not specified, the destination module is automatically determined.

    Returns:
        The number of bytes written.
    """
    if dest is None:
        dest = (0x11 if param1 == 0 else 0x20 + param1) if self._is_slot_system else 0x50

    # `source` is always 0x01 for a message sent from a computer
    if data is None:
        msg = pack("<HBBBB", message_id, param1, param2, dest, 0x01)
    else:
        msg = pack("<HHBB", message_id, len(data), dest | 0x80, 0x01) + data

    return self._ftdi.write(msg)

ThorlabsHardwareInfo (NamedTuple) ¤

Information about Thorlabs motion-controller hardware.

Attributes:

Name Type Description
serial str

Unique 8-digit serial number.

model str

Alphanumeric model number.

type int

Hardware type.

firmware_version str

Firmware version number.

notes str

Device-dependant notes.

data str

Device-dependant data.

hardware_version int

The hardware version number.

modification_state int

The modification state of the hardware.

num_channels int

The number of channels the hardware has.

ThorlabsHomeParameters dataclass ¤

ThorlabsHomeParameters(
    channel: int,
    direction: Literal["forward", "reverse"],
    limit_switch: Literal["forward", "reverse"],
    velocity: float,
    offset: float,
)

Parameters used when homing a Thorlabs motion controller.

Attributes:

Name Type Description
channel int

The channel associated with the homing parameters.

direction Literal['forward', 'reverse']

The direction sense for a move to home.

limit_switch Literal['forward', 'reverse']

The hardware-limit switch associated with the home position.

velocity float

The homing velocity, in millimetres/second or degrees/second.

offset float

The distance (in millimetres or degrees) of the home position from the Home Limit Switch.

ThorlabsLimitParameters dataclass ¤

ThorlabsLimitParameters(
    channel: int,
    cw_hardware: int,
    ccw_hardware: int,
    cw_software: float,
    ccw_software: float,
    mode: int,
)

Limit-switch parameters.

Attributes:

Name Type Description
channel int

The channel associated with the parameters.

cw_hardware int

The operation of the clockwise hardware limit switch when contact is made.

ccw_hardware int

The operation of the counter-clockwise hardware limit switch when contact is made.

cw_software float

Clockwise software limit (in millimetres or degrees).

ccw_software float

Counter-clockwise software limit (in millimetres or degrees).

mode int

Software limit-switch mode.

ThorlabsMoveParameters dataclass ¤

ThorlabsMoveParameters(
    channel: int, min_velocity: float, max_velocity: float, acceleration: float
)

Parameters used when moving a Thorlabs motion controller.

Attributes:

Name Type Description
channel int

The channel associated with the move parameters.

min_velocity float

The minimum velocity, in millimetres/second or degrees/second. Currently not used, must be 0.

max_velocity float

The maximum velocity, in millimetres/second or degrees/second.

acceleration float

The acceleration, in millimetres/second/second or degrees/second/second.

ThorlabsResponse (NamedTuple) ¤

A response from a Thorlabs motion controller.

Attributes:

Name Type Description
message_id int

The message ID of the response.

module int

The module in the motion controller that sent the response.

data bytes

The response data.