Link test: download series (bulk sampled, warm bursts, paced) and burst-vs-idle; car results
CI / Analyze & Test (push) Successful in 10s
CI / Debug APK (push) Successful in 15s

Orin -> tablet over NCM: paced streams up to 4 MB/s are clean; unpaced
bursts and bulk hit multi-second holds that inflate the RTO to 10-16 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
2026-10-11 16:33:55 +09:00
parent 66eab916bc
commit 8f4e34d956
6 changed files with 330 additions and 12 deletions
+34 -7
View File
@@ -4,7 +4,12 @@
The tablet's own sockets are the only ones bound to the USB link (`adb shell`
traffic goes over WiFi), so the app drives the test and this end only serves:
7791/tcp down: client sends "<id> <secs>\\n", gets zeros for <secs>
7791/tcp down: client sends one line, then reads until the server closes:
"<id> <secs>" zeros for <secs>
"<id> burst" then per line "<bytes>": exactly <bytes> zeros,
on the same connection until the client closes
"<id> paced <B/s> <secs>" 30 frames/s, each [u32 length][f64 send time][zeros],
length counting the whole frame
7792/tcp up: client sends "<id>\\n" then data until EOF; gets "<bytes>\\n" back
7793/udp echo
7794/tcp results: one JSON line per connection, appended to the results file
@@ -51,15 +56,37 @@ def readline(conn):
def down(conn, peer):
test_id, secs = readline(conn).split()
end = time.monotonic() + float(secs)
test_id, *args = readline(conn).split()
mode = args[0] if args[0] in ("burst", "paced") else "bulk"
sent = 0
try:
while time.monotonic() < end:
sent += conn.send(CHUNK)
if mode == "burst":
# One warm connection, as an utterance would arrive on the open WebSocket.
while line := readline(conn):
left = int(line)
while left > 0:
n = conn.send(CHUNK[:min(left, len(CHUNK))])
left -= n
sent += n
elif mode == "paced":
rate, secs = float(args[1]), float(args[2])
size = max(12, int(rate / 30))
t0 = time.monotonic()
for i in range(int(secs * 30)):
# Pace on the schedule, not on the last send: a stall must not lower the offered rate.
delay = t0 + i / 30 - time.monotonic()
if delay > 0:
time.sleep(delay)
conn.sendall(struct.pack("!Id", size, time.time()) + bytes(size - 12))
sent += size
else:
end = time.monotonic() + float(args[0])
while time.monotonic() < end:
sent += conn.send(CHUNK)
except OSError as e:
log({"id": test_id, "kind": "tcp_down_orin", "error": str(e)})
log({"id": test_id, "kind": "tcp_down_orin", "peer": peer, "bytes": sent, **tcp_info(conn)})
log({"id": test_id, "kind": "tcp_down_orin", "mode": mode, "error": str(e)})
log({"id": test_id, "kind": "tcp_down_orin", "mode": mode, "args": args, "peer": peer, "bytes": sent,
**tcp_info(conn)})
def up(conn, peer):