Fix USB read length to match official tool protocol

The XVF3800 expects exact wLength: count * type_size + 1 (status byte).
Requesting wrong length caused stale/corrupted responses when polling.
Split _read into _read_uint16 and _read_float matching official format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex
2026-04-12 17:30:29 -05:00
parent 8d73aaad5e
commit f4452865d1

View File

@@ -53,17 +53,25 @@ class XVF3800:
# don't call set_configuration() or detach_kernel_driver(), # don't call set_configuration() or detach_kernel_driver(),
# the audio driver needs to keep its interface. # the audio driver needs to keep its interface.
def _read(self, resid: int, cmdid: int, length: int) -> bytes: def _read_uint16(self, resid: int, cmdid: int, count: int) -> bytes:
"""Read parameter via USB control transfer. """Read uint16 parameters. Returns raw bytes including 1-byte status header."""
wValue = cmdid | 0x80 (read flag), wIndex = resid."""
try: try:
length = count * 2 + 1 # +1 for status byte
data = self.dev.ctrl_transfer( data = self.dev.ctrl_transfer(
CTRL_REQUEST_TYPE_IN, 0, CTRL_REQUEST_TYPE_IN, 0,
0x80 | cmdid, # wValue: cmdid with read bit 0x80 | cmdid, resid, length, timeout=1000)
resid, # wIndex: resource ID return bytes(data)
length * 4, # bytes to read (length is in words) except Exception as e:
timeout=1000 logger.debug("USB read error (resid=%d, cmd=%d): %s", resid, cmdid, e)
) return b""
def _read_float(self, resid: int, cmdid: int, count: int) -> bytes:
"""Read float32 parameters. Returns raw bytes including 1-byte status header."""
try:
length = count * 4 + 1 # +1 for status byte
data = self.dev.ctrl_transfer(
CTRL_REQUEST_TYPE_IN, 0,
0x80 | cmdid, resid, length, timeout=1000)
return bytes(data) return bytes(data)
except Exception as e: except Exception as e:
logger.debug("USB read error (resid=%d, cmd=%d): %s", resid, cmdid, e) logger.debug("USB read error (resid=%d, cmd=%d): %s", resid, cmdid, e)
@@ -88,10 +96,10 @@ class XVF3800:
def read_doa(self) -> tuple[int, bool]: def read_doa(self) -> tuple[int, bool]:
"""Read Direction of Arrival. Returns (angle 0-359, vad True/False). """Read Direction of Arrival. Returns (angle 0-359, vad True/False).
Response format: 1 status byte + 2 uint16 words (angle, vad).""" Response format: 1 status byte + 2 uint16 words (angle, vad)."""
data = self._read(GPO_RESID, DOA_VALUE_CMD, 2) # 2 uint16 words data = self._read_uint16(GPO_RESID, DOA_VALUE_CMD, 2)
if len(data) < 5: # 1 header + 4 data bytes if len(data) < 5:
return 0, False return 0, False
angle, vad = struct.unpack_from("<HH", data, 1) # skip 1-byte header angle, vad = struct.unpack_from("<HH", data, 1) # skip status byte
return angle % 360, bool(vad) return angle % 360, bool(vad)
# --- LEDs --- # --- LEDs ---