Skip to content
18 changes: 18 additions & 0 deletions src/instruments/thorlabs/thorlabsapt.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())


# CLASSES #####################################################################

# pylint: disable=too-many-lines
Expand Down Expand Up @@ -375,6 +376,15 @@ class PiezoChannel(APTPiezoDevice.PiezoDeviceChannel):
on the Thorlabs APT controller.
"""

def __init__(self, apt, idx_chan):
"""Initialize speicaltiez with Piezo channels."""
super().__init__(apt, idx_chan)
self._idx_chan_initial = idx_chan
if apt._is_multi:
self._idx_chan = 2**idx_chan
else:
self._idx_chan = idx_chan + 1

# PROPERTIES #

@property
Expand Down Expand Up @@ -547,6 +557,10 @@ def enabled_single(self, newval):
"controller is a {}.".format(self._apt.model_number)
)

if newval:
self._idx_chan = self._idx_chan_initial + 1 # single mode
else:
self._idx_chan = self._idx_chan_initial**2 # multi mode
param = self._idx_chan if newval else 0x00
pkt = _packets.ThorLabsPacket(
message_id=_cmds.ThorLabsCommands.PZMOT_SET_PARAMS,
Expand Down Expand Up @@ -868,6 +882,7 @@ def move_jog_stop(self):

self._apt.sendpacket(pkt)

_is_multi = True # activated until user changes it
_channel_type = PiezoChannel

# PROPERTIES #
Expand Down Expand Up @@ -943,10 +958,13 @@ def enabled_multi(self, mode):

if mode == 0:
param = 0x00
self._is_multi = False
elif mode == 1:
param = 0x05
self._is_multi = True
elif mode == 2:
param = 0x06
self._is_multi = True
else:
raise ValueError(
"Please select a valid mode: 0 - all "
Expand Down
134 changes: 101 additions & 33 deletions tests/test_thorlabs/test_thorlabs_apt.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
(3, "Unknown type: 3"),
)

channels = [0, 1, 2, 3] # Possible channels for multi-channel instrument


@pytest.mark.parametrize("hw_type", hw_types_setup)
def test_apt_hw_info(hw_type):
Expand Down Expand Up @@ -324,19 +326,21 @@ def init_kpz001():
# CHANNELS #


def test_apt_pia_channel_drive_op_parameters(init_kim101):
@pytest.mark.parametrize("ch", channels)
def test_apt_pia_channel_drive_op_parameters(init_kim101, ch):
"""Test the drive op parameters for the APT Piezo Inertia Actuator.

Tested with KIM101 driver connected to PIM1 mirror mount.
"""
ch_send = 2**ch
with expected_protocol(
ik.thorlabs.APTPiezoInertiaActuator,
[
init_kim101[0],
ThorLabsPacket( # receive a package
message_id=ThorLabsCommands.PZMOT_REQ_PARAMS,
param1=0x07,
param2=0x01,
param2=ch_send,
dest=0x50,
source=0x01,
data=None,
Expand All @@ -347,7 +351,7 @@ def test_apt_pia_channel_drive_op_parameters(init_kim101):
param2=None,
dest=0x50,
source=0x01,
data=struct.pack("<HHHll", 0x07, 0x01, 100, 1000, 10000),
data=struct.pack("<HHHll", 0x07, ch_send, 100, 1000, 10000),
).pack(),
],
[
Expand All @@ -356,17 +360,17 @@ def test_apt_pia_channel_drive_op_parameters(init_kim101):
message_id=ThorLabsCommands.PZMOT_GET_PARAMS,
dest=0x01,
source=0x50,
data=struct.pack("<HHHll", 0x07, 0x01, 90, 500, 5000),
data=struct.pack("<HHHll", 0x07, ch_send, 90, 500, 5000),
).pack(),
],
sep="",
) as apt:
assert apt.channel[0].drive_op_parameters == [
assert apt.channel[ch].drive_op_parameters == [
u.Quantity(90, u.V),
u.Quantity(500, 1 / u.s),
u.Quantity(5000, 1 / u.s**2),
]
apt.channel[0].drive_op_parameters = [
apt.channel[ch].drive_op_parameters = [
u.Quantity(100, u.V),
u.Quantity(1000, 1 / u.s),
u.Quantity(10000, 1 / u.s**2),
Expand Down Expand Up @@ -449,19 +453,21 @@ def test_apt_pia_channel_enabled_single_wrong_controller(init_tim101):
apt.channel[0].enabled_single = True


def test_apt_pia_channel_jog_parameters(init_kim101):
@pytest.mark.parametrize("ch", channels)
def test_apt_pia_channel_jog_parameters(init_kim101, ch):
"""Test the jog parameters for the APT Piezo Inertia Actuator.

Tested with KIM101 driver connected to PIM1 mirror mount.
"""
ch_send = 2**ch
with expected_protocol(
ik.thorlabs.APTPiezoInertiaActuator,
[
init_kim101[0],
ThorLabsPacket( # receive a package
message_id=ThorLabsCommands.PZMOT_REQ_PARAMS,
param1=0x2D,
param2=0x01,
param2=ch_send,
dest=0x50,
source=0x01,
data=None,
Expand All @@ -472,7 +478,7 @@ def test_apt_pia_channel_jog_parameters(init_kim101):
param2=None,
dest=0x50,
source=0x01,
data=struct.pack("<HHHllll", 0x2D, 0x01, 2, 100, 100, 1000, 10000),
data=struct.pack("<HHHllll", 0x2D, ch_send, 2, 100, 100, 1000, 10000),
).pack(),
],
[
Expand All @@ -481,19 +487,19 @@ def test_apt_pia_channel_jog_parameters(init_kim101):
message_id=ThorLabsCommands.PZMOT_GET_PARAMS,
dest=0x01,
source=0x50,
data=struct.pack("<HHHllll", 0x2D, 0x01, 1, 500, 1000, 400, 5000),
data=struct.pack("<HHHllll", 0x2D, ch_send, 1, 500, 1000, 400, 5000),
).pack(),
],
sep="",
) as apt:
assert apt.channel[0].jog_parameters == [
assert apt.channel[ch].jog_parameters == [
1,
500,
1000,
u.Quantity(400, 1 / u.s),
u.Quantity(5000, 1 / u.s**2),
]
apt.channel[0].jog_parameters = [
apt.channel[ch].jog_parameters = [
2,
100,
100,
Expand All @@ -503,21 +509,21 @@ def test_apt_pia_channel_jog_parameters(init_kim101):

# invalid setter
with pytest.raises(TypeError):
apt.channel[0].jog_parameters = 3.14
apt.channel[ch].jog_parameters = 3.14
# list out of range
with pytest.raises(ValueError):
apt.channel[0].jog_parameters = [42, 42]
apt.channel[ch].jog_parameters = [42, 42]
# invalid parameters
with pytest.raises(ValueError):
apt.channel[0].jog_parameters = [0, 100, 100, 1000, 10000]
apt.channel[ch].jog_parameters = [0, 100, 100, 1000, 10000]
with pytest.raises(ValueError):
apt.channel[0].jog_parameters = [2, 0, 100, 1000, 10000]
apt.channel[ch].jog_parameters = [2, 0, 100, 1000, 10000]
with pytest.raises(ValueError):
apt.channel[0].jog_parameters = [2, 100, 2500, 1000, 10000]
apt.channel[ch].jog_parameters = [2, 100, 2500, 1000, 10000]
with pytest.raises(ValueError):
apt.channel[0].jog_parameters = [2, 100, 100, 2500, 10000]
apt.channel[ch].jog_parameters = [2, 100, 100, 2500, 10000]
with pytest.raises(ValueError):
apt.channel[0].jog_parameters = [2, 100, 100, 1000, 100001]
apt.channel[ch].jog_parameters = [2, 100, 100, 1000, 100001]


def test_apt_pia_channel_jog_parameters_invalid_controller(init_tim101):
Expand All @@ -531,19 +537,21 @@ def test_apt_pia_channel_jog_parameters_invalid_controller(init_tim101):
apt.channel[0].jog_parameters = [2, 100, 100, 1000, 1000]


def test_apt_pia_channel_position_count(init_kim101):
@pytest.mark.parametrize("ch", channels)
def test_apt_pia_channel_position_count(init_kim101, ch):
"""Get / Set position count for APT Piezo Inertia Actuator.

Tested with KIM101 driver connected to PIM1 mirror mount.
"""
ch_send = 2**ch
with expected_protocol(
ik.thorlabs.APTPiezoInertiaActuator,
[
init_kim101[0],
ThorLabsPacket( # receive a package
message_id=ThorLabsCommands.PZMOT_REQ_PARAMS,
param1=0x05,
param2=0x01,
param2=ch_send,
dest=0x50,
source=0x01,
data=None,
Expand All @@ -554,7 +562,7 @@ def test_apt_pia_channel_position_count(init_kim101):
param2=None,
dest=0x50,
source=0x01,
data=struct.pack("<HHll", 0x05, 0x03, 100, 0x00),
data=struct.pack("<HHll", 0x05, ch_send, 100, 0x00),
).pack(),
],
[
Expand All @@ -563,59 +571,119 @@ def test_apt_pia_channel_position_count(init_kim101):
message_id=ThorLabsCommands.PZMOT_GET_PARAMS,
dest=0x01,
source=0x50,
data=struct.pack("<HHll", 0x05, 0x01, 0, 0),
data=struct.pack("<HHll", 0x05, ch_send, 0, 0),
).pack(),
],
sep="",
) as apt:
assert apt.channel[0].position_count == 0
apt.channel[2].position_count = 100
assert apt.channel[ch].position_count == 0
apt.channel[ch].position_count = 100


def test_apt_pia_channel_move_abs(init_kim101):
@pytest.mark.parametrize("ch", channels)
def test_apt_pia_channel_move_abs(init_kim101, ch):
"""Absolute movement of APT Piezo Inertia Actuator.

Tested with KIM101 driver connected to PIM1 mirror mount.
"""
ch_send = 2**ch
with expected_protocol(
ik.thorlabs.APTPiezoInertiaActuator,
[
init_kim101[0],
ThorLabsPacket(
message_id=ThorLabsCommands.PZMOT_MOVE_ABSOLUTE,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack("<Hl", ch_send, 100),
).pack(),
],
[init_kim101[1]],
sep="",
) as apt:
apt.channel[ch].move_abs(100)


@pytest.mark.parametrize("ch", channels)
def test_apt_pia_channel_move_abs_single_mode(init_kim101, ch):
"""Absolute movement of APT Piezo Inertia Actuator, first set to single mode.

Tested with KIM101 driver connected to PIM1 mirror mount.
First send single mode, test correct channel address, then switch back to
multi mode and test again.
"""
ch_send_single = ch + 1
ch_send_multi = ch**2
with expected_protocol(
ik.thorlabs.APTPiezoInertiaActuator,
[
init_kim101[0],
ThorLabsPacket(
message_id=ThorLabsCommands.PZMOT_SET_PARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack("<HH", 0x2B, ch_send_single),
).pack(),
ThorLabsPacket(
message_id=ThorLabsCommands.PZMOT_MOVE_ABSOLUTE,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack("<Hl", ch_send_single, 100),
).pack(),
ThorLabsPacket(
message_id=ThorLabsCommands.PZMOT_SET_PARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack("<HH", 0x2B, 0x00),
).pack(),
ThorLabsPacket(
message_id=ThorLabsCommands.PZMOT_MOVE_ABSOLUTE,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack("<Hl", 0x01, 100),
data=struct.pack("<Hl", ch_send_multi, 100),
).pack(),
],
[init_kim101[1]],
sep="",
) as apt:
apt.channel[0].move_abs(100)
apt.channel[ch].enabled_single = True
apt.channel[ch].move_abs(100)
apt.channel[ch].enabled_single = False
apt.channel[ch].move_abs(100)


def test_apt_pia_channel_move_jog(init_kim101):
@pytest.mark.parametrize("ch", channels)
def test_apt_pia_channel_move_jog(init_kim101, ch):
"""Jog forward and reverse with APT Piezo Inertia Actuator.

Tested with KIM101 driver connected to PIM1 mirror mount.
"""
ch_send = 2**ch
with expected_protocol(
ik.thorlabs.APTPiezoInertiaActuator,
[
init_kim101[0],
ThorLabsPacket( # forward
message_id=ThorLabsCommands.PZMOT_MOVE_JOG,
param1=0x01,
param1=ch_send,
param2=0x01,
dest=0x50,
source=0x01,
data=None,
).pack(),
ThorLabsPacket( # reverse
message_id=ThorLabsCommands.PZMOT_MOVE_JOG,
param1=0x01,
param1=ch_send,
param2=0x02,
dest=0x50,
source=0x01,
Expand All @@ -625,8 +693,8 @@ def test_apt_pia_channel_move_jog(init_kim101):
[init_kim101[1]],
sep="",
) as apt:
apt.channel[0].move_jog()
apt.channel[0].move_jog("rev")
apt.channel[ch].move_jog()
apt.channel[ch].move_jog("rev")


def test_apt_pia_channel_move_jog_stop(init_kim101):
Expand Down