从源码解读RNX2GTEX:GNSS电离层TEC提取与处理实践
2026/9/1 20:10:31 网站建设 项目流程

简介:这是一套面向电离层研究者、GNSS数据处理工程师及空间天气分析人员的Fortran科学计算源码,专注于将标准RINEX格式的GNSS观测数据高精度反演为电离层总电子含量(TEC),并输出为专用GTEX格式,有效支撑定位误差校正、电离层建模与太阳活动影响评估等关键任务。资源共42个文件,含15个Fortran源文件(.f)实现核心算法(如TEC反演、轨道读取、周跳修正)、15个编译目标文件(.o)、3个Shell脚本(.sh)用于自动化流程控制,以及Makefile、头文件、参数配置列表和详细README文档,结构完整、模块清晰,便于二次开发与教学实践。压缩包仅290KB,轻量但功能完备。目前已有155人学习下载,用户可直接获取可编译运行的完整工程,涵盖从RINEX数据解析、卫星几何定位、伪距/相位组合TEC计算到GTEX格式写入的全链路实现,并附带Julian日历转换、IGS周数计算等实用工具模块。 从源码读懂GNSS电离层处理,这事放到现在依然有吸引力。RNX2GTEX这个名字,常做GNSS数据处理的人一看就明白,它做的是把RINEX观测文件转成TEX格式输出。这里的TEX不是LaTeX那套排版工具,而是电离层TEC交换文件(Total Electron Content Exchange)的简写。整个程序的核心任务,就是从双频GNSS观测值中提取总电子含量(TEC),再把逐卫星、逐历元的TEC结果写成标准格式,供后续电离层建模、单频用户电离层改正、空间天气研究使用。

我用这个程序处理过不少测站数据,也把源码从头到尾翻过几遍,坦白说这不是一个代码风格很“现代”的项目,但它把GNSS电离层测量的整条链路用最朴素的方式跑通了。这篇内容我就从一个实际使用者的角度,把RNX2GTEX涉及的物理原理、源码结构、编译运行细节、输出格式和常见坑都拆开讲一遍,适合刚接触GNSS电离层数据处理、或者想通过老Fortran代码理解观测方程的同学参考。

1. 先把名词理清楚:RINEX、TEX、TEC各自扮演什么角色

很多初学者看到RNX2GTEX这个软件名,第一反应是去查怎么编译,结果被RINEX文件名、TEX格式、TEC单位这些概念绕晕。我建议先花半小时把下面几个名词的关系理清,后面看代码会轻松很多。

1.1 TEC是什么,GNSS为什么能测到电离层

TEC的全称是Total Electron Content,翻译过来就是总电子含量,指沿信号传播路径上单位截面积柱体内的自由电子总数,单位是电子数每平方米。平时我们更常用TECU做单位,1 TECU等于10的16次方个电子每平方米。

电离层里的自由电子会对GNSS信号产生折射延迟,这个延迟的大小和信号频率的平方成反比。对频率为f的信号,伪距上的电离层延迟近似为40.3乘STEC除以f的平方,这里的STEC就是斜路径上的TEC。因为两个载波频率不一样,同一颗卫星发出的L1和L2信号穿过电离层时延迟量不同,通过对比两个频率的观测值,就能把电离层影响单独分离出来。

我在实际给学生讲的时候喜欢用一个类比:电离层就像一块有色玻璃,不同颜色的光穿过时速度不一样。GNSS接收机同时收到两个频率的信号,相当于拿两束不同颜色的光同时穿过这块玻璃,通过比较它们的到达时间差,就能反推玻璃的“厚度”。这里的“厚度”就是TEC。

1.2 从RINEX到TEX,RNX2GTEX在流水线中的位置

RINEX是GNSS观测数据的标准交换格式,接收机厂商输出的原始数据经过转换后,会以RINEX格式保存伪距、载波相位、多普勒等观测值。RINEX文件里包含的信息非常丰富,但直接拿RINEX文件做电离层研究并不方便,因为观测值里混着钟差、轨道误差、对流层延迟等一大堆无关量,而且不同接收机输出的文件格式细节也不一样。

TEX格式则是专门面向电离层TEC数据设计的交换格式,它把处理好的TEC结果按站点、按历元、按卫星组织起来,省去了用户重复做预处理的工作。RNX2GTEX就是连接RINEX和TEX的桥梁:输入一个或多个RINEX观测文件,经过质量检查和组合计算,输出TEX格式的TEC时间序列。

整个GNSS电离层处理链路大致是:RINEX观测文件加精密星历和钟差,经过预处理、周跳探测、无几何组合、相位平滑等步骤,得到STEC,再做硬件延迟校正和映射函数转换,输出网格化的VTEC或直接输出STEC序列。RNX2GTEX覆盖的是从RINEX到STEC/TEC这一核心段落。

1.3 单位转换和数量级:怎么判断算出来的TEC是否合理

用这个程序之前,最好对TEC的数量级心里有数。中纬度地区平静电离层情况下,天顶方向VTEC通常在10到50 TECU之间,低纬赤道异常区可以到80甚至100 TECU以上,高纬和夜间会低很多,夜间经常只有几个TECU。如果算出来的结果在一个测站、一整天内变化几百上千个TECU,那基本可以判断数据或处理方法出了问题。

从几何关系上也要有概念。斜路径STEC一般是VTEC的好几倍,仰角越低路径越长,STEC越大。仰角30度左右时,如果VTEC是30 TECU,STEC大概在50到60 TECU。程序输出的如果明显偏离这个范围,就要回头检查组合系数、单位换算是哪里出了问题。

还有一个非常实用的换算关系必须掌握:伪距组合P1减P2的1米差异,大约对应9.52 TECU,具体推导依据是40.3乘以(1/f1的平方减1/f2的平方)的倒数,f1是1575.42兆赫兹,f2是1227.60兆赫兹。这个系数在验证程序输出时特别有用,算完TEC以后可以反算一下P4残差,看看是否在合理范围内。

2. 源码核心逻辑:STEC是怎么从观测值里算出来的

RNX2GTEX本质上是把教科书里的双频电离层探测公式翻译成了Fortran代码。所以读这份源码,数学上并不难,难的是理解代码里各种变量、常量和文件操作背后对应的物理过程。我个人推荐在读源码之前,先把下面几个关键逻辑在纸上推导一遍。

2.1 无几何组合的推导和实现要点

电离层探测的第一个关键组合叫无几何组合(geometry-free combination),也叫电离层残差组合。对于伪距,组合形式是P4等于P1减P2;对于载波相位,组合形式是L4等于L1减L2。这个组合能消掉卫星钟差、接收机钟差、对流层延迟、几何距离等与频率无关的项,剩下的主要就是电离层延迟差异和硬件延迟偏差。

在源码里,这个组合通常不是直接算两个观测值相减就完事,还要考虑P1和P2的观测值类型。有的接收机输出C1和P2,有的输出P1和P2,还有的只有C1和C2,不同组合对应的硬件延迟偏差不一样,代码里一般会有对应的分支判断。这也是为什么源码里出现一长串if条件判断的原因。

从组合值换算到STEC的时候,符号问题特别容易搞晕。如果程序里写的是P4等于P1减P2,那么P4大约等于负的40.3乘STEC乘以(1除以f1平方减1除以f2平方),换算成TEC需要乘一个负系数;如果程序里用的是P2减P1,符号就反过来。我看到不少人在看源码时卡在这里,最后发现是符号理解反了。

2.2 载波相位平滑伪距:为什么需要,代码里怎么体现

伪距观测的噪声比较大,尤其C/A码和P码,噪声水平通常是分米级甚至米级,直接用伪距组合算STEC,结果会非常毛糙。载波相位观测的噪声小得多,一个量级的差距,但相位观测值存在整周模糊度,差分后依然有一个未知常数偏差,无法直接给出绝对TEC。解决办法是用相位组合的变化量来平滑伪距组合的绝对值,这就是经典的载波相位平滑伪距算法。

具体实现思路是:先对L4做周跳检测,把连续的、没有周跳的弧段找出来;在每个弧段内,计算(L4减去P4)的时间平均,这个平均值包含了模糊度和硬件延迟的综合常数;然后用P4观测值加上这个平均值,得到平滑后的STEC。程序里通常会有一个循环逐历元处理,遇到周跳就重新初始化平滑窗口。

我在源码里看到平滑窗口长度设置时,不同版本差别比较大。窗口太短,平滑效果差;窗口太长,又容易把电离层的真实变化也抹平了。一般长弧段取20到30分钟比较稳妥,但如果电离层活跃、TEC变化剧烈,窗口要适当缩短。这个参数值得根据你的数据和研究目的多试几组。

2.3 DCB偏差处理:源码的边界在哪里

这里要特别注意,伪距组合P4里面除了电离层延迟,还包含卫星差分码偏差和接收机差分码偏差,统称DCB。即使做了相位平滑,DCB依然保留在结果里。如果不修正,STEC会出现一个系统性偏置,中纬度地区这个偏置折算下来少则几个TECU,多则十几二十个TECU,对电离层建模影响很大。

RNX2GTEX这个层级的程序,通常是不做DCB估计的。它的定位是把原始观测换算成不含几何项的电离层组合量,并输出成TEX,DCB修正一般留给后续处理链路,比如用IGS或者CODE发布的DCB产品做后处理剔除。源码里可能预留了DCB文件的读取接口,也可能完全没有,要看具体版本。你在把TEX数据用于定量研究之前,一定要确认DCB处理在哪一步完成,否则结果会整体偏移。

这条边界一定要搞清楚。如果你拿到一个RNX2GTEX输出的TEX文件,第一件事不是画图看趋势,而是要问:这份数据是原始STEC还是已经做了DCB修正,做了卫星端修正还是接收机端也修正了。我见过有人直接拿未修正DCB的数据做VTEC地图,出来的图上整个测区都有一个固定偏置,事后排查了半天才发现问题出在数据源头。

3. 把老Fortran代码跑起来:编译与运行的全过程

RNX2GTEX是Fortran写的,一般是Fortran 77风格的固定格式代码。这种老代码在今天的Linux环境上编译,通常会遇到一些小问题,但解决起来也不难,关键是要知道几个典型的坑。

3.1 源码文件组成与gfortran编译命令

从源码库拿到的RNX2GTEX,可能是一个单独的.f文件,也可能是主程序加若干子程序文件的结构。以常见版本为例,主程序文件名一般是RNX2GTEX.f,里面包含若干个subroutine,比如读取RINEX文件头的子程序、读取观测记录的子程序、计算TEC的子程序、写出TEX文件的子程序,每个子程序对应一个独立的处理阶段。

用gfortran编译时,最简单的命令是:

gfortran -O2 -ffixed-line-length-132 -o rnx2gtex RNX2GTEX.f

如果源码拆成了多个文件,就全部列在命令后面:

gfortran -O2 -ffixed-line-length-132 -o rnx2gtex RNX2GTEX.f SUBRTN.f CONST.f

-ffixed-line-length-132这个选项容易忽略,但经常是编译报错的关键。很多老Fortran代码的注释和续行标志在固定格式下默认只认72列,超过的部分会被忽略,如果源码里某一行比较长,不调整行长限制的话,编译时会报一堆莫名其妙的语法错误。

如果是64位Linux系统,一般不用加额外选项就能编译,但个别版本会用到非标准的库函数,这时要根据报错信息去源码里查具体是哪个函数,再决定是替换实现还是增加兼容代码。我不建议一开始就改动源码逻辑,先试着原样编译,遇到问题再对症下药。

3.2 RINEX文件命名约定,这个坑最容易栽

RNX2GTEX对输入文件的命名有约定,基本上遵循标准RINEX文件命名规则:前四个字符是站名缩写,第五到第七个字符是年积日,第八个字符是日内文件序号,第九第十个字符是年份,点后面两位是文件类型标识。比如abmf0010.15o,表示abmf站、年积日第001天、序号0、2015年、观测文件。

这个命名约定是程序正确运行的前提,因为很多老程序不会做太智能的文件名解析,而是直接按位置截取字符串来提取站名、年份和年积日。如果你把文件重命名成test_obs.15o之类的名字,程序要么报错,要么给出完全错误的输出。

我建议在运行前把所有输入文件统一改成标准命名,并放在同一个目录下,文件名全部用小写或者全部用大写,不要混用。有的程序在文件系统大小写敏感的环境里,对文件名的判断很严格,稍微不一致就会找不到文件。

3.3 运行、交互输入与TEX输出验证

编译成功后,运行方式通常有两种,取决于你拿到的版本。老版本一般是交互式提示输入,运行后程序会问你要RINEX文件名;有的版本支持命令行参数直接指定文件名,比如:

./rnx2gtex abmf0010.15o

运行结束后,目录下会多出一个TEX文件。这时不要急着拿去用,先打开文件看一下头部信息对不对,站名是否与输入一致,历元数是否合理,有没有出现大量零值或负值。我一般习惯用head命令看前几十行,再用awk统计一下STEC列的数值范围,如果最小值是负几十、最大值是正几百,说明数据预处理环节大概率有问题。

如果输出文件是空的或者程序中途崩溃,最优先检查的永远是RINEX文件本身,可以先确认它能否被其他常用软件正常读取,比如用teqc或者gfzrnx做一下质量检查,排除RINEX文件损坏的可能,再回头查程序参数设置。

4. 输出文件长什么样:TEX格式解析与Python后处理

TEX格式是RNX2GTEX的输出,也是后续处理的起点。虽然不同版本输出格式存在差异,但结构上大体一致,理解之后用脚本解析并不难。

4.1 TEX文件头部和正文结构

一个典型的TEX输出文件,头部通常包含生成程序的标识、站点名称、站点坐标、数据的时间范围、观测文件的相关信息等内容。正文部分按历元组织,每个历元下列出可见卫星的TEC值,可能带有卫星编号、仰角等信息。

对于源码里的写语句,建议逐个对照看。有的版本输出STEC,有的版本输出VTEC,有的版本会同时输出仰角供你后续自己换算。源码中每个格式描述符对应的内容,最好对照RINEX文件和程序内部变量Name来确认,不要只看文件后缀就默认是VTEC。

4.2 用Python快速解析并画一条VTEC时间序列

TEX文件虽然可以直接用文本编辑器打开看,但要做时间序列分析或画图,还是得写脚本。下面给一个很基础的Python解析示例,具体列位置需要根据你那个版本的写语句调整:

import matplotlib.pyplot as plt records = [] with open('abmf0010.tex', 'r') as f: for line in f: if line.startswith('RNX2GTEX OUTPUT'): parts = line.split() station = parts[2] elif line.strip() and line[0].isdigit(): doy = int(line[0:3]) sec = float(line[4:14]) prn = int(line[15:17]) stec = float(line[18:27]) records.append((doy, sec, prn, stec)) for prn in sorted(set(r[2] for r in records)): data = [r for r in records if r[2] == prn] times = [r[1] / 3600.0 for r in data] values = [r[3] for r in data] plt.plot(times, values, label=f'PRN {prn}') plt.xlabel('Hour of Day') plt.ylabel('STEC (TECU)') plt.legend() plt.show()

这段代码把每个卫星的STEC按小时画成一条线,可以快速看出各卫星之间的系统偏差是否正常。如果某颗卫星整体比别的卫星高出一截,大概率是卫星DCB没有修正;如果出现锯齿状跳变,说明周跳处理有问题。

4.3 数据后处理的几点建议

我处理TEX数据时习惯做三步检查。第一步看单颗卫星连续弧段是否平滑,有没有突跳;第二步把所有卫星的STEC映射到天顶方向做VTEC,按站点看日变化曲线是否合理,正常情况中午高、夜间低;第三步用同一天相邻测站的数据做交叉验证,如果两个测站距离很近,VTEC应该高度一致。

映射STEC到VTEC时,经典做法是除以仰角的正弦值,近似映射函数。但要注意低仰角时映射函数误差很大,一般会把仰角低于10度或15度的数据去掉再换算。RNX2GTEX如果本身不输出仰角,你可能还需要从RINEX文件或者星历计算里补上这个信息,这也是不少人在后续处理时额外写模块的原因。

5. 常见问题与排查技巧实录

这个项目我用下来,真正运行顺利的情况其实不多,大多数时间都在跟各种细节较劲。下面这些问题,几乎每个使用RNX2GTEX的人都会碰到,我按阶段整理成一张排查表,方便你遇到问题时直接对照。

5.1 编译阶段的问题

编译是第一个拦路虎,也是最容易劝退新手的环节。我见过最多的报错有两类:一类是固定格式行长问题,用-ffixed-line-length-132基本能解决;另一类是源码里用了非标准的扩展语法,比如Tab开头的代码行、超过72列的续行、或者比较老的Fortran特性,gfortran默认模式下不接受。

遇到这类问题,先把编译器的警告信息完整看一遍,重点找第一个报错位置,因为后续报错往往是连锁反应。如果某个语法确实是老扩展,最简单的处理是把那几行改写成标准Fortran 77语法。我不建议为了编译通过而关掉所有警告,容易埋下运行时隐患。

5.2 运行阶段的数据问题

程序编译通过只是开始,运行结果异常的排查才更耗时间。以下几种情况我都在实际数据里遇到过:

第一种,输出全部为0或者全部为负值。最常见的原因是观测文件里没有程序期望的观测值类型,比如程序默认读P1和P2,但现代接收机只输出C1和C2,或者双频数据里有大量L2观测值缺失。这时需要检查RINEX文件头里的观测类型列表,确认实际包含哪些信号。

第二种,输出的TEC在长时间段内整体偏置。这是DCB没修正的典型特征,尤其是接收机端DCB,在同一台接收机的数据里是常数,很容易被误认为是真实电离层变化。如果相邻两天的数据在同一时刻都有固定差异,大概率就是DCB问题。

第三种,单颗卫星数据在某个历元突然跳变,之后又恢复正常,这通常是观测数据本身存在跳变或者周跳漏检。可以检查平滑窗口的周跳检测阈值是否需要调整,阈值设得太松会把小周跳放过去,设得太紧又会频繁重置平滑窗口,导致结果噪声增大。

5.3 结果异常排查速查表

现象可能原因处理建议
输出全部为零或负数观测值类型不匹配、无P1/P2组合查看RINEX头文件观测类型,确认输入数据
STEC整体偏置,不同卫星各自的基线不同未做卫星端/接收机端DCB修正接入DCB产品做后处理修正
个别卫星弧段突跳周跳漏检、平滑窗口参数不合理调低周跳检测阈值,缩短平滑窗口
处理后VTEC夜间出现负值平滑噪声、低仰角数据污染提高截止仰角,检查高度角输入
程序运行时提示文件无法打开文件名不符合RINEX命名规则按ssssdddf.yyt格式重命名输入文件
编译时出现unclassifiable statement固定格式行长或非标准扩展语法加编译选项,修改老式语法
输出文件只有头部没有正文RINEX观测记录读取失败检查RINEX文件完整性,先用teqc等工具质检

表中列出的问题,覆盖了我看到的大多数求助。实际上每次排查这些问题,都能加深对程序和数据格式的理解。你把这个表存下来,遇到问题先按图索骥,能省不少时间。

6. 源码里值得多读几遍的几个地方

RNX2GTEX代码量不大,但里面有几个片段非常值得反复读。第一个是RINEX头部解析部分,这里能看到程序如何处理不同版本的RINEX格式,老代码往往用许多分支来兼容不同年代的文件格式,读这部分能学到不少处理历史数据的经验。

第二个是周跳检测和平滑窗口的实现。这个模块直接决定输出STEC的质量,也是后来人改动最多的部分。有的版本用L4变化量超过固定阈值来判断周跳,有的版本用相邻历元差分的统计量动态设定阈值,两种方式各有优劣。你完全可以在理解原逻辑后,把平滑部分替换成更新的算法,比如基于卡尔曼滤波的方式。

第三个是TEX文件输出的写语句。这部分能直观看出程序作者对输出格式的考虑,哪些信息被保留,哪些信息被丢弃,背后都有取舍。如果你要做更细致的分析,可能需要在这里增加输出内容,比如加上方位角、高度角、信噪比等。

我在读这份源码过程中最大的体会是,老程序虽然界面不友好、代码风格不现代,但它的逻辑非常直接,几乎没有多余的设计。这种直接性反而让学习变得容易,你能清楚地看到每一步算子对应教科书里的哪个公式。对于想理解GNSS电离层处理全流程、或者想写自己的TEC处理工具的人来说,RNX2GTEX是一份非常合适的入门源码。

如果后续想扩展它的能力,我建议优先考虑这几个方向:一是增加对Galileo和北斗观测值的支持,老程序最初主要是针对GPS设计的;二是把DCB修正模块直接集成进去,省去后处理环节;三是增加输出精度因子和高度角信息,方便下游做质量加权。改动过程中注意保持原有输出格式的兼容性,因为很多下游工具默认了TEX的旧版结构。

最后分享一个实际工作中的小习惯:每次处理一个新的测站或新一天的数据时,我会保留程序输出的原始TEX文件,用脚本自动生成一份包含最大值、最小值、均值、有效数据率的统计报告。这样处理大量数据时,能快速挑出异常那天。GNSS电离层数据处理,麻烦往往不在程序本身,而在数据质量的把控上,这个习惯帮我省了不少排查时间。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询