feat(omny): rt_positions with the 4 Zygo channels (13-field sampler rows as flOMNI plus RotX), mirror table after the interferometer rework (1 Inc Angle ssi_3, 2 Inc ST FZP Y ssi_1, 3 Inc RotX ssi_4, 5 FZP Y ssi_1, 7 FZP X ssi_2; 4/6/8 unused), simulation rows
CI for csaxs_bec / test (push) Successful in 3m46s

Matches OMNY branch zygo-4004 (RT_sampler / OMNY_server r<n> 13 fields). Mirror table from Mirko 2026-11-05.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8zoDvEVcT4jbFjSNU9Rii
This commit is contained in:
Mirko HollerandClaude Opus 5.5 committed 2026-11-05 15:05:01 +01:00
1 parent 94a6ed3c08
commit 453abe925f
3 files changed
+51 -105

No files matched your search

@@ -33,7 +33,9 @@ class OMNY_rt_client:
"Device rtx is disabled; cannot read interferometer mirror parameters."
)
self.mirror_parameters = {
j: dev.rtx.controller.get_mirror_parameters(j) for j in range(1, 9)
j: params
for j in range(1, 9)
if (params := dev.rtx.controller.get_mirror_parameters(j)) # unused channels: None
}
@staticmethod
@@ -138,6 +140,8 @@ class OMNY_rt_client:
]
+ self.mirror_amplitutde_increase,
)
elif key.isdigit() and 1 <= int(key) <= 8 and int(key) not in self.mirror_parameters:
print(f"\nMirror channel {key} is not used (interferometer rework 2026-11-05).\r")
elif key.isdigit() and 1 <= int(key) <= 8:
self.mirror_channel = int(key)
opt_mirrorname = self.mirror_parameters[self.mirror_channel][
+40 -94
View File
@@ -109,13 +109,13 @@ class RtOMNYController(Controller):
self.ssi = {}
self._min_scan_buffer_reached = False
self.switchbox_socket = socket.socket()
# TODO (OMNY interferometer rework 2026-11-05, branch zygo-4004): the signal channels changed meaning -
# ssi_1 ST FZP Y, ssi_2 ST FZP X, ssi_3 RotZ, ssi_4 RotX, ssi_5 not read (before: ssi_1 OSA FZP Y, ssi_2 ST OSA Y,
# ssi_3 OSA FZP X, ssi_4 ST OSA X, ssi_5 Angle). The mirror list below is still the one before the rework; which
# mirrors remain and on which channel each is optimized is to be defined by Mirko (OMNY docs/feedback_report.md).
# Interferometer rework 2026-11-05 (Mirko): signals ssi_1 ST FZP Y, ssi_2 ST FZP X, ssi_3 RotZ, ssi_4 RotX,
# ssi_5 not read. Mirror (switchbox) channels: 1 Inc Angle (RotZ, ssi_3), 2 Inc ST FZP Y (was Inc ST OSA Y,
# ssi_1), 3 Inc RotX (new angle interferometer, ssi_4; thresholds provisional from channel 1), 4 unused (was
# OSA Y), 5 FZP Y (ssi_1), 6 unused (was OSA X), 7 FZP X (ssi_2), 8 unused (was OSA Y on the OSA FZP signal).
self.mirror_parameters = {
1: {
"opt_signalchannel": "ssi_5",
"opt_signalchannel": "ssi_3",
"opt_mirrorname": "Inc Angle",
"opt_averaging_time": 0.1,
"opt_steps1_pos": 1,
@@ -131,8 +131,8 @@ class RtOMNYController(Controller):
"opt_amplitude2_neg": 2000,
},
2: {
"opt_signalchannel": "ssi_2",
"opt_mirrorname": "Inc ST OSA Y",
"opt_signalchannel": "ssi_1",
"opt_mirrorname": "Inc ST FZP Y",
"opt_averaging_time": 0.1,
"opt_steps1_pos": 1,
"opt_steps1_neg": 1,
@@ -147,36 +147,20 @@ class RtOMNYController(Controller):
"opt_amplitude2_neg": 2000,
},
3: {
"opt_signalchannel": "ssi_1",
"opt_mirrorname": "Inc OSA FZP Y",
"opt_averaging_time": 0.25,
"opt_steps1_pos": 1,
"opt_steps1_neg": 2,
"opt_steps2_pos": 1,
"opt_steps2_neg": 3,
"opt_signal_stop": 7200,
"opt_signal_min_begin": 3000,
"opt_step_divider": 1,
"opt_amplitude1_pos": 2500,
"opt_amplitude1_neg": 2500,
"opt_amplitude2_pos": 2500,
"opt_amplitude2_neg": 2500,
},
4: {
"opt_signalchannel": "ssi_2",
"opt_mirrorname": "OSA Y",
"opt_signalchannel": "ssi_4",
"opt_mirrorname": "Inc RotX",
"opt_averaging_time": 0.1,
"opt_steps1_pos": 1,
"opt_steps1_neg": 1,
"opt_steps2_pos": 1,
"opt_steps2_neg": 1,
"opt_signal_stop": 5000,
"opt_signal_min_begin": 2000,
"opt_steps2_pos": 10,
"opt_steps2_neg": 10,
"opt_signal_stop": 2400,
"opt_signal_min_begin": 1000,
"opt_step_divider": 1,
"opt_amplitude1_pos": 3000,
"opt_amplitude1_neg": 3000,
"opt_amplitude2_pos": 3000,
"opt_amplitude2_neg": 3000,
"opt_amplitude1_pos": 2000,
"opt_amplitude1_neg": 2000,
"opt_amplitude2_pos": 2000,
"opt_amplitude2_neg": 2000,
},
5: {
"opt_signalchannel": "ssi_1",
@@ -194,24 +178,8 @@ class RtOMNYController(Controller):
"opt_amplitude2_pos": 3000,
"opt_amplitude2_neg": 3000,
},
6: {
"opt_signalchannel": "ssi_4",
"opt_mirrorname": "OSA X",
"opt_averaging_time": 0.2,
"opt_steps1_pos": 2,
"opt_steps1_neg": 1,
"opt_steps2_pos": 1,
"opt_steps2_neg": 1,
"opt_signal_stop": 8850,
"opt_signal_min_begin": 3000,
"opt_step_divider": 1,
"opt_amplitude1_pos": 3000,
"opt_amplitude1_neg": 3000,
"opt_amplitude2_pos": 3700,
"opt_amplitude2_neg": 2500,
},
7: {
"opt_signalchannel": "ssi_3",
"opt_signalchannel": "ssi_2",
"opt_mirrorname": "FZP X",
"opt_averaging_time": 0.2,
"opt_steps1_pos": 4,
@@ -226,24 +194,9 @@ class RtOMNYController(Controller):
"opt_amplitude2_pos": 2500,
"opt_amplitude2_neg": 2500,
},
8: {
"opt_signalchannel": "ssi_1",
"opt_mirrorname": "OSA Y USING SIGNAL OSA FZP",
"opt_averaging_time": 0.2,
"opt_steps1_pos": 1,
"opt_steps1_neg": 1,
"opt_steps2_pos": 1,
"opt_steps2_neg": 1,
"opt_signal_stop": 5000,
"opt_signal_min_begin": 2000,
"opt_step_divider": 1,
"opt_amplitude1_pos": 3000,
"opt_amplitude1_neg": 3000,
"opt_amplitude2_pos": 3000,
"opt_amplitude2_neg": 3000,
},
}
# def is_axis_moving(self, axis_Id) -> bool:
# # this checks that axis is on target
# axis_is_on_target = bool(float(self.socket_put_and_receive("o")))
@@ -270,7 +223,8 @@ class RtOMNYController(Controller):
threading.Thread(target=send_positions, args=(self, positions), daemon=True).start()
def get_mirror_parameters(self, channel):
return self.mirror_parameters[channel]
# None for an unused switchbox channel (4, 6, 8 since the interferometer rework 2026-11-05)
return self.mirror_parameters.get(channel)
def laser_tracker_check_and_wait_for_signalstrength(self):
# Plain print(), not send_client_info(): this runs on every RT setpoint move
@@ -1162,22 +1116,18 @@ class RtOMNYFlyer(Device):
AsyncMultiSignal,
name="data",
signals=[
# 4 Zygo 4004 channels since the OMNY interferometer rework (2026-11-05; RT_sampler / server r<n> with 13
# fields): as RtFlomniFlyer plus RotX. Before: ST-OSA, OSA-FZP, rotz and the combined ST-FZP (19 fields).
"target_x",
"average_x_st_osa",
"stdev_x_st_osa",
"target_y",
"average_y_st_osa",
"stdev_y_st_osa",
"average_x_osa_fzp",
"stdev_x_osa_fzp",
"average_y_osa_fzp",
"stdev_y_osa_fzp",
"average_rotz_st",
"stdev_rotz_st",
"average_x_st_fzp",
"stdev_x_st_fzp",
"target_y",
"average_y_st_fzp",
"stdev_y_st_fzp",
"average_rotz",
"stdev_rotz",
"average_rotx",
"stdev_rotx",
"average_stdeviations_x_st_fzp",
"average_stdeviations_y_st_fzp",
],
@@ -1316,25 +1266,21 @@ class RtOMNYFlyer(Device):
)
def _get_signals_from_table(self, return_table) -> dict:
self.average_stdeviations_x_st_fzp += float(return_table[16])
self.average_stdeviations_y_st_fzp += float(return_table[18])
# r<n>: id, valid samples, angle, target x, avg x, stdev x, target y, avg y, stdev y, avg rotz, stdev rotz,
# avg rotx, stdev rotx (13 fields since 2026-11-05; x / y = ST-FZP measured directly)
self.average_stdeviations_x_st_fzp += float(return_table[5])
self.average_stdeviations_y_st_fzp += float(return_table[8])
signals = {
"target_x": {"value": float(return_table[3])},
"average_x_st_osa": {"value": float(return_table[4])},
"stdev_x_st_osa": {"value": float(return_table[5])},
"average_x_st_fzp": {"value": float(return_table[4])},
"stdev_x_st_fzp": {"value": float(return_table[5])},
"target_y": {"value": float(return_table[6])},
"average_y_st_osa": {"value": float(return_table[7])},
"stdev_y_st_osa": {"value": float(return_table[8])},
"average_x_osa_fzp": {"value": float(return_table[9])},
"stdev_x_osa_fzp": {"value": float(return_table[10])},
"average_y_osa_fzp": {"value": float(return_table[11])},
"stdev_y_osa_fzp": {"value": float(return_table[12])},
"average_rotz_st": {"value": float(return_table[13])},
"stdev_rotz_st": {"value": float(return_table[14])},
"average_x_st_fzp": {"value": float(return_table[15])},
"stdev_x_st_fzp": {"value": float(return_table[16])},
"average_y_st_fzp": {"value": float(return_table[17])},
"stdev_y_st_fzp": {"value": float(return_table[18])},
"average_y_st_fzp": {"value": float(return_table[7])},
"stdev_y_st_fzp": {"value": float(return_table[8])},
"average_rotz": {"value": float(return_table[9])},
"stdev_rotz": {"value": float(return_table[10])},
"average_rotx": {"value": float(return_table[11])},
"stdev_rotx": {"value": float(return_table[12])},
"average_stdeviations_x_st_fzp": {
"value": self.average_stdeviations_x_st_fzp / (int(return_table[0]) + 1)
},
+6 -10
View File
@@ -11,8 +11,8 @@ port 8083) and seeded accordingly.
RT (OMNY_server.cpp, port 3333): the flOMNI protocol's ancestor. SimRtOmnySocket is a
dialect of the flOMNI RT simulation: identical pa/pr/l/T/Ts/y/k/scan handling, plus
J0/J1/J3 (no reply) and J2 -> "status,ssi0..ssi4", and 19-field sample rows
(ST-OSA and OSA-FZP interferometer combinations, rotz, ST-FZP).
J0/J1/J3 (no reply) and J2 -> "status,ssi0..ssi4", and 13-field sample rows
(ST-FZP x / y, rotz, rotx; Zygo 4004 only since the interferometer rework 2026-11-05, before 19 fields).
EPICS periphery (sample storage incl. shuttle/dewar bookkeeping, dewar, temperatures)
uses the patched-device pattern with in-memory mock PVs.
@@ -125,7 +125,7 @@ class SimOMNYGalilMotor(OMNYGalilMotor):
class SimRtOmnyState(SimRtFlomniState):
"""OMNY RT server state: flOMNI state plus 5 interferometer signals and 19-field rows."""
"""OMNY RT server state: flOMNI state plus 5 interferometer signals and 13-field rows."""
def __init__(self, host=None, port=None):
super().__init__(host=host, port=port)
@@ -141,20 +141,16 @@ class SimRtOmnyState(SimRtFlomniState):
angle = self.angle_rad
noise = _POSITION_NOISE_UM
fields = [f"{index}", "100", f"{angle:.6f}"]
# [3..8] target/avg/stdev of x and y (sample stage <-> OSA)
# [3..8] target/avg/stdev of x and y (sample stage <-> FZP)
for target_val in (target[0], target[1]):
fields += [
f"{target_val:.5f}",
f"{target_val + random.gauss(0.0, noise):.5f}",
f"{abs(random.gauss(noise, noise / 4)):.5f}",
]
# [9..12] OSA <-> FZP x/y avg/stdev
fields += [f"{random.gauss(0.0, noise):.5f}", f"{noise:.5f}"] * 2
# [13..14] rotz avg/stdev
# [9..10] rotz avg/stdev, [11..12] rotx avg/stdev
fields += [f"{random.gauss(0.0, 0.1):.5f}", f"{0.02:.5f}"]
fields += [f"{random.gauss(0.0, 0.1):.5f}", f"{0.02:.5f}"]
# [15..18] sample stage <-> FZP x/y avg/stdev
for target_val in (target[0], target[1]):
fields += [f"{target_val + random.gauss(0.0, noise):.5f}", f"{noise:.5f}"]
return ", ".join(fields) + "\n"