diff --git a/csaxs_bec/bec_ipython_client/plugins/LamNI/lamni.py b/csaxs_bec/bec_ipython_client/plugins/LamNI/lamni.py index 90cfcecc..286982ec 100644 --- a/csaxs_bec/bec_ipython_client/plugins/LamNI/lamni.py +++ b/csaxs_bec/bec_ipython_client/plugins/LamNI/lamni.py @@ -733,6 +733,21 @@ class LamNI(TomoQueueMixin, LamNIAlignmentMixin, LamNIOpticsMixin, LamniGuiTools def lights_on(self): self.device_manager.devices.lsamx.controller.lights_on() + def lamni_move_to_scan_center(self, shift_x: float, shift_y: float, angle: float): + """ + LamNI scan to move the interferometer to the computed scan center based on the provided shift and angle. + + Args: + shift_x (float): Shift x. + shift_y (float): Shift y. + angle (float): Angle. + + Returns: + ScanReport + """ + scans = builtins.__dict__.get("scans") + return scans.lamni_move_to_scan_center(shift_x=shift_x, shift_y=shift_y, angle=angle) + # ------------------------------------------------------------------ # Global parameters (backed by BEC global vars) # ------------------------------------------------------------------ diff --git a/docs/user/ptychography/lamni.md b/docs/user/ptychography/lamni.md index 12bc9c03..4257352b 100644 --- a/docs/user/ptychography/lamni.md +++ b/docs/user/ptychography/lamni.md @@ -67,6 +67,20 @@ To capture a single fresh frame without running the full alignment: `lamni.xrayeye_update_frame()` or with the shutter left open: `lamni.xrayeye_update_frame(keep_shutter_open=True)` +##### Checking the x-ray eye alignment with the eye still in + +While the X-ray eye is still in, you can move the interferometer to the scan centre of any angle and look at where the sample ends up, without starting a scan: +`lamni.lamni_move_to_scan_center(shift_x, shift_y, angle)` + +This is the same move that the alignment procedure and every scan use to place the sample. `shift_x` and `shift_y` are the base shift in mm, i.e. the centre found with the first click at 0° (`lamni.tomo_fovx_offset`, `lamni.tomo_fovy_offset`), `angle` is the rotation angle in degrees. The shift is rotated with the angle using the LamNI geometry. The command does not rotate the sample itself and it returns a scan report, so add `.wait()` to block until the move is done. + +Example, checking the correction at 90°: + +1. `lamni.feedback_disable()` and rotate: `umv(dev.lsamrot, 90)` +2. `lamni.lamni_move_to_scan_center(lamni.tomo_fovx_offset, lamni.tomo_fovy_offset, 90).wait()` (this re-enables the feedback) +3. `lamni.xrayeye_update_frame(keep_shutter_open=True)` to look at the sample +4. `lamni.lamni_compute_additional_correction_xeye_mu(90)` prints the correction (x and y, in mm) that the scan will add on top of this position. The sample should appear displaced from the FZP centre by the size of this correction, and the correction compensates for that displacement. A clearly different displacement means the fit is off, e.g. a bad click, and the alignment should be repeated. + * If slits were opened during alignment, close the slits: `slits` to around 0.3 * `lamni.leye_out()` remove the X-ray eye and move the flight tube in * *possibly check slit1, idgap* @@ -75,6 +89,22 @@ or with the shutter left open: `lamni.xrayeye_update_frame(keep_shutter_open=Tru The sample fine alignment can be obtained using ptychography. For this a short laminogram has to be recorded. +**How the shift at each projection angle is computed.** `lamni.tomo_scan_projection(angle)`, which is used by the alignment scan and by the laminography scan, combines several contributions. All are evaluated for the requested angle: + +| Contribution | Source | Applied as | +|---|---|---| +| Base shift | first click (0°) of the x-ray eye alignment, stored as `lamni.tomo_fovx_offset` / `tomo_fovy_offset` | scan centre (`center_x`, `center_y`), rotated with the angle | +| X-ray eye correction | sinusoidal fit `A·sin(angle + B) + C` of the 8 clicked positions, `lamni.lamni_compute_additional_correction_xeye_mu(angle)` | added to the shift | +| Manual shift | `lamni.manual_shift_x` / `manual_shift_y`, constant for all angles | added to the shift | +| Additional correction 1 | lookup table from the fine alignment, `lamni.read_additional_correction(file)` | subtracted from the shift | +| Additional correction 2 | lookup table of the second iteration, `lamni.read_additional_correction_2(file)` | subtracted from the shift | + +The shift is therefore `manual + x-ray eye − correction 1 − correction 2`, on top of the rotated base shift. The x-ray eye fit was measured after the base shift was applied, so the two do not count twice. + +The correction tables are read from the files written by `BEC_ptycho_align`, with one shift per alignment angle. There is no interpolation: for each projection the entry with the closest angle is used. If a table has no data, it is skipped and `Not applying additional correction` is printed. Without an x-ray eye fit, `Not applying any X-ray eye correction` is printed. Each correction can be removed again with `lamni.reset_correction()`, `lamni.reset_correction_2()` and `lamni.reset_xray_eye_correction()`. + +The alignment scan itself already uses the x-ray eye correction and any correction tables that are loaded. So the ptychographic alignment measures what is left over after those corrections, which is why a second iteration with `read_additional_correction_2()` is useful. + * `lamni.tomo_parameters()` adjust the ptychographic scan parameters for the alignment scan (FOV/step size/counting time) — `tomo_type` and number of projections are ignored, since the alignment scan always runs its own fixed 12 points spread evenly across the full 360 degrees, independent of the main tomogram's settings. * `lamni.tomo_alignment_scan()` perform the alignment scan. Requires x-ray-eye alignment to have already been done — it will abort with a message otherwise. Scan numbers, angles and the existing x-ray-eye-fit offset at each angle are written to `~/data/raw/logs/ptychotomoalign_scannum.txt` and also printed at the end. * Run `BEC_ptycho_align` (in Matlab, **force ptycho=1**, and **correct scan numbers**) using the printed/logged scan numbers.