The shell (shell.dart): a drawer from the left edge or each page's menu button. Dash, Signals, Captures; Tools (Camera, Link test, Orin power options), the rarely used ones, after pirate-station-aide; Settings (gateway address, dark/bright, build-time theme and landing). Landing view at build time: YUKART_HOME=dash|signals, signals for now. Dash, Signals and Captures stay alive while hidden; Captures has its own navigator, so a run survives switching views. Back goes inside Captures, then to the landing view, then out. The mock banner sits over every page. main.dart is startup only; the old home screen is pages/signals.dart; dialogs and sheets moved to actions/; the protocol client, USB binding and Reading to link/. lib/README.md catalogues it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
7.3 KiB
type, date
| type | date |
|---|---|
| report | 2026-10-11 |
USB link test: plain cable vs PD hub
Does the PD hub degrade the tablet ⇄ Orin USB (NCM) link? Measured from the app
(the only place on the tablet whose sockets use the link) with the link test,
lib/linktest.dart (now lib/pages/link_test.dart) + tool/linktest_server.py.
Short version
- No measurable effect of the PD hub, with or without the charger connected. All three setups: 0% loss at every size and rate up to 500 packets/s, the same latencies, 0 TCP retransmits.
- The loss seen earlier the same day did not come back. Before these runs, ICMP ping from the Orin lost 2.5–32% at 20 packets/s through the hub. After a replug, the same ping sweep is 0% through the hub too. That connection was in a bad state; the hub is not shown to be the cause. Cause unknown; worth watching for (the soak below is the app-shaped check).
- The link is asymmetric: tablet → Orin ~22 MB/s, Orin → tablet 1.9–5.6 MB/s and variable, with ~30 ms RTT under load and no loss (the Orin's gadget TX side queues rather than drops).
- App-shaped traffic is fine: 30 s at 10 packets/s, longest silence ≤ 144 ms, against a 5 s heartbeat timeout.
- adb push through the SSH tunnel stays slow (0.14 MB/s, 223 Orin retransmits
per 20 MB) on a healthy link, while the app's TCP over the same link runs at
5.6 MB/s with none. Specific to the adbd connection; not the hub, not routing
(
tcp_fwmark_accept=1). Unexplained.
Results
Link USB 2.0 high-speed in all runs. TCP: 5 s each way; UDP: 3 s per cell, RTT p50 / p99 / max in ms.
| Plain C-to-C | Hub + charger | Hub, no charger | |
|---|---|---|---|
| UDP loss, 64 B and 1400 B, 10–500 /s | 0% | 0% | 0% |
| UDP 64 B 10/s RTT | 4.1 / 24.2 / 24.2 | 4.0 / 10.5 / 10.5 | 4.1 / 5.6 / 5.6 |
| UDP 1400 B 500/s RTT | 2.8 / 7.3 / 39.7 | 2.7 / 9.4 / 42.5 | 2.7 / 16.6 / 60.2 |
| Soak 64 B 10/s 30 s: loss, max gap | 0%, 144 ms | 0%, 135 ms | 0%, 104 ms |
| TCP Orin → tablet | 1.85 MB/s | 4.06 MB/s | 5.57 MB/s |
| TCP tablet → Orin | 21.8 MB/s | 22.4 MB/s | 22.6 MB/s |
| Orin TCP retransmits | 0 | 0 | 0 |
| ICMP ping from the Orin, 20/s, 56–1472 B | 0% | 0% | 0% |
For reference, the same test over WiFi to the Orin's LAN address: 0% loss, about 25 MB/s each way.
Raw records: ~/linktest/results.jsonl on the car Orin; tabulate with
python3 tool/linktest_summary.py ~/linktest/results.jsonl [label…].
Running it
On the Orin: python3 linktest_server.py (from tool/, copied to ~/linktest/).
On the tablet: Link test in the link bar, or remotely:
adb -s localhost:15555 shell am start -n dev.yukart.display/.MainActivity --es linktest <label>[@<host>]
The host defaults to the gateway's. A run takes about 75 s and reports each result to the Orin as it goes.
Download in the car setup (PD hub + charger)
Two identical download series (dl:), then a burst-vs-idle series (bi:). Raw
records under the labels car-dl1, car-dl2, car-bi1.
Paced streams are clean. 30 frames/s at 0.5, 1, 2 and 4 MB/s, 10 s each, in both series: every frame arrived, the full rate was delivered, and queueing never exceeded 44 ms. A camera-like stream up to 4 MB/s (32 Mbit/s) fits.
Unpaced traffic is not:
| Series 1 | Series 2 | |
|---|---|---|
| Bulk, 5 × 10 s: rate while flowing | 5.6 MB/s | 5.6 MB/s |
| Bulk: longest stall per run | 1.8, 14.0, 3.6, 0.3, 0.8 s | 0.2, 3.7, 0.8, 0.8, 14.4 s |
| Bulk: Orin retransmits | 0 | 0 |
| Burst 64 KB after 1 s idle, p50 | 17 ms | 18 ms |
| Burst 256 KB, p50 | 12.1 s | 14.1 s |
| Burst 1 MB, p50 | 14.8 s | 13.8 s |
| Burst 4 MB, p50 | 4.5 s | 4.6 s |
| Burst connection: Orin retransmits, smoothed RTT, RTO | 50, 2.35 s, 10.8 s | 49, 2.39 s, 11.0 s |
Not idle-related. 256 KB bursts on fresh connections, 8 each:
| Idle before each burst | p50 | Bursts over 1 s | Orin retransmits, smoothed RTT |
|---|---|---|---|
| 0 | 12.6 s | 7 of 8 | 15, 1.6 s |
| 50 ms | 61 ms | 1 of 8 | 8, 1.8 s |
| 200 ms | 14.3 s | 6 of 8 | 13, 1.8 s |
| 1 s | 56 ms | 0 of 8 | 0, 16 ms |
| 1 s, 50/s UDP keepalive | 56 ms | 0 of 8 (3 near 0.5 s) | 5, 21 ms |
| 0 (repeat) | 13.6 s | 8 of 8 | 14, 1.5 s |
Reading. A fresh connection's first burst is fast. Then a few segments go missing, and the Orin measures round trips of 1.5–2.4 s on a 3 ms link: packets are being held for seconds, not only dropped. The smoothed RTT and the RTO (10–16 s) inflate, and every later burst on that connection waits out the RTO. Bulk stalls of up to 14 s with zero retransmits are the same holding, seen without loss. Paced traffic at ≤ 4 MB/s never triggers it.
Nothing on the Orin's side records it: no kernel messages, tx_dropped and
tx_errors 0 on usb1. The packets vanish or wait inside the USB layer, in the
Orin's NCM gadget or the tablet's NCM host driver; the tablet's sysfs is not
readable without root.
Consequences:
- Anything the Orin sends to the tablet should be paced, not dumped at line rate: video at a steady frame rate is fine; a TTS utterance should stream in chunks rather than be written in one go (the protocol already plans chunks).
- A connection that has hit this is poisoned for many seconds (inflated RTO). The heartbeat timeout will see it as a dead link and reconnect, which resets it.
adb push/installis exactly an unpaced bulk transfer, which explains its 0.14 MB/s.
Pacing fixes it
The Tegra kernel (5.15.148-tegra) ships without fq, tbf, htb or fq_codel,
so egress cannot be shaped per interface with tc. TCP's own pacing needs no
qdisc: the server capped each download socket with SO_MAX_PACING_RATE at
5 MB/s (LINKTEST_PACE=5000000), just under the ~5.6 MB/s the link carries
when it flows. Labels pace5-bi, pace5-dl:
| Unpaced | Paced to 5 MB/s | |
|---|---|---|
| Bulk, 5 × 10 s | 2.3–5.4 MB/s, stalls up to 14 s | 4.96–4.99 MB/s every run, longest stall 94 ms |
| Bulk, 250 ms windows | min 0, up to 56 empty | min 4.1 MB/s, none empty |
| Burst 256 KB, p50 | 12–14 s | 58–78 ms |
| Burst 1 MB / 4 MB, p50 | 14 s / 4.5 s | 225 ms / 863 ms |
| Burst-connection retransmits | ~50 | 0–1 |
| Idle gap before burst | erratic | no effect |
| Paced 30 fps streams, ≤ 4 MB/s | clean | clean |
Every Orin → tablet sender should cap its socket at ~5 MB/s
(SO_MAX_PACING_RATE, one setsockopt). For svc-gw that is the WebSocket
socket; for a camera relay, its stream socket. Senders that cannot be changed
(sshd for the adb tunnel) stay affected.
No second data point
- RNDIS: with NCM removed from the gadget (RNDIS + ACM only), the Redmi creates no network interface at all: its kernel has no RNDIS host driver, or does not bind it. NCM is the only option for this tablet.
- Kyocera Torque 5G (KYG01, Qualcomm lito, Android 13, kernel 4.19): has
cdc_ncmbut no RNDIS driver either. Neither through the PD hub nor on a direct C-to-C cable did it enumerate the Orin: on the direct cable it reports itself as host with a device attached, yet nothing appears on its bus and the Orin's UDC stayssuspended. Through the hub it charges and is not host at all. Even enumerated, its Android 13 Ethernet service only acceptseth\d+, while the NCM link would be namedusb0(the Redmi's Android 16 acceptsusb\d+).
So whether the holds come from the Orin's NCM gadget or the Redmi's host side is still open; the ~5.6 MB/s downstream ceiling is likewise unattributed.