卫星星历是描述卫星轨道运动的一组参数,根据这些参数可以计算出卫星在任意适合的位置及其运动速度。有了卫星的位置,加上接收机的观测值,就可以对接收机的位置进行求解。

本文首先对RINEX广播星历进行格式介绍,然后基于C语言,以程序设计的角度讲解如何读取数据,每行代码皆有详细注释和讲解,希望可以为测绘学子们带来帮助。

观测值文件的数据读取,可以查看观测值文件的数据读取

目录

下载数据

广播星历N文件格式解读

N文件头

N文件数据块

读取N文件程序设计

结构体声明

创建N文件头结构体

创建N文件数据块结构体

N文件数据读取

获取文件行数(getrow)

字符串转换成浮点数(strtonum)

数据读取


下载数据

武汉大学IGS数据中心可以下载观测值文件、广播星历和精密星历。(最近一年的可能没有)

武汉大学IGS数据中心 (gnsswhu.cn)http://www.igs.gnsswhu.cn/index.php/Home/DataProduct/igs.html

右侧日期范围需要键盘输入手动调整,观测值文件的下载需要选择测站,点击根据日期搜索即可。

需要注意的是,初学者最好选择RINEX2.1.1版本的单系统的观测值文件(15年以前大多都是),普通txt形式打开文件会造成格式错误,需要以notepad++或者vs编译器打开。

广播星历N文件格式解读

广播星历包包含了卫星一系列参数和必要的摄动改正数。当前的卫星轨道参数是根据前一段时间求出的轨道参数外推得到的,因此广播星历也叫预报星历。

N文件示例:

N文件头

以下是用notepad++打开的N文件。

左边为信息,右侧为对应的标签(第60个字符) ,记住这个60个字符,在后续读取数据中会用到。

第一行:记录了RINEX的版本号和观测类型

第八行:"END OF HEADER"是头文件的结束标志。

在进行程序设计时,N文件只需要读取上述两行的内容即可。

N文件数据块

从第九行开始,记录了每颗卫星的参数信息,八行为一个数据块,卫星之间的信息没有空行。

第一行第一列代表了卫星的PRN号,之后八行四列的参数意义如下:

卫星钟时间(toc时刻,年月日时分秒)     卫星钟差(a0,s)     卫星钟偏(a1,s/s)   卫星钟偏移(a2,s/s²)

数据龄期(AODE)  轨道半径改正项(Crs,rad)  平均角速度改正项(deltan)    平近点角(M0,rad)

升交点角距改正项(Cuc,rad)  轨道偏心率(e) 升交点角距改正项(Cus,rad)  轨道长半轴平方根(sqrtA)

星历的参考时刻(TOE)  轨道倾角的改正项(Cic,rad) 升交点经度(OMEGA) 轨道倾角改正项(Cis,rad)

轨道倾角(i0) 轨道半径的改正项(Crc,m) 近地点角距(omega,rad) 升交点赤经变化(deltaomega,rad)

轨道倾角的变率(IDOT)         L2频道C/A码标识        GPS时间周(GPS Week)              L2P码标识

卫星精度(SVA,m)                 卫星健康(SVH)          电离层延迟(TGD,s)       星钟的数据质量(IODC)

信息发射时间                       空                                空                                  空

以上是导航星历数据块的全部参数,伪距单点定位读取到第六行第二列IDOT即可。 

读取N文件程序设计

读取N文件主要需要C语言中的文件操作、动态内存管理(malloc)以及指针等。

结构体声明

创建N文件头结构体

N文件头只需要存储第一行的信息,比较简单。为了之后调用方便,在创建结构体时,最好用typedef对结构体进行重命名。

//导航头文件结构体
typedef struct nav_head
{double ver;//rinex 版本号char type[20];//读取的数据类型
}nav_head, *pnav_head;

用typedef重命名后,在创建结构体变量或结构体指针时,用下列等式右边的即可:

  • struct nav_head  =  nav_head
  • sturct nav_head* =  pnav_head

后续有关结构体声明也是如此。

创建N文件数据块结构体

按照上述所讲的参数以及类型,依次创建结构体成员,除了卫星的PRN号、年、月、日、时、分(秒是double类型的)以外,其他参数均为double类型,为了更加直观,建议在创建结构体时按行进行创建(这里不考虑结构体内存对齐)。

//导航数据结构体
typedef struct nav_body
{//数据块第一行内容:int sPRN;//卫星PRN号//历元:TOC中卫星钟的参考时刻int TOC_Y;//年int TOC_M;//月int TOC_D;//日int TOC_H;//时int TOC_Min;//分int TOC_Sec;//秒double sa0;//卫星钟差double sa1;//卫星钟偏double sa2;//卫星钟漂//数据块第二行内容:double IODE;//数据、星历发布时间(数据期龄)double Crs;//轨道半径的正弦调和改正项的振幅(单位:m)double deltan;//卫星平均运动速率与计算值之差(rad/s)double M0;//参考时间的平近点角(rad)//数据块第三行内容:double Cuc;//维度幅角的余弦调和改正项的振幅(rad)double e;//轨道偏心率double Cus;//轨道幅角的正弦调和改正项的振幅(rad)double sqrtA;//长半轴平方根//数据块第四行内容:double TOE;//星历的参考时刻(GPS周内秒)double Cic;//轨道倾角的余弦调和改正项的振幅(rad)double OMEGA;//参考时刻的升交点赤经double Cis;//维度倾角的正弦调和改正项的振幅(rad)//数据块第五行内容:double i0;//参考时间的轨道倾角(rad)double Crc;//轨道平径的余弦调和改正项的振幅(m)double omega;//近地点角距double deltaomega;//升交点赤经变化率(rad)//数据块第六行内容:double IDOT;//近地点角距(rad/s)double L2code;//L2上的码double GPSweek;//GPS周,于TOE一同表示double L2Pflag;//L2,p码数据标记//数据块第七行内容double sACC;//卫星精度double sHEA;//卫星健康状态double TGD;//secdouble IODC;//钟的数据龄期//数据块第八行内容double TTN;//电文发送时间double fit;//拟合区间double spare1;//空double spare2;//空}nav_body, *pnav_body;

创建结构体成员时,最好按照上文所给的官方命名进行声明,增加代码的可读性。

N文件数据读取

文件读取的思路是:

  • 创建文件指针,打开文件
  • 读取文件一共有多少行,计算N文件有多少个卫星的数据块(读取完行数后需要将文件指针返回初始位置)
  • 分别创建N文件头和N文件数据块的指针,并为其开辟内存。
  • 将数据依次按行进行读取
  • 关闭文件

具体代码示例如下:

//数据读取FILE* fp_nav = NULL;//导航星历文件指针FILE* fp_obs = NULL;//观测值文件指针pnav_head nav_h = NULL;pnav_body nav_b = NULL;//N文件读取fp_nav = fopen("abpo0480.15n", "r");//以只读的方式打开N文件int n_n = getrow(fp_nav);//获取导航文件观测行数rewind(fp_nav);//将文件指针返回值起始位置nav_h = (pnav_head)malloc(sizeof(nav_head));//给N文件头开辟空间nav_b = (pnav_body)malloc(sizeof(nav_body) * (n_n / 8));if (nav_h && nav_b){readrinex_n(fp_nav, nav_h, nav_b, n_n);//读取数据}fclose(fp_nav);//关闭N文件

上述代码中,getrowreadrinex_n是我们需要创建的函数,其中readrinex_n中还包含一个将字符串转换成double类型的函数strtonum,这也是需要自行创建的,下面分别对其进行介绍。

获取文件行数(getrow)

用fgets函数逐行对文件进行读取:

fegts函数需要包含头文件<stdio.h>,使用方法如下:

  • char * fgets ( char * str, int num, FILE * stream );
  • 输入一个char*类型的指针(用来存放读取的数据),读取的个数,文件指针,其返回类型为char* 也就是读取后的新位置。

以头文件“END OF HEADER”为标志,下一行开始计数,读取到的行数除以8为N文件中数据块的数量。

//获取文件数据块行数,从END OF HEADER后开始起算
extern int getrow(FILE* fp_nav)
{int row = 0;int flag = 0;char buff[MAXRINEX] = { 0 };//用来存放读取到的字符串char* lable = buff + 60;//gets函数,读取一行,当读取结束后返回NULL指针,格式如下://char * fgets ( char * str, int num, FILE * stream );while (fgets(buff, MAXRINEX, fp_nav)){//strstr:查找字符串中的指定字符或字符串,格式如下://const char * strstr ( const char * str1, const char * str2 );if (flag == 1){row++;continue;}if (strstr(lable, "END OF HEADER")){flag = 1;}}return row;
}

row为最后要返回的行数,falg为判断标志,当读取到文件头结束标签“END OF HEADER”时,flag=1,row开始计数,char* lable +60用来查找标签。

字符串转换成浮点数(strtonum)

在用fgets函数读取数据时,我们是创建了一个buff的字符串接收的,对于正好是字符串类型的信息,例如观测类型tpye,我们可以利用strncpy函数(需包含头文件<string.h>),对字符串进行拷贝,拷贝到我们创建的结构体变量中。

但是很多参数是int、double类型的,那么我们需要先将其从char类型转换成double类型的,再对结构体成员进行赋值。

//将字符串转换为浮点数,i起始位置,n输入多少个字符
static double strtonum(const char* buff, int i, int n)
{double value = 0.0;char str[256] = { 0 };char* p = str;/************************************* 当出现以下三种情况报错,返回0.0* 1.起始位置<0* 2.读取字符串个数<i* 3.str里面存放的字节数<n*************************************/if (i < 0 || (int)strlen(buff) < i || (int)sizeof(str) - 1 < n){return 0.0;}for (buff += i; *buff && --n >= 0; buff++){//三目操作符:D和d为文件中科学计数法部分,将其转换成二进制能读懂的e*p++ = ((*buff == 'D' || *buff == 'd') ? 'e' : *buff);}*p = '\0';//三目操作符,将str中存放的数以格式化读取到value中。return sscanf(str, "%lf", &value) == 1 ? value : 0.0;
}

判断傻瓜错误: 

上述代码中,if部分是用来判断人为传入参数时所引起错误:

  • 起始位置<0
  • 读取字符串个数<i
  • str里面存放的字节数<n

(当然,上面这些傻瓜式错误相信大多数人都不会犯的,而且程序员真想写bug怎么也拦不住。但是rtklib库里是有这么一个判断,还是相信大佬的智慧吧)

关于科学计数法:

仔细观察N文件会发现:参数都是以科学计数法的方式来存储的(D或者d(老版本会有d)),而C语言中科学计数法的标志是‘e’,在读取时还需注意这一点。

例如:10的科学计数法在文本中存储的形式是1.0D-1,需要把它转换成1.0e-1。

明白上面的例子后,再来看这个三目操作符就很简单了:

*p++ = ((*buff == 'D' || *buff == 'd') ? 'e' : *buff);

  • (*buff == 'D' || *buff == 'd')是判断部分,当二者有一个为真是则执行‘e’,都为假时则执行*buff(该穿数字传数字,该传空格穿空格)
  • 将右侧三目操作符的结果赋值给*P
  • 赋值完成后,p++,指针向后移动一位,继续一下参数的转换

 格式化转换

当对科学计数法处理完后,利用sscanf可以将其格式化输入到我们创建的变量value中,再将其作为返回值返回。

sscanf(str, "%lf", &value) == 1 ? value : 0.0;

sscanf函数的用法如下:

  • 包含头文件<stdio.h>
  • int sscanf( const char *buffer, const char *format [, argument ] ... );
  • 需要传入的参数分别为:源头、形式、目的地
  • 返回值是传入参数的个数(或项),失败则返回EOF

根据返回值再对其做一个三目操作符的判断,如果为1则说明传入成功,不是1则返回0。

数据读取

当前期的准备工作完成后,后期的读取工作则非常简单了,主要思想如下:

  • 按卫星数据块为单位,for循环进行读取
  • 每个数据块利用switch语句按行进行读取
  • 读取的时需要对标N文件,找到数据对应的格式

相应代码如下:

//读取N文件
extern void readrinex_n(FILE* fp_nav, pnav_head nav_h, pnav_body nav_b, int n_n)
{char buff[MAXRINEX] = { 0 };char* lable = buff + 60;int i = 0;int j = 0;while (fgets(buff, MAXRINEX, fp_nav)){if (strstr(lable, "RINEX VERSION / TYPE")){nav_h->ver = strtonum(buff, 0, 9);strncpy((nav_h->type), buff + 20, 15);continue;}else if (strstr(lable, "END OF HEADER"))//这时开始对数据进行读取{for (i = 0; i < (n_n / 8); i++)//n_n为不包含头的行数,除以8为数据块数量{for (j = 0; j < 8; j++)//从数据块第一行开始读{fgets(buff, MAXRINEX, fp_nav);switch (j){case 0:nav_b[i].sPRN = (int)strtonum(buff, 0, 2);nav_b[i].TOC_Y = (int)strtonum(buff, 3, 2) + 2000;nav_b[i].TOC_M = (int)strtonum(buff, 6, 2);nav_b[i].TOC_D = (int)strtonum(buff, 9, 2);nav_b[i].TOC_H = (int)strtonum(buff, 12, 2);nav_b[i].TOC_Min = (int)strtonum(buff, 15, 2);nav_b[i].TOC_Sec = strtonum(buff, 18, 2);nav_b[i].sa0 = strtonum(buff, 22, 19);nav_b[i].sa1 = strtonum(buff, 22 + 19, 19);nav_b[i].sa2 = strtonum(buff, 22 + 19 + 19, 19);break;case 1:nav_b[i].IODE = strtonum(buff, 3, 19);nav_b[i].Crs = strtonum(buff, 3 + 19, 19);nav_b[i].deltan = strtonum(buff, 3 + 19 + 19, 19);nav_b[i].M0 = strtonum(buff, 3 + 19 + 19 + 19, 19);break;case 2:nav_b[i].Cuc = strtonum(buff, 3, 19);nav_b[i].e = strtonum(buff, 3 + 19, 19);nav_b[i].Cus = strtonum(buff, 3 + 19 + 19, 19);nav_b[i].sqrtA = strtonum(buff, 3 + 19 + 19 + 19, 19);break;case 3:nav_b[i].TOE = strtonum(buff, 3, 19);nav_b[i].Cic = strtonum(buff, 3 + 19, 19);nav_b[i].OMEGA = strtonum(buff, 3 + 19 + 19, 19);nav_b[i].Cis = strtonum(buff, 3 + 19 + 19 + 19, 19);break;case 4:nav_b[i].i0 = strtonum(buff, 3, 19);nav_b[i].Crc = strtonum(buff, 3 + 19, 19);nav_b[i].omega = strtonum(buff, 3 + 19 + 19, 19);nav_b[i].deltaomega = strtonum(buff, 3 + 19 + 19 + 19, 19);break;case 5:nav_b[i].IDOT = strtonum(buff, 3, 19);nav_b[i].L2code = strtonum(buff, 3 + 19, 19);nav_b[i].GPSweek= strtonum(buff, 3 + 19 + 19, 19);nav_b[i].L2Pflag = strtonum(buff, 3 + 19 + 19 + 19, 19);break;case 6:nav_b[i].sACC = strtonum(buff, 3, 19);nav_b[i].sHEA = strtonum(buff, 3 + 19, 19);nav_b[i].TGD = strtonum(buff, 3 + 19 + 19, 19);nav_b[i].IODC = strtonum(buff, 3 + 19 + 19 + 19, 19);break;case 7:nav_b[i].TTN = strtonum(buff, 3, 19);nav_b[i].fit = strtonum(buff, 3 + 19, 19);nav_b[i].spare1 = strtonum(buff, 3 + 19 + 19, 19);nav_b[i].spare2 = strtonum(buff, 3 + 19 + 19 + 19, 19);break;}}}}}
}

读取的过程比较冗长,主要是需要传入的参数太多了, 这里只读到了第六行的IDOT,用来做伪距单点定位,后面的参数读取格式也是一样的。

需要注意的是:每次case的情况完成后需要加入break,防止程序依次进入下一个case。 

将上述函数在头文件中进行声明,而后在主函数中调用即可,为了防止出错,一定要养成写一步调试一步的习惯,也要养成写注释的好习惯。不同版本的观测值文件的格式会有所不同,但广播星历文件的格式都是大致一样的。

RINEX广播星历文件读取(N文件)相关推荐

  1. QT Creator使用matlab库文件读取.mat文件数据

    QT Creator使用matlab库文件读取.mat文件数据 一.环境配置 二.关于编程介绍 三.关于使用函数的介绍 1:关于假设数据类型介绍 2:关于使用函数介绍 一.环境配置 第一步先点开我的电 ...

  2. CVE-2020-1938 幽灵猫( GhostCat ) Tomcat-Ajp协议 任意文件读取/JSP文件包含漏洞分析

    title: CVE-2020-1938 幽灵猫( GhostCat ) Tomcat-Ajp协议 任意文件读取/JSP文件包含漏洞分析 date: 2021-05-19 01:07:08 categ ...

  3. 读服务器文件,读取服务器文件

    读取服务器文件 内容精选 换一换 客户端IP指的是访问者(用户设备)的IP地址.在Web应用开发中,通常需要获取客户端真实的IP地址.例如,投票系统为了防止刷票,需要通过获取客户端真实IP地址,限制每 ...

  4. numpy如何对txt文件读取_NumPy——文件读取与写入

    一维及二维数据的存取 CSV(Comma-Separated Value,逗号分隔值) CSV是一种常见的文件格式,用来存储批量数据. 将数据写入CSV文件: np.savetxt(file,arra ...

  5. python数据驱动+读取yaml文件+读取excel文件+mySQL

    简介 1.安装 pip install ddt 2.使用 import unittest,ddtdata=[{"user":'admin',"pwd":2222 ...

  6. rfa 文件读取_RFA文件扩展名 - 什么是.rfa以及如何打开? - ReviverSoft

    你在这里因为你有,有一个文件扩展名结尾的​​文件 .rfa. 文件与文件扩展名 .rfa 只能通过特定的应用程序推出.这有可能是 .rfa 文件是数据文件,而不是文件或媒体,这意味着他们并不是在所有观 ...

  7. 保存页面文本到本地文件读取本地文件内容到页面

    问题描述:假设网页有个文本框之类的东西,想通过点击某一按钮后,将用户在文本框中输入的内容直接保存在本地某个文件中.同理,也想通过页面直接读取本地文件中的内容. 问题分析:因为整个处理过程不涉及到后台的 ...

  8. linux properties文件,读取Properties文件六种方法

    开发项目时,经常把一些参数存入Properties文件,以增加程序的灵活性.所以读取properties文件可以说是我们的java基础.我们可以通过以下六种方法读取配置参数(注意:spring对pro ...

  9. golang 文件(文件打开,文件写入,文件读取,文件删除)的基本操作

     代码如下 复制代码 package main import (  "bufio"  //缓存IO  "fmt"  "io/ioutil" ...

  10. 文件读取ini文件另一种读取办法

    时间紧张,先记一笔,后续优化与完善. Windows下的ini文件的读取可以应用系统提供的api来实现 GetPrivateProfileString GetPrivateProfileInt ... ...

最新文章

  1. (二)企业部分之lnmp环境的搭建:mysql源码安装
  2. latex 特殊符号
  3. 跳转控制语句 break || continue || goto
  4. 解决rspec 生成报告时报utf-8错误的方法
  5. [react] React为什么要搞一个Hooks?
  6. 杂牌手柄模拟xboxone手柄_震了,Xbox One 精英手柄2代摸了一次就不舍得放下了
  7. 【TensorFlow】TensorFlow从浅入深系列之十二 -- 教你深入理解卷积神经网络中的池化层
  8. hive metastore mysql_Hive初步使用、安装MySQL 、Hive配置MetaStore、配置Hive日志《二》...
  9. html文本框连接数据库失败,从按钮点击将数据从MySQL数据库加载到HTML文本框
  10. 手动实现一个vue的mvvm,思路解析
  11. 高频板和普通PCB板的区别
  12. 推荐一款Mac远程桌面工具——Parallels Client(免费)
  13. 梅西 (Lionel Messi)
  14. 2021年,shopee虾皮一件代发模式的优势和背后风险是什么?
  15. DBeaver SQL format 第三方插件方案
  16. phoneGap3.0安装步骤(以windows下的android环境为例):
  17. 面试积累(简单的单例模式)
  18. jQuery Ajax 调用WebService实例详解
  19. Depends工具 使用说明和注意
  20. 看完这篇还不会Elasticsearch,我跪搓衣板,90%程序员已收藏

热门文章

  1. Pycharm 搭建 Django 项目 (非常详细)
  2. 操作系统源码及GeekOS学习
  3. LTE网络架构 学习整理
  4. 可以写进简历的软件测试项目实战经验(包含电商、银行、app等)
  5. List数据去重的五种有效方法
  6. IDEA 2018下载及破解
  7. ubnt+ros 接入无线
  8. 迷宫里抓神兽Java游戏_塞尔达传说荒野之息全神兽迷宫进入方法 四大神兽怎么打?-游侠网...
  9. 一篇非常 Nice 的 UmiJS 教程
  10. Nginx跨域配置--端口转发