上个月我干了件有点疯的事:把我那块运动手表的 GPS 辅助定位数据链路完整逆向了一遍,然后用公开的卫星数据自己”造”了一份同样格式的数据,搭了自己的服务器,让我自己的 App 替代官方应用给手表下发星历。
折腾了两周多,最终手表在脱离官方 App 的情况下依然秒级定位。这篇文章把整个过程讲清楚,顺带科普一下天天挂在嘴边的”卫星定位”到底是怎么回事。
先声明:本文章仅供学习交流使用,请勿用于任何商业或不当用途。文中涉及的格式分析仅为个人研究记录。

一、天上到底有哪些卫星?
我们平常说”GPS”,其实只是一个习惯性叫法。天上不止美国人一家有卫星导航系统,目前全球共有 六大卫星导航系统(GNSS),这也是厂商宣传的”六星”:
| 系统 | 国家/地区 | 简称 | 卫星数 |
|---|---|---|---|
| GPS | 美国 | GPS | 31 颗左右 |
| 北斗 | 中国 | BDS | 30+ 颗(三号系统) |
| 格洛纳斯 | 俄罗斯 | GLONASS | 24+ 颗 |
| 伽利略 | 欧盟 | Galileo | 28+ 颗 |
| 准天顶 | 日本 | QZSS | 5 颗(区域增强) |
| NavIC | 印度 | IRNSS | 7 颗(区域) |
前四个是全球系统,后两个是区域增强系统(主要覆盖亚太)。所谓”六星定位”,就是接收机能同时搜到这几个系统的卫星一起算位置——天上的”路”多了,高楼之间、树林里自然更容易定位。
我这块手表接收的就是其中五个系统(GPS、北斗、GLONASS、伽利略、QZSS),其中北斗是我日常在国内用得最多的。
二、”双频”又是什么意思?
每颗导航卫星会同时在几个不同频率上广播信号,比如 GPS 的 L1(1575.42 MHz)和 L5(1176.45 MHz)。
信号从两万公里的高空传到地面,要穿过电离层。电离层会让信号传播速度变慢、产生延迟,这个误差最大能到几十米,是定位误差的重要来源。而电离层延迟和频率有关:频率不同,延迟不同。如果接收机能同时收两个频率的信号,就能用这个差异把电离层误差直接算出来消掉,精度可以从”米级”提升到”亚米级”。
这就是”双频”的意义。手表受体积和功耗限制,多数还是单频;手机这几年高端机型基本普及了双频。
三、定位到底在算什么东西?
卫星信号里带着它自己的轨道参数和时间。接收机知道”卫星此刻在哪里”(星历)和”信号走了多久”(伪距),三颗以上卫星就可以列方程解出自己在地球上的位置——本质上是测距交会。
这里有两个关键概念:
星历(Ephemeris):精确描述每颗卫星当前轨道参数的”近期档案”,有效期只有 2 小时左右。收齐一颗星的星历通常需要 30 秒(每颗卫星只有特定时刻才广播自己的数据)。
历书(Almanac):所有卫星的”粗略全家福”,精度几公里,但有效期长达数月、收齐只要 12 分钟。它的用途是辅助搜星——让接收机大概知道”朝天上看哪些方向有卫星”。
由此就有了定位界最著名的指标 TTFF(首次定位时间):
- 冷启动:接收机什么都没有,得先收历书再收星历,可能要 1~2 分钟甚至更久。
- 热启动:存着星历(2 小时内),几秒内就能出位置。
- 辅助定位(A-GPS):这就是各家厂商发力的点——通过手机网络把”星历”直接下载下来推给手表,手表开机就”知道”卫星在哪,省掉空中接收的 30 秒,把冷启动压到 10 秒以内。
手表没有 SIM 卡,所以它依赖手机中继:手表通过蓝牙告诉配套 App”我要星历”,App 去云端下载、解码,再通过蓝牙分片传回手表。
四、难点不在”下载”,在”预测”
2 小时有效期的星历,服务器现抓现发就行。但真正的问题是:手表不是天天连着手机的。
登山、越野跑、骑行,经常一出去就是三五天,中间手机没电、没网、或者干脆不想带。这就要求辅助数据必须能”预知未来”——在离线的几天里,卫星会走到哪、钟差会变成多少,都要提前算好打包给手表。
所以各大厂商的辅助数据都不是原始星历,而是预测轨道:拿未来若干天的预报星历,拟合成一条时间-位置曲线。我这块表的方案是把未来 3 天切成 36 个窗口(每 2 小时一个),每个窗口内用一组数学系数(我逆向出来是 ECEF 坐标三个方向的 20 阶切比雪夫多项式拟合,分辨率 1 毫米)来描述每颗卫星的轨迹。手表收到后在未来 72 小时内都能”秒定位”,这就是手表离线 3 天也能快速定位的秘密——数据是出发前就”预支”好的。
五、我为什么非要自己动手
故事的起因有点哭笑不得:这块表的离线定位越来越依赖官方 App 每天同步,一旦几天没同步,定位就慢得让人崩溃(我实测冷启动最长等到 12 分钟)。
我想要的很简单:永远不依赖官方 App,手表永远有新鲜的星历。
分析下来,整条链路是这样的:
- 手表通过蓝牙向官方 App 请求几种数据:预测星历、实时辅助数据(AGNSS)、电离层参数,每种数据有个类似 URL 的”标签”(tag);
- App 去云端下载一个 seed 文件(JSON,里面是每个星座的轨道数据);
- App 内置的一个闭源原生解码库把 seed 解码成手表认识的二进制文件;
- 通过蓝牙分片加密传给手表。
我决定把这条链路全部搬到自己的基础设施上:自己造 seed、自己解码、自己打包、自己的 App 应答手表。好处是还能做 A/B 对比实验(同一块表,同一天,两种数据源交替下发,客观对比 TTFF)。
六、我是怎么实现的
1. 逆向格式
先静态分析官方 App 的 Java 层,搞清楚 tag 分派和云端接口;再从手机缓存里拿到 seed 样本。最难的是 seed 里 gpsNav 那段位流:没有文档,只能对着解码库的汇编硬啃。我用”给解码库打补丁强制导出中间值”的办法,拿到了解码过程的内部数据,再拿公开广播星历 propagated 出来的轨道和这些系数做相关性分析,最终确认:那 60 个数就是每颗卫星 ECEF 三维坐标在 12 小时区间上的 20 阶切比雪夫拟合系数。
验证方式也很硬核:用官方解码库分别解码”官方 App 抓的 seed”和”我自己造的 seed”,输出文件 MD5 逐字节一致,才算格式通关。
2. 数据从哪来
造数据需要两样公开资料(全部来自 IGS 国际 GNSS 服务,免费):
- 广播星历 RINEX:全球测站实时汇总的所有卫星当前轨道参数,每天一个文件,滚动更新;
- 超快速预报轨道 SP3:几家分析中心发布的未来 48 小时卫星精密轨道预报,5 分钟一个点,每小时出新版。
生成器的逻辑:对每个星座每颗卫星,取未来 12 小时区间内的轨道采样点(预报覆盖不到的地方退回用广播星历外推),做 20 阶切比雪夫最小二乘拟合,定点化成 seed 里的位流。采样点必须用 8 点拉格朗日插值——我一开始图省事用线性插值,拟合出来的轨道直接偏了 6 公里。
3. 全链路
|
服务器端做了双源设计:官方源(从官方 App 缓存里每天自动抓最新 seed 重打包)和我的自造源,可以一键切换,还有兜底巡检脚本——自造数据一旦覆盖不了当前时段,自动切回官方源,保证手表永不断粮。
4. 蓝牙协议
手表和应用之间走蓝牙私有协议:手表先发”我要什么数据”的协商请求,App 回配置,然后手表按文件分片拉取。这里有个大坑后面讲——手表型号对分片加密方式要求不同,我的表必须用 TLV 加密模式,明文传一律被拒,这也是我最初”进度 0%”的根因。
七、自造数据踩过的坑(精华版)
两周时间踩的坑能写满一页 A4 纸,挑几个最有代表性的:
1. JSON 必须紧凑写出。 seed 是 JSON,但解码库按字节偏移读它。Python 默认 json.dump 会带空格分隔,解码器一个字节对不上,静默产出 0 个文件,不报错。排查半天才发现是分隔符问题。
2. 时间必须单调,空段也不能偷懒。 seed 里每颗卫星分 14 个时段。我一开始只更新自己造的那几段,没造的几段留着模板里的旧时间——结果整个时间序列出现”倒退”,解码器判定数据非法,输出全部清零。连续 5 轮测试全是 0/36 个有效窗口。教训:宁可写全零段,也不能留旧时间戳。
3. 跨周要回绕。 GPS 系统时间以周为单位(一周 604800 秒),时段结束时间跨周时必须取模回绕,否则一个大于 604800 的数就足以让整份数据被判废。
4. 质量检查差一点,手表就瞎 12 分钟。 我的质量门最初只检查”文件非空”,结果一次自造数据只有 1/36 个有效窗口也上线了,顶掉了手表里 3 天有效的官方数据。那天下午我在室外站了 12 分 17 秒没等到定位。后来质量门改成硬性检查”必须覆盖当前时段”。
5. “手机里有”不等于”手表手里有”。 好多次我兴冲冲测试”新数据生效没”,结果手表根本没来拉——手表按自己的节奏决定何时更新,数据没到期它看都不看。测试效率极低,也是我最终放弃频繁实测的原因之一。
6. 小样本相关性是陷阱。 分析系数含义时,我拿 24 颗卫星做回归,相关系数 0.9+,以为找到了映射关系;扩充到全部 30 颗后相关性掉到 0.49。拟合系数是压缩派生量,不能靠线性回归反推含义——最后是靠”打补丁导出中间值 + 物理验证”才确认。
7. 蓝牙链路”谁占谁应答”。 官方 App 常驻后台会抢走手表的请求。我的解法是平时禁用它,凌晨开一个 15 分钟的窗口让它”借尸还魂”刷新一次 seed,再切回来。
8. 网络环境的坑。 运营商 DNS 解析我自托管域名失败、VPN 全局代理卡住下载——最后加了 DoH 域名解析兜底和禁用代理才稳定。
八、现状与效果
折腾两周后,两条链路都跑通了:
- 官方源链路(目前线上主用):每天凌晨自动抓最新 seed、解码、打包,手表拿到的永远是 36/36 满窗口、覆盖未来 3 天的数据。实测秒级定位。
- 自造链路:从公开数据到手表全链路打通,手表真实收到过我造的星历。与官方同窗对比,420 个时段里 401 个逐字节一致;我重新生成的时段与官方拟合差约 50 米量级——对辅助定位来说完全够用(它只是帮手表”找星”,最终定位还是靠手表自己收信号解算)。
唯一的差距是前向覆盖时长:公开预报轨道只有未来 24 小时左右,我自造的数据有效期约 21.5 小时,而官方做到了 72 小时。要做到 3 天全自造,需要自己做短弧定轨和轨道外推(我做了个原型,12 小时外推误差 2.4 公里),那是另一个量级的工程量,我暂时不打算继续了——反正双源切换 + 兜底巡检已经能保证手表永不断粮。
九、写在最后
回头复盘,这件事最有价值的部分其实不是结果,而是过程:为了造一份几百 KB 的数据,我把卫星导航、轨道力学、二进制格式逆向、BLE 协议栈全都摸了一遍。”双频””六星””A-GPS”这些天天挂在嘴边的词,拆开看每一个都值得写一篇文章——这篇就算交作业了。
手表现在还戴在我手上,每天凌晨服务器自动刷新星历。它不知道数据是谁发的,只管准时准点地,在我抬腕的那一秒,告诉我我在哪。
再次声明:本文章仅供学习交流,所有格式分析仅为个人研究记录,请勿用于商业或不当用途。
参考资料
- IGS - International GNSS Service(公开星历与预报轨道数据)
- GPS SPS Performance Standard(GPS 官方性能标准文档)
- 手表官方配套 App 及其内置解码库(仅作个人研究分析)
评论
0 条评论