用途
该技能面向需要多设备时钟对齐的场景:同一同步房间内每台设备各有时钟与漂移,若不校准,“在 1234 毫秒恢复”这类时间点对两端没有一致含义。它估算两个时钟之间的偏移(offset),供后续定位同步等逻辑使用。
主要能力
- 从往返消息中估算偏移:pong 携带客户端的发送时间以及对等端自身的接收与发送时间,据此先算出单向延迟,再得到偏移:
val receivedAt = elapsedRealtime() // MONOTONIC, injected by the caller
val roundTrip = receivedAt - clientTime
val peerProcessing = peerSendTime - peerReceiveTime
// Time the peer spent thinking is not time on the wire, so it comes out before the halving.
val networkRoundTrip = max(0L, roundTrip - peerProcessing)
val sampleOffset = peerSendTime + networkRoundTrip / 2.0 - receivedAt
if (networkRoundTrip < bestRoundTripMs) bestRoundTripMs = networkRoundTrip
val weight = if (networkRoundTrip <= bestRoundTripMs + GOOD_SAMPLE_MARGIN_MS) 0.25 else 0.05
offsetMs = offsetMs?.let { it + weight * (sampleOffset - it) } ?: sampleOffset
- 加权而非平均:往返接近历史最佳值的样本权重为 0.25,否则为 0.05,使单个拥塞帧只停留在噪声水平。
- 可空偏移值:
offsetMs为可空类型,下游必须显式处理“尚未校准”的情形。
使用方式与注意事项
- 先扣除对等端处理时间再折半:否则中继排队 40 ms 会让单向估计永久偏大 20 ms。若对等端未同时上报接收与发送时间,无法扣除,应把结果视为下限而非精确值。
- 本地时钟源必须单调且由外部注入:以构造参数
() -> Long传入,各平台传自己的实现,测试传入可控变量;直接在估算器内读取墙上时钟会导致不可测试。 - 未校准时回退到原始值:“当前时间”访问器返回 null,位置校正返回输入不变;对等端时间戳为 0 表示未知,应短路到原始位置而非参与运算。
- 首几次 ping 需密集发送:第一个样本直接设定偏移,之后每个样本最多移动四分之一,估计变化缓慢,需先密集ping再过渡到稳定间隔。
- 折入前先校验样本:检查非正发送时间、发送时间位于本地未来、样本过旧、对等端发送时间早于其接收时间、对等端接收时间为 0 五种情况;尤其最后一个,未加防护会使最佳往返基线被钉在 0,后续样本全部被压成噪声。
- 把“时钟变为可用”做成事件:仅第一个被接受的样本使折叠操作返回 true,供上层宣布校正位置可用。
- 公式是端间契约:测试中的期望值应照抄参考实现作为字面量,不得“简化”重算,否则几毫秒的偏差就是失步。
验证方式
通过 grep 统计时钟源与算术出现位置:列举使用单调时钟的文件、持有墙上时钟的文件,两者必须不相交,同时出现在两份列表中的文件即为缺陷;统计往返与偏移算术命中,预期集中在一个文件,出现第二份算术副本意味着两份公式会漂移。然后阅读测试文件,应断言首个样本前读数为 null、一个样本后精确时间、流未运行时位置不变、重置后位置不变;仅断言“接近”某值的测试无法发现折半错误。
适用场景
多设备需要共用时间基准的房间同步、位置校正等场景。
限制
资料未提供具体平台支持范围、性能数据或安装方式;对等端不上报接收与发送时间时只能得到偏移下限。