结论:GNSS 在这套系统里是二选一的双路径:存在指定串口设备节点且未设置强制 Quectel 开关时选 u-blox,由 pigeond 与 ubloxd 同时运行,其余情况选 qcomgpsd;两条路径输出的定位主题名不同——u-blox 走 gpsLocationExternal,Quectel 走 gpsLocation,系统时间进程按参数选其中一条,不能只改名字就视为等价。时间校正本身条件严格:所选主题刚更新、logMonoTime 距当前不超过两秒、hasFix=True 且 GPS 时间在允许范围内才调用设置时间,且差值小于十秒不改。本文为固定源码快照 c8fb906 的静态阅读,未连接 u-blox、Quectel、天线或实板。
平台门与消息契约
平台选择由设备节点与持久化参数共同决定:存在指定串口设备节点且未设置强制 Quectel 开关时选 u-blox,此时 pigeond 和 ubloxd 同时运行,ublox() 还更新 UbloxAvailable 参数;其他情况下选 qcomgpsd。三者均受硬件与行车条件限制,设备节点判断不是芯片健康检测。需要注意的是,管理器会按当前设备节点重评 GNSS 生产者,但系统时间进程常驻且只在启动时选订阅主题,因此驻车与行车切换、运行中设备切换并不等价。
| 边界 | 已核对行为 |
|---|---|
| 平台门 | 串口设备节点存在且未设强制 Quectel 开关时选 u-blox,否则选 qcomgpsd |
| u-blox 定位主题 | 输出 gpsLocationExternal |
| Quectel 定位主题 | 输出 gpsLocation |
| 原始/卫星主题 | ubloxRaw 是串口字节包;ubloxGnss 与 qcomGnss 承载测量、星历、卫星状态与诊断 |
| 订阅选择 | 系统时间进程按 UbloxAvailable 选一条,且只在初始化时决定 |
ubloxRaw 是串口字节包,ubloxGnss 与 qcomGnss 承载测量、星历、卫星状态与诊断等结构,它们都不是 GpsLocationData 的替代品。两条服务的频率与字段来源不同,接入时不能假定可互换。
| 对比项 | u-blox 路径 | Quectel 路径 |
|---|---|---|
| 运行进程 | pigeond 与 ubloxd 同时运行 |
qcomgpsd |
| 物理链路 | 电源、复位、安全启动引脚与串口设备节点 | AT 端口与 DIAG 串口,共享互斥锁文件 |
| 帧格式与校验 | UBX 前导 B5 62、长度与 Fletcher 校验 |
HDLC 解转义与 CRC16-X25 |
| 定位主题 | gpsLocationExternal |
gpsLocation |
| 时间来源 | NAV-PVT 的 UTC,异常时置零 | 固定 18 秒 GPS-UTC 差,留有闰秒待办 |
u-blox:供电、串口、UBX 与辅助定位
set_power() 与串口封装类管理电源使能、复位、安全启动引脚与串口设备节点。初始化先做电源循环,再由 init_baudrate() 把默认 9600 波特率切到 460800;init_pigeon() 用带 ACK 的配置报文设置输出频率、消息种类与导航参数,最多尝试十轮。run_receiving() 把串口块发布为 ubloxRaw,首字节为 0x00 时尝试重新初始化;后台线程等待有效系统时间与网络后,从 u-blox 服务或代理下载辅助数据并送设备,接收过程中每五分钟尝试保存星历备份。
UbxFramer.add_data() 从跨块字节流寻找 UBX 前导 B5 62、长度与 Fletcher 校验,坏帧丢首字节后重找;解析器parse_frame() 只处理 NAV-PVT、RXM-SFRBX、RXM-RAWX、NAV-SAT、MON-HW/HW2 等已列类型,其余返回空。NAV-PVT 映射把经纬度、海拔、速度、NED 速度和精度换算到 gpsLocationExternal,根据 flags 置 hasFix,UTC 日期异常时 Unix 时间置零。
有两个容易误判的静态边界:其一,init() 调用 init_pigeon() 却不检查其布尔返回,十轮失败后仍进入接收循环,因此应显式上报电源、UART、ACK、首定位与持续断流状态;其二,主循环记录上游包时间,却并未把它写回发布消息的 logMonoTime——后者由消息构造在解析时生成,所以日志时间不能代表卫星测量时刻,硬件替换时必须单独保留采样时刻与处理延迟。
Quectel:AT 与 DIAG 路径
wait_for_modem() 等 AT 端口及 GNSS 子系统应答;at_cmd() 在一个共享内存互斥锁文件上加锁,与蜂窝管理进程共享 AT 端口——这个锁是接口所有权边界,新板卡驱动不能绕过它并发发 AT 指令。setup_quectel() 通过 DIAG/NV 开启 OEMDRE 日志、关闭 GNSS 省电模式、设置 NMEA 输出与时间并启动 GNSS,cleanup() 在信号退出时关闭 GNSS 与日志并拉低电源引脚。
ModemDiag.hdlc_decapsulate() 对 DIAG 串口数据解转义并校验 CRC16-X25;主循环再按内外长度、日志类型和版本解析 OEMDRE、GPS 与 GLONASS 测量及位置,位置报告要求位置来源字段等于 2 且 GPS 周不是无效哨兵,海拔精度等于 500 时置 hasFix=False。位置映射把 ENU 速度转换到 NED 输出 gpsLocation,源码按固定 18 秒 GPS-UTC 差计算 Unix 时间,并留有闰秒更新待办;若把该路径作为设备时间源,必须验证当前闰秒、模组固件与真实 UTC 的一致性。
系统时间与硬件契约
系统时间进程发布 clocks,仅在所选 GPS 主题刚更新、logMonoTime 距当前不超过两秒、hasFix=True 且 GPS 时间在允许范围内时调用设置时间,差值小于十秒不改系统时间;该流程只处理系统 UTC,不保证相机帧起始、IMU 中断、DIAG 日志时间与卫星测量已经共时钟。面向硬件与插件化的最低契约是:每条 GNSS 提供方须声明物理链路、供电与复位、安全关机、协议及校验、观测时钟域、UTC 与闰秒来源、定位有效位与精度、首次定位及断流超时;gpsLocationExternal 与 gpsLocation 只能通过受控适配器切换,不能让任意插件覆盖系统时间或伪造定位有效位。
常见问题
系统怎么决定用 u-blox 还是 Quectel?
由设备节点与持久化参数共同决定:存在指定串口设备节点且未设置强制 Quectel 开关时选 u-blox,由 pigeond 与 ubloxd 同时运行并更新 UbloxAvailable;其他情况选 qcomgpsd。设备节点判断只是平台门,不是芯片健康检测。
gpsLocationExternal 和 gpsLocation 可以互换吗?
不能只改名互换。前者由 u-blox 的 NAV-PVT 映射产出,后者由 Quectel 的 DIAG 位置映射产出,两条服务频率与字段来源不同;系统时间进程按 UbloxAvailable 选其中一条,且只在初始化时决定,平台切换后要检查服务重启与参数同步。
日志里的时间等于卫星测量时刻吗?
不一定。u-blox 主循环并未把上游包时间写回发布消息的 logMonoTime,该字段由消息构造在解析时生成,因此日志时间不能代表卫星测量时刻,硬件替换时必须单独保留采样时刻与处理延迟。
为什么 hasFix=False 会出现?
两条路径都有置假条件:u-blox 根据 NAV-PVT 的 flags 置 hasFix 并在 UTC 日期异常时把 Unix 时间置零;Quectel 要求位置来源字段等于 2 且 GPS 周有效,海拔精度等于 500 时置 hasFix=False。因此有效位要连同来源与时间一起解读。
延伸阅读
- GNSS UBX 二进制解析:前导、长度与校验 —— 展开本文 u-blox 路径的 UBX 帧解析细节。
- GNSS Quectel DIAG 记录契约 —— 展开本文 Quectel 路径的 DIAG 解析与位置来源判定。
- 车载信号为什么会说谎:TTL、源时间与 500 ms 新鲜度阈值 —— 对照本文两秒新鲜度与有效位语义。
- 相机与 IMU 的时间同步契约 —— 说明为何系统 UTC 校正不等于所有传感器共时钟。
- 调制解调器 PPP、路由与 DNS 所有权契约 —— 解释本文 Quectel AT 端口互斥锁的上游归属。
参考资料
- 项目 02 ——
openpilot-research/GNSS_TIME_DEEP_DIVE.md(取用了平台门、两条定位主题、u-blox 与 Quectel 解析链、系统时间条件与硬件契约)。 - 项目 02 ——
openpilot-research/GNSS_BACKEND_SELECTION_SUBSCRIPTION_LIFECYCLE_DEEP_DIVE.md(仅用于确认订阅选择生命周期与运行中切换的边界)。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复