基于rtklib的载波相位平滑伪距

前言

学习了载波相位平滑伪距相关算法,又对RTKLIB做了一段时间的学习,突发奇想的想将两者结合起来,于是本项目基于RTKLIB对载波相位平滑伪距做探索性的实践,仅供学习交流使用。本项目还有一些不足,详细见注。

项目地址:README.md · yitian00/基于rtklib的载波相位平滑伪距的spp - Gitee.com

单频测试

数据来自于TEXBAT原始中频数据

未平滑

平滑后

使用方法

平台环境

windows vs2017-2022实测可行,具体编译和rtklib一样,可以在浏览器查找(项目属性我已配置好,理论上不用再配置)

main函数设置

1、在src\main文件的main函数里设置观测值文件、星历和结果输出文件:有注释,自行查找

2、pha_s_pse = 1;//o:不进行平滑,1:进行平滑

3、prcopt.nf = 1 1:单频平滑 2:双频平滑

滑动窗口设置

平滑结果和平滑窗口有直接关系,需要在pntpos.c文件里根据经验效果修改窗口

1、单频窗口

smooth_L1函数修改窗口:N。建议最小20,最大60

c 复制代码
//L1平滑伪距
static int smooth_L1(rtk_t *rtk, obsd_t *obs, const nav_t *nav)
{
	int n = rtk->ssat[obs->sat - 1].n_continue;
	const double *lam = nav->lam[obs->sat - 1];
	double Lifn_last = rtk->ssat[obs->sat - 1].Lif; //上一个历元相位值
	if (lam[0] == 0.0  || obs->P[0] == 0.0  || Lifn_last == 0
		|| obs->L[0] == 0.0  || rtk->ssat[obs->sat - 1].rif_ == 0 || n == 0)
	{
		initial_obs(rtk, obs);
		return 0;
	}
	double L_n, R;

	L_n = obs->L[0] * lam[0];
	rtk->ssat[obs->sat - 1].n_continue += 1;
	int N;//滤波器长度
	if (rtk->ssat[obs->sat - 1].n_continue < 20) { N = 20; }
	else if (rtk->ssat[obs->sat - 1].n_continue > 40) { N = 40; }
	else N = rtk->ssat[obs->sat - 1].n_continue;

	R = obs->P[0] / N + (N - 1)*(rtk->ssat[obs->sat - 1].rif_ + L_n - Lifn_last) / N;
	
	rtk->ssat[obs->sat - 1].Lif = L_n;
	rtk->ssat[obs->sat - 1].rif_ = R;
}

2、双频窗口

smooth 函数修改n

c 复制代码
//正常历元正常卫星平滑
static int smooth(rtk_t *rtk, obsd_t *obs, const nav_t *nav) {
	double  h1, h2, R1, R2, f1, f2,Rifn,Lifn,Lifn_last;

	int n= rtk->ssat[obs->sat - 1].n_continue;
	const double *lam = nav->lam[obs->sat - 1];

	Lifn_last = rtk->ssat[obs->sat - 1].Lif; //上一个历元相位组合值
	if (lam[0] == 0.0 || lam[1] == 0.0 || obs->P[0] == 0.0 || obs->P[1] == 0.0|| Lifn_last==0
		|| obs->L[0] == 0.0 || obs->L[1] == 0.0 || rtk->ssat[obs->sat - 1].rif_ == 0 || n == 0) 
	{
		initial_obs(rtk, obs);
		return 0;
	}
	double L0 = obs->L[0]; L0 *= lam[0];
	double L1 = obs->L[1]; L1 *= lam[1];
	f1 = CLIGHT / lam[0], f2 = CLIGHT / lam[1];
	h1 = (f1*f1) / (f1*f1 - f2 * f2); h2 = -(f2*f2) / (f1*f1 - f2 * f2);

	Rifn = h1 * obs->P[0] + h2 * obs->P[1];//当前历元伪距组合值
	Lifn = h1 * L0 + h2 * L1;//当前历元载波组合值
	rtk->ssat[obs->sat - 1].Lif = Lifn;//更新载波组合值
	rtk->ssat[obs->sat - 1].n_continue += 1;//正常平滑,平滑历元数加1
	n = rtk->ssat[obs->sat - 1].n_continue;
	
	if(n<50) n=50;//当历元不足50时,滤波窗口设定为50
	
	double Rif_ = ((n - 1) * (rtk->ssat[obs->sat - 1].rif_ + (Lifn - Lifn_last)) / n + Rifn / n);
	rtk->ssat[obs->sat - 1].rif_ = Rif_;
}

以下是我做的主要改动

procpos函数

基于RTKLIB进行载波相位平滑伪距需要先对 procpos 函数中的观测数据提取做详细了解。

c 复制代码
solstatic=sopt->solstatic&&//静态解
              (popt->mode==PMODE_STATIC||popt->mode==PMODE_PPP_STATIC);
    
    rtkinit(&rtk,popt);
    rtcm_path[0]='\0';//实时数据流
c 复制代码
while ((nobs=inputobs(obs,rtk.sol.stat,popt))>=0) {

在这个函数中循环读取一个历元的观测数据,而广播星历则是一次读取完。

c 复制代码
/* exclude satellites 排除没有参与定位的卫星*/
        for (i=n=0;i<nobs;i++) {
            if ((satsys(obs[i].sat,NULL)&popt->navsys)&&
                popt->exsats[obs[i].sat-1]!=1) obs[n++]=obs[i];
        }
 if (n<=0) continue;
        //n为一个历元有效观测卫星数
c 复制代码
if (!rtkpos(&rtk,obs,n,&navs)) continue;

正式进入定位模块,后面的条件判断则是一个历元定位结束后根据滤波方向决定下一个读取的历元。

inputobs()

在inputobs中通过不同的滤波方式读取基准站,流动站观测数据;由于后向滤波(从文件末尾读)只适用于后处理,在这里只介绍前向滤波(从文件头读取数据)。

其中obss.data[] 存储了所有历元的卫星观测数据,在inputobs()函数中通过流动站索引 iobsu ,基准站索引 iobsrobss.data 中获取 *obsd_t obs 其中存储了一个历元的所有卫星的观测数据,由于在spp,ppp,模式中不涉及到基准站,inputobs()函数对基准站的处理不易读懂,算法很巧妙,需要下一定的功夫理通

c 复制代码
* input obs data, navigation messages and sbas correction -------------------*/
static int inputobs(obsd_t *obs, int solq, const prcopt_t *popt)
{
    gtime_t time={0};
    char path[1024];
    int i,nu,nr,n=0;
    
    trace(3,"infunc  : revs=%d iobsu=%d iobsr=%d isbs=%d\n",revs,iobsu,iobsr,isbs);
    
    if (0<=iobsu&&iobsu<obss.n) {
        settime((time=obss.data[iobsu].time));
        if (checkbrk("processing : %s Q=%d",time_str(time,0),solq)) {
            aborts=1; showmsg("aborted"); return -1;
        }
    }
    if (!revs) { /* input forward data 从第一个历元读取*/
        if ((nu=nextobsf(&obss,&iobsu,1))<=0) return -1;//获取流动站观测卫星数/,&iobsu是上一个历元的基准站观测值索引,需要更新到当前历元的流动站观测值索引
        if (popt->intpref) {//内插参考观测值时,将iobsr索引(上一个历元的基准站索引)定位到当前历元的流动站索引
            for (;(nr=nextobsf(&obss,&iobsr,2))>0;iobsr+=nr)//最终iobsr被定位到当前历元流动站索引
                if (timediff(obss.data[iobsr].time,obss.data[iobsu].time)>-DTTOL) break;//iobsr所在历元时间段与当前历元的流动站历元时间差大于阈值时,退出
        }
        else {//不内插参考观测值时
            for (i=iobsr;(nr=nextobsf(&obss,&i,2))>0;iobsr=i,i+=nr)//获取上一个历元基准站观测数;iobsr被定位到当前历元基准站索引
                if (timediff(obss.data[i].time,obss.data[iobsu].time)>DTTOL) break;//上一个历元基准站索引时间与当前历元流动站索引时间差大于阈值时
        }

注:

①当 popt->intpref 为1时,表明参考站观测值是内嵌到流动站观测中的,也就是说一个历元观测值为(流动站观测值+基准站观测值);

​ &iobsr 初始为上一个历元的基准站索引,nr=上一历元基准站观测数,(假设历元时间段为30s)此时timediff= -30<阈值;iobsr+=nr成为了当前历元的流动站索引值,nr=当前历元的流动站数,timediff=0;结束循环

popt->intpref 为0时,流动站观测值和基准站观测值是分开的。

​ &iobsr 初始为上一个历元的基准站索引,nr=上一历元基准站观测数,(假设历元时间段为30s)此时timediff= -30<阈值;iobsr=i不变,i +=nr,成为了当前历元基准站索引,timediff=0<阈值; iobsr=i 成为了当前历元基准站索引,i +=nr成为了下一个历元的基准站索引,timediff=30>阈值,结束循环

c 复制代码
nr=nextobsf(&obss,&iobsr,2);//确保iobsr被定位到当前历元基准站索引,获取当前历元基准站观测数

如果 popt->intpref 为1时 iobsr从当前历元的流动站索引定位到基准站索引,然后获取观测卫星数,popt->intpref 为0时只获取观测卫星数。

如果是单点定位模式,则在第一次调用nextobsf() 时,由于观测数据的rcv无法匹配到给定参数为"2"的rcv,&iobsr 被遍历到最后一个历元,在之后调用nextobsf() 时,不会进入到if语句 的内部

c 复制代码
for (i=0;i<nu&&n<MAXOBS*2;i++) obs[n++]=obss.data[iobsu+i];//复制流动站观测卫星数据
for (i=0;i<nr&&n<MAXOBS*2;i++) obs[n++]=obss.data[iobsr+i];//复制基准站观测卫星数据
iobsu+=nu;//流动站索引此时变为参考站索引 或下一个历元的流动站索引(spp,ppp)

nextobsf()

c 复制代码
search next observation data index 前向滤波的方式--------------------------*/
static int nextobsf(const obs_t *obs, int *i, int rcv)
{
    double tt;
    int n;

 /*如果rcv为流动站,i在上一级函数中通过+=nu成为了上一个历元的基准站索引;如果是rtk模式一个历元段包括(rcv=1:流动站段 + rcv=2:基准站段)(参考观测值内插时)*/
    for (;*i<obs->n;(*i)++) if (obs->data[*i].rcv==rcv) break;//rtk模式:rcv=1(基准站索引->当前历元流动站站索引);rcv=2(-->下一段索引,可能是基准站,也可能是流动站)
    for (n=0;*i+n<obs->n;n++) {//遍历当前段观测卫星,
        tt=timediff(obs->data[*i+n].time,obs->data[*i].time);//第n个观测卫星时间与索引观测卫星时间差
        if (obs->data[*i+n].rcv!=rcv||tt>DTTOL) break;//如果卫星rcv改变或者历元时差过大,结束循环
    }
    return n;//返回索引所在历元rcv观测卫星数

双频载波平滑伪距

1、算法流程

2、需要克服的难题

(1)、

在双频消电离层模型中需要探测周跳,并进行相应初始化,而原SPP算法中无周跳探测功能。

(2)、

由于载波相位要进行历元间差分,需要记录上一个历元的观测值与平滑结果。

(3)、

上述难题涉及到RTK结构体,需要引入RTK结构体或者定义新的结构体。

3、攻克难题

(1)、

引入ppp模式中的周跳探测函数 detslp_lldetslp_gfpntpos 函数中,进行周跳探测

detslp_gf()

几何无关周跳探测

c 复制代码
/* detect cycle slip by geometry free phase jump -----------------------------*/
static void detslp_gf(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav)
{//根据双频观测量相减,剩余电离层残差以及整周模糊度,稳定项,前后做差检测周跳
    double g0,g1;
    int i,j;
    
    trace(3,"detslp_gf: n=%d\n",n);
    
    for (i=0;i<n&&i<MAXOBS;i++) {//遍历每个卫星
        
        if ((g1=gfmeas(obs+i,nav))==0.0) continue;//L1-L2
        
        g0=rtk->ssat[obs[i].sat-1].gf;//前一历元的L1-L2
        rtk->ssat[obs[i].sat-1].gf=g1;
        
        trace(4,"detslip_gf: sat=%2d gf0=%8.3f gf1=%8.3f\n",obs[i].sat,g0,g1);
        
        if (g0!=0.0&&fabs(g1-g0)>rtk->opt.thresslip) {//历元间L1-L2大于一定阈值时,发生周跳
            trace(3,"detslip_gf: slip detected sat=%2d gf=%8.3f->%8.3f\n",
                  obs[i].sat,g0,g1);
            
            for (j=0;j<rtk->opt.nf;j++) rtk->ssat[obs[i].sat-1].slip[j]|=1;
        }
    }

|= 1; :这是一个位或赋值操作。它将**rtk->ssat[obs[i].sat - 1].slip[j]** 的当前值与1进行位或操作,并将结果存回原位置。由于1在二进制中表示为000...0001(只有一个最低位是1,其余都是0),所以这个操作实际上是将rtk->ssat[obs[i].sat - 1].slip[j]的最低位设置为1,而不管它的其他位是什么

补充c语言位运算

&按位与 :同一二进制位上的数字都是1的话,&的结果为1,否则为0

0 & 0 = 0;

0 & 1 = 0;

1 & 1 = 1;

|按位或 :同一二进制位上的数字其中有一个是1,结果就是1,同0才为0

​ 0 | 0 = 0;

​ 0 | 1 = 1;

​ 1 | 0 = 1;

​ 1 | 1 = 1;

^按位异或 :同一二进制位上的数字互异,结果就为1,否则为0

​ 0 ^ 1 = 1;

​ 1 ^ 0 = 1;

​ 1 ^ 1 = 0;

​ 0 ^ 0 = 0;

<<按位左移 :将目标二进制数字向左移动相应的位数,左移补0:1111 1111 <<1 == 1111 1110,换算十进制的话是原来数值的2倍

>>按位右移 :将目标二进制数字向右移动相应的位数,右移看情况:负数补1,正数补0.需要看符号位。同样,换算为十进制数值变为原来的1/2

按位取反 :对一个二进制数进行取反,1变0,0变1 (~的优先级是逻辑运算符中最高的,必须优先计算)

参考:C语言中的逻辑运算符:按位与,按位或,按位异或,取反,左右移位_按位逻辑与的意思是什么-CSDN博客

detslp_ll()

通过LLI探测周跳

c 复制代码
/* detect cycle slip by LLI --------------------------------------------------*/
static void detslp_ll(rtk_t *rtk, const obsd_t *obs, int n)
{
    int i,j;
    
    trace(3,"detslp_ll: n=%d\n",n);
    
    for (i=0;i<n&&i<MAXOBS;i++) for (j=0;j<rtk->opt.nf;j++) {
        if (obs[i].L[j]==0.0||!(obs[i].LLI[j]&3)) continue;
        
        trace(3,"detslp_ll: slip detected sat=%2d f=%d\n",obs[i].sat,j+1);
        
        rtk->ssat[obs[i].sat-1].slip[j]=1;
    }
}

(2)、

前面已详细分析 procpos 函数中每个历元流动站观测值更新的流程,因此我打算从这里入手,获取上一个历元的观测值指针和观测卫星数;

先在rtklib.h文件定义如下全局变量

c 复制代码
extern int last_iu;			//上一个历元流动站观测值索引
extern int last_nu;			//上一个历元观测值数

回到 **postpos.c **文件

inputobs 函数中将当前流动站索引赋给 last_iu

c 复制代码
	last_iu = iobsu;//此时为当前历元流动站索引
	iobsu+=nu;//流动站索引此时变为参考站索引 或下一个历元的流动站索引(spp,ppp)

postpos 函数中对 procpos 函数做如下修改:

c 复制代码
    //读取观测值
    while (nobs_>=0) {
		last_nu = nobs_;//上一个历元的观测值数量

		if ((nobs = inputobs(obs, rtk.sol.stat, popt)) < 0) break;
		//用于载波相位平滑伪距的上一个历元观测索引和观测数赋值
		if (last_nu == 0) last_iu = 0;//首历元时,上一个历元观测值数置0
		else  last_iu -= nobs_;//将当前历元索引置到上一个历元流动站观测值索引
		nobs_ = nobs;//作为下一个历元的的上一个历元观测数

最终 last_iu 为上一个历元的的流动站观测值索引;last_nu 为上一个历元的流动站观测值

(3)、

解决思路:在 rtkpos.c 文件的 rtkpos 函数中修改pntpos函数,将rtk结构体传入

c 复制代码
if (!pntpos(rtk,obs,nu,nav,&rtk->opt,&rtk->sol,NULL,rtk->ssat,msg)) {

然后再在 rtklib.h 文件的 ssat_t 结构体中添加载波相位平滑伪距所需数据定义:

c 复制代码
	double  rif_;		/*上一个历元的伪距平滑值*/
	double  Lif ;		/*上一个历元的载波组合值*/
	int     n_continue;	/*连续平滑历元数*/

(pntpos.c文件的pntpos函数也需要修改相应传参)

至此载波相位平滑伪距的准备工作完成

4、代码编写

首先在 main.c(postpos) 文件中设置载波相位平滑标志

c 复制代码
 //载波相位平滑伪距标志
pha_s_pse = 1;//0:不进行平滑,1:进行平滑  

其中 pha_s_pse 是自定义的全局 int 变量

然后进入到 pntpos.c 文件的 pntpos 函数中编写如下代码:

c 复制代码
	if (pha_s_pse == 1) {//相位平滑伪距模式下对流动站进行观测值赋值
		if(opt->nf==1) carrier_smooth_pse_L1(rtk, obs, n,nav);//单频
		else carrier_smooth_pse_L1L2(rtk,obs ,n, nav);//双频
	}

carrier_smooth_pse_L1L2()

c 复制代码
//载波相位平滑伪距实现代码
static void carrier_smooth_pse_L1L2(rtk_t *rtk,obsd_t *obs, int n, const nav_t *nav)
{
	last_iu;
	int i ,j;

	//清除卫星周跳标志位
	for (i = 0; i < MAXSAT; i++) for (j = 0; j < rtk->opt.nf; j++) {
		rtk->ssat[i].slip[j] = 0;
	}
	//周跳探测
	/* detect cycle slip by LLI */
	detslp_ll(rtk, obs, n);
	/* detect cycle slip by geometry-free phase jump */
	detslp_gf(rtk, obs, n, nav);
	//首历元遍历当前历元观测卫星,初始化相位平滑值
	if (last_nu == 0) 
	{
		for (i = 0; i < n; i++) {
			if ((rtk->ssat[obs[i].sat - 1].rif_ = initRIF(rtk, obs + i, nav)) == 0) continue;
		}
	}
	else {//非首历元平滑
		for (i = 0; i < n; i++) //遍历当前历元观测卫星
		{
			if ((rtk->ssat[obs[i].sat - 1].slip[j]) == 1) //检测周跳
			{
				rtk->ssat[obs[i].sat - 1].rif_ = initRIF(rtk, obs + i, nav);//初始化
			}
			else {
				int sign = 0;//匹配标志
				for (j = 0; j < last_nu; j++) //与上一个历元的卫星进行匹配,没有匹配到则进行初始化
					if (obss.data[last_iu + j].sat == obs[i].sat) 
					{ 
						sign = 1;//匹配成功,标志置1
						break; 
					}
				if(sign==0) rtk->ssat[obs[i].sat - 1].rif_ = initRIF(rtk, obs + i, nav);//初始化
				else smooth(rtk, obs + i, nav);//历元平滑
				
			}
		}
	}

}

smooth()

c 复制代码
//正常历元正常卫星平滑
static int smooth(rtk_t *rtk, obsd_t *obs, const nav_t *nav) {
	double  h1, h2, R1, R2, f1, f2,Rifn,Lifn,Lifn_last;

	int n= rtk->ssat[obs->sat - 1].n_continue;
	const double *lam = nav->lam[obs->sat - 1];

	Lifn_last = rtk->ssat[obs->sat - 1].Lif; //上一个历元相位组合值
	if (lam[0] == 0.0 || lam[1] == 0.0 || obs->P[0] == 0.0 || obs->P[1] == 0.0|| Lifn_last==0
		|| obs->L[0] == 0.0 || obs->L[1] == 0.0 || rtk->ssat[obs->sat - 1].rif_ == 0 || n == 0) 
	{
		initial_obs(rtk, obs);
		return 0;
	}
	double L0 = obs->L[0]; L0 *= lam[0];
	double L1 = obs->L[1]; L1 *= lam[1];
	f1 = CLIGHT / lam[0], f2 = CLIGHT / lam[1];
	h1 = (f1*f1) / (f1*f1 - f2 * f2); h2 = -(f2*f2) / (f1*f1 - f2 * f2);

	Rifn = h1 * obs->P[0] + h2 * obs->P[1];//当前历元伪距组合值
	Lifn = h1 * L0 + h2 * L1;//当前历元载波组合值
	rtk->ssat[obs->sat - 1].Lif = Lifn;//更新载波组合值
	rtk->ssat[obs->sat - 1].n_continue += 1;//正常平滑,平滑历元数加1
	n = rtk->ssat[obs->sat - 1].n_continue;
	
	if(n<20) n=20;//当历元不足20时,滤波窗口设定为20
	
	double Rif_ = ((n - 1) * (rtk->ssat[obs->sat - 1].rif_ + (Lifn - Lifn_last)) / n + Rifn / n);
	rtk->ssat[obs->sat - 1].rif_ = Rif_;
}

initRIF()

c 复制代码
//初始化相位平滑值
static double initRIF(rtk_t *rtk, obsd_t *obs, const nav_t *nav) {
	double  h1, h2,f1,f2;
	const double *lam = nav->lam[obs->sat - 1];
	rtk->ssat[obs->sat - 1].n_continue = 0;
	if (lam[0] == 0.0 || lam[1] == 0.0 || obs->P[0] == 0.0 || obs->P[1] == 0.0 ||
		obs->L[0] == 0.0 || obs->L[1] == 0.0) {
		initial_obs(rtk, obs);
		return 0.0;
	}
	double L0 = obs->L[0]; L0 *= lam[0];
	double L1 = obs->L[1]; L1 *= lam[1];
	f1 = CLIGHT/lam[0],f2 = CLIGHT/lam[1];
	h1 = (f1*f1) / (f1*f1 - f2 * f2); h2 = -(f2*f2) / (f1*f1 - f2 * f2);
	rtk->ssat[obs->sat - 1].Lif = h1 * L0 + h2 * L1;//载波组合值
	rtk->ssat[obs->sat - 1].n_continue = 1;//平滑历元数置1
	double RIF= h1 * obs->P[0] + h2 * obs->P[1];
    return RIF;
}
initial_obs()
c 复制代码
//将载波平滑有关参数置0
static void initial_obs(rtk_t *rtk, obsd_t *obs) {
	rtk->ssat[obs->sat - 1].rif_ = 0;
	rtk->ssat[obs->sat - 1].n_continue = 0;//平滑历元数置0
	rtk->ssat[obs->sat - 1].Lif = 0;
}

单频载波平滑伪距

carrier_smooth_pse_L1()

c 复制代码
static void carrier_smooth_pse_L1(rtk_t *rtk, obsd_t *obs, int n, const nav_t *nav)
{
	last_iu;
	int i, j;
	if (last_nu == 0)//
	{
		for (i = 0; i < n; i++) {
			if ((rtk->ssat[obs[i].sat - 1].rif_ = initRIF_L1(rtk, obs + i, nav)) == 0) continue;
		}
	}
	else {//非首历元平滑
		for (i = 0; i < n; i++) //遍历当前历元观测卫星
		{
			int sign = 0;//匹配标志
			for (j = 0; j < last_nu; j++) //与上一个历元的卫星进行匹配,没有匹配到则进行初始化
				if (obss.data[last_iu + j].sat == obs[i].sat)
				{
					sign = 1;//匹配成功,标志置1
					break;
				}
			if (sign == 0) rtk->ssat[obs[i].sat - 1].rif_ = initRIF_L1(rtk, obs + i, nav);//初始化
			else smooth_L1(rtk, obs + i, nav);//历元平滑
		}
	}

}
c 复制代码
//L1平滑伪距
static int smooth_L1(rtk_t *rtk, obsd_t *obs, const nav_t *nav)
{
	int n = rtk->ssat[obs->sat - 1].n_continue;
	const double *lam = nav->lam[obs->sat - 1];
	double Lifn_last = rtk->ssat[obs->sat - 1].Lif; //上一个历元相位值
	if (lam[0] == 0.0  || obs->P[0] == 0.0  || Lifn_last == 0
		|| obs->L[0] == 0.0  || rtk->ssat[obs->sat - 1].rif_ == 0 || n == 0)
	{
		initial_obs(rtk, obs);
		return 0;
	}
	double L_n, R;
	int N = 50;//滤波器长度
	L_n = obs->L[0] * lam[0];
	rtk->ssat[obs->sat - 1].n_continue += 1;

	R = obs->P[0] / N + (N - 1)*(rtk->ssat[obs->sat - 1].rif_ + L_n - Lifn_last) / N;
	
	rtk->ssat[obs->sat - 1].Lif = L_n;
	rtk->ssat[obs->sat - 1].rif_ = R;
}
c 复制代码
//单频载波相片平滑伪距初始化
static double initRIF_L1(rtk_t *rtk, obsd_t *obs, const nav_t *nav)
{
	const double *lam = nav->lam[obs->sat - 1];
	rtk->ssat[obs->sat - 1].n_continue = 0;
	if (lam[0] == 0.0  || obs->P[0] == 0.0 ||obs->L[0] == 0.0 ) {
		initial_obs(rtk, obs);
		return 0.0;
	}
	rtk->ssat[obs->sat - 1].Lif = obs->L[0] * lam[0];
	rtk->ssat[obs->sat - 1].n_continue = 1;//平滑历元数置1
	return obs->P[0];

算法参考论文与博客

双频GPS数据的最优相位平滑伪距算法研究

基于IPSO-HATCH的北斗相位平滑伪距定位算法_席志红

Hatch Filter - 知乎 (zhihu.com)

1、

IGS观测数据甚至市面上的部分接收机都已经进行过载波相位平滑伪距,本博主在应用IGS观测数据平滑时,与原数据处理结果基本一样。并且该平滑算法更适合历元间隔短的,对于历元间隔过大的数据相位差分,亲测不具有稳定性。

2、

在双频载波平滑时,由于历元间隔过大,导致我在前几个历元根据论文以历元数n设置滤波窗口时,相位历元差分出现明显的异常。因此当连续观测历元数不足20时,我将20(只要该数大一点都行)设为滤波窗口,才能得到和原数据一致的精度。

3、

在论文1中作者有提到对卫星进行严密定权,否则可能会出现突刺情况,这一步骤,我并没有实现,后续我对算法公式理解的更透彻时会进一步实践。

算法参考论文与博客

双频GPS数据的最优相位平滑伪距算法研究

基于IPSO-HATCH的北斗相位平滑伪距定位算法_席志红

Hatch Filter - 知乎 (zhihu.com)

相关推荐
MM_MS16 小时前
Halcon控制语句
java·大数据·前端·数据库·人工智能·算法·视觉检测
mit6.82416 小时前
山脉二分找中值|子集型回溯
算法
清水白石00816 小时前
深入 Python 的底层世界:从 C 扩展到 ctypes 与 Cython 的本质差异全解析
c语言·python·neo4j
乃瞻衡宇17 小时前
Agent Skills 完全指南:让你的 AI Agent 拥有超能力
算法
mit6.82417 小时前
pair<int, TreeNode*> dfs
算法
程序员zgh17 小时前
Linux 系统调用
linux·运维·服务器·c语言·c++·系统安全
初晴や17 小时前
【C++】图论:基础理论与实际应用深入解析
c++·算法·图论
李泽辉_18 小时前
深度学习算法学习(五):手动实现梯度计算、反向传播、优化器Adam
深度学习·学习·算法
李泽辉_18 小时前
深度学习算法学习(一):梯度下降法和最简单的深度学习核心原理代码
深度学习·学习·算法