diff --git a/csaxs_bec/scans/flomni_fermat_scan.py b/csaxs_bec/scans/flomni_fermat_scan.py index c063dd4..17edbc9 100644 --- a/csaxs_bec/scans/flomni_fermat_scan.py +++ b/csaxs_bec/scans/flomni_fermat_scan.py @@ -20,6 +20,8 @@ import time from typing import Annotated import numpy as np +from scipy.spatial import cKDTree + from bec_lib import messages from bec_lib.alarm_handler import Alarms from bec_lib.endpoints import MessageEndpoints @@ -62,6 +64,7 @@ class FlomniFermatScan(ScanBase): } MIN_POSITIONS = 20 # Minimum number of positions required for a valid scan + MAX_CORRIDOR_SIZE = 3 # Corridor size is capped at this value (in um) for stability def __init__( # fmt: off @@ -122,6 +125,13 @@ class FlomniFermatScan(ScanBase): logger.warning("The zshift is smaller than -100 um. It will be limited to -100 um.") self.zshift = -100 + if self.corridor_size is not None and self.corridor_size > self.MAX_CORRIDOR_SIZE: + logger.warning( + f"The corridor_size is larger than {self.MAX_CORRIDOR_SIZE} um. It will be" + f" limited to {self.MAX_CORRIDOR_SIZE} um." + ) + self.corridor_size = self.MAX_CORRIDOR_SIZE + self.update_scan_info( exp_time=exp_time, frames_per_trigger=frames_per_trigger, @@ -154,8 +164,12 @@ class FlomniFermatScan(ScanBase): f"The number positions must exceed {self.MIN_POSITIONS}. Currently: {len(positions)}." ) + corridor_size = self.corridor_size + if corridor_size is None: + corridor_size = min(self._estimate_corridor_size(positions), self.MAX_CORRIDOR_SIZE) + self.positions = self.components.optimize_trajectory( - positions, optimization_type="corridor", corridor_size=self.corridor_size + positions, optimization_type="corridor", corridor_size=corridor_size ) flip_axes = self.reverse_trajectory() if flip_axes: @@ -318,8 +332,8 @@ class FlomniFermatScan(ScanBase): if self.flomni_rotation_status: self.flomni_rotation_status.wait() - rtx_status = dev.rtx.set(self.cenx) - rtz_status = dev.rtz.set(self.positions[0][2]) + rtx_status = self.actions.set(dev.rtx, self.cenx, wait=False) + rtz_status = self.actions.set(dev.rtz, self.positions[0][2], wait=False) dev.rtx.controller.laser_tracker_on() @@ -370,6 +384,24 @@ class FlomniFermatScan(ScanBase): ) self.flomni_rotation_status = self.actions.set(self.dev.fsamroy, angle, wait=False) + @staticmethod + def _estimate_corridor_size(positions: np.ndarray, factor: float = 1.5) -> float: + """ + Estimate the corridor size based on the median nearest-neighbor distance + of the positions, matching the estimation used by the corridor path + optimizer if no corridor_size is provided. + + Args: + positions (np.ndarray): Array of positions + factor (float): Scaling factor for the median distance + + Returns: + float: Estimated corridor size + """ + tree = cKDTree(positions[:, :2]) + dists, _ = tree.query(positions[:, :2], k=2) # k=1 is itself + return factor * np.median(dists[:, 1]) + @staticmethod def get_flomni_fermat_spiral_pos( m1_start: float, diff --git a/csaxs_bec/scans/lamni_components.py b/csaxs_bec/scans/lamni_components.py index 3c9648a..71f0f6d 100644 --- a/csaxs_bec/scans/lamni_components.py +++ b/csaxs_bec/scans/lamni_components.py @@ -69,6 +69,7 @@ class LamNIComponents(ScanComponents): # disable the feedback self._dev.rtx.controller.feedback_disable() + time.sleep(0.05) rtx_current = self._dev.rtx.readback.get() rty_current = self._dev.rty.readback.get() @@ -109,8 +110,11 @@ class LamNIComponents(ScanComponents): logger.info( f"Compensating {[val/1000 for val in self.lamni_to_stage_coordinates(x_drift,y_drift)]}" ) - self._dev.lsamx.set(move_x).wait() - self._dev.lsamy.set(move_y).wait() + lsamx_set = self._dev.lsamx.set(move_x) + lsamy_set = self._dev.lsamy.set(move_y) + + lsamx_set.wait() + lsamy_set.wait() time.sleep(0.01) rtx_current = self._dev.rtx.readback.get() diff --git a/csaxs_bec/scans/omny_fermat_scan.py b/csaxs_bec/scans/omny_fermat_scan.py index 5236f1f..d6795c3 100644 --- a/csaxs_bec/scans/omny_fermat_scan.py +++ b/csaxs_bec/scans/omny_fermat_scan.py @@ -42,7 +42,7 @@ class OmnyFermatScan(ScanBase): # Scan name: This is the name of the scan, e.g. "line_scan". This is used for display purposes and to identify the scan type in user interfaces. # Choose a descriptive name that does not conflict with existing scan names. # It must be a valid Python identifier, that is, it can only contain letters, numbers, and underscores, and must not start with a number. - scan_name = "omny_fermat_scan_v4" + scan_name = "omny_fermat_scan" gui_config = { "Scan Parameters": [