From 82b03274b7a67bde70aa8368b312e544d5e00ce7 Mon Sep 17 00:00:00 2001 From: x12sa Date: Tue, 30 Jun 2026 14:57:36 +0200 Subject: [PATCH 1/9] fix docs --- docs/user/ptychography/flomni.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/user/ptychography/flomni.md b/docs/user/ptychography/flomni.md index 14910d4..ebee457 100644 --- a/docs/user/ptychography/flomni.md +++ b/docs/user/ptychography/flomni.md @@ -48,13 +48,12 @@ Manually move the gripper to a transfer position #### Coarse alignment After the sample transfer the sample stage moved to the measurement position with your new sample. The Xray eye will automatically move in and the shutter will open. You may already see the sample in the omny xeye interface running on the windows computer. -+Height is now centered automatically as part of step 3 below, so manual height adjustment is only needed if the sample is far off screen to begin with: +If you see your sample already at the approximately correct height, you can skip steps 1 to 3. Otherwise adjust the height: - -+1. If the sample is far off screen, `flomni.feedback_disable()` then `flomni.umvr_fsamy_tracked(0.01)`, attention: unit , move the sample stage relative up (positive) or down (negative) until the sample is visible in the xray eye screen, then `flomni.feedback_enable_with_reset()` +1. `flomni.feedback_disable()` disable the closed loop operation to allow movement of coarse stages +1. `umvr(dev.fsamy, 0.01)`, attention: unit , move the sample stage relative up (positive) or down (negative) until the sample is approximately vertically centered in xray eye screen 1. `flomni.xrayeye_update_frame()` will update the current image on the xray eye screen -+1. `flomni.xrayeye_alignment_start()` start the coarse alignment: mark the sample once to auto-center its height, then measure (clicking in the X-ray eye software) the sample position at 0, 45, 90, 135, 180 degrees. The GUI will present a fit of this data, which is automatically loaded to BEC for aligning the sample. - +1. `flomni.xrayeye_alignment_start()` start the coarse alignment of the sample by measuring (clicking in the X-ray eye software) the sample position at its height and angles of 0, 45, 90, 135, 180 degrees. The GUI will present a fit of this data, which is automatically loaded to BEC for aligning the sample. #### Fine alignment @@ -363,5 +362,4 @@ flomni.move_fheater_up() The functions are safe in the sense that no collisions should occur. E.g. the OSA will be moved back before a movement of the heater. -__The heater still needs commissioning in BEC!!!__ - +__The heater still needs commissioning in BEC!!!__ \ No newline at end of file From d949797eb1a612ffc528b1ddce761d12e610570d Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Tue, 30 Jun 2026 11:02:31 +0200 Subject: [PATCH 2/9] fix: reorder positions in lamni fermat scan test to comply with ascending default --- tests/tests_scans/test_lamni_fermat_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests_scans/test_lamni_fermat_scan.py b/tests/tests_scans/test_lamni_fermat_scan.py index d3751a4..4985519 100644 --- a/tests/tests_scans/test_lamni_fermat_scan.py +++ b/tests/tests_scans/test_lamni_fermat_scan.py @@ -271,8 +271,8 @@ def device_manager_mock(): }, "num_points": 2, "positions": [ - [1.3681828686580249, 2.1508313829565293], [-0.7700589354581364, -0.8406005210092851], + [1.3681828686580249, 2.1508313829565293], ], "scan_name": "lamni_fermat_scan", "scan_type": "step", From b16ab20ddf315beaae026ce0e3c6bebb8c829a02 Mon Sep 17 00:00:00 2001 From: x12sa Date: Wed, 1 Jul 2026 11:56:30 +0200 Subject: [PATCH 3/9] added predict gap to csaxs --- .../bec_ipython_client/plugins/cSAXS/cSAXS.py | 102 +++++++++++++++--- .../cSAXS/intensity_map_predict_gap.py | 26 +++++ 2 files changed, 114 insertions(+), 14 deletions(-) create mode 100755 csaxs_bec/bec_ipython_client/plugins/cSAXS/intensity_map_predict_gap.py diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/cSAXS.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/cSAXS.py index f24d80d..42325de 100644 --- a/csaxs_bec/bec_ipython_client/plugins/cSAXS/cSAXS.py +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/cSAXS.py @@ -1,23 +1,21 @@ -# import builtins -# import datetime -# import os -# import subprocess -# import time -# from pathlib import Path +import inspect -# import numpy as np from bec_lib import bec_logger -# from bec_lib.alarm_handler import AlarmBase -# from bec_lib.pdf_writer import PDFWriter from typeguard import typechecked - +from csaxs_bec.bec_ipython_client.plugins.cSAXS.diagnostics import cSAXSDiagnostics +from csaxs_bec.bec_ipython_client.plugins.cSAXS.filter_transmission import cSAXSFilterTransmission +from csaxs_bec.bec_ipython_client.plugins.cSAXS.intensity_map_predict_gap import ( + predict_gap as _predict_gap, +) +from csaxs_bec.bec_ipython_client.plugins.cSAXS.slits import cSAXSSlits from csaxs_bec.bec_ipython_client.plugins.cSAXS.smaract import cSAXSInitSmaractStages from csaxs_bec.bec_ipython_client.plugins.cSAXS.smaract import cSAXSSmaract from csaxs_bec.bec_ipython_client.plugins.omny.omny_general_tools import OMNYTools -from csaxs_bec.bec_ipython_client.plugins.cSAXS.filter_transmission import cSAXSFilterTransmission -from csaxs_bec.bec_ipython_client.plugins.cSAXS.diagnostics import cSAXSDiagnostics -from csaxs_bec.bec_ipython_client.plugins.cSAXS.slits import cSAXSSlits + +logger = bec_logger.logger + + class cSAXSError(Exception): pass @@ -36,6 +34,80 @@ class cSAXS( self.diagnostics = cSAXSDiagnostics() super().__init__(client=client) + # ------------------------------------------------------------------ + # Undulator + # ------------------------------------------------------------------ + + def predict_gap(self, energy: float, n: int = 3) -> None: + """Print the predicted undulator gap for *energy* [keV] on harmonic *n*. + + Examples + -------- + csaxs.predict_gap(6.2) # h=3 (default) + csaxs.predict_gap(10.0, n=5) # explicit harmonic + """ + import math + + gap = float(_predict_gap(energy, n=n)) + if math.isnan(gap): + print(f"Energy {energy:.3f} keV is unreachable on harmonic {n}.") + else: + print(f"Predicted gap for {energy:.3f} keV (h={n}): {gap:.4f} mm") + + # ------------------------------------------------------------------ + # Help / discovery + # ------------------------------------------------------------------ + + def commands(self) -> None: + """Print a table of all available cSAXS commands and sub-namespaces.""" + from rich import box + from rich.console import Console + from rich.table import Table + + console = Console() + + entries: list[tuple[str, str]] = [] + seen: set[str] = set() + + for cls in type(self).__mro__: + if cls is object: + continue + module = getattr(cls, "__module__", "") or "" + if "csaxs_bec" not in module: + continue + for name, func in inspect.getmembers(cls, predicate=inspect.isfunction): + if name.startswith("_") or name in seen: + continue + seen.add(name) + doc = (inspect.getdoc(func) or "").split("\n")[0].strip() + entries.append((name, doc)) + + entries.sort(key=lambda x: x[0]) + + tbl = Table(title="cSAXS Commands", box=box.SQUARE, show_lines=False) + tbl.add_column("Command", style="cyan bold", no_wrap=True, min_width=46) + tbl.add_column("Description") + for name, doc in entries: + tbl.add_row(f"csaxs.{name}()", doc) + console.print(tbl) + console.print("") + + ns = Table(title="Sub-namespaces", box=box.SQUARE, show_lines=False) + ns.add_column("Access", style="cyan bold", no_wrap=True, min_width=46) + ns.add_column("Description") + for access, desc in [ + ("csaxs.diagnostics.show_all()", "All diagnostic device readbacks"), + ( + "csaxs.diagnostics.bpm_xbox1 / .bpm_xbox2", + "BPM diagnostics — .show_all(), .gain(val)", + ), + ("csaxs.diagnostics.bim", "BIM diagnostics — .show_all(), .gain(val)"), + ("csaxs.diagnostics.beamstop", "Beamstop diode — .show_all(), .gain(val)"), + ("csaxs.diagnostics.polarization", "Polarization diodes — .show_all(), .gain(val)"), + ("csaxs.OMNYTools.*", "OMNY instrument tools"), + ]: + ns.add_row(access, desc) + console.print(ns) # this is the csaxs master file that imports all routines from csaxs @@ -45,4 +117,6 @@ class cSAXS( # csaxs = cSAXS(bec) # # then all commands can be accessed by for example -# csaxs._cSAXS_smaract_stages_..... \ No newline at end of file +# csaxs.commands() +# csaxs.predict_gap(6.2) +# csaxs._cSAXS_smaract_stages_... \ No newline at end of file diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/intensity_map_predict_gap.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/intensity_map_predict_gap.py new file mode 100755 index 0000000..e991ba7 --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/intensity_map_predict_gap.py @@ -0,0 +1,26 @@ +"""Undulator gap predictor emitted by plot_intensity_map.py. +Edit the fitted constants in the signature to retune.""" + +import numpy as np + + +def predict_gap(energy, n=3, gap_min=5.0, + E_inf=3.83167, c0=3.1133, c1=-0.644678, c2=0.0210398): + """Undulator gap [mm] to place `energy` [keV] on harmonic `n`. + Fitted constants are the defaults below; edit them to retune. + Returns NaN where the energy is unreachable on that harmonic.""" + energy = np.asarray(energy, float) + arg = E_inf * n / energy - 1.0 # required K^2/2; must be > 0 + with np.errstate(invalid="ignore", divide="ignore"): + y = np.log(arg) + if abs(c2) < 1e-12: + g = (y - c0) / c1 + else: + disc = c1 * c1 - 4.0 * c2 * (c0 - y) + sq = np.sqrt(np.where(disc >= 0, disc, np.nan)) + r1 = (-c1 + sq) / (2.0 * c2) + r2 = (-c1 - sq) / (2.0 * c2) + g = np.where(c1 + 2.0 * c2 * r1 < 0, r1, r2) + g = np.where(arg > 0, g, np.nan) # above harmonic cutoff + g = np.where(g >= gap_min, g, np.nan) # below mechanical minimum + return g From 64694331e3af7dfe89eea336911f4b5b255c4f99 Mon Sep 17 00:00:00 2001 From: menzel Date: Wed, 1 Jul 2026 17:57:37 +0200 Subject: [PATCH 4/9] Update predict_gap constants to corrected 3-parameter fit Replace the 4-parameter (quadratic) constants with the 3-parameter pure-exponential fit from plot_intensity_map.py (operating-locus calibration, gap-residual RMS ~14 um). c2 was insignificant (0.2 sigma) and left E_inf degenerate (+/-11 keV). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../plugins/cSAXS/intensity_map_predict_gap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/intensity_map_predict_gap.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/intensity_map_predict_gap.py index e991ba7..dbdc992 100755 --- a/csaxs_bec/bec_ipython_client/plugins/cSAXS/intensity_map_predict_gap.py +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/intensity_map_predict_gap.py @@ -5,7 +5,7 @@ import numpy as np def predict_gap(energy, n=3, gap_min=5.0, - E_inf=3.83167, c0=3.1133, c1=-0.644678, c2=0.0210398): + E_inf=3.2878, c0=2.46086, c1=-0.468091, c2=0.0): """Undulator gap [mm] to place `energy` [keV] on harmonic `n`. Fitted constants are the defaults below; edit them to retune. Returns NaN where the energy is unreachable on that harmonic.""" From e13ca157daf05bdffbb2f2297c23bad704658e96 Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 2 Jul 2026 07:34:55 +0200 Subject: [PATCH 5/9] optics out also in x --- .../bec_ipython_client/plugins/flomni/flomni_optics_mixin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py index 0ac68d8..1b825e7 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py @@ -98,8 +98,9 @@ class FlomniOpticsMixin: dev.rtx.controller.feedback_disable() self.fosa_out() + foptx_out = self._get_user_param_safe("foptx", "out") fopty_out = self._get_user_param_safe("fopty", "out") - umv(dev.fopty, fopty_out) + umv(dev.foptx, foptx_out, dev.fopty, fopty_out) if "rtx" in dev and dev.rtx.enabled: time.sleep(1) @@ -237,7 +238,7 @@ class FlomniOpticsMixin: console.print(table) - diameters = [150e-6, 250e-6] + diameters = [140e-6, 170e-6, 200e-6, 250e-6] console = Console() table = Table(title="Outermost zone width \033[1m30 nm\033[0m", box=box.SQUARE) From 43d18e0ca16e70df093e25b9d610ca8a2125c815 Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 2 Jul 2026 07:35:16 +0200 Subject: [PATCH 6/9] single point acquisition zero z damaged hardware --- .../bec_ipython_client/plugins/flomni/flomni.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index d2c6fff..2dbca15 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -2560,6 +2560,10 @@ class Flomni( + self.manual_shift_y ) sum_offset_z = offsets[2] + + # TODO this fix is while the tracker z is broken + probe_propagation = -sum_offset_z * 1e-6 + sum_offset_z = 0 # --- positioning + laser tracker, mirroring # flomni_fermat_scan._prepare_setup_part2 --- @@ -2580,6 +2584,7 @@ class Flomni( # --- acquire --- n_frames = frames_per_trigger if frames_per_trigger is not None else self.frames_per_trigger scans.acquire(exp_time=self.tomo_countingtime, frames_per_trigger=n_frames) + self.tomo_reconstruct(probe_propagation=probe_propagation) def _tomo_type1_actual_grid(self) -> tuple[int, float, int]: """Compute the actual (achievable) tomo_type==1 grid from the @@ -2618,13 +2623,17 @@ class Flomni( print(f"Frames per trigger (burst) = {self.frames_per_trigger}") print(f"Single point instead of fermat = {self.single_point_instead_of_fermat_scan}") print("") + if self.tomo_type == 1: print("\x1b[1mTomo type 1:\x1b[0m 8 equally spaced sub-tomograms") print(f"Angular range = {self.tomo_angle_range} degrees") - print( - f"Total number of projections: {(self.tomo_angle_range/self.tomo_angle_stepsize)*8}" - ) - print(f"Angular step within sub-tomogram: {self.tomo_angle_stepsize} degrees") + # N, step, total_projections all come from the same helper + # sub_tomo_scan() effectively uses internally - see + # _tomo_type1_actual_grid() for why this can't just read + # self.tomo_angle_stepsize directly. + _, achievable_step, total_projections = self._tomo_type1_actual_grid() + print(f"Total number of projections: {total_projections}") + print(f"Angular step within sub-tomogram: {achievable_step:.3f} degrees") print( "Angular step of the final (combined) tomogram:" f" {self.tomo_angle_range / total_projections:.3f} degrees" From cc10cee82b3a997166d5f9818027e2aad97e0576 Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 2 Jul 2026 07:35:35 +0200 Subject: [PATCH 7/9] parameter adjustments --- csaxs_bec/device_configs/ptycho_flomni.yaml | 31 +++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/csaxs_bec/device_configs/ptycho_flomni.yaml b/csaxs_bec/device_configs/ptycho_flomni.yaml index 0b29b8c..c7d8d7b 100644 --- a/csaxs_bec/device_configs/ptycho_flomni.yaml +++ b/csaxs_bec/device_configs/ptycho_flomni.yaml @@ -76,7 +76,10 @@ foptx: connectionTimeout: 20 userParameter: #170 micron, 60 nm - in: -13.831 + #in: -13.831 + #250 micron, 30 nm, Abe structures + in: -13.8809375 + out: -14.1809 fopty: description: Optics Y deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor @@ -95,8 +98,11 @@ fopty: connectionTimeout: 20 userParameter: #170 micron, 60 nm - in: 0.42 - out: 0.57 + #in: 0.42 + #out: 0.57 + #250 micron, 30 nm, Abe structures + in: 2.8299 + out: 2.8299 foptz: description: Optics Z deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor @@ -157,7 +163,7 @@ fsamy: host: mpc2844.psi.ch limits: - 2 - - 3.3 + - 3.8 port: 8081 sign: 1 enabled: true @@ -305,7 +311,10 @@ fosax: #in: 8.7568 #out: 5.1 #170 micron, 60 nm, 7.9 kev - in: 8.731922 + # in: 8.731922 + # out: 5.1 + #250 micron, 30 nm, Abe structures + in: 8.755141 out: 5.1 fosay: description: OSA Y @@ -327,8 +336,9 @@ fosay: #170 micron, 60 nm, 7.6 kev #in: -0.0235 #170 micron, 60 nm, 7.6 kev - in: -0.0422 - + #in: -0.0422 + #250 micron, 30 nm, Abe structures + in: -2.357436 fosaz: description: OSA Z deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor @@ -350,8 +360,11 @@ fosaz: #in: 8.5 #out: 6 #170 micron, 60 nm, 7.9 kev, foptz 15.9 - in: 11.9 - out: 6 + # in: 11.9 + # out: 6 + # micron, 30 nm, 7.9 kev, foptz 32 //abe's fzp's + in: -2 + out: -5 ############################################################ #################### flOMNI RT motors ###################### From 453057e1858d4663b5bc19b8f5d017b9c8ceb1ef Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 2 Jul 2026 07:35:51 +0200 Subject: [PATCH 8/9] save roi in pixels with images --- .../plugins/flomni/x_ray_eye_align.py | 82 +++++++++++-------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py b/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py index 732dbd9..a1e2717 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py @@ -70,14 +70,26 @@ class XrayEyeAlign: # so a second submission at step==1 is treated as the real angle-0 # fit point instead of triggering another height correction. self._height_centered = False + # Raw pixel coords + ROI size collected at each submit: + # [[step_k, x_px, y_px, w_px, h_px, image_idx], ...] + # image_idx refers to alignment_images[image_idx], i.e. the last + # frame captured before that submit (shutter is closed at submit time). + self.roi_pixel_data = [] - def _save_alignment_data(self, file_path: str): - os.makedirs(os.path.dirname(file_path), exist_ok=True) - with h5py.File(os.path.expanduser(file_path), "w") as f: + def _save_alignment_data(self, file_path: str, fit_data: np.ndarray | None = None): + expanded = os.path.expanduser(file_path) + os.makedirs(os.path.dirname(expanded), exist_ok=True) + with h5py.File(expanded, "w") as f: f.create_dataset( "alignment_values", data=np.array(list(self.alignment_values.values())) ) f.create_dataset("alignment_images", data=np.array(self.alignment_images)) + if self.roi_pixel_data: + ds = f.create_dataset("roi_pixel_data", data=np.array(self.roi_pixel_data)) + ds.attrs["columns"] = ["step_k", "x_px", "y_px", "w_px", "h_px", "image_idx"] + if fit_data is not None: + ds = f.create_dataset("alignment_fit", data=fit_data) + ds.attrs["rows"] = ["angles_deg", "offsets_um", "zeros"] def update_frame(self, keep_shutter_open=False): if self.flomni._flomnigui_check_attribute_not_exists("xeyegui"): @@ -220,6 +232,20 @@ class XrayEyeAlign: # reset submit channel dev.omny_xray_gui.submit.set(0) + # Raw pixel position and ROI size at submit time. + # The relevant image is the last captured frame (shutter is + # closed by the time the user clicks submit). + _raw_x = getattr(dev.omny_xray_gui, f"xval_x_{k}").get() + _raw_y = getattr(dev.omny_xray_gui, f"yval_y_{k}").get() + _raw_w = getattr(dev.omny_xray_gui, f"width_x_{k}").get() + _raw_h = getattr(dev.omny_xray_gui, f"width_y_{k}").get() + _img_idx = len(self.alignment_images) - 1 + print( + f" Submit k={k}: px x={_raw_x:.1f} y={_raw_y:.1f} " + f"w={_raw_w:.1f} h={_raw_h:.1f} img={_img_idx}" + ) + self.roi_pixel_data.append([k, _raw_x, _raw_y, _raw_w, _raw_h, _img_idx]) + # Controls whether `k` advances to the next step below. Left # True except for the height-centering submission, which # reuses k==1 for a second, real submission afterwards. @@ -252,9 +278,8 @@ class XrayEyeAlign: self.gui.show_crosshair() self.send_message( - "Adjust sample height with the arrows if needed, then mark " - "the sample and submit - height will be centered automatically" - ) + "Submit height. Use arrows if far off." + ) self.gui.enable_submit_button(True) self.movement_buttons_enabled(True, True) @@ -373,37 +398,22 @@ class XrayEyeAlign: ) def write_output(self): - file = os.path.expanduser("~/data/raw/logs/xrayeye_alignmentvalues/xrayeye_alignmentvalues") timestamp = time.strftime("%Y%m%d_%H%M%S") - self._save_alignment_data(file + f"_image_data_{timestamp}.h5") - if not os.path.exists(file): - os.makedirs(os.path.dirname(file), exist_ok=True) + file_h5 = f"~/data/raw/logs/xrayeye_alignmentvalues/xrayeye_alignmentvalues_{timestamp}.h5" - with open(file, "w") as alignment_values_file: - alignment_values_file.write("angle\thorizontal\n") + fovx_offsets = np.zeros(5) + for k in range(1, 6): + fovx_offset = self.alignment_values[0] - self.alignment_values[k] + fovx_offsets[k - 1] = fovx_offset + print(f"Alignment number {k}, value x {fovx_offset}") - # Initialize an empty list to store fovx values - fovx_list = [] - fovx_offsets = np.zeros(5) # holds offsets for k = 1..5 - - for k in range(1, 6): - fovx_offset = self.alignment_values[0] - self.alignment_values[k] - fovx_offsets[k - 1] = fovx_offset # store in array - - fovx_x = (k - 1) * 45 - fovx_list.append([fovx_x, fovx_offset * 1000]) # Append the data to the list - - print(f"Alignment number {k}, value x {fovx_offset}") - alignment_values_file.write(f"{fovx_x}\t{fovx_offset * 1000}\n") - - # Now build final numpy array: - data = np.array( - [ - [0, 45, 90, 135, 180], # angles - fovx_offsets * 1000, # fovx_offset values - [0, 0, 0, 0, 0], - ] - ) + data = np.array( + [ + [0, 45, 90, 135, 180], # angles + fovx_offsets * 1000, # fovx_offset values + [0, 0, 0, 0, 0], + ] + ) + self._save_alignment_data(file_h5, fit_data=data) self.gui.submit_fit_array(data) - print(f"fit submited with {data}") - # self.flomni.flomnigui_show_xeyealign_fittab() \ No newline at end of file + print(f"fit submited with {data}") \ No newline at end of file From 0972f4971b9376fd33356f604f6886cec1b20b37 Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 2 Jul 2026 08:44:10 +0200 Subject: [PATCH 9/9] added ROI selector to saved datafile --- .../bec_ipython_client/plugins/flomni/x_ray_eye_align.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py b/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py index a1e2717..422046f 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py @@ -278,7 +278,12 @@ class XrayEyeAlign: self.gui.show_crosshair() self.send_message( +<<<<<<< Updated upstream "Submit height. Use arrows if far off." +======= + "Adjust sample height with the arrows if needed, then mark " + "the sample and submit - height will be centered automatically" +>>>>>>> Stashed changes ) self.gui.enable_submit_button(True) self.movement_buttons_enabled(True, True)