feat(latency): wall-clock skew handshake for cross-machine latency measurement
ci / rust (push) Has been cancelled

ClockProbe/ClockEcho on the QUIC control stream — 8 NTP-style rounds right after
Start; the min-RTT sample gives the host-client clock offset (clock_offset_ns
estimator in punktfunk-core). The client adds the offset to its receive instant
before differencing against the AU pts_ns, so the capture->reassembled latency
percentiles are valid across machines (skew_corrected=true), not just same-host.
Back-compat: an old host that doesn't answer the probe times out and the client
falls back to a shared-clock assumption (skew_corrected=false).

Host adds one ClockProbe dispatch arm in the control task; the client runs
clock_sync after Start, before the --remode/--speed-test tasks take the stream.

Validated cross-LAN (GNOME box -> dev box): offset ~ -1.57 ms (reproducible),
rtt ~140 us, p50 1.30 ms skew-corrected capture->reassembled — the offset is
exactly the systematic error the handshake removes. Unit tests for the message
codecs and the min-RTT offset estimator.

Roadmap §12: skew handshake done; remaining for true glass-to-glass is the Apple
client present-stamp (decode->present) plus the host render->capture term.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-12 11:20:20 +00:00
parent 50c9db785a
commit 05bc9ab22c
6 changed files with 225 additions and 13 deletions
+15 -2
View File
@@ -27,8 +27,8 @@ use punktfunk_core::config::{CompositorPref, FecConfig, FecScheme, GamepadPref,
use punktfunk_core::input::{InputEvent, InputKind};
use punktfunk_core::packet::{FLAG_PIC, FLAG_PROBE, FLAG_SOF};
use punktfunk_core::quic::{
endpoint, io, Hello, PairChallenge, PairProof, PairRequest, PairResult, ProbeRequest,
ProbeResult, Reconfigure, Reconfigured, Start, Welcome,
endpoint, io, ClockEcho, ClockProbe, Hello, PairChallenge, PairProof, PairRequest, PairResult,
ProbeRequest, ProbeResult, Reconfigure, Reconfigured, Start, Welcome,
};
use punktfunk_core::transport::UdpTransport;
use punktfunk_core::Session;
@@ -545,6 +545,19 @@ async fn serve_session(
if probe_tx.send(req).is_err() {
break; // data plane gone
}
} else if let Ok(probe) = ClockProbe::decode(&msg) {
// Wall-clock skew handshake: echo the client's t1 with our receive (t2) and
// send (t3) stamps, both in the host clock the AU pts_ns uses. Answered
// inline on the control stream — cheap, no data-plane involvement.
let t2_ns = now_ns();
let echo = ClockEcho {
t1_ns: probe.t1_ns,
t2_ns,
t3_ns: now_ns(),
};
if io::write_msg(&mut ctrl_send, &echo.encode()).await.is_err() {
break;
}
} else {
tracing::warn!("unknown control message — ignoring");
}