RTKLIB开源源码调试快速上手指南

文章目录

    • [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:数据流转发)
    • [4. `src/` 核心源码结构](#4. src/ 核心源码结构)
    • [5. 关键数据结构](#5. 关键数据结构)
      • [5.1 `gtime_t`](#5.1 gtime_t)
      • [5.2 `obsd_t` 与 `obs_t`](#5.2 obsd_tobs_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)
    • [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/           # 各厂商接收机协议解析

优先阅读顺序:

  1. rtklib.h:先认识数据结构;
  2. postpos.c:理解后处理流程;
  3. pntpos.c:理解最简单定位;
  4. rtkpos.c:理解差分和滤波;
  5. lambda.c:理解模糊度固定;
  6. rinex.crtcm3.craw.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_tobs_t

观测值结构。

obsd_t 表示一个卫星在某一历元的观测值,通常包含:

  • time:观测时间;
  • sat:卫星编号;
  • rcv:接收机编号;
  • P[]:伪距;
  • L[]:载波相位;
  • D[]:多普勒;
  • SNR[]:信噪比;
  • LLI[]:失锁标志;
  • code[]:观测码类型。

obs_tobsd_t 数组容器。

调试建议:

  • 如果定位结果不稳定,先确认 P[]L[]satrcv 是否正常;
  • RTK 中流动站和基准站通常通过 rcv=1/2 区分;
  • 周跳、失锁问题重点看 LLI[] 和载波相位变化。

导航数据结构,保存:

  • 广播星历;
  • 精密星历;
  • 精密钟差;
  • 电离层参数;
  • GLONASS 频点;
  • 天线参数等。

调试建议:

  • "有观测但算不出位置"经常是星历没读到;
  • 下断点查看 nav->nnav->ngnav->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:处理选项。

调试建议:

  • 卡尔曼滤波是否发散,看 xP
  • 固定解失败,看 xassat[]、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 数据时,建议断点顺序:

  1. postpos():确认文件路径、起止时间、选项;
  2. readobsnav():确认观测和星历读取数量;
  3. procpos():确认历元循环是否正常;
  4. rtkpos():确认进入哪种定位模式;
  5. satposs():确认卫星位置和钟差;
  6. zdres() / ddres():确认残差;
  7. filter():确认滤波更新;
  8. resamb_LAMBDA():确认是否尝试固定;
  9. 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() 输出的 rsdts 是否正常;
  • 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 的核心思想:

  1. 用基准站和流动站同时观测同一组卫星;
  2. 对观测值做站间差和星间差,形成双差;
  3. 双差可以显著削弱卫星钟差、接收机钟差等误差;
  4. 用卡尔曼滤波估计基线、模糊度、对流层等状态;
  5. 用 LAMBDA 算法把浮点模糊度固定为整数;
  6. 固定成功后得到高精度固定解。

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 解决的问题:

载波相位观测中有一个未知整数周数,称为整周模糊度。滤波先估计浮点模糊度,再通过整数最小二乘搜索最可能的整数解。

典型步骤:

  1. 输入浮点模糊度和协方差;
  2. 对协方差降相关;
  3. 搜索候选整数解;
  4. 计算最佳解和次佳解;
  5. 用 ratio test 判断固定是否可靠。

调试重点:

  • 输入模糊度数量 na 是否足够;
  • 协方差矩阵是否病态;
  • ratio 是否低于阈值;
  • 是否存在周跳或错误观测导致固定失败;
  • prcopt_t.modearprcopt_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_*()

如果要新增接收机协议,一般步骤:

  1. 新建 src/rcv/myreceiver.c
  2. 实现初始化函数;
  3. 实现逐字节输入函数;
  4. 解析观测、星历、时间等消息;
  5. raw.c 中注册格式;
  6. 在配置或命令行中增加格式选项。

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. 新手调试路线

建议按以下顺序上手:

  1. 编译 rnx2rtkp
  2. 找一组官方或公开 RINEX 示例数据。
  3. 先跑 Single 模式,确认 RINEX 和星历读取正常。
  4. 切换 Kinematic 或 Static RTK 模式,加入基准站观测。
  5. postpos()procpos() 看历元循环。
  6. pntpos() 看单点定位如何给初值。
  7. rtkpos()relpos() 看 RTK 状态更新。
  8. ddres() 看双差残差。
  9. filter() 看卡尔曼滤波更新。
  10. resamb_LAMBDA()lambda() 看固定解过程。
  11. 最后再看实时 rtkrcvstr2strrtksvr.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 问题,优先画 rationsagestat 随时间变化;
  • 新增接收机协议时,先用 convbin 验证能否正确生成 RINEX;
  • 新增定位算法时,优先复用 rtk_tobsd_tnav_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.cpreceph.c satposs()eph2pos()peph2pos()
RINEX rinex.c readrnx()readrnxobs()readrnxnav()
RTCM rtcm*.c input_rtcm3()decode_rtcm3()
原始数据 raw.crcv/*.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. 进一步阅读

建议先通读官方文档中的 manual,再结合本文按调用链下断点。RTKLIB 的代码年代较早,宏、全局选项和 C 风格数组较多,但主线非常清晰:数据输入、卫星位置、误差模型、滤波估计、模糊度固定、结果输出。抓住这条主线,调试会轻松很多。