Auto-align: level the sample on the DC bias levels from the camera window
The operator frames a good spot, confirms the two DC levels the detector reads there, and the rig then measures its own tilt: step 1.5 mm either side on X and then on Y, and tilt the platform until those levels come back. The correction that fixes an offset point is the correction that levels the whole travel — height error and tilt effect are both proportional to the offset — so the procedure ends by applying it and leaving it applied. Both directions are measured from the same starting tilt and averaged, which makes their disagreement a flatness read-out rather than something averaged away silently. core/auto_align.py holds the geometry and the search, Qt-free. The three T-axes' azimuths are the whole geometry: T1 lies along +X so it alone tilts along X, and T0/T2 move as an equal-and-opposite pair to tilt along Y without touching X (tilt_response derives that, and the tests pin it — an axis map that drifts would still converge, on the wrong axis). The search is a secant null on the split-detector difference: probe once to learn what a microstep is worth, sign included, then step at the null. It refuses to servo on a scope that has not re-triggered, escalates a probe that reads as no response before calling an axis dead, and stops at a per-axis travel limit. gui/align_bridge.py runs it on a worker thread; stopping is a threading.Event rather than a queued command, because the worker is inside a long handler for the whole run. The camera window carries the button and the progress window, and locks the scan panel and the jog pads while a run owns the stage. Adds immediate MEAN measurements and an acquisition count to the scope driver, and read_bias_mv to core/scope_inspect — the one scalar the inspection state was missing. KNOWN_ISSUES.md records what only the rig can settle: the probe step, the travel limit, the hold current, and whether the piston the X phase applies alongside its tilt matters. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,9 @@ scanengine-3 is a unified platform for scanning acoustic microscopy and precisio
|
||||
- **SAW Quality Check**: Acquire one row per angle — the row-wise middle of
|
||||
the ROI — as a v11 `.sras`, then compare every angle's SAW frequency on one
|
||||
graph to judge the alignment before a full run
|
||||
- **Auto-Align**: Level the sample from the camera window — step the stage
|
||||
1.5 mm either side on X and then Y, tilt the T-axes until the DC bias levels
|
||||
read what they read at the reference point, and leave the correction applied
|
||||
- **Real-time Monitoring**: Live status updates and progress tracking
|
||||
|
||||
## Hardware Components
|
||||
@@ -63,8 +66,9 @@ scanengine-3/
|
||||
│ ├── scan_resume.py # Resume planning (frontier rule)
|
||||
│ ├── scope_sras.py # Oscilloscope SCPI policy for SRAS
|
||||
│ ├── scope_burst.py # Burst-mode FastFrame sizing + row splitting
|
||||
│ ├── scope_inspect.py # Scope setup for pre-scan angle inspection
|
||||
│ ├── scope_inspect.py # Scope setup for inspection + bias read-back
|
||||
│ ├── angle_inspect.py # AngleInspector — park on a point per angle
|
||||
│ ├── auto_align.py # AutoAligner — tilt the sample level on the DC levels
|
||||
│ ├── saw_check.py # Middle-row SAW check: plan + alignment read-out
|
||||
│ ├── rotation.py # GR rotation axis settings + moves
|
||||
│ ├── sras_format.py # v7/v11 .sras writer, v6/v10 reader (mmap)
|
||||
@@ -84,6 +88,7 @@ scanengine-3/
|
||||
├── gui/ # Shared PyQt6 layer
|
||||
│ ├── scan_bridge.py # QtScanController over core.scan_engine
|
||||
│ ├── inspect_bridge.py # QtAngleInspector over core.angle_inspect
|
||||
│ ├── align_bridge.py # QtAutoAligner over core.auto_align
|
||||
│ ├── qt_t3r.py # Qt adapter over the T3R driver
|
||||
│ ├── qt_workers.py # QueueWorker / PollingQueueWorker bases
|
||||
│ ├── jog_panel.py # T3R + BBD202 jog controls (camera window)
|
||||
@@ -246,6 +251,32 @@ with SrasFile("/data/SRAS/demo-sawcheck.sras") as sras:
|
||||
|
||||
`saw_check_viewer.py` is the same read-out with the curves drawn.
|
||||
|
||||
### Levelling the sample (auto-align)
|
||||
|
||||
Two phases, because the operator sits between them: `prepare()` configures
|
||||
the rig and reads the DC levels where the stage stands, and `run()` only
|
||||
starts once those levels have been confirmed as the ones to hold.
|
||||
|
||||
```python
|
||||
from core.auto_align import AlignCallbacks, AutoAligner
|
||||
|
||||
aligner = AutoAligner(stage, scope, t3r,
|
||||
callbacks=AlignCallbacks(on_status=print))
|
||||
reference = aligner.prepare() # scope + T-axes configured, one reading
|
||||
print(reference.describe()) # "is the image correct?" happens here
|
||||
result = aligner.run() # X on T1, then Y on T0/T2
|
||||
print(result.describe())
|
||||
aligner.stop() # stage parked; the tilt stays applied
|
||||
```
|
||||
|
||||
The scope has to be cabled CH1 SAW / CH2 trigger / CH3 DC 1 / CH4 DC 2 — the
|
||||
same channels a scan uses, except that CH3 carries the DC monitor here rather
|
||||
than the max-velocity gate. Nothing rewires it; the app asks the operator to
|
||||
confirm the cabling, and refuses to servo on a scope that is not triggering.
|
||||
|
||||
In the main app the button is in the camera window, because judging the image
|
||||
is the first step of the procedure.
|
||||
|
||||
### Reading a scan file
|
||||
|
||||
`SrasFile` memory-maps the data block, so opening a multi-gigabyte scan
|
||||
|
||||
Reference in New Issue
Block a user