refactor(aerotech): aerotech_cont_grid always runs as a snake
CI for csaxs_bec / test (push) Successful in 3m27s

Remove the snake argument and the raster path (flyback with PSO disarm/re-arm);
snake is the only mode used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8zoDyJxbBLmjZCEPUo5FM
This commit is contained in:
Mirko HollerandClaude Opus 5.5 committed 2026-10-28 17:30:11 +01:00
1 parent 46751e4fcc
commit 40c8d90072
3 files changed
+6 -45

No files matched your search

+2 -3
View File
@@ -59,11 +59,11 @@ Documentation: A3200 6.04.004 help (`A3200.chm`, topic "ASCII Command Interface"
```python
scans.aerotech_cont_grid(dev.aerox, 0, 1, 0.01, dev.aeroy, 0, 1, 0.1, exp_time=0.01,
snake=True, trigger_source="ddg", settle_time=0.15)
trigger_source="ddg", settle_time=0.15)
```
- fast axis X moves continuously at `fast_step_size / exp_time`, the step axis Y steps between
lines; `snake=True` (default) alternates the line direction
lines; the lines always run as a snake (alternating direction)
- line start triggered by position: one PSO window per scan,
`[fast_start - v * shutter_delay, exposure_end + v * shutter_delay]`, armed once in
pre_scan. Its output rises on entering the window: at the low edge on forward lines, at
@@ -86,7 +86,6 @@ scans.aerotech_cont_grid(dev.aerox, 0, 1, 0.01, dev.aeroy, 0, 1, 0.1, exp_time=0
ramp at 1 mm/s / 10 mm/s^2). In a snake the run-out of a line is the run-up of the next.
- at the turnarounds X does not wait for In Position (`wait_in_position` signal off during
the scan); X reaches 1 um within ~60 ms but 10 nm only after 0.5-2 s
- `snake=False`: the flyback passes the PSO window, so the PSO is switched off during it
- `capture_positions=True` (default False until commissioned): device `aerocapture` latches
the encoder counters on each detector trigger (DATAACQ on UINT1, up to 16 kHz, 4 kHz with
MXR sources); after each line they are read back (~2600 values/s over the ASCII interface)
+4 -14
View File
@@ -1,8 +1,8 @@
"""
Continuous grid scan for Aerotech A3200 stages with a position triggered line start.
The fast axis moves continuously, the step axis steps between lines. By default the lines
run as a snake (alternating direction). The start of each line is triggered in hardware by
The fast axis moves continuously, the step axis steps between lines. The lines always run
as a snake (alternating direction). The start of each line is triggered in hardware by
the A3200 PSO output of the fast axis: one PSO window per scan,
[fast_start - v * shutter_delay, exposure_end + v * shutter_delay]
@@ -114,7 +114,6 @@ class AerotechContGrid(ScanBase):
"Acquisition Parameters": [
"exp_time",
"relative",
"snake",
"trigger_source",
"capture_positions",
],
@@ -133,7 +132,6 @@ class AerotechContGrid(ScanBase):
stepper_step_size: Annotated[float, ScanArgument(display_name="Step Step Size", description="Step size of the step axis in units of the motor.")],
exp_time: DefaultArgType.ExposureTime,
relative: DefaultArgType.Relative = False,
snake: bool = True,
trigger_source: str | None = "ddg",
trigger_edge: str = "falling",
trigger_device: str = "pandatrig",
@@ -161,7 +159,6 @@ class AerotechContGrid(ScanBase):
stepper_step_size (float): Step size of the step axis in units of the motor.
exp_time (float): Exposure time (period of the detector gates) in seconds.
relative (bool): Whether the positions are relative to the current position. Default is False.
snake (bool): Alternate the direction of the lines. Default is True.
trigger_source (str | None): "ddg" (beamline, DDG1/MCS), "panda" (PandABox line trigger) or None (no trigger hardware). Default is "ddg".
trigger_edge (str): DDG1 external trigger edge, "falling" (PSO open collector / differential output) or "rising" (opto output). Default is "falling".
trigger_device (str): Name of the PandaLineTrigger device for trigger_source="panda". Default is "pandatrig".
@@ -198,7 +195,6 @@ class AerotechContGrid(ScanBase):
self.stepper_step_size = stepper_step_size
self.relative = relative
self.exp_time = exp_time
self.snake = snake
self.trigger_source = trigger_source
self.readout_time = readout_time
self.shutter_delay = shutter_delay
@@ -323,13 +319,7 @@ class AerotechContGrid(ScanBase):
for line_index in range(line["num_lines"]):
stepper_pos = self.positions[line_index][1]
if line_index > 0:
if self.snake:
self.actions.set([self.stepper_axis], [stepper_pos]).wait()
else:
# the flyback passes the PSO window: no line trigger while returning
self.fast_axis.disarm_line_trigger()
self.actions.set(self.motors, [line["line_start"], stepper_pos]).wait()
self.fast_axis.arm_line_trigger(line["trigger_low"], line["trigger_high"])
self.actions.set([self.stepper_axis], [stepper_pos]).wait()
target = line["line_end"] if self._is_forward(line_index) else line["line_start"]
self.at_each_point(
motors=[self.fast_axis], positions=np.array([target]), line_index=line_index
@@ -399,7 +389,7 @@ class AerotechContGrid(ScanBase):
#######################################################
def _is_forward(self, line_index: int) -> bool:
return not self.snake or line_index % 2 == 0
return line_index % 2 == 0
def _wait_mcs_frames(self, line_index: int, timeout: float = 2.0, poll: float = 0.05):
"""
@@ -140,12 +140,6 @@ def test_snake_positions(v4_scan_assembler, device_manager):
assert np.allclose(computed[5:10, 0], [1.0, 0.5, 0.0, -0.5, -1.0])
def test_raster_positions(v4_scan_assembler, device_manager):
scan = _assemble(v4_scan_assembler, device_manager, snake=False)
scan.prepare_scan()
assert np.allclose(scan.positions, [[1.0, -2.0], [1.0, 0.0], [1.0, 2.0]])
def test_shutter_delay_without_ddg(v4_scan_assembler, device_manager):
scan = _assemble(v4_scan_assembler, device_manager, trigger_source=None)
scan.prepare_scan()
@@ -281,28 +275,6 @@ def test_snake_scan_core(v4_scan_assembler, device_manager, nth_done_status_mock
scan.fast_axis.disarm_line_trigger.assert_not_called()
def test_raster_scan_core_disarms_pso_for_flyback(
v4_scan_assembler, device_manager, nth_done_status_mock
):
scan = _ready_for_core(
_assemble(v4_scan_assembler, device_manager, trigger_source=None, snake=False),
nth_done_status_mock,
)
scan.scan_core()
line = scan._line
targets = [list(c.args[1]) for c in scan.actions.set.call_args_list]
assert targets == [
[line["line_end"]],
[line["line_start"], 0.0],
[line["line_end"]],
[line["line_start"], 2.0],
[line["line_end"]],
]
assert scan.fast_axis.disarm_line_trigger.call_count == 2
assert scan.fast_axis.arm_line_trigger.call_count == 2
def test_panda_lines_are_checked(v4_scan_assembler, device_manager, nth_done_status_mock):
scan = _ready_for_core(
_assemble(v4_scan_assembler, device_manager, trigger_source="panda"), nth_done_status_mock