Skip to content

st10_controller

frog.hardware.plugins.stepper_motor.st10_controller ¤

Code for interfacing with the ST10-Q-NN stepper motor controller.

Applied Motion have their own bespoke programming language ("Q") for interfacing with their devices, of which we're only using a small portion here.

The specification is available online

https://appliedmotion.s3.amazonaws.com/Host-Command-Reference_920-0002W_0.pdf

Attributes¤

Classes¤

ST10AlarmCode ¤

Bases: IntFlag

The set of possible alarm codes for the ST10 motor controller.

These values are taken from the manual. Note that the alarm code is a bit mask, so several of these may be set at once (if you're especially unlucky!).

Functions¤
__str__() ¤

Convert the set alarm code bits to a string.

Source code in src/frog/hardware/plugins/stepper_motor/st10_controller.py
49
50
51
52
def __str__(self) -> str:
    """Convert the set alarm code bits to a string."""
    error_str = ", ".join(code.name for code in self)  # type: ignore[misc]
    return f"Alarm code 0x{self:04X}: {error_str}"

ST10Controller(port, baudrate=9600, timeout=5.0, nadir_offset=0.0, hot_bb_angle=0.0, cold_bb_angle=0.0) ¤

Bases: SerialDevice, StepperMotorBase

An interface for the ST10-Q-NN stepper motor controller.

This class allows for moving the mirror to arbitrary positions and retrieving its current position.

It is assumed that the optoswitch used for homing is connected to input 6 (rising edge). Limit switches, if present, will be disabled.

Create a new ST10Controller.

Parameters:

Name Type Description Default
port str

Description of USB port (vendor ID + product ID)

required
baudrate int

Baud rate of port

9600
timeout float

Connection timeout

5.0
nadir_offset float

Angle of nadir relative to homing switch (degrees)

0.0
hot_bb_angle float

Angle of hot black body relative to nadir (degrees)

0.0
cold_bb_angle float

Angle of cold black body relative to nadir (degrees)

0.0

Raises:

Type Description
SerialException

Error communicating with device

SerialTimeoutException

Timed out waiting for response from device

ST10ControllerError

Malformed message received from device

Source code in src/frog/hardware/plugins/stepper_motor/st10_controller.py
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
def __init__(
    self,
    port: str,
    baudrate: int = 9600,
    timeout: float = 5.0,
    nadir_offset: float = 0.0,
    hot_bb_angle: float = 0.0,
    cold_bb_angle: float = 0.0,
) -> None:
    """Create a new ST10Controller.

    Args:
        port: Description of USB port (vendor ID + product ID)
        baudrate: Baud rate of port
        timeout: Connection timeout
        nadir_offset: Angle of nadir relative to homing switch (degrees)
        hot_bb_angle: Angle of hot black body relative to nadir (degrees)
        cold_bb_angle: Angle of cold black body relative to nadir (degrees)

    Raises:
        SerialException: Error communicating with device
        SerialTimeoutException: Timed out waiting for response from device
        ST10ControllerError: Malformed message received from device
    """
    SerialDevice.__init__(self, port, baudrate)

    if timeout <= 0.0:
        raise ValueError("Timeout must be greater than zero")

    if abs(nadir_offset) >= 360.0:
        raise ValueError("Nadir offset must be strictly between -360° and 360°")

    self._reader = _SerialReader(self.serial, timeout)
    self._reader.async_read_completed.connect(self._on_initial_move_end)
    self._reader.read_error.connect(self.send_error_message)
    self._reader.start()

    self._init_error_timer = QTimer()
    """A timer to raise an error if the motor takes too long to move."""
    self._init_error_timer.setInterval(round(STEPPER_MOTOR_HOMING_TIMEOUT * 1000))
    self._init_error_timer.setSingleShot(True)
    self._init_error_timer.timeout.connect(
        lambda: self.send_error_message(
            RuntimeError("Timed out waiting for motor to move")
        )
    )

    # Check that we are connecting to an ST10
    self._check_model_id()

    self._disable_limit_switches()

    # Move mirror to home position
    self._home_and_reset(nadir_offset)

    StepperMotorBase.__init__(
        self, hot_bb_angle=hot_bb_angle, cold_bb_angle=cold_bb_angle
    )
Attributes¤
HOME_SWITCH_INPUT = 6 class-attribute instance-attribute ¤

The input number for the home switch.

ST10_MODEL_ID = '024' class-attribute instance-attribute ¤

The model ID for the ST10 controller we are using.

STEPS_PER_ROTATION = 50800 class-attribute instance-attribute ¤

The total number of steps in one full rotation of the mirror.

alarm_code property ¤

Get the current alarm code for the controller, if any.

is_moving property ¤

Whether the motor is moving.

This is done by checking whether the status code has the moving bit set.

status_code property ¤

The status code of the device.

For a complete list of status codes and their meanings, consult the manual.

step property writable ¤

The current state of the device's step counter.

This makes use of the "IP" command, which estimates the immediate position of the motor. If the motor is moving, this is an estimated (calculated trajectory) position. If the motor is stationary, this is the actual position.

Raises:

Type Description
SerialException

Error communicating with device

SerialTimeoutException

Timed out waiting for response from device

ST10ControllerError

Malformed message received from device

steps_per_rotation property ¤

Get the number of steps that correspond to a full rotation.

Functions¤
close() ¤

Close device and leave mirror facing downwards.

This prevents dust accumulating.

Source code in src/frog/hardware/plugins/stepper_motor/st10_controller.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
def close(self) -> None:
    """Close device and leave mirror facing downwards.

    This prevents dust accumulating.
    """
    StepperMotorBase.close(self)

    if not self.serial.is_open:
        return

    # Don't handle move end events because they will likely happen after the device
    # is disconnected
    self._reader.async_read_completed.disconnect()

    try:
        self.move_to("nadir")
    except Exception as e:
        logging.error(f"Failed to reset mirror to downward position: {e}")

    # Set flag that indicates the thread should quit
    self._reader.quit()

    # If _reader is blocking on a read (which is likely), we could end up waiting
    # forever, so close the socket so that the read operation will terminate
    SerialDevice.close(self)
stop_moving() ¤

Immediately stop moving the motor.

Source code in src/frog/hardware/plugins/stepper_motor/st10_controller.py
604
605
606
def stop_moving(self) -> None:
    """Immediately stop moving the motor."""
    self._write_check("ST")

ST10ControllerError ¤

Bases: DeviceError

Indicates that an error has occurred with the ST10 controller.