diff --git a/csaxs_bec/bec_ipython_client/plugins/omny/omny_rt.py b/csaxs_bec/bec_ipython_client/plugins/omny/omny_rt.py index f1b8c4af..f128598a 100644 --- a/csaxs_bec/bec_ipython_client/plugins/omny/omny_rt.py +++ b/csaxs_bec/bec_ipython_client/plugins/omny/omny_rt.py @@ -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][ diff --git a/csaxs_bec/devices/omny/rt/rt_omny_ophyd.py b/csaxs_bec/devices/omny/rt/rt_omny_ophyd.py index fb849c8d..82b0833a 100644 --- a/csaxs_bec/devices/omny/rt/rt_omny_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_omny_ophyd.py @@ -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 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: 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) }, diff --git a/csaxs_bec/devices/sim/sim_omny.py b/csaxs_bec/devices/sim/sim_omny.py index aec6b4df..aeb8c3de 100644 --- a/csaxs_bec/devices/sim/sim_omny.py +++ b/csaxs_bec/devices/sim/sim_omny.py @@ -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"