作为一名大二导航工程专业的学生,假期留校和老师做科研。众所周知,导航的目标是一下三个:P(positioning)V(velocity)T(timing)。其中,定位和测速有着不可分割的关系。下面就先来看看GPS定位的基本原理和程序实现。
目前,绝大多数的导航原始文件都来源于IERS(国际地球自转服务)所提供的数据。大家可以随便找一个能够下载卫星星历的网站,下载卫星星历供调试程序使用。卫星星历下载下来之后是一个压缩包,解压后的文件夹里面有好多文件。我在写单点定位的程序的时候只用了GPS卫星进行单点定位,那么就只用到其中的o文件(观测值文件)和n文件(GPS广播星历文件)。当然了,如果想要北斗,GALILEO或者是GLONASS卫星进行定位,可以采用其他的文件。目前的文件记录格式都是采用RINEX进行记录的。我采用的数据是RINEX3.01格式的,其实2.11版本的也可以,只是文件格式略有不同而已,大家在读文件的时候注意一下就好。RINEX3.01的数据记录格式说明(英文版)我已经传到资源区了。
单点定位分为标准单点定位和精密单点定位。在本篇博客中,我想介绍标准单点定位的相关内容。
标准单点定位就是利用广播星历给出的卫星轨道和卫星钟差以及伪距观测值来进行的,定位精度不是很好,一般为10米级左右。观测方程如下
其中的参数学测绘的应该都能看懂,要是看不懂,建议参考武汉大学出版社出版的李征航老师和黄劲松老师编写的GPS测量与数据处理这本书,比较详细。
有了观测方程之后就需要线性化,然后根据最小二乘的原理进行最优估计,详细理论过程大家可以参考吴云老师的最优估计及其在GNSS中的应用这本书。由于这篇博客想要重点介绍程序是如何实现,因此在理论部分不多说了。
在读取卫星的广播星历的文件时,首先要跳过文件头,然后读取相应的卫星星历数据。我在读取GPS卫星数据的时候,采用的是单链表的形式。其实用C++库中的vector类就可以,但是我认为这样不自由,因此我采用了数据结构中的单链表的形式。在头文件中定义了用于存储广播星历参数的数据结构。
//定义用于存储GPS星历参数的结点
typedef struct GPSeph{
int PRN;
int year,month,day,hour,minute;
float second;
long double a0,a1,a2;
long double IODE,Crs,deltan,M0;
long double Cuc,e,Cus,sqrtA;
long double TOW,Cic,OMEGA,Cis;
long double i0,Crc,omega,OMEGAdot;
long double IDOT,L2,WN,L2P;
long double accurancy,health,TGD,IODC;
long double sendtime;
struct GPSeph * nextdata;
long double deltat;//求卫星钟差的时候要用到
}GPSeph;
有了用来存储卫星星历的数据结构,下面就是读取广播星历了。
大家通过观察广播星历的数据记录格式可以发现,数据是下面这种记录形式
也就是说中间是用D隔开,以科学计数法的形式记录的。在读取的时候可以像下面这样读取
GPS_nexteph->a0=a*pow(10,b);
fo>>a>>g>>b;
其中g是char类型的变量,a,b都是long double 类型的变量。利用ifstream读取至文件末尾。整个的读文件的程序我在这里就不附上代码了,因为代码太长了,而且大体上都是这样。在跳过文件头的时候,有两种方法:一种是偷懒型的,就是直接getline,跳过指定的行数(哈哈哈,我就是用的这种方法);还有一种就是利用String类中的字符串匹配方法查找是否含有“End Of Header”。最后读取到文件末尾,留下头指针,广播星历文件读取完毕。
我下载的观测值文件是间隔为半分钟的观测值,里面虽然数据看上去很多,但是我们只需要伪距观测值就够了,其它的以后再说。
上面的图片是我从观测值文件头中截取的一小部分,这里面记录了各种卫星有哪些类型的观测值,里面的字母代表的意义大家可以去我上传的RINEX3.01的说明文档的附录中查看。总之,我们利用每个卫星的PRN号后面的第一个观测值就够了。观测值文件的特点是一个时间点下面有好多颗卫星,因此我也为观测值文件的读取定义了一个单链表。
typedef struct OBSERVATIONdata{
int year,month,day,hour,minute;
long double second;
int flag,satellite_number;
vector satellite_system;//存储卫星所属的系统
vector pseudorange;//存储卫星的伪距观测值
vector PRN;
OBSERVATIONdata * nextdata;
}OBSERVATIONdata;
其实用vector类直接就可以完成数据的存储,但是我想让数据的存储格式变得灵活一些,同时便于后期想要向代码中添加新的元素,我采用了比较复杂的自己定义单链表的形式。
o文件的读取不像n文件那样复杂,没有什么想要特殊说明的。
至此,第一部分总算完成了。当时请教老师的时候,老师就说文件的读取会占用代码的很大一部分,果不其然。另外,还要特别的感谢老师,在暑假期间还为我当面为我解答一些问题。下一篇博客打算写读取文件后的计算过程,代码等我完成测速后,一起打包传到资源区。不得不说,看上去过程简单,实际上想要运行出来真正正确的结果对于初学者来说真的不容易。希望大家代码的bug少少,今年新年我的愿望一定是希望我在以后写程序的时候代码少一点啊~
另外,大家如果想要找我交流什么,可以给我留言,不足之处请大家多多指教~