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>
- Collapsed Args:/Returns:/Raises: blocks that only restated the
signature (364 lines): tektronix_base 48% -> ~20% doc density,
helios_laser and uc480_camera likewise. Only docstrings whose entire
body was those sections were touched.
- Preserved verbatim the comments that carry hardware knowledge the code
can't express: uc480's USB split-transaction contention note (with its
measured fps), the IS_ALLOW_STARTER_FW_UPLOAD segfault explanation, the
QImage-copy rationale, and tektronix's NUMFRAMESACQuired warning.
- README: project structure, quick start, and every usage example now
describe code that exists (they referenced hardware/bbd202.py,
CoherentHOPSLaser, get_curve_binary, and 'python -m scanengine.app',
none of which do). Added a headless-scan example and a read-a-scan-file
example, since reuse without the GUI is the point of the refactor.
- SETUP: structure section defers to README instead of keeping a second
stale copy; documents the vendored uEye SDK and the Genesis quarantine.
- ruff is now clean repo-wide: fixed the remaining raise-from, unused
loop variables, placeholder f-strings, and a non-strict zip; the
widget-layout semicolon idiom is an explicit config ignore rather than
22 standing warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verified by repo-wide name search + transitive closure over internal
calls: the live apps use 25 methods (plus raw write/query); everything
else — cosmetic label styling, unused getters/setters, transfer_waveform,
acquire_waveform — had no callers. Also: linear-time chunk join in
read_raw instead of quadratic bytes += concat, and a typed except on its
debug path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>