feat(flomni): trigger after a position change (interferometer_trigger_set_after_move, emitter_set_trigger_on_move -> em), three-axis move (controller.move_xyz -> p<x>,<y>,<z>); eg with 3 values
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TNtKEk4jAyjebMDQFwRHJQ
This commit was merged in pull request #335.
This commit is contained in:
1 parent
d9b23b9dc2
commit
8796953913
3 files changed
+57
-4
No files matched your search
@@ -724,6 +724,9 @@ class FlomniSampleTransferMixin:
|
||||
f"for {values['ontarget_hold_ms']:.1f} ms.")
|
||||
else:
|
||||
print("Detector trigger: former criterion (on-target flags of the FZP PID loops).")
|
||||
if values.get("trigger_on_move") is not None:
|
||||
mode = "on" if values["trigger_on_move"] else "off"
|
||||
print(f"Trigger after every position change (not only at scan points): {mode}.")
|
||||
|
||||
def interferometer_trigger_set_tolerance_nm(self, tolerance_nm: float):
|
||||
"""Set the detector-trigger tolerance in nm (per axis, x and y).
|
||||
@@ -755,6 +758,16 @@ class FlomniSampleTransferMixin:
|
||||
dev.rtx.controller.emitter_set_ontarget_hold_ms(hold_ms)
|
||||
self.interferometer_trigger_show()
|
||||
|
||||
def interferometer_trigger_set_after_move(self, enable: bool):
|
||||
"""Send one detector trigger after every position change, not only at scan points.
|
||||
|
||||
With True every direct move of the sample stage (e.g. umv of rtx / rty / rtz, or
|
||||
dev.rtx.controller.move_xyz) gives exactly one trigger, as soon as the position is stable
|
||||
(same tolerance and hold time as for scan points). False: triggers at scan points only.
|
||||
"""
|
||||
dev.rtx.controller.emitter_set_trigger_on_move(1 if enable else 0)
|
||||
self.interferometer_trigger_show()
|
||||
|
||||
def interferometer_trigger_help(self):
|
||||
"""Print the detector-trigger commands with a short explanation."""
|
||||
lines = [
|
||||
@@ -765,6 +778,9 @@ class FlomniSampleTransferMixin:
|
||||
" 2 finest; 0 = former",
|
||||
" criterion (FZP PID flags)",
|
||||
" flomni.interferometer_trigger_set_hold_ms(val) e.g. 3",
|
||||
" flomni.interferometer_trigger_set_after_move(True) also one trigger after every",
|
||||
" position change (False: off)",
|
||||
" dev.rtx.controller.move_xyz(x, y, z) move all three axes at once",
|
||||
"The values are live; after a restart of the control loop the start values",
|
||||
"(10 nm, 3 ms) from the loop parameter file apply again.",
|
||||
]
|
||||
|
||||
@@ -55,6 +55,8 @@ class RtFlomniController(Controller):
|
||||
"emitter_set_ontarget_tolerance",
|
||||
"emitter_set_ontarget_hold_ms",
|
||||
"emitter_help",
|
||||
"emitter_set_trigger_on_move",
|
||||
"move_xyz",
|
||||
]
|
||||
|
||||
def __init__(
|
||||
@@ -448,16 +450,21 @@ class RtFlomniController(Controller):
|
||||
without interruption.
|
||||
|
||||
Returns:
|
||||
dict: ontarget_tolerance_um, ontarget_hold_ms.
|
||||
dict: ontarget_tolerance_um, ontarget_hold_ms, trigger_on_move (None from a server
|
||||
that does not report it yet).
|
||||
"""
|
||||
ret = self.socket_put_and_receive("eg")
|
||||
ret = ret.split("\n")[0]
|
||||
values = [float(val) for val in ret.split(",")]
|
||||
if len(values) != 2:
|
||||
if len(values) not in (2, 3):
|
||||
raise RtCommunicationError(
|
||||
f"Expected to receive 2 on-target values. Instead received {values}"
|
||||
f"Expected to receive 2 or 3 on-target values. Instead received {values}"
|
||||
)
|
||||
return {"ontarget_tolerance_um": values[0], "ontarget_hold_ms": values[1]}
|
||||
return {
|
||||
"ontarget_tolerance_um": values[0],
|
||||
"ontarget_hold_ms": values[1],
|
||||
"trigger_on_move": values[2] if len(values) == 3 else None,
|
||||
}
|
||||
|
||||
def emitter_set_ontarget_tolerance(self, val_um: float) -> None:
|
||||
"""Set the detector-trigger tolerance in um, per axis.
|
||||
@@ -471,6 +478,30 @@ class RtFlomniController(Controller):
|
||||
"""Set the time in ms the position must stay within the tolerance before the trigger."""
|
||||
self.socket_put(f"eh{val_ms:.3f}")
|
||||
|
||||
def emitter_set_trigger_on_move(self, val: int) -> None:
|
||||
"""1: send one detector trigger after every direct position change (pa / p), once on target
|
||||
(same criterion as a step-scan point); 0: triggers at step-scan points only."""
|
||||
self.socket_put(f"em{1 if val else 0}")
|
||||
|
||||
def move_xyz(self, x: float, y: float, z: float) -> None:
|
||||
"""Move rtx, rty and rtz with one command (CommunicationServer p<x>,<y>,<z>).
|
||||
|
||||
The three targets change in the same loop step, so with trigger_on_move there is exactly one
|
||||
detector trigger for the move. Values as for the rtx / rty / rtz axes (axis sign applied).
|
||||
Does not wait for the move.
|
||||
"""
|
||||
if self.laser_tracker_check_signalstrength() == "toolow":
|
||||
raise RtError(
|
||||
"The interferometer signal is too low for movements. Realignment required."
|
||||
)
|
||||
targets = []
|
||||
for name, val in (("rtx", x), ("rty", y), ("rtz", z)):
|
||||
axis = self.get_axis_by_name(name)
|
||||
target_val = val * axis.sign
|
||||
axis.user_setpoint.setpoint = target_val
|
||||
targets.append(target_val)
|
||||
self.socket_put(f"p{targets[0]:.4f},{targets[1]:.4f},{targets[2]:.4f}")
|
||||
|
||||
def emitter_help(self) -> None:
|
||||
"""Print the ConstEmitter commands (loop runtime constants) with a short explanation."""
|
||||
t = PrettyTable()
|
||||
@@ -492,6 +523,8 @@ class RtFlomniController(Controller):
|
||||
" <= 0: FZP PID flags"),
|
||||
("emitter_set_ontarget_hold_ms(val_ms)", "eh",
|
||||
"detector trigger: ... held for val ms without interruption"),
|
||||
("emitter_set_trigger_on_move(val)", "em",
|
||||
"1: one detector trigger after every direct move (pa / p) once on target; 0: off"),
|
||||
]
|
||||
for row in rows:
|
||||
t.add_row(row)
|
||||
|
||||
@@ -229,6 +229,10 @@ over 0.5 ms) for a hold time. Both values are live and can be changed between sc
|
||||
low-resolution scan, 10 standard, 2 for the highest resolution (a few ms more per point);
|
||||
0 selects the former criterion (on-target flags of the FZP loops)
|
||||
- `flomni.interferometer_trigger_set_hold_ms(3)` — hold time in ms
|
||||
- `flomni.interferometer_trigger_set_after_move(True)` — additionally one trigger after every position change
|
||||
of the sample stage (e.g. `umv(dev.rtx, ...)`), as soon as the position is stable with the same tolerance and
|
||||
hold time; `False` (default): triggers at scan points only
|
||||
- `dev.rtx.controller.move_xyz(x, y, z)` — moves rtx, rty and rtz with one command (one trigger for the move)
|
||||
- `flomni.interferometer_trigger_help()` — this list
|
||||
|
||||
After a restart of the control loop the start values (10 nm, 3 ms) apply again. Measured with 3 µm steps
|
||||
|
||||
Reference in new issue
Block a user