Adding even more ROM tests

This commit is contained in:
Julian Stirling 2025-10-17 01:29:36 +01:00
parent 664c7de5f8
commit 3d49cbf921
2 changed files with 60 additions and 12 deletions

View file

@ -43,6 +43,7 @@ MEDIUM_STEP = 50
BIG_STEP = 200 BIG_STEP = 200
PARASITIC_MOTION_TOL = 0.1 PARASITIC_MOTION_TOL = 0.1
DETECT_MOTION_TOL = 0.65
class RomDataTracker: class RomDataTracker:
@ -196,7 +197,7 @@ class RangeofMotionThing(lt.Thing):
} }
def _set_stream_resolution(self, cam: CamDep) -> None: def _set_stream_resolution(self, cam: CamDep) -> None:
"""Set the self._stream_reolution attribute by reading camera. """Set the self._stream_resolution attribute by reading camera.
:param cam: The camera dependency. :param cam: The camera dependency.
""" """
@ -288,7 +289,7 @@ class RangeofMotionThing(lt.Thing):
""" """
if self._stream_resolution is None: if self._stream_resolution is None:
raise RuntimeError( raise RuntimeError(
"Stream resolution mut be set before generating movement in coords" "Stream resolution must be set before generating movement in coords"
) )
img_index = 0 if axis == "x" else 1 img_index = 0 if axis == "x" else 1
@ -372,13 +373,11 @@ class RangeofMotionThing(lt.Thing):
) -> bool: ) -> bool:
"""Carry out 3 small moves in a given direction and axis. """Carry out 3 small moves in a given direction and axis.
Each position after the first is correlated with the previous position An image is taken before and after each move to check has moved as far as it
to check the stage has moved as far as it should. If the correlation is should. If the offset (calculated by cross correlation) is less than expected,
less than expected, 3 attempts are made to refocus the image to ensure that 3 attempts are made to refocus the image to ensure that image quality is not
image quality is not causing the correlation to be unsuccessful. If the correlation causing the offset to mistakenly be reported as too low be unsuccessful. If the
is still too low then the edge is found. calculated offset is still too low then the edge is found.
Delta is updated and tracked after each move.
:param axis: The axis which is being measured. This must be 'x' or 'y'. :param axis: The axis which is being measured. This must be 'x' or 'y'.
:param direction: The direction the stage moves. :param direction: The direction the stage moves.
@ -387,7 +386,7 @@ class RangeofMotionThing(lt.Thing):
movement = self._movement_in_img_coords( movement = self._movement_in_img_coords(
fov_perc=SMALL_STEP, axis=axis, direction=direction fov_perc=SMALL_STEP, axis=axis, direction=direction
) )
abs_min_offset = abs(movement[axis] * 0.65) abs_min_offset = abs(movement[axis] * DETECT_MOTION_TOL)
for _loop in range(3): for _loop in range(3):
offset = self._move_and_measure( offset = self._move_and_measure(
@ -472,8 +471,7 @@ class RangeofMotionThing(lt.Thing):
if ``abs_min_offset`` is also set if ``abs_min_offset`` is also set
:param abs_min_offset: The absolute minimum (on-axis) offset, under which the :param abs_min_offset: The absolute minimum (on-axis) offset, under which the
autofocus is repeated. autofocus is repeated.
:return: All required data for the next move. This includes the updated delta :return: The calculated offset from cross correlation.
value and offset.
""" """
# Take image before move # Take image before move
before_img = rom_deps.cam.grab_as_array() before_img = rom_deps.cam.grab_as_array()

View file

@ -219,3 +219,53 @@ def test_move_back_until_motion_detected(rom_thing, mock_rom_deps, mocker):
for i, call_args in enumerate(mock_move_n_meas.call_args_list): for i, call_args in enumerate(mock_move_n_meas.call_args_list):
call_args.kwargs["movement"] = {"x": -(2**i), "y": 0} call_args.kwargs["movement"] = {"x": -(2**i), "y": 0}
call_args.kwargs["perform_autofocus"] = False call_args.kwargs["perform_autofocus"] = False
@pytest.mark.parametrize(
("good_moves", "expected_to_detect_motion", "offset_calls"),
[
([0, 1, 2], True, 3), # First 3 pass all good
([1, 2, 3], True, 4), # First is bad, will refocus, still complete
([2, 3, 4], True, 5), # First 2 are bad, will refocus twice, still complete
([3, 4, 5], True, 6), # First 3 are bad, will refocus 3 times, still complete
([4, 5, 6], False, 4), # First 4 are bad, fails
# Check second and 3rd measurement can fail after first fails 3 times
([3, 5, 7], True, 8),
([3, 6, 9], True, 10),
([3, 7, 11], True, 12),
# But they can't fail 4 times
([3, 8, 9], False, 8),
([3, 7, 12], False, 12),
],
)
def test_stage_still_moves(
good_moves,
expected_to_detect_motion,
offset_calls,
rom_thing,
mock_rom_deps,
mocker,
):
"""Test _stage_still_moves correctly detects stage movement."""
rom_thing._stream_resolution = [800, 600]
min_offset = 800 * stage_measure.SMALL_STEP / 100 * stage_measure.DETECT_MOTION_TOL
def gen_offsets(*_args, **_kwargs):
"""Generate offset dictionaries with small moves unless count matches ``good_moves``."""
i = 0
while True:
x = min_offset * 1.2 if i in good_moves else 0.1
yield {"x": x, "y": 0}
i += 1
mock_offset_from = mocker.patch.object(
rom_thing, "_offset_from", side_effect=gen_offsets()
)
still_moves = rom_thing._stage_still_moves(
axis="x",
direction=1,
rom_deps=mock_rom_deps,
)
assert still_moves is expected_to_detect_motion
assert mock_offset_from.call_count == offset_calls