From 036b3b1bd2a4c696ec9aa09aef35ba0749351080 Mon Sep 17 00:00:00 2001 From: x12sa Date: Tue, 7 Jul 2026 12:32:13 +0200 Subject: [PATCH] feat(flomni): acquire empty frames at start of tomo_scan Add collect_empty_frames(), which acquires 10 flat-field frames at angle 0 with fsamx shifted out of the beam by fovx/2 (opposite the normal alignment x-offset). Called once at the start of a new tomo_scan() (not on resume). Frames are excluded from the ptycho reconstruction queue and logged to tomography_scannumbers.txt with subtomo_number=0. --- .../plugins/flomni/flomni.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index bb3ed46..051b993 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -2259,6 +2259,68 @@ class Flomni( end_scan_number=end_scan_number, ) + def collect_empty_frames(self): + """Acquire 10 empty-frame (flat-field) images at angle 0, with fsamx + shifted out of the beam by half the field of view (fovx/2), in the + direction opposite the normal alignment x-offset at angle 0. + + Called once at the start of a new tomo_scan() (not on resume). + These frames are deliberately not passed through tomo_reconstruct() + -- they are flat fields, not ptycho projections, and must not be + added to the reconstruction queue. Logged to + tomography_scannumbers.txt with subtomo_number=0 so they remain + traceable but are clearly distinguished from the angular grid + (subtomo_number 1-8). + """ + scans = builtins.__dict__.get("scans") + + angle = 0 + + # --- rotation --- + fsamroy_current_setpoint = dev.fsamroy.user_setpoint.get() + if angle != fsamroy_current_setpoint: + umv(dev.fsamroy, angle) + else: + print("No rotation required") + + # --- alignment offset at angle 0, then push x out of the beam by + # fovx/2, in the direction opposite the normal offset --- + offsets = self.get_alignment_offset(angle) + normal_offset_x = offsets[0] + direction = -1 if normal_offset_x >= 0 else 1 + sum_offset_x = normal_offset_x + direction * (self.fovx / 2) + direction * 10 + sum_offset_y = ( + offsets[1] + - self.compute_additional_correction_y(angle) + - self.compute_additional_correction_y_2(angle) + + self.manual_shift_y + ) + # sum_offset_z = offsets[2] + # Todo + sum_offset_z = 0 + + + dev.rtx.controller.laser_tracker_on() + umv(dev.rtx, sum_offset_x, dev.rty, sum_offset_y, dev.rtz, sum_offset_z) + tracker_signal = dev.rtx.controller.laser_tracker_check_signalstrength() + # checks that the fsamx coarse stage is at a position that leaves + # sufficient piezo range on the fine (rtx) stage + dev.rtx.controller.move_samx_to_scan_region(sum_offset_x) + + if tracker_signal == "low": + logger.warning("Signal strength of the laser tracker is low. Realignment recommended!") + elif tracker_signal == "toolow": + raise FlomniError( + "Signal strength of the laser tracker is too low for scanning. Realignment required!" + ) + + print("Acquiring 10 empty frames at angle 0, fsamx shifted out of the beam.") + start_scan_number = bec.queue.next_scan_number + scans.acquire(exp_time=self.tomo_countingtime, frames_per_trigger=10) + end_scan_number = bec.queue.next_scan_number + for scan_nr in range(start_scan_number, end_scan_number): + self._write_tomo_scan_number(scan_nr, angle, subtomo_number=0) + def tomo_scan(self, subtomo_start=1, start_angle=None, projection_number=None): """start a tomo scan""" @@ -2311,6 +2373,7 @@ class Flomni( self.progress["estimated_finish_time"] = None self.progress["accumulated_idle_time"] = 0.0 self.progress["heartbeat"] = None + self.collect_empty_frames() with scans.dataset_id_on_hold: if self.tomo_type == 1: