diff --git a/csaxs_bec/scans/scans_v4/aerotech_cont_grid.py b/csaxs_bec/scans/scans_v4/aerotech_cont_grid.py index 5940b142..075daf4b 100644 --- a/csaxs_bec/scans/scans_v4/aerotech_cont_grid.py +++ b/csaxs_bec/scans/scans_v4/aerotech_cont_grid.py @@ -272,8 +272,10 @@ class AerotechContGrid(ScanBase): self.capture = devices[capture_device] self._captured_lines: list[dict] = [] self._step_status = None - self._line_times: list[float] = [] - self._scan_start_time = None + self._line_times: list[float] = [] # line start .. trigger checked + self._line_starts: list[float] = [] + self._readout_times: list[float] = [] # capture readout per line + self._last_line_end = None self.motors = [fast_axis, stepper_axis] self.frames_per_line = 0 @@ -324,6 +326,7 @@ class AerotechContGrid(ScanBase): frames_per_trigger=self.frames_per_line, computed_positions=positions, num_lines=num_lines, + readout_time=self._line["readout_time"], ) if self.mcs is not None: @@ -367,7 +370,6 @@ class AerotechContGrid(ScanBase): ) self.panda.arm() self.actions.pre_scan_all_devices() - self._scan_start_time = time.time() @scan_hook def scan_core(self): @@ -401,6 +403,7 @@ class AerotechContGrid(ScanBase): then check that the line was triggered and read the captured positions. """ line_start_time = time.time() + self._line_starts.append(line_start_time) self.capture.arm(self.frames_per_line) move_status = self.actions.set(motors, positions, wait=False) next_line = line_index + 1 if line_index + 1 < self._line["num_lines"] else None @@ -423,7 +426,10 @@ class AerotechContGrid(ScanBase): self.panda.wait_lines_done(line_index + 1, timeout=self._line_duration() + 2) self._line_times.append(time.time() - line_start_time) + readout_start = time.time() self._captured_lines.append(self.capture.read_line(centre_shift=self._centre_shift())) + self._last_line_end = time.time() + self._readout_times.append(self._last_line_end - readout_start) @scan_hook def post_scan(self): @@ -538,12 +544,19 @@ class AerotechContGrid(ScanBase): f" backward - forward [um]: mean {diff.mean():+.2f}, slope {slope:+.2f} / mm," f" peak-peak {np.ptp(diff):.2f}", ] - if self._line_times: - total = time.time() - self._scan_start_time if self._scan_start_time else 0.0 + if self._line_starts and self._last_line_end is not None: + # line period: start to start of the next line (motion, trigger check, capture + # readout, wait for the step); the last line ends with its capture readout + ends = self._line_starts[1:] + [self._last_line_end] + periods = np.subtract(ends, self._line_starts) + lines_total = self._last_line_end - self._line_starts[0] text.append( - f" times: scan {total:.1f} s, line mean {np.mean(self._line_times):.2f} s" - f" (min {np.min(self._line_times):.2f}, max {np.max(self._line_times):.2f})," - f" exposure per line {n * self.exp_time:.2f} s" + f" times: lines {lines_total:.1f} s (first line start to last line end," + f" without staging / premove / close), line period mean {periods.mean():.2f} s" + f" (min {periods.min():.2f}, max {periods.max():.2f}), of which motion and" + f" trigger check {np.mean(self._line_times):.2f} s, capture readout" + f" {np.mean(self._readout_times):.2f} s; exposure per line" + f" {n * self.exp_time:.2f} s" ) return "\n".join(text) diff --git a/tests/tests_scans/test_aerotech_cont_grid_scan.py b/tests/tests_scans/test_aerotech_cont_grid_scan.py index c73d3eb1..833a6120 100644 --- a/tests/tests_scans/test_aerotech_cont_grid_scan.py +++ b/tests/tests_scans/test_aerotech_cont_grid_scan.py @@ -418,11 +418,22 @@ def test_statistics_of_a_symmetric_snake(v4_scan_assembler, device_manager): bwd = {"enc6_centre": list((cells + 3e-3)[::-1]), "enc2_centre": [0.5] * 5} scan._captured_lines = [fwd, bwd, fwd] scan._line_times = [1.0, 1.2, 1.1] + scan._readout_times = [0.2, 0.2, 0.2] + scan._line_starts = [100.0, 101.5, 103.0] # periods 1.5, 1.5, last line ends at 104.4 + scan._last_line_end = 104.4 text = scan._statistics() assert "forward mean +1.00" in text assert "backward mean +3.00" in text assert "backward - forward [um]: mean +2.00" in text and "peak-peak 0.00" in text - assert "line mean 1.10 s" in text + assert "lines 4.4 s" in text + assert "line period mean 1.47 s (min 1.40, max 1.50)" in text + assert "motion and trigger check 1.10 s, capture readout 0.20 s" in text + + +def test_readout_time_in_scan_info(v4_scan_assembler, device_manager): + scan = _assemble(v4_scan_assembler, device_manager) + scan.prepare_scan() + assert scan.scan_info.readout_time == pytest.approx(2e-4) def test_on_exception_restores_ddg_pso_and_axis(v4_scan_assembler, device_manager):