文章目录
-
- [1. RTKLIB 是什么](#1. RTKLIB 是什么)
- [2. 目录结构速览](#2. 目录结构速览)
- [3. 应用入口怎么选](#3. 应用入口怎么选)
-
- [3.1 `rnx2rtkp`:后处理定位,最适合算法调试](#3.1
rnx2rtkp:后处理定位,最适合算法调试) - [3.2 `rtkrcv`:实时定位服务](#3.2
rtkrcv:实时定位服务) - [3.3 `convbin`:原始数据转 RINEX](#3.3
convbin:原始数据转 RINEX) - [3.4 `str2str`:数据流转发](#3.4
str2str:数据流转发)
- [3.1 `rnx2rtkp`:后处理定位,最适合算法调试](#3.1
- [4. `src/` 核心源码结构](#4.
src/核心源码结构) - [5. 关键数据结构](#5. 关键数据结构)
-
- [5.1 `gtime_t`](#5.1
gtime_t) - [5.2 `obsd_t` 与 `obs_t`](#5.2
obsd_t与obs_t) - [5.3 `nav_t`](#5.3
nav_t) - [5.4 `prcopt_t`](#5.4
prcopt_t) - [5.5 `rtk_t`](#5.5
rtk_t) - [5.6 `sol_t`](#5.6
sol_t)
- [5.1 `gtime_t`](#5.1
- [6. 后处理主流程](#6. 后处理主流程)
- [7. 单点定位 SPP 算法](#7. 单点定位 SPP 算法)
- [8. RTK 相对定位算法](#8. RTK 相对定位算法)
- [9. PPP 精密单点定位算法](#9. PPP 精密单点定位算法)
- [10. LAMBDA 模糊度固定](#10. LAMBDA 模糊度固定)
- [11. 观测数据与星历读取](#11. 观测数据与星历读取)
-
- [11.1 RINEX](#11.1 RINEX)
- [11.2 RTCM](#11.2 RTCM)
- [11.3 厂商原始数据](#11.3 厂商原始数据)
- [12. 卫星位置、钟差和误差模型](#12. 卫星位置、钟差和误差模型)
-
- [12.1 卫星位置与钟差](#12.1 卫星位置与钟差)
- [12.2 电离层与对流层](#12.2 电离层与对流层)
- [13. 坐标系统与时间系统](#13. 坐标系统与时间系统)
- [14. 配置系统](#14. 配置系统)
- [15. 输出系统](#15. 输出系统)
- [16. Trace 调试系统](#16. Trace 调试系统)
- [17. 推荐断点清单](#17. 推荐断点清单)
-
- [17.1 后处理定位](#17.1 后处理定位)
- [17.2 RINEX 读取](#17.2 RINEX 读取)
- [17.3 RTCM 实时流](#17.3 RTCM 实时流)
- [17.4 RTK 固定解](#17.4 RTK 固定解)
- [17.5 卫星位置和星历](#17.5 卫星位置和星历)
- [18. 新手调试路线](#18. 新手调试路线)
- [19. 常见问题排查](#19. 常见问题排查)
-
- [19.1 没有解](#19.1 没有解)
- [19.2 只有 Single,没有 Float/Fix](#19.2 只有 Single,没有 Float/Fix)
- [19.3 Float 不收敛](#19.3 Float 不收敛)
- [19.4 Fix 失败或误固定](#19.4 Fix 失败或误固定)
- [19.5 PPP 精度差](#19.5 PPP 精度差)
- [20. 读代码时的逻辑](#20. 读代码时的逻辑)
- [21. 最小调试案例建议](#21. 最小调试案例建议)
- [22. 修改源码时的建议](#22. 修改源码时的建议)
- [23. 关键函数索引](#23. 关键函数索引)
- [24. 进一步阅读](#24. 进一步阅读)

本文面向第一次阅读和调试 RTKLIB 源码的开发者,目标是帮助你快速建立代码地图:目录在哪里、主流程从哪里进、关键数据结构是什么、定位算法在哪些函数里实现、调试时应该下哪些断点。
参考对象主要是官方 RTKLIB 仓库 tomojitakasu/RTKLIB 的经典结构;很多工程会基于 rtklibexplorer/RTKLIB 分支开发,核心目录和关键函数大体一致。
1. RTKLIB 是什么
RTKLIB 是一个开源 GNSS 标准定位和精密定位程序包,包含:
- 可移植的 C 语言核心库;
- 后处理工具;
- 实时定位工具;
- 数据流转发工具;
- RINEX/RTCM/厂商原始数据解析与转换工具;
- Windows GUI 应用和命令行应用。
支持的典型定位模式包括:
- Single:单点定位;
- DGPS/DGNSS:差分定位;
- RTK Kinematic/Static:载波相位差分定位;
- Moving Baseline:动基线;
- PPP Kinematic/Static/Fixed:精密单点定位。
2. 目录结构速览
典型 RTKLIB 根目录如下:
text
RTKLIB/
├── app/ # 应用程序入口,含命令行和 GUI
├── bin/ # 预编译可执行文件、示例配置
├── brd/ # 广播星历等示例数据
├── data/ # 示例观测数据、导航数据
├── doc/ # 官方说明文档、手册
├── lib/ # 平台相关库或工程依赖
├── src/ # 核心算法库,最重要
├── test/ # 测试或示例
├── util/ # 辅助工具
└── readme.txt # 官方 README
新手调试主要看两个目录:
app/consapp/:命令行程序入口,便于下断点和复现;src/:核心库,定位算法、文件解析、流处理都在这里。
3. 应用入口怎么选
3.1 rnx2rtkp:后处理定位,最适合算法调试
路径通常是:
text
app/consapp/rnx2rtkp/rnx2rtkp.c
用途:
- 读取 RINEX 观测文件、导航文件、精密星历、钟差等;
- 执行单点、DGPS、RTK、PPP 后处理;
- 输出
.pos定位结果。
推荐新手从这里开始,因为它不依赖实时串口、NTRIP 或多线程流服务,输入输出稳定,便于复现问题。
典型调用链:
text
main()
-> postpos()
-> readobsnav()
-> execses()
-> procpos()
-> rtkpos() / pppos() / pntpos()
-> outsol()
建议断点:
main():看命令行参数和配置文件如何进入;postpos():后处理总入口;readobsnav():观测值和星历读取;procpos():逐历元处理;rtkpos():RTK 主算法入口;pntpos():单点定位入口;pppos():PPP 主算法入口;resamb_LAMBDA():整周模糊度固定入口。
3.2 rtkrcv:实时定位服务
路径通常是:
text
app/consapp/rtkrcv/rtkrcv.c
用途:
- 从串口、TCP、NTRIP、文件流读取数据;
- 实时解码 RTCM 或接收机原始数据;
- 实时执行 RTK/PPP/Single;
- 输出实时定位结果。
典型调用链:
text
main()
-> rtkopen()
-> strsvrstart() / rtkrcvstart()
-> rtksvrstart()
-> rtksvrthread()
-> decoderaw() / input_rtcm3()
-> rtkpos()
-> writesol()
适合调试:
- 数据流问题;
- RTCM 接收问题;
- 实时解算异常;
- 多线程和缓存问题。
3.3 convbin:原始数据转 RINEX
路径通常是:
text
app/consapp/convbin/convbin.c
用途:
- 把接收机原始数据、RTCM、BINEX 等转为 RINEX;
- 调试接收机协议解析时非常有用。
典型关注函数:
convrnx();input_raw();input_rtcm2();input_rtcm3();decode_*()厂商协议解析函数。
3.4 str2str:数据流转发
路径通常是:
text
app/consapp/str2str/str2str.c
用途:
- 串口、TCP、NTRIP、文件之间的数据转发;
- 可选 RTCM 或厂商原始格式转换;
- 常用于把接收机数据转发给
rtkrcv或其他软件。
适合调试:
- 串口/NTRIP 连接;
- stream 输入输出;
- 实时数据是否正常到达。
4. src/ 核心源码结构
不同版本文件数量略有差异,但核心文件一般包括:
text
src/
├── rtklib.h # 全局数据结构、常量、函数声明
├── rtkcmn.c # 通用函数:时间、坐标、矩阵、卫星编号、trace
├── pntpos.c # 单点定位 SPP
├── rtkpos.c # RTK 主算法
├── ppp.c # PPP 主算法
├── lambda.c # LAMBDA/MLAMBDA 整周模糊度搜索
├── postpos.c # 后处理流程控制
├── rinex.c # RINEX 读写
├── preceph.c # 精密星历/钟差
├── ephemeris.c # 广播星历、卫星位置钟差
├── sbas.c # SBAS
├── ionex.c # IONEX 电离层格网
├── tides.c # 潮汐改正
├── geoid.c # 大地水准面
├── solution.c # 解输出、NMEA、position 文件
├── options.c # 配置项读写
├── stream.c # 串口/TCP/NTRIP/文件流
├── streamsvr.c # 数据流服务器
├── rtksvr.c # 实时定位服务器
├── rtcm.c # RTCM 公共逻辑
├── rtcm2.c # RTCM 2.x 解码
├── rtcm3.c # RTCM 3.x 解码
├── raw.c # 接收机原始数据解码调度
└── rcv/ # 各厂商接收机协议解析
优先阅读顺序:
rtklib.h:先认识数据结构;postpos.c:理解后处理流程;pntpos.c:理解最简单定位;rtkpos.c:理解差分和滤波;lambda.c:理解模糊度固定;rinex.c、rtcm3.c、raw.c:按你的输入数据类型再看。
5. 关键数据结构
以下结构大多定义在 src/rtklib.h。
5.1 gtime_t
GNSS 时间结构。
常见相关函数:
epoch2time():年月日时分秒转内部时间;time2epoch():内部时间转年月日时分秒;gpst2time()/time2gpst():GPS 周秒转换;timediff():时间差;timeadd():时间加秒。
调试建议:
- 历元错乱、跨周问题、文件时间对不齐时,先看
gtime_t; - GPS Time、UTC、BDT、GLOT 不要混用。
5.2 obsd_t 与 obs_t
观测值结构。
obsd_t 表示一个卫星在某一历元的观测值,通常包含:
time:观测时间;sat:卫星编号;rcv:接收机编号;P[]:伪距;L[]:载波相位;D[]:多普勒;SNR[]:信噪比;LLI[]:失锁标志;code[]:观测码类型。
obs_t 是 obsd_t 数组容器。
调试建议:
- 如果定位结果不稳定,先确认
P[]、L[]、sat、rcv是否正常; - RTK 中流动站和基准站通常通过
rcv=1/2区分; - 周跳、失锁问题重点看
LLI[]和载波相位变化。
5.3 nav_t
导航数据结构,保存:
- 广播星历;
- 精密星历;
- 精密钟差;
- 电离层参数;
- GLONASS 频点;
- 天线参数等。
调试建议:
- "有观测但算不出位置"经常是星历没读到;
- 下断点查看
nav->n、nav->ng、nav->ne等数量字段; - 卫星位置异常时进入
satposs()、eph2pos()、peph2pos()。
5.4 prcopt_t
处理选项结构,是算法行为的核心开关。
常见字段:
mode:定位模式,如 Single、Kinematic、Static、PPP;nf:使用频点数;navsys:启用星座;elmin:截止高度角;ionoopt:电离层模型;tropopt:对流层模型;sateph:星历类型;modear:模糊度固定模式;baseline:基线约束;exsats:排除卫星。
调试建议:
- 同一份数据结果不同,优先比较
prcopt_t; - 低高度角、星座、频点、AR 模式会显著影响结果;
- 配置文件
.conf最终会被读到这个结构里。
5.5 rtk_t
RTK/PPP 解算状态结构,保存滤波器状态和当前解。
重点字段:
sol:当前定位结果;x:滤波状态向量;P:状态协方差;xa:固定解状态;Pa:固定解协方差;ssat[]:每颗卫星的状态;nx:状态维度;na:固定解相关状态维度;opt:处理选项。
调试建议:
- 卡尔曼滤波是否发散,看
x和P; - 固定解失败,看
xa、ssat[]、ratio; - 某颗卫星异常,看
ssat[sat-1]。
5.6 sol_t
定位结果结构。
常见字段:
time:结果时间;rr[]:位置/速度;qr[]:协方差;stat:解状态;ns:参与解算卫星数;age:差分龄期;ratio:模糊度固定 ratio。
常见 stat:
SOLQ_NONE:无解;SOLQ_SINGLE:单点解;SOLQ_DGPS:差分解;SOLQ_FLOAT:浮点解;SOLQ_FIX:固定解;SOLQ_PPP:PPP 解。
6. 后处理主流程
最适合新手理解的是 rnx2rtkp -> postpos() 这条线。
text
读取配置和命令行参数
-> 读取观测文件、导航文件、精密产品
-> 按时间排序观测值
-> 按历元循环
-> 每个历元执行定位
-> 输出定位结果
核心函数关系:
text
postpos()
-> readobsnav()
-> readrnxt()
-> readrnx()
-> readrnxobs()
-> readrnxnav()
-> execses()
-> procpos()
-> inputobs()
-> rtkpos()
-> relpos() / pntpos()
-> outsol()
调试一份 RINEX 数据时,建议断点顺序:
postpos():确认文件路径、起止时间、选项;readobsnav():确认观测和星历读取数量;procpos():确认历元循环是否正常;rtkpos():确认进入哪种定位模式;satposs():确认卫星位置和钟差;zdres()/ddres():确认残差;filter():确认滤波更新;resamb_LAMBDA():确认是否尝试固定;outsol():确认输出结果。
7. 单点定位 SPP 算法
核心文件:
text
src/pntpos.c
核心入口:
c
extern int pntpos(const obsd_t *obs, int n, const nav_t *nav,
const prcopt_t *opt, sol_t *sol, double *azel,
ssat_t *ssat, char *msg);
典型流程:
text
pntpos()
-> satposs() # 计算卫星位置和钟差
-> estpos() # 迭代最小二乘估计接收机位置
-> rescode() # 构造伪距残差
-> lsq() # 最小二乘
-> valsol() # 解有效性检查
-> estvel() # 用多普勒估计速度
核心数学模型:
伪距观测方程可简化为:
text
P = rho + c * (dtr - dts) + I + T + error
其中:
P:伪距;rho:接收机到卫星几何距离;dtr:接收机钟差;dts:卫星钟差;I:电离层延迟;T:对流层延迟。
调试重点:
satposs()输出的rs、dts是否正常;rescode()中每颗卫星残差是否异常;azel高度角是否低于截止角;vsat是否标记卫星可用;msg中是否有失败原因。
8. RTK 相对定位算法
核心文件:
text
src/rtkpos.c
核心入口:
c
extern int rtkpos(rtk_t *rtk, const obsd_t *obs, int n,
const nav_t *nav);
典型流程:
text
rtkpos()
-> pntpos() # 通常先给流动站做单点定位
-> relpos() # 相对定位主流程
-> udstate() # 时间更新:位置、钟差、模糊度等状态
-> zdres() # 非差残差
-> ddres() # 双差残差
-> filter() # 卡尔曼滤波更新浮点解
-> valpos() # 浮点解有效性检查
-> resamb_LAMBDA()
-> lambda() # LAMBDA 整周搜索
-> holdamb() # 可选:固定模糊度约束
RTK 的核心思想:
- 用基准站和流动站同时观测同一组卫星;
- 对观测值做站间差和星间差,形成双差;
- 双差可以显著削弱卫星钟差、接收机钟差等误差;
- 用卡尔曼滤波估计基线、模糊度、对流层等状态;
- 用 LAMBDA 算法把浮点模糊度固定为整数;
- 固定成功后得到高精度固定解。
RTK 调试重点:
obs中是否同时包含流动站和基准站数据;rcv是否正确区分 rover/base;satposs()是否为两站共同卫星提供有效卫星位置;zdres()非差残差是否异常;ddres()双差残差是否异常;rtk->x中模糊度状态是否稳定;rtk->sol.ratio是否达到阈值;rtk->sol.stat是否从FLOAT变为FIX。
9. PPP 精密单点定位算法
核心文件:
text
src/ppp.c
常见入口:
text
pppos()
典型流程:
text
pppos()
-> udstate_ppp() # PPP 状态时间更新
-> satposs() # 卫星位置钟差,通常使用精密产品
-> ppp_res() # 构造 PPP 残差
-> filter() # 卡尔曼滤波
-> ppp_ar() # PPP 模糊度固定,视版本和配置而定
PPP 常估计状态:
- 接收机位置;
- 接收机钟差;
- 对流层湿延迟;
- 载波相位模糊度;
- 部分版本支持接收机/卫星硬件偏差相关状态。
调试重点:
- 是否正确读取 SP3 精密星历;
- 是否正确读取 CLK 精密钟差;
prcopt_t.sateph是否选择精密星历;- 电离层组合、对流层模型设置是否合理;
- PPP 需要收敛时间,不应期待刚开始就是厘米级。
10. LAMBDA 模糊度固定
核心文件:
text
src/lambda.c
核心函数:
text
lambda()
RTK 中常见调用:
text
resamb_LAMBDA()
-> lambda()
LAMBDA 解决的问题:
载波相位观测中有一个未知整数周数,称为整周模糊度。滤波先估计浮点模糊度,再通过整数最小二乘搜索最可能的整数解。
典型步骤:
- 输入浮点模糊度和协方差;
- 对协方差降相关;
- 搜索候选整数解;
- 计算最佳解和次佳解;
- 用 ratio test 判断固定是否可靠。
调试重点:
- 输入模糊度数量
na是否足够; - 协方差矩阵是否病态;
- ratio 是否低于阈值;
- 是否存在周跳或错误观测导致固定失败;
prcopt_t.modear和prcopt_t.thresar[]设置是否合理。
11. 观测数据与星历读取
11.1 RINEX
核心文件:
text
src/rinex.c
关键函数:
readrnx():RINEX 总入口;readrnxobs():读取观测文件;readrnxnav():读取导航文件;outrnxobsb():输出 RINEX 观测记录;outrnxnavb():输出 RINEX 导航记录。
调试建议:
- 如果
obs_t.n == 0,先查 RINEX 头和版本; - 观测类型是否被正确映射到
code[]; - 多系统 RINEX 中 GPS/GLO/GAL/BDS/QZSS 是否启用;
- RINEX 3 的信号名和 RTKLIB 内部 code 是否对应。
11.2 RTCM
核心文件:
text
src/rtcm.c
src/rtcm2.c
src/rtcm3.c
关键函数:
input_rtcm2();input_rtcm3();decode_rtcm2();decode_rtcm3()。
调试建议:
- 实时流没有解,先确认 RTCM 消息类型是否包含观测和星历;
- MSM 消息解析后是否正确生成
obsd_t; - 基站坐标消息是否到达;
- 数据流中是否混有非 RTCM 数据。
11.3 厂商原始数据
核心文件:
text
src/raw.c
src/rcv/*.c
常见厂商解析文件:
text
src/rcv/ublox.c
src/rcv/novatel.c
src/rcv/septentrio.c
src/rcv/oem*.c
关键入口:
input_raw();input_ubx();input_oem4();input_stq();- 其他
input_*()。
如果要新增接收机协议,一般步骤:
- 新建
src/rcv/myreceiver.c; - 实现初始化函数;
- 实现逐字节输入函数;
- 解析观测、星历、时间等消息;
- 在
raw.c中注册格式; - 在配置或命令行中增加格式选项。
12. 卫星位置、钟差和误差模型
12.1 卫星位置与钟差
核心文件:
text
src/ephemeris.c
src/preceph.c
关键函数:
satposs():计算一组卫星位置和钟差;satpos():计算单颗卫星;eph2pos():广播星历计算卫星位置;geph2pos():GLONASS 星历;peph2pos():精密星历插值;ephclk():广播星历钟差;pephclk():精密钟差。
调试重点:
- 卫星位置
rs是否为 0; - 卫星钟差
dts是否异常; svh卫星健康状态;- 精密星历和观测时间是否覆盖。
12.2 电离层与对流层
常见函数:
ionmodel():广播电离层模型;ionmapf():电离层映射函数;tropmodel():Saastamoinen 对流层模型;tropmapf():对流层映射函数;sbstropcorr():SBAS 对流层改正。
调试重点:
- 单频 RTK 对电离层更敏感;
- 长基线下电离层残差会明显影响固定;
- PPP 对对流层估计和精密产品依赖更强。
13. 坐标系统与时间系统
核心文件:
text
src/rtkcmn.c
常见函数:
pos2ecef():经纬高转 ECEF;ecef2pos():ECEF 转经纬高;ecef2enu():ECEF 向量转 ENU;enu2ecef():ENU 向量转 ECEF;xyz2enu():构造转换矩阵;geodist():几何距离;satazel():卫星方位角、高度角。
调试建议:
sol.rr[]通常是 ECEF 坐标;- 输出经纬高前会经过坐标转换;
- 基站坐标配置错误会直接导致 RTK 基线错误;
- 高程异常时注意椭球高、大地水准面高、ENU 的区别。
14. 配置系统
核心文件:
text
src/options.c
关键结构:
text
prcopt_t # 处理选项
solopt_t # 输出选项
filopt_t # 文件选项
常见函数:
loadopts():读取配置文件;getsysopts():把配置同步到系统选项;setsysopts():设置系统选项;saveopts():保存配置文件。
调试建议:
- 算法行为异常时,先打印
prcopt_t; - GUI 和命令行最终都会落到这些 option 结构;
.conf中的字段名和options.c中的选项表对应。
15. 输出系统
核心文件:
text
src/solution.c
关键函数:
outsol():输出定位结果;outsols():输出一条解;outpos():输出位置;outnmea_gga():输出 NMEA GGA;decode_sol():读取 solution 文件。
调试建议:
rtk->sol.stat决定输出解类型;solopt_t.posf决定输出格式;- 如果内部有解但文件没输出,检查
outsol()和输出选项。
16. Trace 调试系统
RTKLIB 自带 trace 系统,非常适合算法调试。
常见函数:
c
traceopen("debug.trace");
tracelevel(5);
trace(3, "message: %d\n", value);
traceclose();
常用等级:
0:关闭或严重错误;1:错误;2:警告;3:普通流程信息;4:详细信息;5:非常详细的调试信息。
建议:
- 初学者先开
tracelevel(3); - 定位异常再开
tracelevel(4)或5; - trace 文件可能很大,长时间实时运行要注意磁盘空间。
17. 推荐断点清单
17.1 后处理定位
text
app/consapp/rnx2rtkp/rnx2rtkp.c: main
src/postpos.c: postpos
src/postpos.c: readobsnav
src/postpos.c: procpos
src/rtkpos.c: rtkpos
src/pntpos.c: pntpos
src/ppp.c: pppos
src/solution.c: outsol
17.2 RINEX 读取
text
src/rinex.c: readrnx
src/rinex.c: readrnxobs
src/rinex.c: readrnxnav
17.3 RTCM 实时流
text
src/stream.c: strread
src/rtcm3.c: input_rtcm3
src/rtcm3.c: decode_rtcm3
src/rtksvr.c: rtksvrthread
src/rtkpos.c: rtkpos
17.4 RTK 固定解
text
src/rtkpos.c: relpos
src/rtkpos.c: zdres
src/rtkpos.c: ddres
src/rtkpos.c: resamb_LAMBDA
src/lambda.c: lambda
17.5 卫星位置和星历
text
src/ephemeris.c: satposs
src/ephemeris.c: eph2pos
src/preceph.c: peph2pos
18. 新手调试路线
建议按以下顺序上手:
- 编译
rnx2rtkp。 - 找一组官方或公开 RINEX 示例数据。
- 先跑 Single 模式,确认 RINEX 和星历读取正常。
- 切换 Kinematic 或 Static RTK 模式,加入基准站观测。
- 在
postpos()和procpos()看历元循环。 - 在
pntpos()看单点定位如何给初值。 - 在
rtkpos()和relpos()看 RTK 状态更新。 - 在
ddres()看双差残差。 - 在
filter()看卡尔曼滤波更新。 - 在
resamb_LAMBDA()和lambda()看固定解过程。 - 最后再看实时
rtkrcv、str2str和rtksvr.c。
19. 常见问题排查
19.1 没有解
优先检查:
- 观测值是否读入:
obs.n > 0; - 星历是否读入:
nav.n > 0; - 卫星系统是否被配置启用;
- 截止高度角是否太高;
- 时间范围是否匹配;
satposs()是否返回有效卫星位置;pntpos()的msg内容。
19.2 只有 Single,没有 Float/Fix
优先检查:
- 是否真的有基准站数据;
- rover/base 的
rcv标记是否正确; - 基站坐标是否正确;
- 两站是否有共同卫星;
- 差分龄期是否过大;
- 观测频点是否满足配置;
- 载波相位是否有效。
19.3 Float 不收敛
优先检查:
- 周跳是否频繁;
- 信噪比是否低;
- 低高度角卫星是否太多;
- 基线是否过长;
- 电离层/对流层模型是否合适;
- 动态模型是否与实际运动匹配;
- 滤波过程噪声设置是否过大或过小。
19.4 Fix 失败或误固定
优先检查:
ratio是否低;thresar阈值是否合理;- 参与固定的模糊度数量是否太少;
- 某些卫星残差是否明显异常;
- 是否存在未探测的周跳;
- GLONASS、BDS、GPS 混合固定策略是否合适;
- 接收机天线、相位中心、基站坐标是否正确。
19.5 PPP 精度差
优先检查:
- 是否使用精密星历和钟差;
- 精密产品时间是否覆盖观测时间;
- 是否读取天线改正;
- 对流层估计是否启用;
- PPP 是否有足够收敛时间;
- 观测文件中的码和相位是否质量可靠。
20. 读代码时的逻辑
可以把 RTKLIB 分成五层:
text
应用层
rnx2rtkp / rtkrcv / convbin / str2str
流程层
postpos / rtksvr / streamsvr
数据层
rinex / rtcm / raw / stream
模型层
ephemeris / preceph / ionosphere / troposphere / coordinates
算法层
pntpos / rtkpos / ppp / lambda / filter
调试时不要一开始就钻进 lambda.c。更建议的顺序是:
text
数据是否进来
-> 时间是否对齐
-> 星历是否有效
-> 单点是否正常
-> 双差残差是否正常
-> 滤波是否稳定
-> 模糊度是否可固定
21. 最小调试案例建议
如果你要准备一个可复现调试用例,建议文件包括:
text
rover.obs # 流动站 RINEX 观测
base.obs # 基准站 RINEX 观测
brdc.nav # 广播星历
rtk.conf # RTKLIB 配置
expected.pos # 期望输出,可选
命令示例:
bash
rnx2rtkp -k rtk.conf -o result.pos rover.obs base.obs brdc.nav
然后从 rnx2rtkp.c:main() 开始单步,确认配置和文件被正确读入。
22. 修改源码时的建议
- 改算法前先打开 trace,保存修改前后的日志;
- 每次只改一个模型或一个参数;
- 对比
.pos、trace、参与卫星数、ratio; - 不要只看最终坐标,要看每个历元的残差;
- 对 RTK 问题,优先画
ratio、ns、age、stat随时间变化; - 新增接收机协议时,先用
convbin验证能否正确生成 RINEX; - 新增定位算法时,优先复用
rtk_t、obsd_t、nav_t和现有误差模型函数。
23. 关键函数索引
| 功能 | 文件 | 关键函数 |
|---|---|---|
| 后处理总控 | postpos.c |
postpos()、procpos() |
| 单点定位 | pntpos.c |
pntpos()、estpos()、rescode() |
| RTK | rtkpos.c |
rtkpos()、relpos()、ddres() |
| PPP | ppp.c |
pppos()、ppp_res() |
| 模糊度固定 | lambda.c |
lambda() |
| 卡尔曼滤波 | rtkcmn.c 等 |
filter() |
| 卫星位置 | ephemeris.c、preceph.c |
satposs()、eph2pos()、peph2pos() |
| RINEX | rinex.c |
readrnx()、readrnxobs()、readrnxnav() |
| RTCM | rtcm*.c |
input_rtcm3()、decode_rtcm3() |
| 原始数据 | raw.c、rcv/*.c |
input_raw()、input_ubx() 等 |
| 数据流 | stream.c |
stropen()、strread()、strwrite() |
| 实时服务器 | rtksvr.c |
rtksvrstart()、rtksvrthread() |
| 输出 | solution.c |
outsol()、outnmea_gga() |
| 配置 | options.c |
loadopts()、getsysopts() |
| 通用工具 | rtkcmn.c |
时间、坐标、矩阵、trace |
24. 进一步阅读
- 官方仓库:https://github.com/tomojitakasu/RTKLIB
- 官方网站:https://www.rtklib.com/
- 常用增强分支:https://github.com/rtklibexplorer/RTKLIB
建议先通读官方文档中的 manual,再结合本文按调用链下断点。RTKLIB 的代码年代较早,宏、全局选项和 C 风格数组较多,但主线非常清晰:数据输入、卫星位置、误差模型、滤波估计、模糊度固定、结果输出。抓住这条主线,调试会轻松很多。