主线 :Neville 递推原理 →
interppol()逐行对应 →pephpos()的 11 节点 SP3 插值 → 与拉格朗日比较 → 阶数、误差及工程注意事项。本文以 RTKLIB 官方仓库
master/src/preceph.c的所核对实现为准。
1. 澄清三个概念
Neville(内维尔)算法 将相邻数据点的低次插值结果逐层合并,直接求出指定时刻的插值值,不显式计算多项式系数。RTKLIB 的 interppol() 使用的正是 Neville 递推,而不是 Newton(牛顿)差商插值。
| 名称 | 核心思路 | 与 RTKLIB 的关系 |
|---|---|---|
| Neville 插值 | 子节点组逐层递推,在目标时刻求值 | interppol() 的实际算法 |
| Lagrange(拉格朗日)插值 | 构造基函数并加权求和 | 与 Neville 表示同一个插值多项式 |
| Newton(牛顿)差商插值 | 先求差商系数,再用嵌套形式求值 | 数学结果相同,不是该函数的实现 |
对同一组互异节点,三种方法在精确算术下得到同一个唯一插值多项式;换一种求值算法,并不能自动降低该多项式的截断误差。
RTKLIB 在 preceph.c 中定义 NMAX=10:10 次多项式需要 11 个节点,并不意味着只读取 10 个 SP3 历元。
2. 数学原理:从一般公式推到 RTKLIB 原地递推
2.1 两节点线性插值
设节点为 ( x i , y i ) (x_i,y_i) (xi,yi),其中 x i x_i xi 是相对目标时刻的时间(s), y i y_i yi 是一个待插值的卫星坐标分量(m);节点时间两两互异。目标自变量记为 u u u(s)。利用相邻两个节点构造一次多项式:
P i , 1 ( u ) = y i x i + 1 − u x i + 1 − x i + y i + 1 u − x i x i + 1 − x i = ( x i + 1 − u ) y i − ( x i − u ) y i + 1 x i + 1 − x i . \begin{aligned} P_{i,1}(u) &=y_i\frac{x_{i+1}-u}{x_{i+1}-x_i} +y_{i+1}\frac{u-x_i}{x_{i+1}-x_i}\\ &=\boxed{\frac{(x_{i+1}-u)y_i-(x_i-u)y_{i+1}}{x_{i+1}-x_i}}. \end{aligned} Pi,1(u)=yixi+1−xixi+1−u+yi+1xi+1−xiu−xi=xi+1−xi(xi+1−u)yi−(xi−u)yi+1.
其中 i i i 为起始节点下标, P i , 1 P_{i,1} Pi,1 表示由节点 i , i + 1 i,i+1 i,i+1 构造的一次插值多项式。
2.2 从低次到高次的 Neville 递推
记 P i , j ( u ) P_{i,j}(u) Pi,j(u) 为使用节点 i , i + 1 , ... , i + j i,i+1,\ldots,i+j i,i+1,...,i+j 构造的最高 j j j 次 插值多项式在 u u u 处的值。起始条件为
P i , 0 ( u ) = y i . \boxed{P_{i,0}(u)=y_i}. Pi,0(u)=yi.
已有两个相互重叠的 ( j − 1 ) (j-1) (j−1) 次插值多项式:
- P i , j − 1 ( u ) P_{i,j-1}(u) Pi,j−1(u):由节点 i , ... , i + j − 1 i,\ldots,i+j-1 i,...,i+j−1 构造;
- P i + 1 , j − 1 ( u ) P_{i+1,j-1}(u) Pi+1,j−1(u):由节点 i + 1 , ... , i + j i+1,\ldots,i+j i+1,...,i+j 构造。
按首尾节点构造新函数:
Q ( u ) = ( x i + j − u ) P i , j − 1 ( u ) + ( u − x i ) P i + 1 , j − 1 ( u ) x i + j − x i . Q(u)=\frac{(x_{i+j}-u)P_{i,j-1}(u)+(u-x_i)P_{i+1,j-1}(u)}{x_{i+j}-x_i}. Q(u)=xi+j−xi(xi+j−u)Pi,j−1(u)+(u−xi)Pi+1,j−1(u).
在 u = x i u=x_i u=xi、 u = x i + j u=x_{i+j} u=xi+j 和所有中间节点处, Q Q Q 都等于对应的观测值;同时 Q Q Q 的次数不超过 j j j。根据互异节点的插值多项式唯一性, Q = P i , j Q=P_{i,j} Q=Pi,j,得到 Neville 通式:
P i , j ( u ) = ( x i + j − u ) P i , j − 1 ( u ) − ( x i − u ) P i + 1 , j − 1 ( u ) x i + j − x i , j ≥ 1. (1) \boxed{ P_{i,j}(u)=\frac{(x_{i+j}-u)P_{i,j-1}(u)-(x_i-u)P_{i+1,j-1}(u)}{x_{i+j}-x_i} },\quad j\ge 1. \tag{1} Pi,j(u)=xi+j−xi(xi+j−u)Pi,j−1(u)−(xi−u)Pi+1,j−1(u),j≥1.(1)
式中, j j j 为递推层数, x i + j x_{i+j} xi+j 为本层末节点时间,分母为首末节点的时间差(s), P i , j P_{i,j} Pi,j 的量纲始终与 y i y_i yi 相同(轨道插值为 m)。
2.3 为什么 RTKLIB 公式中没有目标时间?
对待插值历元 T ∗ T_* T∗,RTKLIB pephpos() 预先计算
x i = T i − T ∗ , i = 0 , 1 , ... , 10 , (2) \boxed{x_i=T_i-T_*},\qquad i=0,1,\ldots,10, \tag{2} xi=Ti−T∗,i=0,1,...,10,(2)
其中 T i T_i Ti 是第 i i i 个 SP3 节点历元,时间差单位 s。经过平移,目标历元恰好对应 u = 0 u=0 u=0 。将式(1)中的 u u u 置零:
P i , j ( 0 ) = x i + j P i , j − 1 ( 0 ) − x i P i + 1 , j − 1 ( 0 ) x i + j − x i . (3) \boxed{P_{i,j}(0)=\frac{x_{i+j}P_{i,j-1}(0)-x_iP_{i+1,j-1}(0)}{x_{i+j}-x_i}}. \tag{3} Pi,j(0)=xi+j−xixi+jPi,j−1(0)−xiPi+1,j−1(0).(3)
这与 RTKLIB interppol() 的核心赋值式逐项对应:
c
for (j=1;j<n;j++) {
for (i=0;i<n-j;i++) {
y[i]=(x[i+j]*y[i]-x[i]*y[i+1])/(x[i+j]-x[i]);
}
}
return y[0];
| 源码表达式 | 数学含义 |
|---|---|
n |
节点数;RTKLIB 轨道插值时为 11 |
x[i]、x[i+j] |
相对目标历元的首末节点时间 x i , x i + j x_i,x_{i+j} xi,xi+j(s) |
j |
递推层数,对应多项式最高次数 j j j |
i |
当前连续节点组的起始索引 |
更新前 y[i] |
上一层 P i , j − 1 ( 0 ) P_{i,j-1}(0) Pi,j−1(0) |
更新前 y[i+1] |
上一层 P i + 1 , j − 1 ( 0 ) P_{i+1,j-1}(0) Pi+1,j−1(0) |
更新后 y[i] |
本层 P i , j ( 0 ) P_{i,j}(0) Pi,j(0) |
返回 y[0] |
全部节点的结果 P 0 , n − 1 ( 0 ) P_{0,n-1}(0) P0,n−1(0) |
为什么内层 i 必须递增? 因为 y[i] 被原地覆盖,而本层还要读取尚未覆盖的 y[i+1](上一层结果)。若直接改成从大到小遍历,会破坏数据依赖。函数修改输入数组 y,但不修改 x;源码没有重复节点检查。
2.4 三节点手算,核对代码每一层
取二次函数 f ( t ) = 2 t 2 − 3 t + 5 f(t)=2t^2-3t+5 f(t)=2t2−3t+5,节点为 ( − 2 , 19 ) (-2,19) (−2,19)、 ( 1 , 4 ) (1,4) (1,4)、 ( 3 , 14 ) (3,14) (3,14),求 f ( 0 ) f(0) f(0)。
初值: P 0 , 0 = 19 P_{0,0}=19 P0,0=19, P 1 , 0 = 4 P_{1,0}=4 P1,0=4, P 2 , 0 = 14 P_{2,0}=14 P2,0=14。
第一层(j=1):
P 0 , 1 ( 0 ) = 1 × 19 − ( − 2 ) × 4 1 − ( − 2 ) = 9 , P 1 , 1 ( 0 ) = 3 × 4 − 1 × 14 3 − 1 = − 1. P_{0,1}(0)=\frac{1\times19-(-2)\times4}{1-(-2)}=9, \qquad P_{1,1}(0)=\frac{3\times4-1\times14}{3-1}=-1. P0,1(0)=1−(−2)1×19−(−2)×4=9,P1,1(0)=3−13×4−1×14=−1.
第二层(j=2):
P 0 , 2 ( 0 ) = 3 × 9 − ( − 2 ) × ( − 1 ) 3 − ( − 2 ) = 5 = f ( 0 ) . \boxed{P_{0,2}(0)=\frac{3\times9-(-2)\times(-1)}{3-(-2)}=5=f(0)}. P0,2(0)=3−(−2)3×9−(−2)×(−1)=5=f(0).
y 的原地变化:[19, 4, 14] → [9, -1, 14] → [5, -1, 14]。不需要先求出 2 t 2 − 3 t + 5 2t^2-3t+5 2t2−3t+5 的系数。
2.5 RTKLIB 10 次、11 节点的最终公式
令 n = 11 n=11 n=11,式(3)的索引范围为
P i , j ( 0 ) = x i + j P i , j − 1 ( 0 ) − x i P i + 1 , j − 1 ( 0 ) x i + j − x i , j = 1 , ... , 10 i = 0 , ... , 10 − j . (4) \boxed{ P_{i,j}(0)=\frac{x_{i+j}P_{i,j-1}(0)-x_iP_{i+1,j-1}(0)}{x_{i+j}-x_i}, \quad\substack{j=1,\ldots,10\\i=0,\ldots,10-j} }. \tag{4} Pi,j(0)=xi+j−xixi+jPi,j−1(0)−xiPi+1,j−1(0),j=1,...,10i=0,...,10−j.(4)
第一层更新 10 次、第二层 9 次......第十层更新 1 次,因此每个坐标分量共有
10 + 9 + ⋯ + 1 = 55 10+9+\cdots+1=\boxed{55} 10+9+⋯+1=55
次递推更新,最终输出
P 0 , 10 ( 0 ) = x 10 P 0 , 9 ( 0 ) − x 0 P 1 , 9 ( 0 ) x 10 − x 0 . \boxed{P_{0,10}(0)=\frac{x_{10}P_{0,9}(0)-x_0P_{1,9}(0)}{x_{10}-x_0}}. P0,10(0)=x10−x0x10P0,9(0)−x0P1,9(0).
对同一组节点,它等于拉格朗日形式:
P 0 , 10 ( 0 ) = ∑ i = 0 10 y i ℓ i ( 0 ) , ℓ i ( 0 ) = ∏ k = 0 k ≠ i 10 − x k x i − x k . (5) \boxed{P_{0,10}(0)=\sum_{i=0}^{10}y_i\ell_i(0)},\quad \ell_i(0)=\prod_{\substack{k=0\\k\ne i}}^{10}\frac{-x_k}{x_i-x_k}. \tag{5} P0,10(0)=i=0∑10yiℓi(0),ℓi(0)=k=0k=i∏10xi−xk−xk.(5)
其中 ℓ i ( 0 ) \ell_i(0) ℓi(0) 是无量纲拉格朗日基函数值, k k k 为节点索引, y i y_i yi 为该分量的原始节点坐标(m)。两种算法给出同一多项式;实际浮点结果可因运算顺序略有不同。
3. RTKLIB 的 SP3 插值究竟怎么做?
3.1 数据读取与节点选择
readsp3() / readsp3b() 读取 SP3 的 P 记录:位置 km→m 、钟差 µs→s ;若有 V 记录,也会读取速度(其原始单位为 dm/s),但 peph2pos() 不直接利用它求输出速度。SP3 预测轨道/钟差的读取由 readsp3() 的选项控制,默认 opt=0 并不等于剔除预测值。12
pephpos() 的处理顺序:
- 检查
nav->ne >= 11,并检查目标时刻距全局产品首尾历元 是否超过MAXDTE=900 s。 - 对全局历元序列二分查找,用固定逻辑选出 11 个连续历元,边缘处将窗口向产品内部移动。
- 计算各节点相对时间 x i = T i − T ∗ x_i=T_i-T_* xi=Ti−T∗,检查目标卫星在这 11 个历元均有非零位置向量。
- 对每个节点实施 ECEF 坐标旋转,再对三个分量分别调用
interppol()。
窗口起点对应的源码逻辑(index 为目标历元附近的全局历元下标):
c
index=i<=0?0:i-1;
i=index-(NMAX+1)/2;
if (i<0) i=0;
else if (i+NMAX>=nav->ne) i=nav->ne-NMAX-1;
两个边界特别重要:
MAXDTE=900 s不是 11 节点窗口的总时长;对 15 min 间隔 SP3,11 节点覆盖约 150 min。- 源码允许产品首尾附近 最多 900 s 的有限外推。边界处精度与窗口内部不能直接等同;生产级 PPP 通常应明确设置外推限值及告警。
节点从全局历元 取,而不是逐卫星寻找"最近 11 个有效值"。选中的窗口只要有一个目标卫星位置节点缺测,pephpos() 即失败;不会自动改用 9 节点或绕开该节点。
3.2 先旋转 ECEF 节点,再插值
RTKLIB 以 θ i = ω e x i \theta_i=\omega_e x_i θi=ωexi 对每组 SP3 ECEF 位置进行旋转:
X i ′ Y i ′ Z i ′ = cos θ i − sin θ i 0 sin θ i cos θ i 0 0 0 1 X i Y i Z i , θ i = ω e x i . (6) \boxed{ \begin{bmatrix}X'_i\\Y'_i\\Z'_i\end{bmatrix}= \begin{bmatrix} \cos\theta_i&-\sin\theta_i&0\\ \sin\theta_i&\cos\theta_i&0\\ 0&0&1 \end{bmatrix} \begin{bmatrix}X_i\\Y_i\\Z_i\end{bmatrix} },\quad \theta_i=\omega_e x_i. \tag{6} Xi′Yi′Zi′ = cosθisinθi0−sinθicosθi0001 XiYiZi ,θi=ωexi.(6)
其中 ( X i , Y i , Z i ) (X_i,Y_i,Z_i) (Xi,Yi,Zi) 为 SP3 节点原始 ECEF 坐标(m),带撇号的量为旋转后用于插值的坐标(m); ω e \omega_e ωe 为源码使用的地球自转角速度 OMGE(rad/s), θ i \theta_i θi 为角度(rad)。式(6)的旋转矩阵正负号与源码完全一致。
这一步针对不同时刻 ECEF 坐标轴的旋转进行处理;它不是卫星信号传播时间内的 Sagnac 改正,不能用其中一个替代另一个。
依次计算:
X ( T ∗ ) = Neville ( { ( x i , X i ′ ) } i = 0 10 ; 0 ) , Y ( T ∗ ) = Neville ( { ( x i , Y i ′ ) } i = 0 10 ; 0 ) , Z ( T ∗ ) = Neville ( { ( x i , Z i ′ ) } i = 0 10 ; 0 ) . (7) \begin{aligned} X(T_*)&=\operatorname{Neville}\bigl(\{(x_i,X'i)\}{i=0}^{10};0\bigr),\\ Y(T_*)&=\operatorname{Neville}\bigl(\{(x_i,Y'i)\}{i=0}^{10};0\bigr),\\ Z(T_*)&=\operatorname{Neville}\bigl(\{(x_i,Z'i)\}{i=0}^{10};0\bigr). \end{aligned} \tag{7} X(T∗)Y(T∗)Z(T∗)=Neville({(xi,Xi′)}i=010;0),=Neville({(xi,Yi′)}i=010;0),=Neville({(xi,Zi′)}i=010;0).(7)
X ( T ∗ ) , Y ( T ∗ ) , Z ( T ∗ ) X(T_*),Y(T_*),Z(T_*) X(T∗),Y(T∗),Z(T∗) 为目标时刻卫星插值坐标(m), Neville \operatorname{Neville} Neville 代表式(4)在零点的递推求值。三个分量各做一次 55 次更新。
3.3 轨道、钟差、速度不是同一种插值
| 计算对象 | 官方 preceph.c 的实际处理 |
注意事项 |
|---|---|---|
| SP3 位置 XYZ | pephpos():11 节点、10 次 Neville |
前置节点旋转;固定窗口中的缺测会导致失败 |
| SP3 内嵌钟差 | pephpos():相邻两历元线性插值,边界单点取值 |
缺测时可能置零;不是 10 次 Neville |
| 独立 CLK 钟差 | pephclk():相邻节点线性插值 |
无 CLK 序列或超出允许范围时可保留 SP3 钟差;在已有 CLK 序列内遇到必要钟差缺测则可返回失败,并非无条件回退 |
| 卫星速度 | peph2pos():对两次插值坐标做 1 ms 前向差分 |
没有直接使用 SP3 的 V 记录,也不是多项式解析求导 |
| 卫星天线偏移 | peph2pos() 根据 opt 选项调用 satantoff() |
属于插值后的参考点转换;与 Neville 本身无关 |
速度计算的简化表示:
v ( T ∗ ) ≈ r ( T ∗ + Δ t ) − r ( T ∗ ) Δ t , Δ t = 10 − 3 s . (8) \boxed{\boldsymbol v(T_*)\approx\frac{\boldsymbol r(T_*+\Delta t)-\boldsymbol r(T_*)}{\Delta t}}, \quad \Delta t=10^{-3}\ \mathrm{s}. \tag{8} v(T∗)≈Δtr(T∗+Δt)−r(T∗),Δt=10−3 s.(8)
其中 r \boldsymbol r r 为 pephpos() 的未加卫星天线偏移的 ECEF 插值位置(m), v \boldsymbol v v 为速度(m/s), Δ t \Delta t Δt 为差分步长。即使请求了卫星 APC 位置,源码速度仍按轨道位置差分,不是严格的 APC 速度导数 。peph2pos() 还会在组合阶段处理卫星钟差的相对论项;不能把函数最终钟差直接当作 SP3 文件中的原始钟差。1
3.4 源码行为与工程增强应当区分
当前 pephpos() 的检查主要覆盖节点总数、产品时间边界及目标卫星位置是否缺测;它不会自动完整诊断 轨道机动、相邻文件不连续、预测/实测拼接或异常轨道跳变。更严格的 PPP 解算器应额外检查 SP3 标志、数据间隔、时间尺度、产品基准和跨文件连续性,但这些属于建议增加的质量控制,不能描述为 RTKLIB 已实现的行为。
另一个独立的源码注意事项:pephpos() 在 SP3 钟差区间内线性插值分支 的一处标准差计算中,将以秒计的钟差 std 直接加上以 m 计的外推误差项;其边界分支及 pephclk() 相应处理含光速转换。若要将该分支的方差直接用于 PPP 权阵,应核实所用源码版本和单位。这不是 Neville 数学递推的错误。1
4. Neville 与拉格朗日:算法选择
设 n n n 为一个插值窗口的节点数。
| 对比项 | Neville(RTKLIB) | 直接 Lagrange | 重心 Lagrange | Newton 差商 |
|---|---|---|---|---|
| 数学结果 | 同一插值多项式 | 同左 | 同左 | 同左 |
| 实现方式 | 逐层合并子区间结果 | 逐项计算基函数 | 预计算权重后求值 | 差商系数 + 嵌套求值 |
| 单次求值复杂度 | O ( n 2 ) O(n^2) O(n2) | 朴素实现 O ( n 2 ) O(n^2) O(n2) | 权重已知时 O ( n ) O(n) O(n) | 系数已知时 O ( n ) O(n) O(n) |
| 预处理 | 无 | 无 | 一般需 O ( n 2 ) O(n^2) O(n2) 建权重 | 一般需 O ( n 2 ) O(n^2) O(n2) 建差商 |
| 额外内存 | O ( n ) O(n) O(n),原地覆盖 y |
视实现而定 | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) |
| 优点 | 简洁、便于对照 RTKLIB、无需建系数 | 推导直观、便于交叉验证 | 相同窗口多次求值效率高 | 可重用系数,逐步增添节点方便 |
| 局限 | 重复求值开销较高 | 朴素乘积实现存在舍入风险 | 需处理权重数值范围及命中节点 | 差商对节点分布及浮点误差敏感 |
| 能否消除高阶振荡 | 不能 | 不能 | 不能 | 不能 |
选型要点:希望逐句复现 RTKLIB 时选 Neville;同一窗口要对很多目标时刻反复求值时,可评估重心拉格朗日或预计算差商。不要笼统宣称 Neville 总比拉格朗日精度高;它们的截断误差与节点敏感性相同,数值舍入表现取决于实现和节点分布。3
| 其他插值方法 | 优点 | 局限 | GNSS 常见应用 |
|---|---|---|---|
| 分段线性 | 简洁、局部、易处理缺测 | 曲线一阶导数不连续 | 精密钟差、ERP 短时间插值 |
| 三次样条 | 连续且平滑,局部多项式 | 边界条件和异常值处理重要 | 平滑时序、空间场拟合 |
| Hermite | 可使用位置与可信速度/导数 | 导数数据质量直接影响结果 | 轨道位置与速度联合插值 |
| PCHIP | 对单调序列减少过冲 | 并非针对轨道动力学优化 | 单调标定表、部分气象量 |
5. 插值阶数越高越好吗?
**不一定。**更多节点可降低足够光滑函数的部分截断误差,却也扩大时间窗口,可能跨越轨道机动、文件接缝或预测段;对于等间隔高阶插值,还可能增大节点误差的放大和边缘振荡。
5.1 10 次多项式的理论余项
若真实函数 f ( t ) f(t) f(t) 在覆盖 11 个节点和目标时刻的区间内具有连续 11 阶导数,且节点值无误差,则
f ( 0 ) − P 0 , 10 ( 0 ) = f ( 11 ) ( ξ ) 11 ! ∏ i = 0 10 ( − x i ) . (9) \boxed{f(0)-P_{0,10}(0)=\frac{f^{(11)}(\xi)}{11!}\prod_{i=0}^{10}(-x_i)}. \tag{9} f(0)−P0,10(0)=11!f(11)(ξ)i=0∏10(−xi).(9)
其中 f ( 0 ) f(0) f(0) 为真实坐标分量(m), P 0 , 10 ( 0 ) P_{0,10}(0) P0,10(0) 为插值结果(m), f ( 11 ) f^{(11)} f(11) 是对时间的 11 阶导数(m/s 11 ^{11} 11), ξ \xi ξ 为节点与目标区间内的某个未知时间(s), 11 ! 11! 11! 为阶乘。此式仅描述理想光滑函数的截断误差,不等于真实 SP3 产品的总误差。
5.2 节点误差也会传入插值结果
设节点坐标误差为 ε i \varepsilon_i εi(m),由式(5)得
δ P ( 0 ) = ∑ i = 0 10 ℓ i ( 0 ) ε i , ∣ δ P ( 0 ) ∣ ≤ Λ ( 0 ) max i ∣ ε i ∣ , (10) \boxed{\delta P(0)=\sum_{i=0}^{10}\ell_i(0)\varepsilon_i},\qquad \boxed{|\delta P(0)|\le\Lambda(0)\max_i|\varepsilon_i|}, \tag{10} δP(0)=i=0∑10ℓi(0)εi,∣δP(0)∣≤Λ(0)imax∣εi∣,(10)
其中 δ P \delta P δP 为由节点误差引起的结果扰动(m), ℓ i \ell_i ℓi 为拉格朗日基函数, Λ ( 0 ) = ∑ i = 0 10 ∣ ℓ i ( 0 ) ∣ \Lambda(0)=\sum_{i=0}^{10}|\ell_i(0)| Λ(0)=∑i=010∣ℓi(0)∣ 为无量纲的节点误差放大系数。此性质属于插值多项式自身,改成 Neville 或重心求值仍然存在。34
5.3 工程上怎样选择次数?
对相同 SP3 产品和相同质量筛选规则,分别测试 6、8、10、12 次等候选方法。保留部分历元不参与构造,用其他历元恢复被保留的坐标,统计三维误差 RMS、P95、最大误差、边缘误差、缺测率和耗时;必要时再与独立高精度参考轨道比较。
留出 SP3 节点重建误差只说明对该产品离散样本的重建能力,不能直接当成相对于真实卫星轨道的绝对精度。 高阶并非最优的结论也不意味着固定 10 次一定不好:RTKLIB 的 NMAX=10 是其特定工程实现的选择。
6. 工程易错点与其他应用
| 易错点 | 正确处理 |
|---|---|
| 混淆 Neville 和 Newton 差商 | 以 interppol() 的式(3)为准;二者数学等价但代码不同 |
| 误认 10 次 = 10 节点 | 10 次 = 11 个互异历元 |
把 x 当 ECEF X 或绝对时间戳 |
x_i 是节点减目标时间的秒数;目标已平移到零 |
将 interppol() 内层循环倒序 |
原地更新依赖从小到大遍历 i |
| 忽略节点 ECEF 旋转 | pephpos() 先旋转,再插值 XYZ;注意旋转角符号 |
| 将节点旋转当作传播 Sagnac 改正 | 两者用途不同,需要分别遵循观测模型 |
| 在缺失一个节点时照常使用 11 点插值 | 原版 pephpos() 返回失败;低阶回退需自行设计并验证 |
| 把轨道与精密钟差均做 10 次插值 | RTKLIB 对轨道用 Neville,对钟差用线性插值 |
| 忽略 SP3 预测、缺测、机动、跨天拼接 | 在读取及窗口选择处进行独立质量检查 |
| 把 900 s 理解成 11 节点窗口长度 | MAXDTE 是产品首尾时间许可范围,包含有限外推 |
| 未区分留出样本误差与轨道真值误差 | 两者属于不同验证指标,不能直接互称 |
除了 SP3 精密轨道,Neville 还可用于平滑的卫星/载体轨迹 、部分短时间窗 ERP/EOP 、ZTD 、TEC 时间序列及小规模工程标定数据。但对于精密钟差跳变、强对流导致的快速 ZTD 变化、磁暴期间的 TEC 扰动、卫星姿态角绕回或轨道机动,局部高阶插值可能不适合;应按数据连续性和目标精度选用线性、样条或分段方法。
7. Python 3.6:复现 RTKLIB interppol()
以下代码只复现 Neville 的计算核心 ,并增加节点校验;不包含 pephpos() 的窗口选取、SP3 单位转换和 ECEF 旋转。
python
# -*- coding: utf-8 -*-
# Python >= 3.6
def neville_rtklib(x, y):
"""x: node_time-target_time (s); y: one orbit coordinate (m)."""
n = len(x)
if n != len(y) or n < 2:
raise ValueError('invalid node count')
if any(x[i + 1] <= x[i] for i in range(n - 1)):
raise ValueError('node times must be strictly increasing')
y = list(y) # C 版本直接修改输入 y;这里复制以保护调用者
for j in range(1, n):
for i in range(n - j):
y[i] = (x[i + j] * y[i] - x[i] * y[i + 1]) / (x[i + j] - x[i])
return y[0]
def lagrange_zero(x, y):
"""Lagrange direct form at zero, used only for checking."""
ans = 0.0
for i in range(len(x)):
term = y[i]
for j in range(len(x)):
if j != i:
term *= -x[j] / (x[i] - x[j])
ans += term
return ans
if __name__ == '__main__':
# 三节点:应等于 f(0)=5
assert abs(neville_rtklib([-2., 1., 3.], [19., 4., 14.]) - 5.) < 1e-12
# 11 节点:以五次多项式作精确参考
target = 200.0
ts = [900.0 * (i - 5) for i in range(11)]
x = [t - target for t in ts]
def f(t):
q = t / 900.0
return 2.0e7 + 200.0 * q - 3.0 * q**2 + 0.04 * q**5
y = [f(t) for t in ts]
actual = neville_rtklib(x, y)
expected = f(target)
assert abs(actual - expected) < 1e-5
assert abs(actual - lagrange_zero(x, y)) < 1e-5
try:
neville_rtklib([0., 0., 1.], [1., 1., 2.])
raise AssertionError('duplicate nodes not detected')
except ValueError:
pass
print('Neville verification passed: {:.9f} m'.format(actual))
这段单元测试验证公式与实现一致,不能证明真实 SP3 产品的厘米级或毫米级插值精度。真实应用需加入第 3 节所述全部预处理。
参考文献
- RTKLIB 官方源码 :
src/preceph.c,主要涉及readsp3b()、interppol()、pephpos()、pephclk()、peph2pos()。源代码为本文实施细节的主要依据。 - IGS(2016) :SP3-d 格式规范。用于 SP3 文件字段、位置/速度/钟差单位及相关状态标志的定义。
- Berrut & Trefethen(2004) :Barycentric Lagrange Interpolation,SIAM Review,46(3),501--517。用于插值多项式的不同求值形式及数值稳定性讨论。
- Driscoll & Braun :Fundamentals of Numerical Computation:Stability of polynomial interpolation。用于高阶等间隔节点的 Runge 现象与节点分布讨论。