# Refactor CHI_scan.py using flying_z.py's camonitor + timestamp correlation
**Date:** 2026-04-15
**Branch:** bl4b-alignment-integration (tasking) → a new branch on `bl4b-scripts` (submodule, currently `ScriptScan/2026A`)
**Target output:**`bl4b-scripts/flying_chi.py`
**Status:** Plan — awaiting implementation
---
## 1. Goal
Produce a new standalone Spyder-runnable script at `bl4b-scripts/flying_chi.py`
that performs the **same alignment function** as `CHI_scan.py` (find the
`BL4B:Mot:chis` position that maximises σ_X, or minimises σ_Y, or maximises
integrated intensity of the beam on the 2D detector) using the **flying-scan
pattern** established in `bl4b-scripts/flying_z.py`:
- One continuous motor sweep from `startCHI` to `endCHI` instead of an N-point
step scan.
-`camonitor` callbacks on motor `.RBV`, proton charge, and the detector
observables — each callback appends a `(timestamp, value)` tuple to a
per-PV list.
- Post-sweep reduction: `np.interp` every observable onto the `chis.RBV`
timestamp grid, then run the same CHI_scan.py fit functions on the resulting
`(chi, observable)` arrays.
Net effect: replace 11 discrete dwell points (each accumulating 1 mC of
proton charge) with one continuous sweep whose sample density is set by the
detector's update cadence — typically yielding 30–100× more (chi, σ) samples
in roughly the same wall-clock time.
---
## 2. Source material
| File | Role |
|---|---|
| `bl4b-scripts/flying_z.py` (159 lines, Apr 14 2026) | Template for the camonitor / interp / fit pattern on `BL4B:Mot:hs`. |
| `bl4b-scripts/CHI_scan.py` | Reference for PV list, slit setup, fit functions, ROI, fit-mode semantics, and the final move-to-fit behaviour. |
| `bl4b-scripts/CHI_measurement.py` | Reference for the FitImage (N-slice line-fit) approach — **deferred** from v1. |
| `tasking/analysis-chis-scan-integration.md` §4, §9, §13 | Confirms ADnED exposes 1D projection waveforms as `BL4B:Det:N1:Det1:XY:Stat:ProfileAverage{X,Y}_RBV`. This is the key insight that lets us `camonitor` shape observables without shipping the full 256×304 image. |
---
## 3. Key design differences from flying_z.py
### 3.1 The observable is a waveform, not a scalar
flying_z.py reduces the detector to a single scalar (`EventRate_RBV`) and a
single normalisation (`PCharge`). CHI alignment needs beam *shape*. To get
that without hauling a 256×304 2D array at every update we use ADnED's
pre-computed 1D projections:
-`BL4B:Det:N1:Det1:XY:Stat:ProfileAverageX_RBV` — 256-element X projection
-`BL4B:Det:N1:Det1:XY:Stat:ProfileAverageY_RBV` — 304-element Y projection
Each profile update is ~1–2 KB of payload. Our callback reduces the waveform
to scalars *in the callback* (weighted mean, σ, integrated area over the ROI)
and stores only the scalars. Memory is O(N_samples) × 4 floats per profile,
not O(N_samples × 300).
### 3.2 `EventUpdatePeriod = 10 ms` is unsafe for profile PVs
flying_z.py pins `EventUpdatePeriod = 10 ms` during the sweep. Q5 diagnostic