number stimulus outputs in device stage order

The output index now equals the device stage, so outputs follow the order the
device measures them: master port 1 through its switch positions, then port 2.
The previous position-major numbering permuted the middle two against the
physical order, so data captured before this needs its calibration re-captured.
This commit is contained in:
Ayzen
2026-10-02 13:57:33 +03:00
parent 7329750c0c
commit 61ef8720e1
3 changed files with 97 additions and 46 deletions
@@ -73,13 +73,24 @@ def build_output_channels(
) -> tuple[OutputChannel, ...]:
"""Map every (master port, switch position) pair to an output and a device stage.
Outputs are numbered switch-position-major, so positions of the FIRST switch
setting keep the output indices they have without per-point switching (with
``switch_substage_count=1`` this is the identity). Stages are numbered
port-major, because the master bitstream takes the excited port from the upper
bits of the stage counter and the switch position from the lower bits — which
is also what makes the switch line a plain alternation the controller can drive
without tracking an absolute stage number.
**Output index equals the device stage**, so outputs are numbered in exactly the
order the device measures them: port 1 through all switch positions, then port 2
through all of them. That is the order an operator sees on a scope while hanging
antennas, and matching it is worth more than any renumbering cleverness.
Both numbers are port-major because the master bitstream takes the excited port
from the UPPER bits of the stage counter and the switch position from the lower
bits. The low bit being the switch position is what lets the trigger-loop
controller just invert its pin every cycle instead of tracking an absolute stage.
Before 2026-09-17 outputs were numbered switch-position-major, which kept
outputs 0/1 meaning the same thing with and without per-point switching but
permuted the middle two relative to the physical order (slots 0,1,2,3 landed on
outputs 0,2,1,3). Data captured under that layout is NOT comparable with data
captured now: outputs 1 and 2 swapped meaning, so calibration and reference sets
from before the change have to be re-captured.
With ``switch_substage_count=1`` the layout is the stock one either way.
This is the single source of truth shared by the protocol builder, the cycle
collector and the acquisition service, so the mapping cannot drift between them.
@@ -94,12 +105,13 @@ def build_output_channels(
)
return tuple(
OutputChannel(
index=substage * len(stimulus_ports) + port_index,
# Один и тот же номер: выход — это и есть стадия прибора.
index=port_index * substage_count + substage,
stimulus_port=port,
stage_index=port_index * substage_count + substage,
)
for substage in range(substage_count)
for port_index, port in enumerate(stimulus_ports)
for substage in range(substage_count)
)