标签: 设备配对

  • 二维码编码只到版本 20、纠错固定 L:openpilot 编解码两端并不对称

    结论:openpilot 的二维码模块编码端与解码端能力并不对称:编码只覆盖版本 1 到 20、只构造 byte mode,并固定纠错等级 L 与 mask 0;解码端则支持版本 1 到 40 与四个纠错等级,还会尝试相邻版本、alignment 与转置等候选。更重要的产品边界是:二维码只是载体不是授权——配对令牌取不到时会退化成空令牌 URL,而这个 URL 仍能成功编码成图形,所以「屏幕上有二维码」不证明令牌有效;eSIM 扫码的前置检查也只校验字符串形状。以下为固定源码快照 c8fb906 的静态分析与隔离测试结论。

    编码与屏幕呈现

    make_texture() 把输入字符串编码为 UTF-8,按 _capacity() 从版本 1 到 20 选择首个可容纳的码型,超过上限抛 ValueError。_data_codewords() 只构造 QR 的 byte mode,写入 8/16 位长度、终止位与交替填充字节;_codewords() 按 _EC 表分块、计算 GF(256) Reed-Solomon 余数并交织。当前编码固定纠错等级 L 和 mask 0,并不搜索视觉质量最佳的 mask,编码版本范围也比解码范围窄。

    _Qr 绘制 finder/timing/alignment、格式位与版本位,再以两列之字形填入数据。普通模式外加 4 模块 quiet zone,反色模式不加边框;两者都把每个模块放大为 10×10 RGBA 像素后通过 raylib 上传纹理。make_texture() 的注释认为上传会复制像素,因此中间 NumPy 数组无需保持存活,但实际 GPU 驱动资源生命周期仍由 UI 调用方负责。

    项 编码端 解码端
    版本范围 1 到 20 1 到 40
    纠错等级 固定 L 四个等级均支持
    字符模式 仅 byte mode(UTF-8) 数字、字母数字、byte、Kanji 与受支持 ECI
    mask 固定 mask 0,不搜索 从格式位恢复级别/mask

    配对 QR 的展示边界

    大屏 PairingDialog._generate_qr_code() 先释放旧纹理,再把 DongleId 与配对 JWT 拼入 URL 制作新纹理,每 300 秒刷新,失败时记录日志并清空纹理。二维码中可能有配对令牌,视觉截图、投屏与诊断日志应视为敏感路径;生成纹理只负责表示 URL,不能证明 URL 仍有效或账户已授权。

    mici 的 QR 控件用反色纹理,析构时卸载;__init__() 立即生成纹理,失败时 _texture=None,_render() 改画错误标签。显示缩放只按矩形高度计算,不按宽度取较小比例:若插件给非方形且宽度更窄的矩形,二维码可能横向溢出(当前控件默认建为正方形,配对页还会把它设为屏幕高度的正方形,因此这是组件复用边界而非已实测故障)。

    未配对时 PrimeScroller.__init__() 先为 QR 调用一次 _get_pairing_url() 并生成纹理,但 _last_pairing_qr_generation 初始化为负无穷,首次 _render() 会再次请求令牌、释放初始纹理并生成新纹理,之后按 300 秒周期刷新;这意味着首次展示有两份请求/纹理工作,并可能在两个请求之间取到不同令牌。刷新的时间戳即使 _generate_qr_code() 失败也会更新,错误标签将持续到下一次刷新或重建。若令牌 API 抛异常,_get_pairing_url() 返回 https://connect.comma.ai/?pair=;该字符串仍可成功编码为二维码,故有 QR 图形并不证明配对令牌有效。

    图像到文本的解码链

    顶层 decode() 先定位 finder,再尝试估计版本及相邻两个版本、是否使用 alignment、矩阵是否转置;候选均失败返回 None。它仅捕获 QRError,并非对任意形状、任意 dtype 或超大数组都提供「失败返回 None」的保证。二值图与游程/透视运算会分配与输入像素数相关的内存,外部插件传入图像前必须限制形状、大小与处理预算。

    阶段 关键函数与限制
    灰度二值化 _binarize() 分块取样、算局部对比度并参考邻块/全局阈值;边长小于 21 像素抛 QRError,假定二维灰度数组。
    finder 定位 _find_patterns() 以 1:1:3:1:1 横向候选、纵向复查去重,至少约 2 像素/模块;_pick_finders() 从前十个候选中选几何最接近直角等边的三点。
    透视采样 _perspective() 解单应矩阵;_sample() 可用 alignment pattern 修正右下角,采样越出图像抛 QRError。
    矩阵恢复 decode_matrix() 只接受 21..177 且边长满足 4v+17 的方阵;从两份格式位选汉明距离不超过 3 的级别/mask,再反掩码、去交织。
    纠错与文本 _rs_correct() 做 syndrome、Berlekamp-Massey、错误定位/幅值求解并复验;无 ECI 的 byte 段先试 UTF-8,失败退回 Latin-1。

    eSIM 扫码调用与信任边界

    QRScannerDialog 使用座舱 CabinCameraView:显示时打开驾驶员视角并关闭红外,隐藏时反向恢复;它最多约每 0.25 秒启动一次扫描,上一扫描线程仍活跃时不再启动新线程。取帧时只复制 NV12 的 Y 平面有效宽度,避免 VisionIPC 缓冲回收后后台线程读到复用数据,随后在后台调用 qrcode.decode(gray)。扫码 UI 的轮询与相机/线程生命周期不等于二维码算法内置超时。

    识别到文本后,UI 先用 parse_lpa_activation_code() 检查 LPA: 前缀与三个非空 $ 分段,失败展示短时错误;通过则让用户输入昵称,再交给蜂窝管理器下载 profile。这个前置检查只是字符串形状校验,不认证 SM-DP+ 服务器、激活码归属或 eUICC 安装授权。插件化时应把「二维码像素识别」「业务数据格式校验」「服务端授权」分为三层:配对展示插件只接收短时令牌的受控呈现能力,扫码插件只输出有长度上限的候选字符串,不直接触发下载或持有 eUICC/网络权限。

    已有测试与待验证项

    test_qrcode.py 使用独立生成、打包的 .npz 矩阵 fixture,覆盖 1 到 40 版本、四种纠错级别、多种文本模式/ECI、噪声、旋转、镜像、透视、边缘贴码与无二维码图像,还检查自有编码器版本 1 到 20 的矩阵往返。通过隔离脚本执行原测试文件的 13 个测试方法全部通过,其中版本/纠错等级循环含 160 组矩阵与渲染图检查,纹理编码往返含 20 个版本;隔离器仅提供 pyray.Texture 的导入期类型桩。

    但该测试不覆盖 make_texture() 的 raylib 上传,也不验证真实车载相机曝光/畸变、GPU 纹理生命周期、恶意超大图像或 eSIM 服务端授权。新显示硬件需在重复开闭、令牌失败、图形上下文销毁/重建与非方形布局下检查 GPU 纹理数量与 QR 可扫性。

    常见问题

    屏幕上有配对二维码就说明令牌有效吗?

    不能。令牌 API 抛异常时 _get_pairing_url() 返回 https://connect.comma.ai/?pair=,这个空令牌 URL 仍可成功编码成二维码。生成纹理只负责表示 URL,不证明令牌有效或账户已授权。

    为什么首次进入配对页会出现两次令牌请求?

    因为 PrimeScroller.__init__() 已为 QR 调用过一次 _get_pairing_url() 并生成纹理,而 _last_pairing_qr_generation 初始化为负无穷,首次 _render() 会再次请求令牌、释放初始纹理并生成新纹理;两个请求之间可能取到不同令牌。

    扫码识别到 LPA 激活码就等于能装成功吗?

    不等于。parse_lpa_activation_code() 只检查 LPA: 前缀与三个非空 $ 分段,是字符串形状校验,不认证 SM-DP+ 服务器、激活码归属或 eUICC 安装授权。

    编码端和解码端的版本范围一样吗?

    不一样。编码端只覆盖版本 1 到 20、固定纠错等级 L 与 mask 0;解码端支持版本 1 到 40 与四个纠错等级,并会尝试相邻版本、alignment 与转置等候选。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/QR_CODE_PAIRING_ESIM_PIPELINE_DEEP_DIVE.md(编码/解码两端能力、配对 QR 展示边界与 eSIM 扫码信任边界)。
    • 项目 02 —— openpilot-research/ 同目录探针记录:原二维码测试文件的 13 个隔离测试方法与 160 组矩阵检查结论。

    说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

    更新记录

    首次发布 2026-10-11 最近核对:2026-10-11

  • 配对 300 秒刷新、统计 30 秒轮询:设备页与 Firehose 上传要分清

    结论:设备设置页、配对/订阅状态页与 Firehose 统计页都是观察窗口,不是授权与执行本身。配对二维码每 300 秒刷新、订阅状态约每 5 秒轮询、Firehose 统计约每 30 秒拉取;但页面上显示「已配对」「ACTIVE」并不证明服务端已授权、也不证明数据正在上传。源码快照里,Firehose 页面甚至没有任何「启用上传」参数或上传控制调用,它只是按网络条件给出提示。

    设备页能做什么,不能证明什么

    设备页提供的是有限的本地操作:查看设备标识(来自设备 ID 与硬件序列号)、预览座舱摄像头、复位标定、重启或关机,以及查看培训与监管信息。这些入口都有前置条件——座舱摄像头预览在两种布局上都要求处于停车(offroad)状态才使能,而且预览并不校验摄像头标定精度。

    「复位标定」尤其容易被误解。它在请求时与确认时都会检查是否处于驾驶接管(engaged)状态,然后删除四个学习缓存键,并写入一次行车周期请求;被删除的四个键分别由四个后台学习进程读取并重建。关键是:这不是把传感器硬件恢复出厂,而且四个删除操作与周期请求不是事务——断电或进程并发时的一致性并未被验证。

    重启与关机同样是「请求」而非「执行」:界面做二次确认并检查非 engaged,然后写入对应的电源请求消息,最终执行由系统管理进程与平台决定。大屏在停车时才显示关机按钮,mici 布局在未点火时显示。

    配对与订阅:三套显示门不等于授权

    配对二维码由设备 ID 调用接口获取令牌后组装,指向配对地址,并每 300 秒刷新一次;订阅状态则在 UI 初始化后由后台线程在「非行车且屏幕唤醒」时查询设备与账户接口,约每 5 秒检查一次。这里有两个必须说清的边界:其一,二维码本身包含可用于配对的令牌,应按敏感凭据处理,避免日志、截图或远程投屏无意泄露;其二,接口失败时会保留旧状态并记录错误,因此离线页面可能显示的是缓存值,不能当作实时云端授权结果。

    首页还有一处容易看错的细节:订阅宣传组件用的是「是否订阅」判断,而设置入口组件用的是另一个「是否已配对」判断。两个门不同,所以一台已配对但没有订阅的设备,可能同时看到升级宣传和 Firehose 入口。此外,配对按钮的显示门只检查系统时间是否有效——失败时提示连接 Wi-Fi,成功就弹出配对界面,它并不检查 Wi-Fi 或互联网是否真的可达,也不检查设备标识或服务端令牌签发结果。

    界面元素 看起来代表 源码里的实际含义
    配对二维码 配对完成 仅生成含令牌的配对链接,每 300 秒刷新;是否授权由服务端决定
    订阅摘要 / 升级宣传 订阅有效 读取缓存的订阅类型;接口失败时保留旧状态,可为离线缓存
    首页 Firehose 入口 正在上传数据 由「是否已配对」门控制,与订阅、上传状态均无关
    Firehose 页面 ACTIVE 上传任务运行中 仅表示网络类型非空且非计费网络,是网络条件提示

    Firehose 页面只是网络条件提示

    Firehose 统计页在构造时读取本地缓存并启动后台线程,线程在非行车且屏幕唤醒时约每 30 秒从设备接口拉取统计。注意:这个线程由组件构造启动,不是用户进入页面才启动。

    页面状态的判定非常克制:只有当设备状态里的网络类型非空、且网络非计费(networkMetered=False)时显示 ACTIVE,否则显示 INACTIVE。更重要的是,源码中没有 Firehose 页面专属的「启用上传」参数,也没有上传控制调用。因此页面上写着 ACTIVE,只能说明当前网络条件符合提示规则,不能证明上传任务正在运行、远端已接收,或数据被用于训练。

    刷新对象 周期 触发条件
    配对二维码 约 300 秒 配对界面打开且尚未完成配对
    订阅 / 配对状态 约 5 秒 非行车且屏幕唤醒的后台线程
    Firehose 统计 约 30 秒 非行车且屏幕唤醒的后台线程

    真正的上传器在页面之外

    选择与发送上传文件的是后台上传器,它按已上传标记、锁文件、文件优先级与计费网络限制挑候选文件,再从设备状态读取网络条件执行上传。这里也有一个反直觉的边界:计费网络并非一概不上传,部分小文件或被请求的路段仍可能被选中。换句话说,上传策略由上传器与日志保留策略决定,Firehose 页面既不替代也不覆盖这套策略。

    插件化与台架验证要覆盖什么

    如果要做插件化,边界应这样划:设备能力插件声明屏幕、座舱相机、监管文件、系统电源与校准缓存的可用性,实际的复位/重启/关机要经独立服务验证驻车、驾驶状态与用户确认,而不是只读界面上那个 engaged 布尔值;配对/订阅插件只负责短期令牌展示与状态观察,服务端仍是授权源;上传插件以明确的队列、网络预算、隐私许可与服务端确认结果为契约,说明/统计页只能消费状态,不能越过日志保留或计费网络策略直接读取或上传车内数据。台架验证至少覆盖:确认弹窗期间由驻车变成 engaged、四个缓存删除中途断电、二维码令牌过期、订阅接口离线与重试、计费网络上的文件选择,以及重启/关机的执行与失效恢复。

    常见问题

    Firehose 页面显示 ACTIVE,是不是就在上传数据?

    不是。该状态只依据设备状态里的网络类型非空且网络非计费来判定,是网络条件提示;源码中没有 Firehose 页面专属的启用上传参数或上传控制调用,真正的上传由后台上传器按文件优先级与计费网络限制自行选择执行。

    点「复位标定」会把传感器恢复到出厂状态吗?

    不会。复位只是删除四个学习缓存键并写入一次行车周期请求,这些键由对应的后台学习进程读取并重建,属于软件层面的重新学习,不是把传感器硬件恢复出厂设置。

    配对二维码可以截图或投屏吗?

    不建议。二维码包含可用于配对的令牌,应按敏感凭据处理,避免在日志、截图或远程投屏中无意泄露;令牌的实际有效期与服务器授权规则需要由后端验证。

    设备显示「已配对」,就说明订阅有效吗?

    不能这样推断。首页的订阅宣传与设置入口使用两个不同的判断门,已配对但未订阅的设备可能同时看到升级宣传与 Firehose 入口;订阅摘要读取的是缓存状态,接口失败时会保留旧值,不能当作实时云端授权结果。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/DEVICE_PAIRING_FIREHOSE_DEEP_DIVE.md(设备页、配对与订阅状态、Firehose 页面与上传器、插件边界)

    说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有周期、阈值与配置均出自源文档,标注为未验证的结论保留其原有边界;页面显示状态与外部服务/硬件是否已执行,本文严格区分。

    更新记录

    首次发布 2026-10-11 最近核对:2026-10-11