This commit is contained in:
x01dc
2026-04-21 14:23:37 +02:00
committed by appel_c
parent 3da241ca10
commit 1f209156e3
6 changed files with 355 additions and 78 deletions
@@ -1,6 +1,11 @@
from .alignment import XrayEyeAlign
from .lamni_alignment_mixin import LamNIAlignmentMixin
from .lamni import LamNI
from .lamni_optics_mixin import LamNIInitError, LaMNIInitStages, LamNIOpticsMixin
__all__ = [
"LamNI", "XrayEyeAlign", "LamNIInitError", "LaMNIInitStages", "LamNIOpticsMixin"
]
"LamNI",
"LamNIAlignmentMixin",
"LamNIInitError",
"LaMNIInitStages",
"LamNIOpticsMixin",
]
@@ -17,9 +17,8 @@ from csaxs_bec.bec_ipython_client.plugins.omny.omny_general_tools import (
TomoIDManager,
)
from csaxs_bec.bec_ipython_client.plugins.LamNI.gui_tools import LamniGuiTools
from csaxs_bec.bec_ipython_client.plugins.LamNI.lamni_alignment_mixin import LamNIAlignmentMixin
from .alignment import XrayEyeAlign
from .x_ray_eye_align import XrayEyeAlign as XrayEyeAlignGUI
from .x_ray_eye_align import XrayEyeAlign as XrayEyeAlignGUI
from .lamni_optics_mixin import LaMNIInitStages, LamNIOpticsMixin
@@ -33,16 +32,22 @@ if builtins.__dict__.get("bec") is not None:
umvr = builtins.__dict__.get("umvr")
class LamNI(LamNIOpticsMixin, LamniGuiTools):
class LamNI(LamNIAlignmentMixin, LamNIOpticsMixin, LamniGuiTools):
def __init__(self, client):
super().__init__()
self.client = client
self.set_client(client)
self.set_client(client)
self.device_manager = client.device_manager
self.align = XrayEyeAlign(client, self)
self.init = LaMNIInitStages(client)
# Correction state (owned by LamNIAlignmentMixin methods)
self.corr_pos_x = []
self.corr_pos_y = []
self.corr_angle = []
self.corr_pos_x_2 = []
self.corr_pos_y_2 = []
self.corr_angle_2 = []
# Extracted collaborators
self.reconstructor = PtychoReconstructor(self.ptycho_reconstruct_foldername)
self.tomo_id_manager = TomoIDManager()
@@ -64,7 +69,6 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
self.progress["total_projections"] = 1
self.progress["angle"] = 0
# ------------------------------------------------------------------
# Special angles
# ------------------------------------------------------------------
@@ -87,31 +91,15 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
self.special_angle_repeats = 1
# ------------------------------------------------------------------
# RT feedback / interferometer helpers
# X-ray eye alignment entry points
# ------------------------------------------------------------------
def start_x_ray_eye_alignment(self):
"""Run the BEC GUI-based X-ray eye alignment procedure.
Creates a fresh XrayEyeAlignGUI instance (which resets the correction
state) and calls its align() method. The GUI window is opened
automatically. Interrupt with Ctrl-C to abort.
"""
aligner = XrayEyeAlignGUI(self.client, self)
try:
aligner.align()
except KeyboardInterrupt as exc:
print("Alignment interrupted by user.")
raise exc
def xrayeye_alignment_start(self, keep_shutter_open: bool = False):
"""Run the BEC GUI-based X-ray eye alignment procedure.
Creates a fresh XrayEyeAlignGUI instance (which resets the correction
state) and calls its align() method. The GUI window is opened
automatically. Interrupt with Ctrl-C to abort.
Creates a fresh :class:`XrayEyeAlignGUI` instance, which resets the
correction state, and calls its ``align()`` method. The GUI window
is opened automatically. Interrupt with Ctrl-C to abort.
Args:
keep_shutter_open: If True the shutter is left open between angle
@@ -127,8 +115,8 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
def xrayeye_update_frame(self, keep_shutter_open: bool = False):
"""Capture a single fresh X-ray eye frame without running full alignment.
Useful for visually checking the sample position. Requires the X-ray
eye GUI to be open (call lamnigui_show_xeyealign() first if needed).
Useful for visually checking the sample position. Opens the X-ray eye
GUI if it is not already visible.
Args:
keep_shutter_open: If True the shutter is left open after the frame.
@@ -136,6 +124,10 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
aligner = XrayEyeAlignGUI(self.client, self)
aligner.update_frame(keep_shutter_open=keep_shutter_open)
# ------------------------------------------------------------------
# RT feedback / interferometer helpers
# ------------------------------------------------------------------
def rt_off(self):
dev.rtx.enabled = False
dev.rty.enabled = False
@@ -388,7 +380,9 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
@golden_projections_at_0_deg_for_damage_estimation.setter
def golden_projections_at_0_deg_for_damage_estimation(self, val: int):
self.client.set_global_var("golden_projections_at_0_deg_for_damage_estimation", val)
self.client.set_global_var(
"golden_projections_at_0_deg_for_damage_estimation", val
)
@property
def sample_name(self):
@@ -440,7 +434,7 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
)
# ------------------------------------------------------------------
# Sample database — delegated to TomoIDManager in omny general tools
# Sample database — delegated to TomoIDManager
# ------------------------------------------------------------------
def add_sample_database(
@@ -464,9 +458,9 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
def tomo_scan_projection(self, angle: float):
scans = builtins.__dict__.get("scans")
additional_correction = self.align.compute_additional_correction(angle)
additional_correction_2 = self.align.compute_additional_correction_2(angle)
correction_xeye_mu = self.align.lamni_compute_additional_correction_xeye_mu(angle)
additional_correction = self.compute_additional_correction(angle)
additional_correction_2 = self.compute_additional_correction_2(angle)
correction_xeye_mu = self.lamni_compute_additional_correction_xeye_mu(angle)
self._current_scan_list = []
@@ -484,8 +478,8 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
stitch_x=stitch_x,
stitch_y=stitch_y,
stitch_overlap=self.tomo_stitch_overlap,
center_x=self.align.tomo_fovx_offset,
center_y=self.align.tomo_fovy_offset,
center_x=self.tomo_fovx_offset,
center_y=self.tomo_fovy_offset,
shift_x=(
self.manual_shift_x
+ correction_xeye_mu[0]
@@ -600,10 +594,8 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
for scan_nr in range(start_scan_number, end_scan_number):
self._write_tomo_scan_number(scan_nr, angle, subtomo_number)
#todo here bl chk, if ok then successfull true
successful = True
def _golden(self, ii, howmany_sorted, maxangle=360, reverse=False):
"""Return the ii-th golden ratio angle within sorted bunches and its subtomo number."""
golden = []
@@ -784,8 +776,8 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
print(f"Circular FOV diam <microns> = {self.tomo_circfov}")
print(f"Reconstruction queue name = {self.ptycho_reconstruct_foldername}")
print("FOV offset rotates to find the ROI; manual shift moves the rotation center.")
print(f" _tomo_fovx_offset <mm> = {self.align.tomo_fovx_offset}")
print(f" _tomo_fovy_offset <mm> = {self.align.tomo_fovy_offset}")
print(f" _tomo_fovx_offset <mm> = {self.tomo_fovx_offset}")
print(f" _tomo_fovy_offset <mm> = {self.tomo_fovy_offset}")
print(f" _manual_shift_x <mm> = {self.manual_shift_x}")
print(f" _manual_shift_y <mm> = {self.manual_shift_y}")
print("")
@@ -948,7 +940,6 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
)
self.client.logbook.send_logbook_message(msg)
def get_calibration_of_capstops_left_and_right(self):
import time
print("""
@@ -959,28 +950,28 @@ class LamNI(LamNIOpticsMixin, LamniGuiTools):
Example: At 0 deg, accessible rty -60 to 51. So the init was 5 microns off.
Then this routine here will provide data for the new capstop left and right.
""")
angle = 0
umv(dev.lsamrot,0)
umv(dev.lsamrot, 0)
print("Capstop right\nAngle, Voltage1, Voltage2")
mv(dev.lsamrot,361)
mv(dev.lsamrot, 361)
while angle <= 360:
angle = dev.lsamrot.readback.get()
voltage1=float(dev.lsamrot.controller.socket_put_and_receive("MG@AN[1]"))
voltage2=float(dev.lsamrot.controller.socket_put_and_receive("MG@AN[2]"))
if angle<360:
voltage1 = float(dev.lsamrot.controller.socket_put_and_receive("MG@AN[1]"))
voltage2 = float(dev.lsamrot.controller.socket_put_and_receive("MG@AN[2]"))
if angle < 360:
print(f"{angle},{voltage1},{voltage2}")
time.sleep(.3)
time.sleep(10)
print("\nCapstop left\nAngle, Voltage1, Voltage2")
mv(dev.lsamrot,-1)
mv(dev.lsamrot, -1)
while angle > 0:
angle = dev.lsamrot.readback.get()
voltage1=float(dev.lsamrot.controller.socket_put_and_receive("MG@AN[1]"))
voltage2=float(dev.lsamrot.controller.socket_put_and_receive("MG@AN[2]"))
if angle>0:
voltage1 = float(dev.lsamrot.controller.socket_put_and_receive("MG@AN[1]"))
voltage2 = float(dev.lsamrot.controller.socket_put_and_receive("MG@AN[2]"))
if angle > 0:
print(f"{angle},{voltage1},{voltage2}")
time.sleep(.3)
print("Finished")
print("Finished")
@@ -0,0 +1,282 @@
"""LamNI alignment correction infrastructure.
This mixin provides the correction infrastructure that is mixed directly into
the :class:`LamNI` class, mirroring the pattern used by
:class:`FlomniAlignmentMixin` in flomni.py.
The mixin assumes the hosting class provides:
- ``self.client`` (BECClient)
State that must be initialised in the host ``__init__``:
- ``self.corr_pos_x``, ``self.corr_pos_y``, ``self.corr_angle``
- ``self.corr_pos_x_2``, ``self.corr_pos_y_2``, ``self.corr_angle_2``
"""
from __future__ import annotations
import builtins
import os
import numpy as np
from bec_lib import bec_logger
from typeguard import typechecked
logger = bec_logger.logger
dev = builtins.__dict__.get("dev")
class LamNIAlignmentMixin:
"""Correction infrastructure for LamNI laminography.
Mixes into :class:`LamNI`. All methods are accessible directly as
``lamni.reset_correction()``, ``lamni.tomo_fovx_offset``, etc.,
matching the FlOMNI user API.
"""
# Pixel calibration: mm per pixel (0.2 mm FOV, 218 px)
PIXEL_CALIBRATION = 0.2 / 218
# ------------------------------------------------------------------
# Correction reset
# ------------------------------------------------------------------
def reset_correction(self):
"""Reset the look-up-table corrections to empty (iteration 1 and 2)."""
self.corr_pos_x = []
self.corr_pos_y = []
self.corr_angle = []
def reset_correction_2(self):
"""Reset the second iteration look-up-table correction to empty."""
self.corr_pos_x_2 = []
self.corr_pos_y_2 = []
self.corr_angle_2 = []
def reset_xray_eye_correction(self):
"""Delete the X-ray eye sinusoidal fit from the BEC global variable store."""
self.client.delete_global_var("tomo_fit_xray_eye")
# ------------------------------------------------------------------
# FOV offset properties (backed by BEC global variable)
# ------------------------------------------------------------------
@property
def tomo_fovx_offset(self):
"""Horizontal FOV offset in mm (rotated with laminography geometry)."""
val = self.client.get_global_var("tomo_fov_offset")
if val is None:
return 0.0
return val[0] / 1000
@tomo_fovx_offset.setter
@typechecked
def tomo_fovx_offset(self, val: float):
val_old = self.client.get_global_var("tomo_fov_offset")
if val_old is None:
val_old = [0.0, 0.0]
self.client.set_global_var("tomo_fov_offset", [val * 1000, val_old[1]])
@property
def tomo_fovy_offset(self):
"""Vertical FOV offset in mm (rotated with laminography geometry)."""
val = self.client.get_global_var("tomo_fov_offset")
if val is None:
return 0.0
return val[1] / 1000
@tomo_fovy_offset.setter
@typechecked
def tomo_fovy_offset(self, val: float):
val_old = self.client.get_global_var("tomo_fov_offset")
if val_old is None:
val_old = [0.0, 0.0]
self.client.set_global_var("tomo_fov_offset", [val_old[0], val * 1000])
# ------------------------------------------------------------------
# X-ray eye sinusoidal correction — read from files or GUI
# ------------------------------------------------------------------
# def read_xray_eye_correction(self, dir_path=None):
# """Load the sinusoidal X-ray eye fit from archived text files.
# Files are written by :meth:`XrayEyeAlign.write_output` at the end of
# every alignment run and are the fallback when the GUI has been closed.
# Args:
# dir_path: Directory containing ``ptychotomoalign_{A,B,C}{x,y}.txt``.
# Defaults to ``~/Data10/specES1/internal/``.
# """
# if dir_path is None:
# dir_path = os.path.expanduser("~/Data10/specES1/internal/")
# tomo_fit_xray_eye = np.zeros((2, 3))
# for i, axis in enumerate(["x", "y"]):
# for j, coeff in enumerate(["A", "B", "C"]):
# with open(
# os.path.join(dir_path, f"ptychotomoalign_{coeff}{axis}.txt"), "r"
# ) as f:
# tomo_fit_xray_eye[i][j] = f.readline()
# self.client.set_global_var("tomo_fit_xray_eye", tomo_fit_xray_eye.tolist())
# print("New alignment parameters loaded from X-ray eye files:")
# self._print_xeye_fit(tomo_fit_xray_eye)
def read_xray_eye_correction_from_gui(self):
"""Load the sinusoidal X-ray eye fit from the live XRayEye GUI widget.
Reads ``fit_params_x`` and ``fit_params_y`` from the ``omny_xray_gui``
device and stores the result as
``tomo_fit_xray_eye = [[Ax, Bx, Cx], [Ay, By, Cy]]``
in the BEC global variable store.
The stored array is consumed by
:meth:`lamni_compute_additional_correction_xeye_mu`.
.. important::
This method reads from the live GUI widget. If the XRayEye GUI
window has been closed since the alignment was performed the call
will fail. In that case use :meth:`read_xray_eye_correction` to
reload from the archived text files.
"""
_dev = builtins.__dict__.get("dev")
tomo_fit_xray_eye = np.zeros((2, 3))
params_x = _dev.omny_xray_gui.fit_params_x.get()
tomo_fit_xray_eye[0][0] = params_x["SineModel_0_amplitude"]
tomo_fit_xray_eye[0][1] = params_x["SineModel_0_shift"]
tomo_fit_xray_eye[0][2] = params_x["LinearModel_1_intercept"]
params_y = _dev.omny_xray_gui.fit_params_y.get()
tomo_fit_xray_eye[1][0] = params_y["SineModel_0_amplitude"]
tomo_fit_xray_eye[1][1] = params_y["SineModel_0_shift"]
tomo_fit_xray_eye[1][2] = params_y["LinearModel_1_intercept"]
self.client.set_global_var("tomo_fit_xray_eye", tomo_fit_xray_eye.tolist())
print("New alignment parameters loaded from XRayEye GUI fit:")
self._print_xeye_fit(tomo_fit_xray_eye)
@staticmethod
def _print_xeye_fit(fit):
"""Pretty-print the 2×3 X-ray eye fit array."""
print(
f" X: A={fit[0][0]:.4f}, B={fit[0][1]:.4f}, C={fit[0][2]:.4f}\n"
f" Y: A={fit[1][0]:.4f}, B={fit[1][1]:.4f}, C={fit[1][2]:.4f}"
)
def lamni_compute_additional_correction_xeye_mu(self, angle):
"""Evaluate the sinusoidal X-ray eye correction at *angle* degrees.
Returns:
tuple: ``(correction_x_mm, correction_y_mm)``
"""
tomo_fit_xray_eye = self.client.get_global_var("tomo_fit_xray_eye")
if tomo_fit_xray_eye is None:
print("Not applying any X-ray eye correction. No fit data available.\n")
return (0, 0)
correction_x = (
tomo_fit_xray_eye[0][0] * np.sin(
np.radians(angle) + tomo_fit_xray_eye[0][1]
)
+ tomo_fit_xray_eye[0][2]
) / 1000
correction_y = (
tomo_fit_xray_eye[1][0] * np.sin(
np.radians(angle) + tomo_fit_xray_eye[1][1]
)
+ tomo_fit_xray_eye[1][2]
) / 1000
print(
f"Xeye correction x={correction_x:.6f} mm,"
f" y={correction_y:.6f} mm @ angle={angle}\n"
)
return (correction_x, correction_y)
# ------------------------------------------------------------------
# Additional look-up-table corrections (iteration 1 and 2)
# ------------------------------------------------------------------
def read_additional_correction(self, correction_file: str):
"""Load the iteration-1 correction lookup table from *correction_file*."""
self.corr_pos_x, self.corr_pos_y, self.corr_angle = self._read_correction_file_xy(
correction_file
)
def read_additional_correction_2(self, correction_file: str):
"""Load the iteration-2 correction lookup table from *correction_file*."""
self.corr_pos_x_2, self.corr_pos_y_2, self.corr_angle_2 = (
self._read_correction_file_xy(correction_file)
)
def _read_correction_file_xy(self, correction_file: str):
"""Parse a correction file containing ``corr_pos_x``, ``corr_pos_y``
and ``corr_angle`` entries.
Returns:
tuple: ``(corr_pos_x, corr_pos_y, corr_angle)`` as lists of floats.
"""
with open(correction_file, "r") as f:
num_elements = f.readline()
int_num_elements = int(num_elements.split(" ")[2])
print(int_num_elements)
corr_pos_x = []
corr_pos_y = []
corr_angle = []
for _ in range(int_num_elements * 3):
line = f.readline()
value = line.split(" ")[2]
name = line.split(" ")[0].split("[")[0]
if name == "corr_pos_x":
corr_pos_x.append(float(value) / 1000)
elif name == "corr_pos_y":
corr_pos_y.append(float(value) / 1000)
elif name == "corr_angle":
corr_angle.append(float(value))
return corr_pos_x, corr_pos_y, corr_angle
def compute_additional_correction(self, angle):
"""Return the iteration-1 lookup-table correction for *angle*.
Returns:
tuple: ``(shift_x_mm, shift_y_mm)``
"""
return self._compute_correction_xy(
angle, self.corr_pos_x, self.corr_pos_y, self.corr_angle, label="1"
)
def compute_additional_correction_2(self, angle):
"""Return the iteration-2 lookup-table correction for *angle*.
Returns:
tuple: ``(shift_x_mm, shift_y_mm)``
"""
return self._compute_correction_xy(
angle, self.corr_pos_x_2, self.corr_pos_y_2, self.corr_angle_2, label="2"
)
def _compute_correction_xy(self, angle, corr_pos_x, corr_pos_y, corr_angle, label=""):
"""Find the correction for the closest angle in the lookup table."""
if not corr_pos_x:
print(f"Not applying additional correction {label}. No data available.\n")
return (0, 0)
shift_x = corr_pos_x[0]
shift_y = corr_pos_y[0]
angledelta = np.fabs(corr_angle[0] - angle)
for j in range(1, len(corr_pos_x)):
newangledelta = np.fabs(corr_angle[j] - angle)
if newangledelta < angledelta:
shift_x = corr_pos_x[j]
shift_y = corr_pos_y[j]
angledelta = newangledelta
if shift_x == 0 and angle < corr_angle[0]:
shift_x = corr_pos_x[0]
shift_y = corr_pos_y[0]
if shift_x == 0 and angle > corr_angle[-1]:
shift_x = corr_pos_x[-1]
shift_y = corr_pos_y[-1]
print(f"Additional correction {label}: x={shift_x}, y={shift_y}")
return (shift_x, shift_y)
@@ -193,7 +193,7 @@ class LamNIOpticsMixin:
leyex_in = self._get_user_param_safe("leyex", "in")
leyey_in = self._get_user_param_safe("leyey", "in")
umv(dev.leyex, leyex_in, dev.leyey, leyey_in)
self.align.update_frame()
self.xrayeye_update_frame()
def _lfzp_in(self):
loptx_in = self._get_user_param_safe("loptx", "in")
@@ -35,7 +35,7 @@ class XrayEyeAlign:
coordinates (x and y) at 8 equally-spaced laminography angles over the
full 360°, submits them to the XRayEye widget's fit tab, and then reads
the resulting sinusoidal fit parameters back via
``lamni.align.read_xray_eye_correction_from_gui()``.
``lamni.read_xray_eye_correction_from_gui()``.
The key difference from the FlOMNI alignment is that LamNI needs *both*
x and y fits because the tilted rotation axis (61°) couples both
@@ -50,6 +50,9 @@ class XrayEyeAlign:
self.lamni = lamni
self.device_manager = client.device_manager
self.scans = client.scans
# Reset correction state on the lamni object (mirrors FlOMNI pattern)
self.lamni.reset_correction()
self.lamni.reset_xray_eye_correction()
# alignment_values[k] = [x_mm, y_mm]
# k=0 : FZP centre
# k=1 : sample at 0° (reference; shift_xy computed from k=0 vs k=1)
@@ -57,9 +60,6 @@ class XrayEyeAlign:
# ...
# k=8 : sample at 315°
self.alignment_values: dict[int, list[float]] = {}
# Reset correction state via the existing alignment object on lamni
self.lamni.align.reset_correction()
self.lamni.align.reset_xray_eye_correction()
# ------------------------------------------------------------------
# GUI shortcut
@@ -171,9 +171,9 @@ class XrayEyeAlign:
self._reset_init_values()
# --- Step 0: FZP centre ------------------------------------------
self._disable_rt_feedback()
#self._disable_rt_feedback()
self.lamni.lfzp_in()
self._enable_rt_feedback()
#self._enable_rt_feedback()
self.update_frame(keep_shutter_open)
@@ -207,9 +207,9 @@ class XrayEyeAlign:
self.gui.enable_submit_button(False)
self.lamni.loptics_out()
self._disable_rt_feedback()
time.sleep(0.3)
self._enable_rt_feedback()
#self._disable_rt_feedback()
#time.sleep(0.3)
#self._enable_rt_feedback()
self.update_frame(keep_shutter_open)
self.send_message("Find the sample and submit its centre.")
@@ -352,7 +352,7 @@ class XrayEyeAlign:
# Read fit parameters from the GUI into the global variable store
print("Loading new alignment parameters from X-ray eye GUI fit.")
self.lamni.align.read_xray_eye_correction_from_gui()
self.lamni.read_xray_eye_correction_from_gui()
if keep_shutter_open:
answer = input("Close the shutter now? [Y/n]: ").strip().lower()
@@ -363,10 +363,9 @@ class XrayEyeAlign:
else:
print("Shutter left open.")
# Return to 0 deg and reset scan centre
# Return to 0 deg
self._disable_rt_feedback()
self.tomo_rotate(0)
umv(dev.rtx, 0)
print(
"Done. You are ready to remove the X-ray eye and start ptychography scans.\n"
"Fine alignment: lamni.tomo_parameters(), then lamni.tomo_alignment_scan()"
@@ -425,4 +424,4 @@ class XrayEyeAlign:
# Push to XRayEye widget: feeds waveform_x (row 1) and waveform_y (row 2)
self.gui.submit_fit_array(data)
print(f"Fit data submitted with shape {data.shape}:\n{data}")
print(f"Fit data submitted with shape {data.shape}:\n{data}")
+10 -10
View File
@@ -41,13 +41,13 @@ With LamNI it can be difficult to relocate the sample between rotations. To keep
`lamni.xrayeye_alignment_start(keep_shutter_open=True)`
To manually reload the fit parameters after the procedure has completed:
`lamni.align.read_xray_eye_correction_from_gui()`
`lamni.read_xray_eye_correction_from_gui()`
**Note:** this reads from the live GUI widget via the `omny_xray_gui` device. It only works as long as the XRayEye GUI window remains open. If the window has been closed, reload from the archived text files instead:
`lamni.align.read_xray_eye_correction()`
`lamni.read_xray_eye_correction()`
(these files are written to `~/Data10/specES1/internal/xrayeye_alignmentvalues` at the end of every alignment run)
The correction is applied at each projection angle via
`lamni.align.lamni_compute_additional_correction_xeye_mu(angle)`
`lamni.lamni_compute_additional_correction_xeye_mu(angle)`
which is called automatically inside `lamni.tomo_scan_projection()`.
To capture a single fresh frame without running the full alignment:
@@ -68,12 +68,12 @@ The sample fine alignment can be obtained using ptychography. For this a short l
* Record a last projection for all scans to reconstruct `lamni.tomo_scan_projection(0)` and wait for the reconstructions to be complete
* Run `SPEC_ptycho_align.m` (in Matlab, **force ptycho=1**, and **correct scan numbers**)
* Click the sample position in the Matlab GUI and then load the generated file by, for example
`lamni.align.read_additional_correction('/sls/X12SA/data/e20632/Data10/cxs_software/ptycho/correction_lamni_um_S05389_lamni_fit.txt')`
* With this alignment a second iteration could be performed. To read the second correction file use `lamni.align.read_additional_correction_2()`
`lamni.read_additional_correction('/sls/X12SA/data/e20632/Data10/cxs_software/ptycho/correction_lamni_um_S05389_lamni_fit.txt')`
* With this alignment a second iteration could be performed. To read the second correction file use `lamni.read_additional_correction_2()`
#### Shifting the FOV
* `lamni.align.tomo_fovx/y_offset=value` [mm] will shift the field of view. Perform this adjustment from projections collected at **lsamrot 0 degrees**.
* `lamni.tomo_fovx/y_offset=value` [mm] will shift the field of view. Perform this adjustment from projections collected at **lsamrot 0 degrees**.
### Laminography scan
@@ -89,9 +89,9 @@ Start the laminography scan by
#### Reset corrections
* `lamni.align.reset_correction()`
* `lamni.align.reset_correction_2()`
* `lamni.align.reset_xray_eye_correction()`
* `lamni.reset_correction()`
* `lamni.reset_correction_2()`
* `lamni.reset_xray_eye_correction()`
#### Adjusting beam size with feedback running
@@ -203,4 +203,4 @@ Following functions exist to move the optics in and out, the naming is self-expl
* `lamni.loptics_out()`
* `lamni.losa_in()`
* `lamni.losa_out()`
* `lamni.lfzp_in()`
* `lamni.lfzp_in()`