Files
Thomas Ales d6a56266b7 Driver support for multi-row FastFrame bursts; fix read_raw block parsing
Groundwork for burst acquisition: the scope needs to report and transfer a
whole multi-row FastFrame acquisition, and the BBD needs to gate its trigger
output per row rather than staying armed for the scan.

tektronix_base
- get_fastframe_max_frames() exposes HORizontal:FASTframe:MAXFRames?, which
  is what sizes a burst once the horizontal settings are fixed.
- transfer_fastframe_bulk() pulls a burst as one contiguous buffer. Unlike
  transfer_fastframe it does not assume how the scope frames the response:
  it accumulates until the expected byte count is reached, so one large IEEE
  block and one block per frame both work.
- set_data_encoding() / set_data_width() make the transfer format settable
  instead of inherited from whatever the front panel was left on.
- read_raw() had two real defects. The length-digit read used a bare recv()
  and only checked the length afterwards, so a short read raised "Failed to
  read data length" on a perfectly good transfer; it now goes through a
  _recv_exact() helper, as does the trailing separator. And a #0
  indeterminate-length block was parsed as int("") -> ValueError. #0 is
  normally delimited by EOI, which a raw socket never sees, so read_raw now
  takes expected_bytes to size it. The bulk transfer relies on this.

pybbd202
- arm_scan_gate(axis, armed) raises and drops the max-velocity trigger
  output the scope's AND-gate uses. A burst spans several rows with the
  scope running throughout, so the gate must be low for the flyback or the
  return move reaches max velocity and injects frames between rows.
- set_trigger_verified() reads the mode back after setting it. set_trigger
  is fire-and-forget over the shared TX queue; burst mode toggles the gate
  between every row, where a dropped change silently corrupts the file
  rather than failing loudly.
- set_trigger_gate_off() so the scan can leave the output idle on exit.
- TRIGOUT_GATE_OFF is deliberately marked unverified. §7.6 of the BBD203
  protocol doc describes `mode` as an enumeration capping at 0x11, which
  contradicts the bitmask this driver actually sends (TRIGOUT_MAXV = 0x90,
  known working), so the doc cannot settle which value idles the pin low.
  The engine's preflight check resolves it on the rig instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 12:16:20 -05:00

65 lines
2.1 KiB
Python
Executable File

'''
APT Constants and Registries
Thomas Ales | Feb 2026
'''
from enum import IntFlag
class StatusBits(IntFlag):
MOT_SB_CWHARDLIMIT = 0x01
MOT_SB_CCWHARDLIMIT = 0x02
MOT_SB_INMOTIONCW = 0x10
MOT_SB_INMOTIONCCW = 0x20
MOT_SB_HOMING = 0x200
MOT_SB_HOMED = 0x400
MOT_SB_TRACKING = 0x1000
MOT_SB_SETTLED = 0x2000
MOT_SB_POSITIONERROR = 0x4000
MOT_SB_INSTRERROR = 0x8000
MOT_SB_INTERLOCK = 0x10000
MOT_SB_OVERTEMP = 0x20000
MOT_SB_BUSVOLTFAULT = 0x40000
MOT_SB_COMMUTATIONERROR = 0x80000
MOT_SB_DIGIP1 = 0x100000
MOT_SB_OVERLOAD = 0x1000000
MOT_SB_POWEROK = 0x10000000
MOT_SB_ERROR = 0x40000000
MOT_SB_ENABLED = 0x80000000
# combined masks for checking various
# states
MOT_ANY_MOVE = MOT_SB_INMOTIONCW | MOT_SB_INMOTIONCCW
MOT_ANY_ERR = (MOT_SB_OVERTEMP | MOT_SB_BUSVOLTFAULT |
MOT_SB_COMMUTATIONERROR | MOT_SB_OVERLOAD |
MOT_SB_ERROR | MOT_SB_INSTRERROR)
class TriggerBitsServo(IntFlag):
TRIGIN_HIGH = 0x01
TRIGIN_RELMOVE = 0x02
TRIGIN_ABSMOVE = 0x04
TRIGIN_HOMEMOVE = 0x08
TRIGOUT_HIGH = 0x10
TRIGOUT_INMOTION = 0x20
TRIGOUT_MOTIONCOMPLETE = 0x40
TRIGOUT_MAXVELOCITY = 0x80
TRIGOUT_MAXV = TRIGOUT_HIGH | TRIGOUT_MAXVELOCITY
# Gate off: no trigger-out function selected, so the pin idles inactive.
#
# Treat this as unverified until it has been checked on the rig. §7.6 of
# docs/hardware/BBD203_Communications_Protocol.md documents `mode` as an
# enumeration capping at 0x11, which flatly contradicts the bitmask this
# driver actually sends (TRIGOUT_MAXV = 0x90, known working), so the doc
# cannot settle what makes the pin idle low. Under the bitmask reading 0x00
# clears everything and the pin sits low. If instead TRIGOUT_HIGH is an
# active-high *polarity* bit, clearing it means active-low and the pin idles
# HIGH — which in burst mode floods the acquisition with flyback frames.
# ScanEngine's gate-off preflight catches that; if it trips, change this to
# TriggerBitsServo.TRIGOUT_HIGH.
TRIGOUT_GATE_OFF = TriggerBitsServo(0)