Skip to content

Commit

Permalink
Merge 20ba6bd into 8894ca0
Browse files Browse the repository at this point in the history
  • Loading branch information
trappitsch committed Mar 24, 2022
2 parents 8894ca0 + 20ba6bd commit 262b2a5
Show file tree
Hide file tree
Showing 2 changed files with 348 additions and 2 deletions.
221 changes: 221 additions & 0 deletions instruments/tests/test_thorlabs/test_thorlabs_apt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,227 @@ def test_apt_mc_backlash_correction_bad_units(init_kdc101):
apt.channel[0].backlash_correction = 10 * u.mm


def test_apt_mc_home_parameters_no_units(init_kdc101):
"""Get / set home_parameters without units or as counts."""
home_direction = 1
limit_switch = 1
velocity = 100
offset = 7000
with expected_protocol(
ik.thorlabs.APTMotorController,
[
init_kdc101[0],
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_SET_HOMEPARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack(
"<HHHll", 0x01, home_direction, limit_switch, velocity, offset
),
).pack(),
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_SET_HOMEPARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack(
"<HHHll", 0x01, home_direction, limit_switch, velocity, offset
),
).pack(),
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_REQ_HOMEPARAMS,
param1=0x01,
param2=0x00,
dest=0x50,
source=0x01,
data=None,
).pack(),
],
[
init_kdc101[1],
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_GET_HOMEPARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack(
"<HHHll", 0x01, home_direction, limit_switch, velocity, offset
),
).pack(),
],
sep="",
) as apt:
apt.channel[0].home_parameters = home_direction, limit_switch, velocity, offset
apt.channel[0].home_parameters = (
home_direction,
limit_switch,
velocity,
offset * u.counts,
)
params_rec = apt.channel[0].home_parameters
assert params_rec[0] == home_direction
assert params_rec[1] == limit_switch
assert params_rec[2] == velocity
unit_eq(params_rec[3], offset * u.count)


def test_apt_mc_home_parameters_set_with_none(init_kdc101):
"""Set home parameters with all `None` sends read back values."""
home_direction = 1
limit_switch = 1
velocity = 1000
offset = 1250
with expected_protocol(
ik.thorlabs.APTMotorController,
[
init_kdc101[0],
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_REQ_HOMEPARAMS,
param1=0x01,
param2=0x00,
dest=0x50,
source=0x01,
data=None,
).pack(),
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_SET_HOMEPARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack(
"<HHHll",
0x01,
home_direction,
limit_switch,
velocity,
offset,
),
).pack(),
],
[
init_kdc101[1],
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_GET_HOMEPARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack(
"<HHHll",
0x01,
home_direction,
limit_switch,
velocity,
offset,
),
).pack(),
],
sep="",
) as apt:
apt.channel[0].home_parameters = None, None, None, None


def test_apt_mc_home_parameters(init_kdc101):
"""Get / set home_parameters in unitful fashion."""
home_direction = 1
limit_switch = 1
velocity = 1 * u.deg / u.s
velocity_enc = 42941
offset = 1 * u.deg
offset_enc = 1919
with expected_protocol(
ik.thorlabs.APTMotorController,
[
init_kdc101[0],
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_SET_HOMEPARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack(
"<HHHll",
0x01,
home_direction,
limit_switch,
velocity_enc,
offset_enc,
),
).pack(),
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_REQ_HOMEPARAMS,
param1=0x01,
param2=0x00,
dest=0x50,
source=0x01,
data=None,
).pack(),
],
[
init_kdc101[1],
ThorLabsPacket(
message_id=ThorLabsCommands.MOT_GET_HOMEPARAMS,
param1=None,
param2=None,
dest=0x50,
source=0x01,
data=struct.pack(
"<HHHll",
0x01,
home_direction,
limit_switch,
velocity_enc,
offset_enc,
),
).pack(),
],
sep="",
) as apt:
apt.channel[0].motor_model = "PRM1-Z8"
apt.channel[0].home_parameters = home_direction, limit_switch, velocity, offset

params_rec = apt.channel[0].home_parameters

assert params_rec[0] == home_direction
assert params_rec[1] == limit_switch
unit_eq(params_rec[2], velocity, abs=0.001)
unit_eq(params_rec[3], offset, abs=0.001)


def test_apt_mc_home_parameters_wrong_values(init_kdc101):
"""Raise a ValueError if wrong number of values are provided."""
with expected_protocol(
ik.thorlabs.APTMotorController,
[init_kdc101[0]],
[init_kdc101[1]],
sep="",
) as apt:
with pytest.raises(ValueError):
apt.channel[0].home_parameters = 1, 1, 1


def test_apt_mc_home_parameters_bad_units(init_kdc101):
"""Raise a ValueError if incompatible units are provided."""
with expected_protocol(
ik.thorlabs.APTMotorController,
[init_kdc101[0]],
[init_kdc101[1]],
sep="",
) as apt:
# velocity
with pytest.raises(ValueError):
apt.channel[0].home_parameters = 1, 1, 1 * u.deg / u.sec, 1919

# offset
with pytest.raises(ValueError):
apt.channel[0].home_parameters = 1, 1, 42000, 1 * u.Hz


def test_apt_mc_position_encoder(init_kdc101):
"""Get unitful position of encoder, in counts."""
with expected_protocol(
Expand Down
129 changes: 127 additions & 2 deletions instruments/thorlabs/thorlabsapt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,8 +1169,8 @@ class MotorChannel(ThorLabsAPT.APTChannel):
# TODO: Z8xx and Z6xx models. Need to add regex support to motor models, too.
"PRM1-Z8": (
u.Quantity(1919.64, "count/deg"),
NotImplemented,
NotImplemented,
u.Quantity(42941.66, u.sec / u.deg),
u.Quantity(14.66, u.sec ** 2 / u.deg),
),
},
}
Expand Down Expand Up @@ -1337,6 +1337,131 @@ def backlash_correction(self, pos):
)
self._apt.sendpacket(pkt)

@property
def home_parameters(self):
"""Get the home parameters for the motor channel.
Parameters are stage specific and not all parameters can be set
for every stage. For example, the MLS203 stage only allows the
homing velocity to be changed.
.. note:: When setting the quantity, pass `None` to values
that you want to leave unchanged (see example below).
.. note:: After changing the offset, the stage must be homed
in order to show the new offset in its values.
:return: Home Direction (1: forward/positive, 2 reverse/negative),
Limit Switch (1: hardware reverse, 4: hardware forward),
Home Velocity,
Offset distance
:rtype: Tuple[int, int, u.Quantity, u.Quantity]
Example:
>>> import instruments as ik
>>> import instruments.units as u
>>> # load the controller, a KDC101 cube
>>> kdc = ik.thorlabs.APTMotorController.open_serial("/dev/ttyUSB0", baud=115200)
>>> # assign a channel to `ch`
>>> ch = kdc.channel[0]
>>> ch.motor_model = 'PRM1-Z8' # select rotation stage
>>> # set offset distance to 4 degrees, leave other values
>>> ch.home_parameters = None, None, None, 4 * u.deg
>>> ch.home_parameters # read it back
(2, 1, <Quantity(9.99, 'degree / second')>, <Quantity(3.99, 'degree')>)
"""
pkt = _packets.ThorLabsPacket(
message_id=_cmds.ThorLabsCommands.MOT_REQ_HOMEPARAMS,
param1=self._idx_chan,
param2=0x00,
dest=self._apt.destination,
source=0x01,
data=None,
)
response = self._apt.querypacket(
pkt,
expect=_cmds.ThorLabsCommands.MOT_GET_HOMEPARAMS,
expect_data_len=14,
)
# chan, home_dir, limit_switch, velocity, ,offset_dist
_, home_dir, lim_sw, vel, offset = struct.unpack("<HHHll", response.data)
return (
int(home_dir),
int(lim_sw),
u.Quantity(vel) / self.scale_factors[1],
u.Quantity(offset, "counts") / self.scale_factors[0],
)

@home_parameters.setter
def home_parameters(self, values):
values = list(values)
if len(values) != 4:
raise ValueError(
"Home parameters muust be set with four values: "
"Home direction, limit switch settings, velocity, and offset. "
"For settings you want to leave untouched, pass `None`."
)

# replace values that are `None`
if None in values:
set_params = self.home_parameters
for it in range(len(values)):
if values[it] is None:
values[it] = set_params[it]

home_dir, lim_sw, velocity, offset = values
# check validity of unitful values
if not isinstance(velocity, u.Quantity):
velocity = int(velocity)
else:
# Ensure velocity is dimensionless
req_units = (1 / self.scale_factors[1]).units
try:
velocity = int(
(velocity.to(req_units) * self.scale_factors[1]).magnitude
)
except:
raise ValueError(
"Provided units for velocity are not compatible "
"with current motor scale factor."
)
if not isinstance(offset, u.Quantity):
offset = int(offset)
else:
if offset.units == u.counts:
offset = int(offset.magnitude)
else:
scaled_vel = offset * self.scale_factors[0]
# Force a unit error.
try:
offset = int(scaled_vel.to(u.counts).magnitude)
except:
raise ValueError(
"Provided units for offset are not compatible "
"with current motor scale factor."
)

# create package to send
pkt = _packets.ThorLabsPacket(
message_id=_cmds.ThorLabsCommands.MOT_SET_HOMEPARAMS,
param1=None,
param2=None,
dest=self._apt.destination,
source=0x01,
data=struct.pack(
"<HHHll",
self._idx_chan,
int(home_dir),
int(lim_sw),
int(velocity),
int(offset),
),
)
self._apt.sendpacket(pkt)

@property
def status_bits(self):
"""
Expand Down

0 comments on commit 262b2a5

Please sign in to comment.