RTKLIB学习日志2—整体结构
写在前面:研究方向从精密单点定位改成了载波相位差分定位和基线解算,因此不再使用GAMP,但是GAMP是基于rtklib二次开发的,因此代码结构以及函数没有很大的差别。
首先给出我觉得写的比较好的博客:
一、主要类(结构体)的解释
首先解释一下各种主要的类,有助于理解接下来的函数作用以及调用关系
1.1 处理选项类prcopt_t
- 类名:
prcopt_t
- 所在文件:rtklib.h
- 功能说明:记录各种处理选项,按照用户需求进行初始化定义
mode
:定位模式选项,单点定位,差分定位,PPP等soltype
:输出结果的形式,有三种模式(0:forward,1:backward,2:combined),具体有什么区别暂时不清楚nf
:频数选项,(1:L1,2:L1+L2,3:L1+L2+L5)navsys
:处理的导航系统elmin
:截止高度角snrmask
:信噪比标记sateph
:星历选项,精密星历,广播星历等- 还有很多,不一一列出来,源代码中有注释,有的变量我也不是很清楚在哪使用
1.2 卫星文件类filopt_t
- 类名:
filopt_t
- 所在文件:rtklib.h
- 功能说明:储存各种卫星文件
satantp
:卫星天线参数文件rcvantp
:接收机天线参数文件stapos
:站点位置文件geoid
:大地水准面数据iono
:电离层数据dcb
:DCBeop
:地球定向参数blq
:潮汐tempdir
:ftp/http 网址临时目录geexe
:用于运行谷歌地球?搞不懂solstat
:结果目录trace
:正在调试的文件,处理数据的过程中经常会调用trace函数,用来标记程序当前的运行位置
1.3 输出结果选项类solopt_t
- 类名:
solopt_t
- 所在文件:rtklib.h
- 功能说明:定义输出的成果文件的格式
1.4 卫星状态类ssat_t
- 类名:
ssat_t
- 所在文件:rtklib.h
- 功能说明:卫星当前状态参数,该卫星所属的导航系统,坐标,高度角等等
sys
:导航系统vs
:有效卫星单一标志,不知道什么东西azel
:方位角,高度角resp
:伪距残差resc
:载波相位残差vsat
:有效卫星标志,不知道什么东西snr
:信噪比,描述信号强度fix
:模糊度的状态,浮点解、固定解slip
:周跳- 其他的以后再补上,这个类储存的信息还是挺重要的
1.5 解算结果类sol_t
- 类名:
sol_t
- 所在文件:rtklib.h
- 功能说明:储存计算成果,定位坐标就存在这个类里面,还有接收机钟差,解决状态(未解决,浮点解,PPP解,差分解,单点解等)
1.6 RTK进程类rtk_t
- 类名:
rtk_t
- 所在文件:rtklib.h
- 功能说明:包含上述的解算结果类
sol_t
以及卫星状态类ssat_t
,在postpos.c文件中的propos函数中产生,由rtkinit函数初始化,再调入rtkpos函数中参与坐标解算,根据解算进程改变储存的卫星状态,并储存最终的解算坐标
二、解算流程
根据上述对主要的几个类的解释,不难想到,处理选项类prcopt_t
、输出结果选项类solopt_t
、卫星文件类filopt_t
需要用户在main函数中先根据用户的需求进行定义,接下来就可以根据这几个类进行解算了。
rtklib源码中给出的示例的main函数是用户在VS的命令行中写入配置文件,然后用一段程序去读取这个命令行,对上述的三个类进行赋值。有需求的可以自己更改,主要就是定义好这三个类,就可以进行解算,笔者会在下一次日志中专门给出一个示例
2.1 main函数
- 函数名:
main
- 所在文件:main.c(rnx2rtkp.c)
- 功能说明:三个基本类的定义
prcopt_t
、solopt_t
、filopt_t
,并将其传入定位解算进程函数postpos
ret=postpos(ts,te,tint,0.0,&prcopt,&solopt,&filopt,infile,n,outfile,"","");
2.2 postpos函数
- 函数名:
postpos
- 所在文件:postpos.c
- 功能说明:函数openses读取天线参数文件以及大地水准面数据,接着根据处理时间进行判断,具体为什么要做这个判断以及不同判断下对文件做了什么样的操作暂时不清楚,有待后续解决,猜想可能是因为一般卫星文件名都会带有一些信息,比如历元,需要从这些文件名中获取一些信息,然后再进行操作。最终都是要调用函数execses_b
/* execute processing session */
stat=execses_b(ts,te,ti,popt,sopt,fopt,1,ifile,index,n,ofile,rov,base);
ts
:进程开始时间te
:进程结束时间ti
:处理间隔tu
:处理单位时间popt
:不解释sopt
:不解释fopt
:不解释infile
:输入文件n
:输入文件数量outfile
:输出文件rov
:流动站IDbase
:基准站ID
2.3 execses_b函数
- 函数名:
execses_b
- 所在文件:postpos.c
- 功能说明:通过函数readpreceph读取精密星历,然后为每个基准站执行操作,这个函数笔者暂时也不是了解的非常清楚,但是大致可以推断出的是,用户传入的基准站可能不止一个,所以在执行处理流动站的函数execses_r之前进行了一些字符串操作,应该是在分割传入的基准站的字符,然后循环处理每个基准站。最后都是要进入函数execses_r,处理每个流动站
stat=execses_r(ts,te,ti,popt,sopt,fopt,flag,infile,index,n,outfile,rov);
2.4 execses_r函数
- 函数名:
execses_r
- 所在文件:postpos.c
- 功能说明:同样的对于流动站的操作首先也需要分割字符串,识别每个流动站,这部分代码与函数execses_b基本一致,最后进入函数execses,处理每一个流动站
/* execute processing session */
stat=execses(ts,te,ti,popt,sopt,fopt,flag,ifile,index,n,ofile);
2.5 execses函数
- 函数名:
execses
- 所在文件:postpos.c
- 功能说明:读取电离层参数
readtec
、erp文件readerp
(地球章动、极移参数)、读取观测值文件以及导航电文readobsnav
、差分码文件readdcb
、设置天线参数setpcv
(天线参数在函数postpos中已经读取过)、地球潮汐改正文件readotl
;之后开始根据开始定义的prcopt_t
变量中储存的定位模式获取天线相位中心位置antpos
;接着写输出的结果文件的头文件outhead
;然后根据prcopt_t
变量中储存的soltype
变量即上文提到的输出结果的形式,对一些索引变量进行赋值操作iobsu=iobsr=isbs=revs=aborts=0;
,revs=1; iobsu=iobsr=obss.n-1; isbs=sbss.n-1;
等,这里不同的soltype会导致输出有什么不同暂时不清楚,待后续查明,最后进入函数procpos进行下一步处理
//选择不同的soltype,索引参数会变化
iobsu=iobsr=isbs=revs=aborts=0;
/* forward */
procpos(fp,&popt_,sopt,0);
/* backward */
revs=1; iobsu=iobsr=obss.n-1; isbs=sbss.n-1;
procpos(fp,&popt_,sopt,0);
/* combined */
isolf=isolb=0;
procpos(NULL,&popt_,sopt,1);
revs=1; iobsu=iobsr=obss.n-1; isbs=sbss.n-1;
procpos(NULL,&popt_,sopt,1);
2.6 procpos函数
- 函数名:
procpos
- 所在文件:postpos.c
- 功能说明:这个函数开始产生变量
rtk_t
,obsd_t
,sol_t
,开始正式进入rtk解算流程,首先通过函数rtkinit
初始化变量rtk_t
;然后进入while循环,调用函数inputobs
,这个函数的作用应该是将之前读入的观测值文件、导航电文等的信息传入rtk_t
、obsd_t
这两个变量之中,obsd_t
记录了卫星号接收机号、伪距、载波、信噪比等等观测值信息,这些都是解算坐标最重要的信息;固定好导航系统后satsys
,下一步进行了载波相位偏差纠正corr_phase_bias_ssr
,使用的SSR方法,具体这个方法不清楚,但是这里存在一个疑惑,笔者整体看了代码,发现解算差分定位之前并没有进行周跳修复检测,对于载波观测值只有这里的偏差纠正,而PPP中是有的,可能是没有找到,有待后续解决;然后进入函数rtkpos
开始解算坐标;本函数后面的代码用于输出结果文件,同样对于不同的soltype
有不同的输出方式
/* carrier-phase bias correction */
//载波相位偏差校正
if (!strstr(popt->pppopt,"-ENA_FCB")) {
corr_phase_bias_ssr(obs,n,&navs);
}
//定位
if (!rtkpos(&rtk,obs,n,&navs)) continue;
//输出结果文件
if (mode==0) { /* forward/backward */
if (!solstatic) {
outsol(fp,&rtk.sol,rtk.rb,sopt);
}
else if (time.time==0||pri[rtk.sol.stat]<=pri[sol.stat]) {
sol=rtk.sol;
for (i=0;i<3;i++) rb[i]=rtk.rb[i];
if (time.time==0||timediff(rtk.sol.time,time)<0.0) {
time=rtk.sol.time;
}
}
}
else if (!revs) { /* combined-forward */
if (isolf>=nepoch) return;
solf[isolf]=rtk.sol;
for (i=0;i<3;i++) rbf[i+isolf*3]=rtk.rb[i];
isolf++;
}
else { /* combined-backward */
if (isolb>=nepoch) return;
solb[isolb]=rtk.sol;
for (i=0;i<3;i++) rbb[i+isolb*3]=rtk.rb[i];
isolb++;
}
2.7 rtkpos函数
- 函数名:
rtkpos
- 所在文件:postpos.c
- 功能说明:结合代码说明,见如下代码
//1、这里定义了一个prcopt_t用来储存传入的rtk_t中的prcopt_t
prcopt_t *opt=&rtk->opt;
//2、设置基准站位置,满足如下的if条件后(应该是单点定位不需要基准站,动基线不太懂),坐标不变
//,每个方向的变化速率变成0
/* set base staion position */
if (opt->refpos<=POSOPT_RINEX&&opt->mode!=PMODE_SINGLE&&
opt->mode!=PMODE_MOVEB) {
for (i=0;i<6;i++) rtk->rb[i]=i<3?opt->rb[i]:0.0;
}
//3、计算流动站/基准站观测值数量,可用于后面判断是否满足差分条件
/* count rover/base station observations */
for (nu=0;nu <n&&obs[nu ].rcv==1;nu++) ;
for (nr=0;nu+nr<n&&obs[nu+nr].rcv==2;nr++) ;
//4、单点定位解算流动站坐标pntpos,单点定位解算的坐标可以作为初始值参与其他精密定位方法
/* rover position by single point positioning */
if (!pntpos(obs,nu,nav,&rtk->opt,&rtk->sol,NULL,rtk->ssat,msg)) {
errmsg(rtk,"point pos error (%s)\n",msg);
if (!rtk->opt.dynamics) {
outsolstat(rtk);
return 0;
}
}
//5、如果定位模式是单点定位,那么就可以直接输出结果结束了
/* single point positioning */
if (opt->mode==PMODE_SINGLE) {
outsolstat(rtk);
return 1;
}
//6、精密单点定位
/* precise point positioning */
if (opt->mode>=PMODE_PPP_KINEMA) {
pppos(rtk,obs,nu,nav);
outsolstat(rtk);
return 1;
}
//7、接下来是需要基准站的差分定位和基线解算,因此首先需要检查基站数据数量和差分时间
/* check number of data of base station and age of differential */
if (nr==0) {
errmsg(rtk,"no base station observation data for rtk\n");
outsolstat(rtk);
return 1;
}
//8、动基线与其他差分定位方式,动基线的基站坐标需要随时间同步变化,所以需要计算出变化速率
//,解释了为什么第二步除了单点定位,动基线也不参与基站解算,动基线在这里单独解算
if (opt->mode==PMODE_MOVEB) { /* moving baseline */
/* estimate position/velocity of base station */
if (!pntpos(obs+nu,nr,nav,&rtk->opt,&solb,NULL,NULL,msg)) {
errmsg(rtk,"base station position error (%s)\n",msg);
return 0;
}
rtk->sol.age=(float)timediff(rtk->sol.time,solb.time);
if (fabs(rtk->sol.age)>TTOL_MOVEB) {
errmsg(rtk,"time sync error for moving-base (age=%.1f)\n",rtk->sol.age);
return 0;
}
for (i=0;i<6;i++) rtk->rb[i]=solb.rr[i];
/* time-synchronized position of base station */
for (i=0;i<3;i++) rtk->rb[i]+=rtk->rb[i+3]*rtk->sol.age;
}
else {
rtk->sol.age=(float)timediff(obs[0].time,obs[nu].time);
if (fabs(rtk->sol.age)>opt->maxtdiff) {
errmsg(rtk,"age of differential error (age=%.1f)\n",rtk->sol.age);
outsolstat(rtk);
return 1;
}
}
//9、上面的步骤只算了相对定位的差分时间和动基线坐标,并没有进行坐标解算
//,这里进行相位定位,并输出最终结果,到这里定位步骤全部完成
/* relative potitioning */
relpos(rtk,obs,nu,nr,nav);
outsolstat(rtk);