/*------------------------------------------------------------------------------ * rtkpos.c : precise positioning * * Copyright (C) 2007-2020 by T.TAKASU, All rights reserved. * * version : $Revision: 1.1 $ $Date: 2008/07/17 21:48:06 $ * history : 2007/01/12 1.0 new * 2007/03/13 1.1 add slip detection by LLI flag * 2007/04/18 1.2 add antenna pcv correction * change rtkpos argin * 2008/07/18 1.3 refactored * 2009/01/02 1.4 modify rtk positioning api * 2009/03/09 1.5 support glonass, gallileo and qzs * 2009/08/27 1.6 fix bug on numerical exception * 2009/09/03 1.7 add check of valid satellite number * add check time sync for moving-base * 2009/11/23 1.8 add api rtkopenstat(),rtkclosestat() * add receiver h/w bias estimation * add solution status output * 2010/04/04 1.9 support ppp-kinematic and ppp-static modes * support earth tide correction * changed api: * rtkpos() * 2010/09/07 1.10 add elevation mask to hold ambiguity * 2012/02/01 1.11 add extended receiver error model * add glonass interchannel bias correction * add slip detectior by L1-L5 gf jump * output snr of rover receiver in residuals * 2013/03/10 1.12 add otl and pole tides corrections * 2014/05/26 1.13 support beidou and galileo * add output of gal-gps and bds-gps time offset * 2014/05/28 1.14 fix bug on memory exception with many sys and freq * 2014/08/26 1.15 add function to swap sol-stat file with keywords * 2014/10/21 1.16 fix bug on beidou amb-res with pos2-bdsarmode=0 * 2014/11/08 1.17 fix bug on ar-degradation by unhealthy satellites * 2015/03/23 1.18 residuals referenced to reference satellite * 2015/05/20 1.19 no output solution status file with Q=0 * 2015/07/22 1.20 fix bug on base station position setting * 2016/07/30 1.21 suppress single solution if !prcopt.outsingle * fix bug on slip detection of backward filter * 2016/08/20 1.22 fix bug on ddres() function * 2018/10/10 1.13 support api change of satexclude() * 2018/12/15 1.14 disable ambiguity resolution for gps-qzss * 2019/08/19 1.15 fix bug on return value of resamb_LAMBDA() * 2020/11/30 1.16 support of NavIC/IRNSS in API rtkpos() * add detecting cycle slips by L1-Lx GF phase jump * delete GLONASS IFB correction in ddres() * use integer types in stdint.h *-----------------------------------------------------------------------------*/ #include #include "rtklib.h" /* constants/macros ----------------------------------------------------------*/ #define SQR(x) ((x)*(x)) #define SQRT(x) ((x)<=0.0||(x)!=(x)?0.0:sqrt(x)) #define MIN(x,y) ((x)<=(y)?(x):(y)) #define MAX(x,y) ((x)>=(y)?(x):(y)) #define ROUND(x) (int)floor((x)+0.5) #define VAR_POS SQR(30.0) /* initial variance of receiver pos (m^2) */ #define VAR_POS_FIX SQR(1e-4) /* initial variance of fixed receiver pos (m^2) */ #define VAR_VEL SQR(10.0) /* initial variance of receiver vel ((m/s)^2) */ #define VAR_ACC SQR(10.0) /* initial variance of receiver acc ((m/ss)^2) */ #define VAR_GRA SQR(0.001) /* initial variance of gradient (m^2) */ #define INIT_ZWD 0.15 /* initial zwd (m) */ #define GAP_RESION 120 /* gap to reset ionosphere parameters (epochs) */ #define TTOL_MOVEB (1.0+2*DTTOL) /* time sync tolerance for moving-baseline (s) */ /* number of parameters (pos,ionos,tropos,hw-bias,phase-bias,real,estimated) */ #define NF(opt) ((opt)->ionoopt==IONOOPT_IFLC?1:(opt)->nf) #define NP(opt) ((opt)->dynamics==0?3:9) #define NI(opt) ((opt)->ionoopt!=IONOOPT_EST?0:MAXSAT) #define NT(opt) ((opt)->tropopttropoptglomodear!=GLO_ARMODE_AUTOCAL?0:NFREQGLO) #define NB(opt) ((opt)->mode<=PMODE_DGPS?0:MAXSAT*NF(opt)) #define NR(opt) (NP(opt)+NI(opt)+NT(opt)+NL(opt)) #define NX(opt) (NR(opt)+NB(opt)) #define NTC 15 /* state variable index */ #define II(s,opt) (NP(opt)+(s)-1) /* ionos (s:satellite no) */ #define IT(r,opt) (NP(opt)+NI(opt)+NT(opt)/2*(r)) /* tropos (r:0=rov,1:ref) */ #define IL(f,opt) (NP(opt)+NI(opt)+NT(opt)+(f)) /* receiver h/w bias */ #define IB(s,f,opt) (NR(opt)+MAXSAT*(f)+(s)-1) /* phase bias (s:satno,f:freq) */ #define ITC_B(s,f,opt) (NTC+MAXSAT*(f)+(s)-1) /* tightly coupled phase bias index(s:satno,f:freq) */ /* poly coeffs used to adjust AR ratio by # of sats, derived by fitting to example from: https://www.tudelft.nl/citg/over-faculteit/afdelingen/geoscience-remote-sensing/research/lambda/lambda*/ static double ar_poly_coeffs[3][5] = { {-1.94058448e-01, -7.79023476e+00, 1.24231120e+02, -4.03126050e+02, 3.50413202e+02}, {6.42237302e-01, -8.39813962e+00, 2.92107285e+01, -2.37577308e+01, -1.14307128e+00}, {-2.22600390e-02, 3.23169103e-01, -1.39837429e+00, 2.19282996e+00, -5.34583971e-02}}; /* global variables ----------------------------------------------------------*/ static int statlevel=0; /* rtk status output level (0:off) */ static FILE *fp_stat=NULL; /* rtk status file pointer */ static char file_stat[1024]=""; /* rtk status file original path */ static gtime_t time_stat={0}; /* rtk status file time */ /* open solution status file --------------------------------------------------- * open solution status file and set output level * args : char *file I rtk status file * int level I rtk status level (0: off) * return : status (1:ok,0:error) * notes : file can constain time keywords (%Y,%y,%m...) defined in reppath(). * The time to replace keywords is based on UTC of CPU time. * output : solution status file record format * * $POS,week,tow,stat,posx,posy,posz,posxf,posyf,poszf * week/tow : gps week no/time of week (s) * stat : solution status * posx/posy/posz : position x/y/z ecef (m) float * posxf/posyf/poszf : position x/y/z ecef (m) fixed * * $VELACC,week,tow,stat,vele,veln,velu,acce,accn,accu,velef,velnf,veluf,accef,accnf,accuf * week/tow : gps week no/time of week (s) * stat : solution status * vele/veln/velu : velocity e/n/u (m/s) float * acce/accn/accu : acceleration e/n/u (m/s^2) float * velef/velnf/veluf : velocity e/n/u (m/s) fixed * accef/accnf/accuf : acceleration e/n/u (m/s^2) fixed * * $CLK,week,tow,stat,clk1,clk2,clk3,clk4,cmn_bias * week/tow : gps week no/time of week (s) * stat : solution status * clk1 : receiver clock bias GPS (ns) * clk2 : receiver clock bias GLO-GPS (ns) * clk3 : receiver clock bias GAL-GPS (ns) * clk4 : receiver clock bias BDS-GPS (ns) * cmn_bias : common phase bias removed from all states * * $ION,week,tow,stat,sat,az,el,ion,ion-fixed * week/tow : gps week no/time of week (s) * stat : solution status * sat : satellite id * az/el : azimuth/elevation angle(deg) * ion : vertical ionospheric delay L1 (m) float * ion-fixed: vertical ionospheric delay L1 (m) fixed * * $TROP,week,tow,stat,rcv,ztd,ztdf * week/tow : gps week no/time of week (s) * stat : solution status * rcv : receiver (1:rover,2:base station) * ztd : zenith total delay (m) float * ztdf : zenith total delay (m) fixed * * $HWBIAS,week,tow,stat,frq,bias,biasf * week/tow : gps week no/time of week (s) * stat : solution status * frq : frequency (1:L1,2:L2,...) * bias : h/w bias coefficient (m/MHz) float * biasf : h/w bias coefficient (m/MHz) fixed * * $SAT,week,tow,sat,frq,az,el,resp,resc,vsat,snr,fix,slip,lock,outc,slipc,rejc,icbias,bias,bias_var,lambda * week/tow : gps week no/time of week (s) * sat/frq : satellite id/frequency (1:L1,2:L2,...) * az/el : azimuth/elevation angle (deg) * resp : pseudorange residual (m) * resc : carrier-phase residual (m) * vsat : valid data flag (0:invalid,1:valid) * snr : signal strength (dbHz) * fix : ambiguity flag (0:no data,1:float,2:fixed,3:hold,4:ppp) * slip : cycle-slip flag (bit1:slip,bit2:parity unknown) * lock : carrier-lock count * outc : data outage count * slipc : cycle-slip count * rejc : data reject (outlier) count * icbias : interchannel bias (GLONASS) * bias : phase bias * bias_var : variance of phase bias * lambda : wavelength * *-----------------------------------------------------------------------------*/ extern int rtkopenstat(const char *file, int level) { gtime_t time=utc2gpst(timeget()); char path[1024]; trace(3,"rtkopenstat: file=%s level=%d\n",file,level); if (level<=0) return 0; reppath(file,path,time,"",""); if (!(fp_stat=fopen(path,"w"))) { trace(1,"rtkopenstat: file open error path=%s\n",path); return 0; } strcpy(file_stat,file); time_stat=time; statlevel=level; return 1; } /* close solution status file -------------------------------------------------- * close solution status file * args : none * return : none *-----------------------------------------------------------------------------*/ extern void rtkclosestat(void) { trace(3,"rtkclosestat:\n"); if (fp_stat) fclose(fp_stat); fp_stat=NULL; file_stat[0]='\0'; statlevel=0; } /* write solution status to buffer -------------------------------------------*/ extern int rtkoutstat(rtk_t *rtk, char *buff) { ssat_t *ssat; double tow,pos[3],vel[3],acc[3],vela[3]={0},acca[3]={0},xa[3]; int i,j,week,est,nfreq,nf=NF(&rtk->opt); char id[32],*p=buff; if (rtk->sol.stat<=SOLQ_NONE) { return 0; } /* write ppp solution status to buffer */ if (rtk->opt.mode>=PMODE_PPP_KINEMA) { return pppoutstat(rtk,buff); } est=rtk->opt.mode>=PMODE_DGPS; nfreq=est?nf:1; tow=time2gpst(rtk->sol.time,&week); /* receiver position */ if (est) { for (i=0;i<3;i++) xa[i]=ina?rtk->xa[i]:0.0; p+=sprintf(p,"$POS,%d,%.3f,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n",week,tow, rtk->sol.stat,rtk->x[0],rtk->x[1],rtk->x[2],xa[0],xa[1], xa[2]); } else { p+=sprintf(p,"$POS,%d,%.3f,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n",week,tow, rtk->sol.stat,rtk->sol.rr[0],rtk->sol.rr[1],rtk->sol.rr[2], 0.0,0.0,0.0); } /* receiver velocity and acceleration */ if (est&&rtk->opt.dynamics) { ecef2pos(rtk->sol.rr,pos); ecef2enu(pos,rtk->x+3,vel); ecef2enu(pos,rtk->x+6,acc); if (rtk->na>=6) ecef2enu(pos,rtk->xa+3,vela); if (rtk->na>=9) ecef2enu(pos,rtk->xa+6,acca); p+=sprintf(p,"$VELACC,%d,%.3f,%d,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f\n", week,tow,rtk->sol.stat,vel[0],vel[1],vel[2],acc[0],acc[1], acc[2],vela[0],vela[1],vela[2],acca[0],acca[1],acca[2]); } else { ecef2pos(rtk->sol.rr,pos); ecef2enu(pos,rtk->sol.rr+3,vel); p+=sprintf(p,"$VELACC,%d,%.3f,%d,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f\n", week,tow,rtk->sol.stat,vel[0],vel[1],vel[2], 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0); } /* receiver clocks */ p+=sprintf(p,"$CLK,%d,%.3f,%d,%d,%.3f,%.3f,%.3f,%.3f\n", week,tow,rtk->sol.stat,1,rtk->sol.dtr[0]*1E9,rtk->sol.dtr[1]*1E9, rtk->sol.dtr[2]*1E9,rtk->sol.dtr[3]*1E9); /* ionospheric parameters */ if (est&&rtk->opt.ionoopt==IONOOPT_EST) { for (i=0;issat+i; if (!ssat->vs) continue; satno2id(i+1,id); j=II(i+1,&rtk->opt); xa[0]=jna?rtk->xa[j]:0.0; p+=sprintf(p,"$ION,%d,%.3f,%d,%s,%.1f,%.1f,%.4f,%.4f\n",week,tow, rtk->sol.stat,id,ssat->azel[0]*R2D,ssat->azel[1]*R2D, rtk->x[j],xa[0]); } } /* tropospheric parameters */ if (est&&(rtk->opt.tropopt==TROPOPT_EST||rtk->opt.tropopt==TROPOPT_ESTG)) { for (i=0;i<2;i++) { j=IT(i,&rtk->opt); xa[0]=jna?rtk->xa[j]:0.0; p+=sprintf(p,"$TROP,%d,%.3f,%d,%d,%.4f,%.4f\n",week,tow, rtk->sol.stat,i+1,rtk->x[j],xa[0]); } } /* receiver h/w bias */ if (est&&rtk->opt.glomodear==GLO_ARMODE_AUTOCAL) { for (i=0;iopt); xa[0]=jna?rtk->xa[j]:0.0; p+=sprintf(p,"$HWBIAS,%d,%.3f,%d,%d,%.4f,%.4f\n",week,tow, rtk->sol.stat,i+1,rtk->x[j],xa[0]); } } return (int)(p-buff); } /* swap solution status file -------------------------------------------------*/ static void swapsolstat(void) { gtime_t time=utc2gpst(timeget()); char path[1024]; if ((int)(time2gpst(time ,NULL)/INT_SWAP_STAT)== (int)(time2gpst(time_stat,NULL)/INT_SWAP_STAT)) { return; } time_stat=time; if (!reppath(file_stat,path,time,"","")) { return; } if (fp_stat) fclose(fp_stat); if (!(fp_stat=fopen(path,"w"))) { trace(2,"swapsolstat: file open error path=%s\n",path); return; } trace(3,"swapsolstat: path=%s\n",path); } /* output solution status ----------------------------------------------------*/ extern void outsolstat(rtk_t *rtk,const nav_t *nav) { ssat_t *ssat; double tow; char buff[MAXSOLMSG+1],id[32]; int i,j,k,n,week,nfreq,nf=NF(&rtk->opt); if (statlevel<=0||!fp_stat||!rtk->sol.stat) return; trace(3,"outsolstat:\n"); /* swap solution status file */ swapsolstat(); /* write solution status */ n=rtkoutstat(rtk,buff); buff[n]='\0'; fputs(buff,fp_stat); if (rtk->sol.stat==SOLQ_NONE||statlevel<=1) return; tow=time2gpst(rtk->sol.time,&week); nfreq=rtk->opt.mode>=PMODE_DGPS?nf:1; /* write residuals and status */ for (i=0;issat+i; if (!ssat->vs) continue; satno2id(i+1,id); for (j=0;jopt); fprintf(fp_stat,"$SAT,%d,%.3f,%s,%d,%.1f,%.1f,%.4f,%.4f,%d,%.0f,%d,%d,%d,%d,%d,%d,%.2f,%.6f,%.5f\n", week,tow,id,j+1,ssat->azel[0]*R2D,ssat->azel[1]*R2D, ssat->resp[j],ssat->resc[j],ssat->vsat[j],ssat->snr_rover[j]*SNR_UNIT, ssat->fix[j],ssat->slip[j]&3,ssat->lock[j],ssat->outc[j], ssat->slipc[j],ssat->rejc[j],rtk->x[k], rtk->P[k+k*rtk->nx],ssat->icbias[j]); } } } /* save error message --------------------------------------------------------*/ static void errmsg(rtk_t *rtk, const char *format, ...) { char buff[256],tstr[32]; int n; va_list ap; time2str(rtk->sol.time,tstr,2); n=sprintf(buff,"%s: ",tstr+11); va_start(ap,format); n+=vsprintf(buff+n,format,ap); va_end(ap); n=nneb?n:MAXERRMSG-rtk->neb; memcpy(rtk->errbuf+rtk->neb,buff,n); rtk->neb+=n; trace(2,"%s",buff); } /* single-differenced observable ---------------------------------------------*/ static double sdobs(const obsd_t *obs, int i, int j, int k) { double pi=(kerr[5]; double fact=1.0; double sinel=sin(el),var; int nf=NF(opt),frq,code; frq=f%nf;code=feratio[frq]; if (fact<=0.0) fact=opt->eratio[0]; /* adjust variances for constellation */ switch (sys) { case SYS_GPS: fact*=EFACT_GPS;break; case SYS_GLO: fact*=EFACT_GLO;break; case SYS_GAL: fact*=EFACT_GAL;break; case SYS_SBS: fact*=EFACT_SBS;break; case SYS_QZS: fact*=EFACT_QZS;break; case SYS_CMP: fact*=EFACT_CMP;break; case SYS_IRN: fact*=EFACT_IRN;break; default: fact*=EFACT_GPS;break; } /* adjust variance for config parameters */ a=fact*opt->err[1]; /* base term */ b=fact*opt->err[2]; /* el term */ c=opt->err[3]*bl/1E4; /* baseline term */ d=CLIGHT*opt->sclkstab*dt; /* clock term */ /* calculate variance */ var=2.0*(a*a+b*b/sinel/sinel+c*c)+d*d; if (opt->err[6]>0) { /* add SNR term */ e=fact*opt->err[6]; var+=e*e*(pow(10,0.1*MAX(snr_max-snr_rover,0))+ pow(10,0.1*MAX(snr_max-snr_base, 0))); } if (opt->err[7]>0.0) { /* add rcvr stdevs term */ if (code) var+=SQR(opt->err[7]*0.01*(1<<(obs->Pstd[frq]+5))); /* 0.01*2^(n+5) */ else var+=SQR(opt->err[7]*obs->Lstd[frq]*0.004*0.2); /* 0.004 cycles -> m) */ } var*=(opt->ionoopt==IONOOPT_IFLC)?SQR(3.0):1.0; return var; } /* baseline length -----------------------------------------------------------*/ static double baseline(const double *ru, const double *rb, double *dr) { int i; for (i=0;i<3;i++) dr[i]=ru[i]-rb[i]; return norm(dr,3); } /* initialize state and covariance -------------------------------------------*/ static void initx(rtk_t *rtk, double xi, double var, int i) { int j,tc,nx; tc = rtk->opt.mode; nx = tc==PMODE_INS_TGNSS?rtk->ins.nx:rtk->nx; if(tc==PMODE_INS_TGNSS){ rtk->ins.x[i]=xi; for (j=0;jins.P[i+j*nx]=rtk->ins.P[j+i*nx]=i==j?var:0.0; } } else{ rtk->x[i]=xi; for (j=0;jP[i+j*nx]=rtk->P[j+i*nx]=i==j?var:0.0; } } } /* select common satellites between rover and reference station --------------*/ static int selsat(const obsd_t *obs, double *azel, int nu, int nr, const prcopt_t *opt, int *sat, int *iu, int *ir) { int i,j,k=0; trace(3,"selsat : nu=%d nr=%d\n",nu,nr); for (i=0,j=nu;iobs[j].sat) i--; else if (azel[1+j*2]>=opt->elmin) { /* elevation at base station */ sat[k]=obs[i].sat; iu[k]=i; ir[k++]=j; trace(4,"(%2d) sat=%3d iu=%2d ir=%2d\n",k-1,obs[i].sat,i,j); } } return k; } /* temporal update of position/velocity/acceleration -------------------------*/ static void udpos(rtk_t *rtk, double tt) { double *F,*P,*FP,*x,*xp,pos[3],Q[9]={0},Qv[9],var=0.0; int i,j,*ix,nx,tc; trace(3,"udpos : tt=%.3f\n",tt); /* fixed mode */ if (rtk->opt.mode==PMODE_FIXED) { for (i=0;i<3;i++) initx(rtk,rtk->opt.ru[i],VAR_POS_FIX,i); return; } /* initialize position for first epoch */ if (norm(rtk->x,3)<=0.0) { for (i=0;i<3;i++) initx(rtk,rtk->sol.rr[i],VAR_POS,i); if (rtk->opt.dynamics) { for (i=3;i<6;i++) initx(rtk,rtk->sol.rr[i],VAR_VEL,i); for (i=6;i<9;i++) initx(rtk,1E-6,VAR_ACC,i); } } /* static mode */ if (rtk->opt.mode==PMODE_STATIC||rtk->opt.mode==PMODE_STATIC_START) return; /* kinmatic mode without dynamics */ if (!rtk->opt.dynamics) { for (i=0;i<3;i++) initx(rtk,rtk->sol.rr[i],VAR_POS,i); return; } /* check variance of estimated position */ for (i=0;i<3;i++) var+=rtk->P[i+i*rtk->nx]; var/=3.0; if (var>VAR_POS) { /* reset position with large variance */ for (i=0;i<3;i++) initx(rtk,rtk->sol.rr[i],VAR_POS,i); for (i=3;i<6;i++) initx(rtk,rtk->sol.rr[i],VAR_VEL,i); for (i=6;i<9;i++) initx(rtk,1E-6,VAR_ACC,i); trace(2,"reset rtk position due to large variance: var=%.3f\n",var); return; } /* generate valid state index */ ix=imat(rtk->nx,1); for (i=nx=0;inx;i++) { /* TODO: The b34 code causes issues so use b33 code for now */ if (i<9||(rtk->x[i]!=0.0&&rtk->P[i+i*rtk->nx]>0.0)) ix[nx++]=i; // 这里保留前9维(pos,velocity,acc)+模糊度并记录他们在x中的index保存在ix中 } /* state transition of position/velocity/acceleration */ F=eye(nx); P=mat(nx,nx); FP=mat(nx,nx); x=mat(nx,1); xp=mat(nx,1); for (i=0;i<6;i++) { F[i+(i+3)*nx]=tt; } /* include accel terms if filter is converged */ if (varopt.thresar[1]) { for (i=0;i<3;i++) { F[i+(i+6)*nx]=SQR(tt)/2.0; } } else trace(3,"pos var too high for accel term\n"); for (i=0;ix[ix[i]]; for (j=0;jP[ix[i]+ix[j]*rtk->nx]; } } /* x=F*x, P=F*P*F' */ matmul("NN",nx,1,nx,1.0,F,x,0.0,xp); matmul("NN",nx,nx,nx,1.0,F,P,0.0,FP); matmul("NT",nx,nx,nx,1.0,FP,F,0.0,P); for (i=0;ix[ix[i]]=xp[i]; for (j=0;jP[ix[i]+ix[j]*rtk->nx]=P[i+j*nx]; } } /* process noise added to only acceleration P=P+Q */ Q[0]=Q[4]=SQR(rtk->opt.prn[3])*fabs(tt); Q[8]=SQR(rtk->opt.prn[4])*fabs(tt); ecef2pos(rtk->x,pos); covecef(pos,Q,Qv); for (i=0;i<3;i++) for (j=0;j<3;j++) { rtk->P[i+6+(j+6)*rtk->nx]+=Qv[i+j*3]; } free(ix); free(F); free(P); free(FP); free(x); free(xp); } /* temporal update of ionospheric parameters ---------------------------------*/ static void udion(rtk_t *rtk, double tt, double bl, const int *sat, int ns) { double el,fact; int i,j; trace(3,"udion : tt=%.3f bl=%.0f ns=%d\n",tt,bl,ns); for (i=1;i<=MAXSAT;i++) { j=II(i,&rtk->opt); if (rtk->x[j]!=0.0&& rtk->ssat[i-1].outc[0]>GAP_RESION&&rtk->ssat[i-1].outc[1]>GAP_RESION) rtk->x[j]=0.0; } for (i=0;iopt); if (rtk->x[j]==0.0) { initx(rtk,1E-6,SQR(rtk->opt.std[1]*bl/1E4),j); } else { /* elevation dependent factor of process noise */ el=rtk->ssat[sat[i]-1].azel[1]; fact=cos(el); rtk->P[j+j*rtk->nx]+=SQR(rtk->opt.prn[1]*bl/1E4*fact)*fabs(tt); } } } /* temporal update of tropospheric parameters --------------------------------*/ static void udtrop(rtk_t *rtk, double tt, double bl) { int i,j,k; trace(3,"udtrop : tt=%.3f\n",tt); for (i=0;i<2;i++) { j=IT(i,&rtk->opt); if (rtk->x[j]==0.0) { initx(rtk,INIT_ZWD,SQR(rtk->opt.std[2]),j); /* initial zwd */ if (rtk->opt.tropopt>=TROPOPT_ESTG) { for (k=0;k<2;k++) initx(rtk,1E-6,VAR_GRA,++j); } } else { rtk->P[j+j*rtk->nx]+=SQR(rtk->opt.prn[2])*fabs(tt); if (rtk->opt.tropopt>=TROPOPT_ESTG) { for (k=0;k<2;k++) { rtk->P[++j*(1+rtk->nx)]+=SQR(rtk->opt.prn[2]*0.3)*fabs(tt); } } } } } /* temporal update of receiver h/w biases ------------------------------------*/ static void udrcvbias(rtk_t *rtk, double tt) { int i,j; trace(3,"udrcvbias: tt=%.3f\n",tt); for (i=0;iopt); if (rtk->x[j]==0.0) { /* add small offset to avoid initializing with zero */ initx(rtk,rtk->opt.thresar[2]+1e-6,rtk->opt.thresar[3],j); } /* hold to fixed solution */ else if (rtk->nfix>=rtk->opt.minfix) { initx(rtk,rtk->xa[j],rtk->Pa[j+j*rtk->na],j); } else { rtk->P[j+j*rtk->nx]+=SQR(rtk->opt.thresar[4])*fabs(tt); } } } /* detect cycle slip by LLI --------------------------------------------------*/ static void detslp_ll(rtk_t *rtk, const obsd_t *obs, int i, int rcv) { uint32_t slip,LLI; int f,sat=obs[i].sat; trace(4,"detslp_ll: i=%d rcv=%d\n",i,rcv); for (f=0;fopt.nf;f++) { if ((obs[i].L[f]==0.0&&obs[i].LLI[f]==0)|| fabs(timediff(obs[i].time,rtk->ssat[sat-1].pt[rcv-1][f]))ssat[sat-1].slip[f],0,2); /* rover */ else LLI=getbitu(&rtk->ssat[sat-1].slip[f],2,2); /* base */ /* detect slip by cycle slip flag in LLI */ if (rtk->tt>=0.0) { /* forward */ if (obs[i].LLI[f]&1) { errmsg(rtk,"slip detected forward (sat=%2d rcv=%d F=%d LLI=%x)\n", sat,rcv,f+1,obs[i].LLI[f]); } slip=obs[i].LLI[f]; } else { /* backward */ if (LLI&1) { errmsg(rtk,"slip detected backward (sat=%2d rcv=%d F=%d LLI=%x)\n", sat,rcv,f+1,LLI); } slip=LLI; } /* detect slip by parity unknown flag transition in LLI */ if (((LLI&2)&&!(obs[i].LLI[f]&2))||(!(LLI&2)&&(obs[i].LLI[f]&2))) { errmsg(rtk,"slip detected half-cyc (sat=%2d rcv=%d F=%d LLI=%x->%x)\n", sat,rcv,f+1,LLI,obs[i].LLI[f]); slip|=1; } /* save current LLI */ if (rcv==1) setbitu(&rtk->ssat[sat-1].slip[f],0,2,obs[i].LLI[f]); else setbitu(&rtk->ssat[sat-1].slip[f],2,2,obs[i].LLI[f]); /* save slip and half-cycle valid flag */ rtk->ssat[sat-1].slip[f]|=(uint8_t)slip; rtk->ssat[sat-1].half[f]=(obs[i].LLI[f]&2)?0:1; } } /* detect cycle slip by geometry free phase jump -----------------------------*/ static void detslp_gf(rtk_t *rtk, const obsd_t *obs, int i, int j, const nav_t *nav) { int k,sat=obs[i].sat; double gf0,gf1; trace(4,"detslp_gf: i=%d j=%d\n",i,j); /* skip check if slip already detected or check disabled*/ if (rtk->opt.thresslip==0) return; for (k=0;kopt.nf;k++) if (rtk->ssat[sat-1].slip[k]&1) return; for (k=1;kopt.nf;k++) { /* calc SD geomotry free LC of phase between freq0 and freqk */ if ((gf1=gfobs(obs,i,j,k,nav))==0.0) continue; gf0=rtk->ssat[sat-1].gf[k-1]; /* retrieve previous gf */ rtk->ssat[sat-1].gf[k-1]=gf1; /* save current gf for next epoch */ if (gf0!=0.0&&fabs(gf1-gf0)>rtk->opt.thresslip) { rtk->ssat[sat-1].slip[0]|=1; rtk->ssat[sat-1].slip[k]|=1; errmsg(rtk,"slip detected GF jump (sat=%2d L1-L%d dGF=%.3f)\n", sat,k+1,gf0-gf1); } } } /* detect cycle slip by doppler and phase difference -------------------------*/ static void detslp_dop(rtk_t *rtk, const obsd_t *obs, const int *ix, int ns, int rcv, const nav_t *nav) { int i,ii,f,sat,ndop=0; double dph,dpt,lam,thres,mean_dop=0; double dopdif[MAXSAT][NFREQ], tt[MAXSAT][NFREQ]; trace(4,"detslp_dop: rcv=%d\n", rcv); if (rtk->opt.thresdop<=0) return; /* skip test if doppler thresh <= 0 */ /* calculate doppler differences for all sats and freqs */ for (i=0;iopt.nf;f++) { dopdif[i][f]=0;tt[i][f]=0.00; if (obs[ii].L[f]==0.0||obs[ii].D[f]==0.0||rtk->ssat[sat-1].ph[rcv-1][f]==0.0) continue; if ((tt[i][f]=fabs(timediff(obs[ii].time,rtk->ssat[sat-1].pt[rcv-1][f])))ssat[sat-1].ph[rcv-1][f]; dpt=-obs[ii].D[f]*tt[i][f]; dopdif[i][f]=(dph-dpt)/tt[i][f]; /* if not outlier, use this to calculate mean */ if (fabs(dopdif[i][f])<3*rtk->opt.thresdop) { mean_dop+=dopdif[i][f]; ndop++; } } } /* calc mean doppler diff, most likely due to clock error */ if (ndop==0) return; /* unable to calc mean doppler, usually very large clock err */ mean_dop=mean_dop/ndop; /* set slip if doppler difference with mean removed exceeds threshold */ for (i=0;iopt.nf;f++) { if (dopdif[i][f]==0.00) continue; if (fabs(dopdif[i][f]-mean_dop)>rtk->opt.thresdop) { rtk->ssat[sat-1].slip[f]|=1; errmsg(rtk,"slip detected doppler (sat=%2d rcv=%d dL%d=%.3f off=%.3f tt=%.2f)\n", sat,rcv,f+1,dopdif[i][f]-mean_dop,mean_dop,tt[i][f]); } } } } /* temporal update of phase biases -------------------------------------------*/ static void udbias(rtk_t *rtk, double tt, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav) { double cp,pr,cp1,cp2,pr1,pr2,*bias,offset,freqi,freq1,freq2,C1,C2; int i,j,k,slip,rejc,reset,nf=NF(&rtk->opt),sysi,f2,tc; double *x,*P; int nx,ib; trace(3,"udbias : tt=%.3f ns=%d\n",tt,ns); tc=rtk->opt.mode==PMODE_INS_TGNSS?1:0; x=tc?rtk->ins.x:rtk->x; P=tc?rtk->ins.P:rtk->P; nx=tc?rtk->ins.nx:rtk->nx; /* clear cycle slips */ for (i=0;iopt.nf;k++) rtk->ssat[sat[i]-1].slip[k]&=0xFC; } /* detect cycle slip by doppler and phase difference */ detslp_dop(rtk,obs,iu,ns,1,nav); detslp_dop(rtk,obs,ir,ns,2,nav); for (i=0;issat[sat[i]-1].half[k]= !((obs[iu[i]].LLI[k]&2)||(obs[ir[i]].LLI[k]&2)); } } for (k=0;kssat[i-1].outc[k]>(uint32_t)rtk->opt.maxout; ib=tc?xiBs(&rtk->opt.insopt,i,k):IB(i,k,&rtk->opt); if (rtk->opt.modear==ARMODE_INST&&x[ib]!=0.0) { initx(rtk,0.0,0.0,ib); } else if (reset&&rtk->x[ib]!=0.0) { initx(rtk,0.0,0.0,ib); trace(3,"udbias : obs outage counter overflow (sat=%3d L%d n=%d)\n", i,k+1,rtk->ssat[i-1].outc[k]); rtk->ssat[i-1].outc[k]=0; } if (rtk->opt.modear!=ARMODE_INST&&reset) { rtk->ssat[i-1].lock[k]=-rtk->opt.minlock; } } /* update phase bias noise and check for cycle slips */ for (i=0;iopt.insopt),sat[i],k):IB(sat[i],k,&rtk->opt); P[j+j*nx]+=rtk->opt.prn[0]*rtk->opt.prn[0]*fabs(tt); slip=rtk->ssat[sat[i]-1].slip[k]; rejc=rtk->ssat[sat[i]-1].rejc[k]; if (rtk->opt.ionoopt==IONOOPT_IFLC) { f2=seliflc(rtk->opt.nf,rtk->ssat[sat[i]-1].sys); slip|=rtk->ssat[sat[i]-1].slip[f2]; } if (rtk->opt.modear==ARMODE_INST||(!(slip&1)&&rejc<2)) continue; /* reset phase-bias state if detecting cycle slip or outlier */ x[j]=0.0; rtk->ssat[sat[i]-1].rejc[k]=0; rtk->ssat[sat[i]-1].lock[k]=-rtk->opt.minlock; /* retain icbiases for GLONASS sats */ if (rtk->ssat[sat[i]-1].sys!=SYS_GLO) rtk->ssat[sat[i]-1].icbias[k]=0; } bias=zeros(ns,1); /* estimate approximate phase-bias by delta phase - delta code */ for (i=j=0,offset=0.0;iopt.ionoopt!=IONOOPT_IFLC) { /* phase diff between rover and base in cycles */ cp=sdobs(obs,iu[i],ir[i],k); /* cycle */ /* pseudorange diff between rover and base in meters */ pr=sdobs(obs,iu[i],ir[i],k+NFREQ); freqi=sat2freq(sat[i],obs[iu[i]].code[k],nav); if (cp==0.0||pr==0.0||freqi==0.0) continue; /* estimate bias in cycles */ bias[i]=cp-pr*freqi/CLIGHT; } else { /* use ionosphere free calc with 2 freqs */ f2=seliflc(rtk->opt.nf,rtk->ssat[sat[i]-1].sys); cp1=sdobs(obs,iu[i],ir[i],0); cp2=sdobs(obs,iu[i],ir[i],f2); pr1=sdobs(obs,iu[i],ir[i],NFREQ); pr2=sdobs(obs,iu[i],ir[i],NFREQ+f2); freq1=sat2freq(sat[i],obs[iu[i]].code[0],nav); freq2=sat2freq(sat[i],obs[iu[i]].code[f2],nav); if (cp1==0.0||cp2==0.0||pr1==0.0||pr2==0.0||freq1<=0.0||freq2<=0.0) continue; C1= SQR(freq1)/(SQR(freq1)-SQR(freq2)); C2=-SQR(freq2)/(SQR(freq1)-SQR(freq2)); /* estimate bias in meters */ bias[i]=(C1*cp1*CLIGHT/freq1+C2*cp2*CLIGHT/freq2)-(C1*pr1+C2*pr2); } ib=tc?xiBs(&rtk->opt.insopt,sat[i],k):IB(sat[i],k,&rtk->opt); if(x[ib]!=0.0) { offset+=bias[i]-rtk->x[ib]; j++; } } /* correct phase-bias offset to enssure phase-code coherency */ if (j>0) { for (i=1;i<=MAXSAT;i++) { ib=tc?xiBs(&rtk->opt.insopt,i,k):IB(i,k,&rtk->opt); if (x[ib]!=0.0){ x[ib]+=offset/j; } } } /* set initial states of phase-bias */ for (i=0;iopt.insopt,sat[i],k):IB(sat[i],k,&rtk->opt); if (bias[i]==0.0||x[ib]!=0.0) continue; initx(rtk,bias[i],SQR(rtk->opt.std[0]),ib); trace(3," sat=%3d, F=%d: init phase=%.3f\n",sat[i],k+1,bias[i]); rtk->ssat[sat[i]-1].lock[k]=-rtk->opt.minlock; } free(bias); } } /* temporal update of states --------------------------------------------------*/ static void udstate(rtk_t *rtk, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav) { double tt=rtk->tt,bl,dr[3]; int tc; trace(3,"udstate : ns=%d\n",ns); tc=rtk->opt.mode==PMODE_INS_TGNSS?1:0; /* temporal update of position/velocity/acceleration */ if(!tc){ udpos(rtk,tt); } /* temporal update of ionospheric parameters */ if (rtk->opt.ionoopt>=IONOOPT_EST) { bl=baseline(rtk->x,rtk->rb,dr); udion(rtk,tt,bl,sat,ns); } /* temporal update of tropospheric parameters */ if (rtk->opt.tropopt>=TROPOPT_EST) { udtrop(rtk,tt,bl); } /* temporal update of receiver h/w bias */ if (rtk->opt.glomodear==GLO_ARMODE_AUTOCAL&&(rtk->opt.navsys&SYS_GLO)) { udrcvbias(rtk,tt); } /* temporal update of phase-bias */ if (rtk->opt.mode>PMODE_DGPS) { udbias(rtk,tt,obs,sat,iu,ir,ns,nav); } } /* UD (undifferenced) phase/code residual for satellite ----------------------*/ static void zdres_sat(int base, double r, const obsd_t *obs, const nav_t *nav, const double *azel, const double *dant, const prcopt_t *opt, double *y, double *freq) { double freq1,freq2,C1,C2,dant_if; int i,nf=NF(opt),f2; if (opt->ionoopt==IONOOPT_IFLC) { /* iono-free linear combination */ freq1=sat2freq(obs->sat,obs->code[0],nav); f2=seliflc(opt->nf,satsys(obs->sat,NULL)); freq2=sat2freq(obs->sat,obs->code[f2],nav); if (freq1==0.0||freq2==0.0) return; if (testsnr(base,0,azel[1],obs->SNR[0]*SNR_UNIT,&opt->snrmask)|| testsnr(base,f2,azel[1],obs->SNR[f2]*SNR_UNIT,&opt->snrmask)) return; C1= SQR(freq1)/(SQR(freq1)-SQR(freq2)); C2=-SQR(freq2)/(SQR(freq1)-SQR(freq2)); dant_if=C1*dant[0]+C2*dant[f2]; if (obs->L[0]!=0.0&&obs->L[f2]!=0.0) { y[0]=C1*obs->L[0]*CLIGHT/freq1+C2*obs->L[f2]*CLIGHT/freq2-r-dant_if; } if (obs->P[0]!=0.0&&obs->P[f2]!=0.0) { y[nf]=C1*obs->P[0]+C2*obs->P[f2]-r-dant_if; } freq[0]=1.0; } else { for (i=0;isat,obs->code[i],nav))==0.0) continue; /* check SNR mask */ if (testsnr(base,i,azel[1],obs->SNR[i]*SNR_UNIT,&opt->snrmask)) { continue; } /* residuals = observable - estimated range */ if (obs->L[i]!=0.0) y[i ]=obs->L[i]*CLIGHT/freq[i]-r-dant[i]; if (obs->P[i]!=0.0) y[i+nf]=obs->P[i] -r-dant[i]; trace(4,"zdres_sat: %d: %.6f %.6f %.6f %.6f\n",obs->sat,obs->L[i],obs->P[i],r,dant[i]); } } } /* undifferenced phase/code residuals ---------------------------------------- calculate zero diff residuals [observed pseudorange - range] output is in y[0:nu-1], only shared input with base is nav args: I base: 0=rover,1=base I obs = sat observations I n = # of sats I rs [(0:2)+i*6]= sat position {x,y,z} (m) I dts[(0:1)+i*2]= sat clock {bias,drift} (s|s/s) I var = variance of ephemeris I svh = sat health flags I nav = sat nav data I rr = rcvr pos (x,y,z) I opt = options I index: 0=rover,1=base O y[(0:1)+i*2] = zero diff residuals {phase,code} (m) O e = line of sight unit vectors to sats O azel = [az, el] to sats */ static int zdres(int base, const obsd_t *obs, int n, const double *rs, const double *dts, const double *var, const int *svh, const nav_t *nav, const double *rr, const prcopt_t *opt, int index, double *y, double *e, double *azel, double *freq) { double r,rr_[3],pos[3],dant[NFREQ]={0},disp[3]; double mapfh,zhd,zazel[]={0.0,90.0*D2R}; int i,nf=NF(opt); trace(3,"zdres : n=%d rr=%.2f %.2f %.2f\n",n,rr[0], rr[1], rr[2]); /* init residuals to zero */ for (i=0;itidecorr) { tidedisp(gpst2utc(obs[0].time),rr_,opt->tidecorr,&nav->erp, opt->odisp[base],disp); for (i=0;i<3;i++) rr_[i]+=disp[i]; } /* translate rcvr pos from ecef to geodetic */ ecef2pos(rr_,pos); /* loop through satellites */ for (i=0;ielmin) continue; /* excluded satellite? */ if (satexclude(obs[i].sat,var[i],svh[i],opt)) continue; /* adjust range for satellite clock-bias */ r+=-CLIGHT*dts[i*2]; /* adjust range for troposphere delay model (hydrostatic) */ zhd=tropmodel(obs[0].time,pos,zazel,0.0); mapfh=tropmapf(obs[i].time,pos,azel+i*2,NULL); r+=mapfh*zhd; /* calc receiver antenna phase center correction */ antmodel(opt->pcvr+index,opt->antdel[index],azel+i*2,opt->posopt[1], dant); /* calc undifferenced phase/code residual for satellite */ trace(4,"sat=%d %.6f %.6f %.6f %.6f\n",obs[i].sat,r,CLIGHT*dts[i*2],zhd,mapfh); zdres_sat(base,r,obs+i,nav,azel+i*2,dant,opt,y+i*nf*2,freq+i*nf); } trace(4,"rr_=%.3f %.3f %.3f\n",rr_[0],rr_[1],rr_[2]); trace(4,"pos=%.9f %.9f %.3f\n",pos[0]*R2D,pos[1]*R2D,pos[2]); for (i=0;iopt.baseline[0]<=0.0) return 0; /* time-adjusted baseline vector and length */ for (i=0;i<3;i++) { xb[i]=rtk->rb[i]; b[i]=x[i]-xb[i]; } bb=norm(b,3); /* approximate variance of solution */ if (P) { for (i=0;i<3;i++) var+=P[i+i*rtk->nx]; var/=3.0; } /* check nonlinearity */ if (var>SQR(thres*bb)) { trace(3,"constbl : equation nonlinear (bb=%.3f var=%.3f)\n",bb,var); /* return 0; */ /* threshold too strict for all use cases, report error but continue on */ } /* constraint to baseline length */ v[index]=rtk->opt.baseline[0]-bb; if (H) { for (i=0;i<3;i++) H[i+index*rtk->nx]=b[i]/bb; } Ri[index]=0.0; Rj[index]=SQR(rtk->opt.baseline[1]); trace(4,"baseline len v=%13.3f R=%8.6f %8.6f\n",v[index],Ri[index],Rj[index]); return 1; } /* precise tropspheric model -------------------------------------------------*/ static double prectrop(gtime_t time, const double *pos, int r, const double *azel, const prcopt_t *opt, const double *x, double *dtdx) { double m_w=0.0,cotz,grad_n,grad_e; int i=IT(r,opt); /* wet mapping function */ tropmapf(time,pos,azel,&m_w); if (opt->tropopt>=TROPOPT_ESTG&&azel[1]>0.0) { /* m_w=m_0+m_0*cot(el)*(Gn*cos(az)+Ge*sin(az)): ref [6] */ cotz=1.0/tan(azel[1]); grad_n=m_w*cotz*cos(azel[0]); grad_e=m_w*cotz*sin(azel[0]); m_w+=grad_n*x[i+1]+grad_e*x[i+2]; dtdx[1]=grad_n*x[i]; dtdx[2]=grad_e*x[i]; } else dtdx[1]=dtdx[2]=0.0; dtdx[0]=m_w; return m_w*x[i]; } /* test satellite system (m=0:GPS/SBS,1:GLO,2:GAL,3:BDS,4:QZS,5:IRN) ---------*/ static int test_sys(int sys, int m) { switch (sys) { case SYS_GPS: return m==0; case SYS_SBS: return m==0; case SYS_GLO: return m==1; case SYS_GAL: return m==2; case SYS_CMP: return m==3; case SYS_QZS: return m==4; case SYS_IRN: return m==5; } return 0; } /* jacobian of double-differncee phase/code by ins position-------------------*/ static void jacob_dd_dp(const ins_t *ins,const double *ei,const double *ej, double *dddp) { dddp[0]=ei[0]-ej[0]; dddp[1]=ei[1]-ej[1]; dddp[2]=ei[2]-ej[2]; } /* double-differenced residuals and partial derivatives ----------------------------------- O rtk->ssat[i].resp[j] = residual pseudorange error O rtk->ssat[i].resc[j] = residual carrier phase error I rtk->rb= base location I nav = sat nav data I dt = time diff between base and rover observations I x = rover pos & vel and sat phase biases (float solution) I P = error covariance matrix of float states I sat = list of common sats I y = zero diff residuals (code and phase, base and rover) I e = line of sight unit vectors to sats I azel = [az, el] to sats I iu,ir = user and ref indices to sats I ns = # of sats O v = double diff innovations (measurement-model) (phase and code) O H = linearized translation from innovations to states (az/el to sats) O R = measurement error covariances O vflg = bit encoded list of sats used for each double diff */ static int ddres(rtk_t *rtk, const nav_t *nav, const obsd_t *obs, double dt, const double *x, const double *P, const int *sat, double *y, double *e, double *azel, double *freq, const int *iu, const int *ir, int ns, double *v, double *H, double *R, int *vflg) { prcopt_t *opt=&rtk->opt; double bl,dr[3],posu[3],posr[3],didxi=0.0,didxj=0.0,*im,icb,threshadj; double *tropr,*tropu,*dtdxr,*dtdxu,*Ri,*Rj,freqi,freqj,*Hi=NULL,df,rr[3],dp[3]={0}; int i,j,k,m,f,nv=0,nb[NFREQ*4*2+2]={0},b=0,sysi,sysj,nf=NF(opt); int ii,jj,frq,code,nx,tc; trace(3,"ddres : dt=%.1f nx=%d ns=%d\n",dt,rtk->nx,ns); /* tc=0: common rtk mode * tc=1: tightly coupled mode * */ tc=opt->mode==PMODE_INS_TGNSS?1:0; if(tc){ ins_2_ant(&rtk->ins,rr); nx=rtk->ins.nx; } else{ matcpy(rr,x,1,3); nx=rtk->nx; } /* bl=distance from base to rover, dr=x,y,z components */ bl=baseline(rr,rtk->rb,dr); /* translate ecef pos to geodetic pos */ ecef2pos(rr,posu); ecef2pos(rtk->rb,posr); Ri=mat(ns*nf*2+2,1); Rj=mat(ns*nf*2+2,1); im=mat(ns,1); tropu=mat(ns,1); tropr=mat(ns,1); dtdxu=mat(ns,3); dtdxr=mat(ns,3); /* zero out residual phase and code biases for all satellites */ for (i=0;issat[i].resp[j]=rtk->ssat[i].resc[j]=0.0; } } /* compute factors of ionospheric and tropospheric delay - only used if kalman filter contains states for ION and TROP delays usually insignificant for short baselines (<10km)*/ for (i=0;iionoopt>=IONOOPT_EST) { im[i]=(ionmapf(posu,azel+iu[i]*2)+ionmapf(posr,azel+ir[i]*2))/2.0; } if (opt->tropopt>=TROPOPT_EST) { tropu[i]=prectrop(rtk->sol.time,posu,0,azel+iu[i]*2,opt,x,dtdxu+i*3); tropr[i]=prectrop(rtk->sol.time,posr,1,azel+ir[i]*2,opt,x,dtdxr+i*3); } } /* step through sat systems: m=0:gps/sbs,1:glo,2:gal,3:bds 4:qzs 5:irn*/ for (m=0;m<6;m++) { /* step through phases/codes */ for (f=opt->mode>PMODE_DGPS?0:nf;fssat[sat[j]-1].sys; if (!test_sys(sysi,m) || sysi==SYS_SBS) continue; if (!validobs(iu[j],ir[j],f,nf,y)) continue; /* skip sat with slip unless no other valid sat */ if (i>=0&&rtk->ssat[sat[j]-1].slip[frq]&LLI_SLIP) continue; if (i<0||azel[1+iu[j]*2]>=azel[1+iu[i]*2]) i=j; } if (i<0) continue; /* calculate double differences of residuals (code/phase) for each sat */ for (j=0;jssat[sat[i]-1].sys; sysj=rtk->ssat[sat[j]-1].sys; freqi=freq[frq+iu[i]*nf]; freqj=freq[frq+iu[j]*nf]; if (freqi<=0.0||freqj<=0.0) continue; if (!test_sys(sysj,m)) continue; if (!validobs(iu[j],ir[j],f,nf,y)) continue; if (H) { Hi=H+nv*nx; for (k=0;kins,&e[iu[i]*3],&e[iu[j]*3],dp); Hi[xiP(&rtk->opt.insopt)+0]=dp[0]; Hi[xiP(&rtk->opt.insopt)+1]=dp[1]; Hi[xiP(&rtk->opt.insopt)+2]=dp[2]; } /* -DE */ for (k=0;k<3;k++) { Hi[k]=-e[k+iu[i]*3]+e[k+iu[j]*3]; /* translation of innovation to position states */ } } if (opt->ionoopt==IONOOPT_EST) { /* adjust double-differenced measurements by double-differenced ionospheric delay term */ ii=tc?xiIo(&rtk->opt.insopt,sat[i]):II(sat[i],opt); jj=tc?xiIo(&rtk->opt.insopt,sat[j]):II(sat[j],opt); didxi=(code?-1.0:1.0)*im[i]*SQR(FREQL1/freqi); didxj=(code?-1.0:1.0)*im[j]*SQR(FREQL1/freqj); v[nv]-=didxi*x[ii]-didxj*x[jj]; if (H) { Hi[II(sat[i],opt)]= didxi; Hi[II(sat[j],opt)]=-didxj; } } if (opt->tropopt==TROPOPT_EST||opt->tropopt==TROPOPT_ESTG) { /* adjust double-differenced measurements by double-differenced tropospheric delay term */ v[nv]-=(tropu[i]-tropu[j])-(tropr[i]-tropr[j]); for (k=0;k<(opt->tropoptopt.insopt,0):IT(0,opt); jj=tc?xiTr(&rtk->opt.insopt,1):IT(1,opt); Hi[ii+k]= (dtdxu[k+i*3]-dtdxu[k+j*3]); Hi[jj+k]=-(dtdxr[k+i*3]-dtdxr[k+j*3]); } } /* index of phase bias */ ii=tc?xiBs(&rtk->opt.insopt,sat[i],frq):IB(sat[i],frq,opt); jj=tc?xiBs(&rtk->opt.insopt,sat[j],frq):IB(sat[j],frq,opt); if (!code) { /* adjust phase residual by double-differenced phase-bias term, IB=look up index by sat&freq */ if (opt->ionoopt!=IONOOPT_IFLC) { /* phase-bias states are single-differenced so need to difference them */ v[nv]-=CLIGHT/freqi*x[ii]-CLIGHT/freqj*x[jj]; if (H) { Hi[ii]= CLIGHT/freqi; Hi[jj]=-CLIGHT/freqj; } } else { v[nv]-=x[ii]-x[jj]; if (H) { Hi[ii]= 1.0; Hi[jj]=-1.0; } } } /* adjust double-difference for glonass sats */ if (sysi==SYS_GLO&&sysj==SYS_GLO) { if (rtk->opt.glomodear==GLO_ARMODE_AUTOCAL && frqopt.glomodear==GLO_ARMODE_FIXHOLD && frqssat[sat[i]-1].icbias[frq]*CLIGHT/freqi - rtk->ssat[sat[j]-1].icbias[frq]*CLIGHT/freqj; v[nv]-=icb; } } /* adjust double-difference for sbas sats */ if (sysj==SYS_SBS&&sysi==SYS_GPS) { if (rtk->opt.glomodear==GLO_ARMODE_FIXHOLD && frqssat[sat[i]-1].icbias[frq]*CLIGHT/freqi - rtk->ssat[sat[j]-1].icbias[frq]*CLIGHT/freqj; v[nv]-=icb; } } /* save residuals */ if (code) rtk->ssat[sat[j]-1].resp[frq]=v[nv]; /* pseudorange */ else rtk->ssat[sat[j]-1].resc[frq]=v[nv]; /* carrier phase */ double *P_; P_=tc?rtk->ins.P:rtk->P; /* if residual too large, flag as outlier */ /* adjust threshold by error stdev ratio unless one of the phase biases was just initialized*/ threshadj=code||(P_[ii+nx*ii]==SQR(rtk->opt.std[0]))|| (P_[jj+nx*jj]==SQR(rtk->opt.std[0]))?opt->eratio[frq]:1; if (opt->maxinno>0.0&&fabs(v[nv])>opt->maxinno*threshadj) { rtk->ssat[sat[j]-1].vsat[frq]=0; rtk->ssat[sat[j]-1].rejc[frq]++; errmsg(rtk,"outlier rejected (sat=%3d-%3d %s%d v=%.3f)\n", sat[i],sat[j],code?"P":"L",frq+1,v[nv]); continue; } /* single-differenced measurement error variances (m) */ Ri[nv] = varerr(sat[i], sysi, azel[1+iu[i]*2], SNR_UNIT*rtk->ssat[sat[i]-1].snr_rover[frq], SNR_UNIT*rtk->ssat[sat[i]-1].snr_base[frq], bl,dt,f,opt,&obs[iu[i]]); Rj[nv] = varerr(sat[j], sysj, azel[1+iu[j]*2], SNR_UNIT*rtk->ssat[sat[j]-1].snr_rover[frq], SNR_UNIT*rtk->ssat[sat[j]-1].snr_base[frq], bl,dt,f,opt,&obs[iu[j]]); /* set valid data flags */ if (opt->mode>PMODE_DGPS) { if (!code) rtk->ssat[sat[i]-1].vsat[frq]=rtk->ssat[sat[j]-1].vsat[frq]=1; } else { rtk->ssat[sat[i]-1].vsat[frq]=rtk->ssat[sat[j]-1].vsat[frq]=1; } if (rtk->opt.glomodear==GLO_ARMODE_AUTOCAL) icb=x[IL(frq,opt)]; else icb=rtk->ssat[sat[i]-1].icbias[frq]*CLIGHT/freqi - rtk->ssat[sat[j]-1].icbias[frq]*CLIGHT/freqj; trace(3,"sat=%3d-%3d %s%d v=%13.3f R=%9.6f %9.6f icb=%9.3f lock=%5d x=%9.3f\n",sat[i], sat[j],code?"P":"L",frq+1,v[nv],Ri[nv],Rj[nv],icb, rtk->ssat[sat[j]-1].lock[frq],tc?rtk->ins.x[xiBs(&rtk->opt.insopt,sat[j],frq)]:rtk->x[IB(sat[j],frq,&rtk->opt)]); vflg[nv++]=(sat[i]<<16)|(sat[j]<<8)|((code?1:0)<<4)|(frq); nb[b]++; } b++; } } /* end of system loop */ /* baseline length constraint for moving baseline */ if (opt->mode==PMODE_MOVEB&&constbl(rtk,x,P,v,H,Ri,Rj,nv)) { vflg[nv++]=3<<4; nb[b++]++; } if (H) {trace(5,"H=\n"); tracemat(5,H,nx,nv,7,4);} /* double-differenced measurement error covariance */ ddcov(nb,b,Ri,Rj,nv,R); free(Ri); free(Rj); free(im); free(tropu); free(tropr); free(dtdxu); free(dtdxr); return nv; } /* time-interpolation of residuals (for post-processing solutions) -----------*/ static double intpres(gtime_t time, const obsd_t *obs, int n, const nav_t *nav, rtk_t *rtk, double *y) { static obsd_t obsb[MAXOBS]; static double yb[MAXOBS*NFREQ*2],rs[MAXOBS*6],dts[MAXOBS*2],var[MAXOBS]; static double e[MAXOBS*3],azel[MAXOBS*2],freq[MAXOBS*NFREQ]; static int nb=0,svh[MAXOBS*2]; prcopt_t *opt=&rtk->opt; double tt=timediff(time,obs[0].time),ttb,*p,*q; int i,j,k,nf=NF(opt); trace(3,"intpres : n=%d tt=%.1f\n",n,tt); /* skip interpolation if delta time very small or > max age of diff */ if (nb==0||fabs(tt)opt->maxtdiff*2.0||ttb==tt) return tt; satposs(time,obsb,nb,nav,opt->sateph,rs,dts,var,svh); if (!zdres(1,obsb,nb,rs,dts,var,svh,nav,rtk->rb,opt,1,yb,e,azel,freq)) { return tt; } for (i=0;i=nb) continue; for (k=0,p=y+i*nf*2,q=yb+j*nf*2;kfabs(tt)?ttb:tt; } /* index for single to double-difference transformation matrix (D') --------------------*/ static int ddidx(rtk_t *rtk, int *ix, int gps, int glo, int sbs) { int i,j,k,m,f,n,nb=0,na=rtk->na,nf=NF(&rtk->opt),nofix; double fix[MAXSAT],ref[MAXSAT]; trace(3,"ddmat: gps=%d/%d glo=%d/%d sbs=%d\n",gps,rtk->opt.gpsmodear,glo,rtk->opt.glomodear,sbs); /* clear fix flag for all sats (1=float, 2=fix) */ for (i=0;issat[i].fix[j]=0; } for (m=0;m<6;m++) { /* m=0:GPS/SBS,1:GLO,2:GAL,3:BDS,4:QZS,5:IRN */ /* skip if ambiguity resolution turned off for this sys */ nofix=(m==0&&gps==0)||(m==1&&glo==0)||(m==3&&rtk->opt.bdsmodear==0); /* step through freqs */ for (f=0,k=na;fx[i]==0.0||!test_sys(rtk->ssat[i-k].sys,m)|| !rtk->ssat[i-k].vsat[f]) { continue; } /* set sat to use for fixing ambiguity if meets criteria */ if (rtk->ssat[i-k].lock[f]>=0&&!(rtk->ssat[i-k].slip[f]&2)&& rtk->ssat[i-k].azel[1]>=rtk->opt.elmaskar&&!nofix) { rtk->ssat[i-k].fix[f]=2; /* fix */ break;/* break out of loop if find good sat */ } /* else don't use this sat for fixing ambiguity */ else rtk->ssat[i-k].fix[f]=1; } if (rtk->ssat[i-k].fix[f]!=2) continue; /* no good sat found */ /* step through all sats (j=state index, j-k=sat index, i-k=first good sat) */ for (n=0,j=k;jx[j]==0.0||!test_sys(rtk->ssat[j-k].sys,m)|| !rtk->ssat[j-k].vsat[f]) { continue; } if (sbs==0 && satsys(j-k+1,NULL)==SYS_SBS) continue; if (rtk->ssat[j-k].lock[f]>=0&&!(rtk->ssat[j-k].slip[f]&2)&& rtk->ssat[j-k].vsat[f]&& rtk->ssat[j-k].azel[1]>=rtk->opt.elmaskar&&!nofix) { /* set D coeffs to subtract sat j from sat i */ ix[nb*2 ]=i; /* state index of ref bias */ ix[nb*2+1]=j; /* state index of target bias */ /* inc # of sats used for fix */ ref[nb]=i-k+1; fix[nb++]=j-k+1; rtk->ssat[j-k].fix[f]=2; /* fix */ n++; /* count # of sat pairs for this freq/constellation */ } /* else don't use this sat for fixing ambiguity */ else rtk->ssat[j-k].fix[f]=1; } /* don't use ref sat if no sat pairs */ if (n==0) rtk->ssat[i-k].fix[f]=1; } } if (nb>0) { trace(3,"refSats=");tracemat(3,ref,1,nb,7,0); trace(3,"fixSats=");tracemat(3,fix,1,nb,7,0); } return nb; } /* translate double diff fixed phase-bias values to single diff fix phase-bias values */ static void restamb(rtk_t *rtk, const double *bias, int nb, double *xa) { int i,n,m,f,index[MAXSAT],nv=0,nf=NF(&rtk->opt); trace(3,"restamb :\n"); for (i=0;inx;i++) xa[i]=rtk->x [i]; /* init all fixed states to float state values */ for (i=0;ina;i++) xa[i]=rtk->xa[i]; /* overwrite non phase-bias states with fixed values */ for (m=0;m<6;m++) for (f=0;fssat[i].sys,m)||rtk->ssat[i].fix[f]!=2) { continue; } index[n++]=IB(i+1,f,&rtk->opt); } if (n<2) continue; xa[index[0]]=rtk->x[index[0]]; for (i=1;inx-rtk->na,nv=0,nf=NF(&rtk->opt); double dd,sum; trace(3,"holdamb :\n"); v=mat(nb,1); H=zeros(nb,rtk->nx); for (m=0;m<6;m++) for (f=0;fssat[i].sys,m)||rtk->ssat[i].fix[f]!=2|| rtk->ssat[i].azel[1]opt.elmaskhold) { continue; } index[n++]=IB(i+1,f,&rtk->opt); rtk->ssat[i].fix[f]=3; /* hold */ } /* use ambiguity resolution results to generate a set of pseudo-innovations to feed to kalman filter based on error between fixed and float solutions */ for (i=1;ix[index[0]]-rtk->x[index[i]]); H[index[0]+nv*rtk->nx]= 1.0; H[index[i]+nv*rtk->nx]=-1.0; nv++; } } /* return if less than min sats for hold (skip if fix&hold for GLONASS only) */ if (rtk->opt.modear==ARMODE_FIXHOLD&&nvopt.minholdsats) { trace(3,"holdamb: not enough sats to hold ambiguity\n"); free(v); free(H); return; } rtk->holdamb=1; /* set flag to indicate hold has occurred */ R=zeros(nv,nv); for (i=0;iopt.varholdamb; /* update states with constraints */ if ((info=filter(rtk->x,rtk->P,H,v,R,rtk->nx,nv))) { errmsg(rtk,"filter error (info=%d)\n",info); } free(R);free(v); free(H); /* skip glonass/sbs icbias update if not enabled */ if (rtk->opt.glomodear!=GLO_ARMODE_FIXHOLD) return; /* Move fractional part of bias from phase-bias into ic bias for GLONASS sats (both in cycles) */ for (f=0;fssat[j].sys,1)&&rtk->ssat[j].vsat[f]&&rtk->ssat[j].lock[f]>=0) { if (i<0) { i=j; /* use first valid sat for reference sat */ index[nv++]=j; } else { /* adjust the rest */ /* find phase-bias difference */ dd=rtk->x[IB(j+1,f,&rtk->opt)]-rtk->x[IB(i+1,f,&rtk->opt)]; dd=rtk->opt.gainholdamb*(dd-ROUND(dd)); /* throwout integer part of answer and multiply by filter gain */ rtk->x[IB(j+1,f,&rtk->opt)]-=dd; /* remove fractional part from phase bias */ rtk->ssat[j].icbias[f]+=dd; /* and move to IC bias */ sum+=dd; index[nv++]=j; } } } } /* Move fractional part of bias from phase-bias into ic bias for SBAS sats (both in cycles) */ for (f=0;fssat[j].sys,0)&&rtk->ssat[j].vsat[f]&&rtk->ssat[j].lock[f]>=0) { if (i<0) { i=j; /* use first valid GPS sat for reference sat */ index[nv++]=j; } else { /* adjust the SBS sats */ if (rtk->ssat[j].sys!=SYS_SBS) continue; /* find phase-bias difference */ dd=rtk->x[IB(j+1,f,&rtk->opt)]-rtk->x[IB(i+1,f,&rtk->opt)]; dd=rtk->opt.gainholdamb*(dd-ROUND(dd)); /* throwout integer part of answer and multiply by filter gain */ rtk->x[IB(j+1,f,&rtk->opt)]-=dd; /* remove fractional part from phase bias diff */ rtk->ssat[j].icbias[f]+=dd; /* and move to IC bias */ sum+=dd; index[nv++]=j; } } } } } /* resolve integer ambiguity by LAMBDA ---------------------------------------*/ static int resamb_LAMBDA(rtk_t *rtk, double *bias, double *xa,int gps,int glo,int sbs) { prcopt_t *opt=&rtk->opt; int i,j,nb,nb1,info,nx=rtk->nx,na=rtk->na; double *DP,*y,*b,*db,*Qb,*Qab,*QQ,s[2]; int *ix; double coeff[3]; double QQb[MAXSAT]; trace(3,"resamb_LAMBDA : nx=%d\n",nx); rtk->sol.ratio=0.0; rtk->nb_ar=0; /* Create index of single to double-difference transformation matrix (D') used to translate phase biases to double difference */ ix=imat(nx,2); if ((nb=ddidx(rtk,ix,gps,glo,sbs))<(rtk->opt.minfixsats-1)) { /* nb is sat pairs */ errmsg(rtk,"not enough valid double-differences\n"); free(ix); return -1; /* flag abort */ } rtk->nb_ar=nb; /* nx=# of float states, na=# of fixed states, nb=# of double-diff phase biases */ y=mat(nb,1); DP=mat(nb,nx-na); b=mat(nb,2); db=mat(nb,1); Qb=mat(nb,nb); Qab=mat(na,nb); QQ=mat(na,nb); /* phase-bias covariance (Qb) and real-parameters to bias covariance (Qab) */ /* y=D*xc, Qb=D*Qc*D', Qab=Qac*D' */ for (i=0;ix[ix[i*2]]-rtk->x[ix[i*2+1]]; } for (j=0;jP[ix[i*2]+(na+j)*nx]-rtk->P[ix[i*2+1]+(na+j)*nx]; } for (j=0;jP[i+ix[j*2]*nx]-rtk->P[i+ix[j*2+1]*nx]; } for (i=0;ixa); /* rtk->xa = rtk->x-Qab*db */ /* rtk->Pa=rtk->P-Qab*Qb^-1*Qab') */ /* covariance of fixed solution (Qa=Qa-Qab*Qb^-1*Qab') */ matmul("NN",na,nb,nb, 1.0,Qab,Qb ,0.0,QQ); /* QQ = Qab*Qb^-1 */ matmul("NT",na,na,nb,-1.0,QQ ,Qab,1.0,rtk->Pa); /* rtk->Pa = rtk->P-QQ*Qab' */ trace(3,"resamb : validation ok (nb=%d ratio=%.2f thresh=%.2f s=%.2f/%.2f)\n", nb,s[0]==0.0?0.0:s[1]/s[0],rtk->sol.thres,s[0],s[1]); /* translate double diff fixed phase-bias values to single diff fix phase-bias values, result in xa */ restamb(rtk,bias,nb,xa); } else nb=0; } else { /* validation failed */ errmsg(rtk,"ambiguity validation failed (nb=%d ratio=%.2f thresh=%.2f s=%.2f/%.2f)\n", nb,s[1]/s[0],rtk->sol.thres,s[0],s[1]); nb=0; } } else { errmsg(rtk,"lambda error (info=%d)\n",info); nb=0; } free(ix); free(y); free(DP); free(b); free(db); free(Qb); free(Qab); free(QQ); return nb; /* number of ambiguities */ } /* resolve integer ambiguity by LAMBDA using partial fix techniques and multiple attempts -----------------------*/ static int manage_amb_LAMBDA(rtk_t *rtk, double *bias, double *xa, const int *sat, int nf, int ns) { int i,f,lockc[NFREQ],ar=0,excflag=0,arsats[MAXOBS]={0}; int gps1=-1,glo1=-1,sbas1=-1,gps2,glo2,sbas2,nb,rerun,dly; float ratio1,var=0,posvar=0; /* calc position variance, will skip AR if too high to avoid false fix */ for (i=0;i<3;i++) posvar+=rtk->P[i+i*rtk->nx]; posvar/=3.0; /* maintain compatibility with previous code */ trace(3,"posvar=%.6f\n",posvar); trace(3,"prevRatios= %.3f %.3f\n",rtk->sol.prev_ratio1,rtk->sol.prev_ratio2); trace(3,"num ambiguities used last AR: %d\n",rtk->nb_ar); /* skip AR if don't meet criteria */ if (rtk->opt.mode<=PMODE_DGPS||rtk->opt.modear==ARMODE_OFF|| rtk->opt.thresar[0]<1.0||posvar>rtk->opt.thresar[1]) { trace(3,"Skip AR\n"); rtk->sol.ratio=0.0; rtk->sol.prev_ratio1=rtk->sol.prev_ratio2=0.0; rtk->nb_ar=0; return 0; } /* if no fix on previous sample and enough sats, exclude next sat in list */ if (rtk->sol.prev_ratio2sol.thres&&rtk->nb_ar>=rtk->opt.mindropsats) { /* find and count sats used last time for AR */ for (f=0;fssat[sat[i]-1].vsat[f] && rtk->ssat[sat[i]-1].lock[f]>=0 && rtk->ssat[sat[i]-1].azel[1]>=rtk->opt.elmin) { arsats[ar++]=i; } if (rtk->excsatexcsat]]; for (f=0;fssat[i-1].lock[f]; /* save lock count */ /* remove sat from AR long enough to enable hold if stays fixed */ rtk->ssat[i-1].lock[f]=-rtk->nb_ar; } trace(3,"AR: exclude sat %d\n",i); excflag=1; } else rtk->excsat=0; /* exclude none and reset to beginning of list */ } /* for inital ambiguity resolution attempt, include all enabled sats */ gps1=1; /* always enable gps for initial pass */ glo1=(rtk->opt.navsys&SYS_GLO)?(((rtk->opt.glomodear==GLO_ARMODE_FIXHOLD)&&!rtk->holdamb)?0:1):0; sbas1=(rtk->opt.navsys&SYS_GLO)?glo1:((rtk->opt.navsys&SYS_SBS)?1:0); /* first attempt to resolve ambiguities */ nb=resamb_LAMBDA(rtk,bias,xa,gps1,glo1,sbas1); ratio1=rtk->sol.ratio; /* reject bad satellites if AR filtering enabled */ if (rtk->opt.arfilter) { rerun=0; /* if results are much poorer than previous epoch or dropped below ar ratio thresh, remove new sats */ if (nb>=0 && rtk->sol.prev_ratio2>=rtk->sol.thres && ((rtk->sol.ratiosol.thres) || (rtk->sol.ratioopt.thresar[0]*1.1 && rtk->sol.ratiosol.prev_ratio1/2.0))) { trace(3,"low ratio: check for new sat\n"); dly=2; for (i=0;issat[sat[i]-1].fix[f]!=2) continue; /* check for new sats */ if (rtk->ssat[sat[i]-1].lock[f]==0) { trace(3,"remove sat %d:%d lock=%d\n",sat[i],f,rtk->ssat[sat[i]-1].lock[f]); rtk->ssat[sat[i]-1].lock[f]=-rtk->opt.minlock-dly; /* delay use of this sat with stagger */ dly+=2; /* stagger next try of new sats */ rerun=1; } } } /* rerun if filter removed any sats */ if (rerun) { trace(3,"rerun AR with new sat removed\n"); /* try again with new sats removed */ nb=resamb_LAMBDA(rtk,bias,xa,gps1,glo1,sbas1); } } rtk->sol.prev_ratio1=ratio1; /* if fix-and-hold gloarmode enabled, re-run AR with final gps/glo settings if differ from above */ if ((rtk->opt.navsys&SYS_GLO) && rtk->opt.glomodear==GLO_ARMODE_FIXHOLD && rtk->sol.ratiosol.thres) { glo2=sbas2=0; /* turn off gpsmode if not enabled and got good fix (used for debug and eval only) */ gps2=rtk->opt.gpsmodear==0&&rtk->sol.ratio>=rtk->sol.thres?0:1; /* if modes changed since initial AR run or haven't run yet,re-run with new modes */ if (glo1!=glo2||gps1!=gps2) nb=resamb_LAMBDA(rtk,bias,xa,gps2,glo2,sbas2); } /* restore excluded sat if still no fix or significant increase in ar ratio */ if (excflag && (rtk->sol.ratiosol.thres) && (rtk->sol.ratio<(1.5*rtk->sol.prev_ratio2))) { i=sat[arsats[rtk->excsat++]]; for (f=0;fssat[i-1].lock[f]=lockc[f]; trace(3,"AR: restore sat %d\n",i); } rtk->sol.prev_ratio1=ratio1>0?ratio1:rtk->sol.ratio; rtk->sol.prev_ratio2=rtk->sol.ratio; return nb; } /* validation of solution ----------------------------------------------------*/ static int valpos(rtk_t *rtk, const double *v, const double *R, const int *vflg, int nv, double thres) { double fact=thres*thres; int i,stat=1,sat1,sat2,type,freq; char *stype; trace(3,"valpos : nv=%d thres=%.1f\n",nv,thres); /* post-fit residual test */ for (i=0;i>16)&0xFF; sat2=(vflg[i]>> 8)&0xFF; type=(vflg[i]>> 4)&0xF; freq=vflg[i]&0xF; stype=type==0?"L":(type==1?"P":"C"); errmsg(rtk,"large residual (sat=%2d-%2d %s%d v=%6.3f sig=%.3f)\n", sat1,sat2,stype,freq+1,v[i],SQRT(R[i+i*nv])); } return stat; } /* relpos()relative positioning ------------------------------------------------------ * args: rtk IO gps solution structure obs I satellite observations nu I # of user observations (rover) nr I # of ref observations (base) nav I satellite navigation data */ static int relpos(rtk_t *rtk, const obsd_t *obs, int nu, int nr, const nav_t *nav) { prcopt_t *opt=&rtk->opt; gtime_t time=obs[0].time; double *rs,*dts,*var,*y,*e,*azel,*freq,*v,*H,*R,*xp,*Pp,*xa,*bias,dt,*x,*P,rr[3]={0}; int i,j,f,n=nu+nr,ns,ny,nv,sat[MAXSAT],iu[MAXSAT],ir[MAXSAT],niter; int info,vflg[MAXOBS*NFREQ*2+1],svh[MAXOBS*2]; int stat=rtk->opt.mode<=PMODE_DGPS?SOLQ_DGPS:SOLQ_FLOAT; int nf=opt->ionoopt==IONOOPT_IFLC?1:opt->nf; int tc,nx; tc=opt->mode==PMODE_INS_TGNSS?1:0; nx=tc?rtk->ins.nx:rtk->nx; x=tc?rtk->ins.x:rtk->x; P=tc?rtk->ins.P:rtk->P; /* time diff between base and rover observations */ dt=timediff(time,obs[nu].time); trace(3,"relpos : nx=%d dt=%.3f nu=%d nr=%d\n",nx,dt,nu,nr); /* define local matrices, n=total observations, base + rover */ rs=mat(6,n); /* range to satellites */ dts=mat(2,n); /* satellite clock biases */ var=mat(1,n); y=mat(nf*2,n); e=mat(3,n); azel=zeros(2,n); /* [az, el] */ freq=zeros(nf,n); /* init satellite status arrays */ for (i=0;issat[i].sys=satsys(i+1,NULL); /* gnss system */ for (j=0;jssat[i].vsat[j]=0; /* valid satellite */ rtk->ssat[i].snr_rover[j]=0; rtk->ssat[i].snr_base [j]=0; } } /* compute satellite positions, velocities and clocks */ satposs(time,obs,n,nav,opt->sateph,rs,dts,var,svh); /* calculate [range - measured pseudorange] for base station (phase and code) output is in y[nu:nu+nr], see call for rover below for more details */ trace(3,"base station:\n"); if (!zdres(1,obs+nu,nr,rs+nu*6,dts+nu*2,var+nu,svh+nu,nav,rtk->rb,opt,1, y+nu*nf*2,e+nu*3,azel+nu*2,freq+nu*nf)) { errmsg(rtk,"initial base station position error\n"); free(rs); free(dts); free(var); free(y); free(e); free(azel); free(freq); return 0; } /* time-interpolation of residuals (for post-processing) */ if (opt->intpref) { dt=intpres(time,obs+nu,nr,nav,rtk,y+nu*nf*2); } /* select common satellites between rover and base-station */ if ((ns=selsat(obs,azel,nu,nr,opt,sat,iu,ir))<=0) { errmsg(rtk,"no common satellite\n"); free(rs); free(dts); free(var); free(y); free(e); free(azel); free(freq); return 0; } /* update kalman filter states (pos,vel,acc,ionosp, troposp, sat phase biases) */ udstate(rtk,obs,sat,iu,ir,ns,nav); trace(4,"x(0)="); // tracemat(4,rtk->x,1,NR(opt),13,4); tracemat(4,x,1,NR(opt),13,4); for (i=0;issat[sat[i]-1].snr_rover[j]=obs[iu[i]].SNR[j]; rtk->ssat[sat[i]-1].snr_base[j] =obs[ir[i]].SNR[j]; } } /* initialize Pp,xa to zero, xp to rtk->x */ xp=mat(nx,1); Pp=zeros(nx,nx); xa=mat(nx,1); // matcpy(xp,rtk->x,nx,1); matcpy(xp,x,nx,1); if (tc) { for (i=0;iopt.insopt);i++) xp[i]=1E-10; } ny=ns*nf*2+2; v=mat(ny,1); H=zeros(nx,ny); R=mat(ny,ny); bias=mat(nx,1); /* add 2 iterations for baseline-constraint moving-base (else default niter=1) */ niter=opt->niter+(opt->mode==PMODE_MOVEB&&opt->baseline[0]>0.0?2:0); /* tc 只进行一次迭代 */ for (i=0;iins,rr); } else{ matcpy(rr,xp,1,3); } /* calculate zero diff residuals [measured pseudorange - range] for rover (phase and code) output is in y[0:nu-1], only shared input with base is nav obs = sat observations nu = # of sats rs = range to sats dts = sat clock biases (rover) svh = sat health flags nav = sat nav data xp = kalman states opt = options y = zero diff residuals (code and phase) e = line of sight unit vectors to sats azel = [az, el] to sats */ trace(3,"rover:\n"); if (!zdres(0,obs,nu,rs,dts,var,svh,nav,rr,opt,0,y,e,azel,freq)) { errmsg(rtk,"rover initial position error\n"); stat=SOLQ_NONE; break; } /* calculate double-differenced residuals and create state matrix from sat angles O rtk->ssat[i].resp[j] = residual pseudorange error O rtk->ssat[i].resc[j] = residual carrier phase error I dt = time diff between base and rover observations I Pp = covariance matrix of float solution I sat = list of common sats I iu,ir = user and ref indices to sats I ns = # of sats O v = double diff residuals (phase and code) O H = partial derivatives O R = double diff measurement error covariances O vflg = list of sats used for dd */ if ((nv=ddres(rtk,nav,obs,dt,xp,Pp,sat,y,e,azel,freq,iu,ir,ns,v,H,R,vflg))<4) { errmsg(rtk,"not enough double-differenced residual, n=%d\n", nv); stat=SOLQ_NONE; break; } /* kalman filter measurement update, updates x,y,z,sat phase biases, etc K=P*H*(H'*P*H+R)^-1 xp=x+K*v Pp=(I-K*H')*P */ matcpy(Pp,P,nx,nx); if ((info=filter(xp,Pp,H,v,R,nx,nv))) { errmsg(rtk,"filter error (info=%d)\n",info); stat=SOLQ_NONE; break; } trace(4,"x(%d)=",i+1); tracemat(4,xp,1,NR(opt),13,4); } /* calc zero diff residuals again after kalman filter update */ if (stat!=SOLQ_NONE&&zdres(0,obs,nu,rs,dts,var,svh,nav,xp,opt,0,y,e,azel,freq)) { /* calc double diff residuals again after kalman filter update for float solution */ nv=ddres(rtk,nav,obs,dt,xp,Pp,sat,y,e,azel,freq,iu,ir,ns,v,NULL,R,vflg); /* validation of float solution, always returns 1, msg to trace file if large residual */ if (valpos(rtk,v,R,vflg,nv,4.0)) { /* update state and covariance matrix from kalman filter update */ matcpy(rtk->x,xp,rtk->nx,1); matcpy(rtk->P,Pp,rtk->nx,rtk->nx); /* update valid satellite status for ambiguity control */ rtk->sol.ns=0; for (i=0;issat[sat[i]-1].vsat[f]) continue; rtk->ssat[sat[i]-1].outc[f]=0; if (f==0) rtk->sol.ns++; /* valid satellite count by L1 */ } /* too few valid phases */ if (rtk->sol.ns<4) stat=SOLQ_DGPS; } else stat=SOLQ_NONE; } /* resolve integer ambiguity by LAMBDA */ if (stat==SOLQ_FLOAT) { /* if valid fixed solution, process it */ if (manage_amb_LAMBDA(rtk,bias,xa,sat,nf,ns)>1) { /* find zero-diff residuals for fixed solution */ if (zdres(0,obs,nu,rs,dts,var,svh,nav,xa,opt,0,y,e,azel,freq)) { /* post-fit residuals for fixed solution (xa includes fixed phase biases, rtk->xa does not) */ nv=ddres(rtk,nav,obs,dt,xa,NULL,sat,y,e,azel,freq,iu,ir,ns,v,NULL,R, vflg); /* validation of fixed solution, always returns valid */ if (valpos(rtk,v,R,vflg,nv,4.0)) { /* hold integer ambiguity if meet minfix count */ if (++rtk->nfix>=rtk->opt.minfix) { if (rtk->opt.modear==ARMODE_FIXHOLD||rtk->opt.glomodear==GLO_ARMODE_FIXHOLD) holdamb(rtk,xa); /* switch to kinematic after qualify for hold if in static-start mode */ if (rtk->opt.mode==PMODE_STATIC_START) { rtk->opt.mode=PMODE_KINEMA; trace(3,"Fix and hold complete: switch to kinematic mode\n"); } } stat=SOLQ_FIX; } } } } /* save solution status (fixed or float) */ if (stat==SOLQ_FIX) { for (i=0;i<3;i++) { rtk->sol.rr[i]=rtk->xa[i]; rtk->sol.qr[i]=(float)rtk->Pa[i+i*rtk->na]; } rtk->sol.qr[3]=(float)rtk->Pa[1]; rtk->sol.qr[4]=(float)rtk->Pa[1+2*rtk->na]; rtk->sol.qr[5]=(float)rtk->Pa[2]; if (rtk->opt.dynamics) { /* velocity and covariance */ for (i=3;i<6;i++) { rtk->sol.rr[i]=rtk->xa[i]; rtk->sol.qv[i-3]=(float)rtk->Pa[i+i*rtk->na]; } rtk->sol.qv[3]=(float)rtk->Pa[4+3*rtk->na]; rtk->sol.qv[4]=(float)rtk->Pa[5+4*rtk->na]; rtk->sol.qv[5]=(float)rtk->Pa[5+3*rtk->na]; } } else { /* float solution */ for (i=0;i<3;i++) { rtk->sol.rr[i]=rtk->x[i]; rtk->sol.qr[i]=(float)rtk->P[i+i*rtk->nx]; } rtk->sol.qr[3]=(float)rtk->P[1]; rtk->sol.qr[4]=(float)rtk->P[1+2*rtk->nx]; rtk->sol.qr[5]=(float)rtk->P[2]; if (rtk->opt.dynamics) { /* velocity and covariance */ for (i=3;i<6;i++) { rtk->sol.rr[i]=rtk->x[i]; rtk->sol.qv[i-3]=(float)rtk->P[i+i*rtk->nx]; } rtk->sol.qv[3]=(float)rtk->P[4+3*rtk->nx]; rtk->sol.qv[4]=(float)rtk->P[5+4*rtk->nx]; rtk->sol.qv[5]=(float)rtk->P[5+3*rtk->nx]; } rtk->nfix=0; trace(3,"sol_rr= ");tracemat(3,rtk->sol.rr,1,6,15,3); } /* save phase measurements */ for (i=0;issat[obs[i].sat-1].pt[obs[i].rcv-1][j]=obs[i].time; rtk->ssat[obs[i].sat-1].ph[obs[i].rcv-1][j]=obs[i].L[j]; } for (i=0;issat[i].fix[j]==2&&stat!=SOLQ_FIX) rtk->ssat[i].fix[j]=1; */ if (rtk->ssat[i].slip[j]&1) rtk->ssat[i].slipc[j]++; /* inc lock count if this sat used for good fix */ if (!rtk->ssat[i].vsat[j]) continue; if (rtk->ssat[i].lock[j]<0||(rtk->nfix>0&&rtk->ssat[i].fix[j]>=2)) rtk->ssat[i].lock[j]++; } free(rs); free(dts); free(var); free(y); free(e); free(azel); free(freq); free(xp); free(Pp); free(xa); free(v); free(H); free(R); free(bias); if (stat!=SOLQ_NONE) rtk->sol.stat=stat; return stat!=SOLQ_NONE; } /* initial ins-gnss coupled ekf estimated states and it covariance----------- * args : insopt_t *opt I ins options * insstate_t *ins IO ins states * return : none * --------------------------------------------------------------------------*/ extern void init_tc(insopt_t *insopt, ins_t *ins, const prcopt_t *opt) { int i; gtime_t t0={0}; trace(3,"initlc:\n"); ins->nx = xnX(insopt); ins->x =mat(ins->nx,1); ins->P =mat(ins->nx,ins->nx); ins->P0=zeros(ins->nx,ins->nx); ins->F =eye (ins->nx); ins->gtime=ins->pgtime=ins->gimu_time=ins->pg_imu_time=ins->ptct=t0; ins->gstat=ins->ns=0; // ins->dtrr=1E-20; ins->rtkp=NULL; for (i=0;inx;i++) ins->x[i]=0.0; for (i=0;i<3;i++) { ins->clk[i]=1E-20; ins->dtrr[i]=1E-20; } rt_insopt_init(insopt); /* lever arm */ matcpy(ins->L_ba_b,insopt->L_ba_b,1,3); /* imu bias */ matcpy(ins->acc_bias,insopt->ba,1,3); matcpy(ins->gyro_bias,insopt->bg,1,3); } /* initialize RTK control ------------------------------------------------------ * initialize RTK control struct * args : rtk_t *rtk IO TKk control/result struct * prcopt_t *opt I positioning options (see rtklib.h) * return : none *-----------------------------------------------------------------------------*/ extern void rtkinit(rtk_t *rtk, const prcopt_t *opt) { sol_t sol0={{0}}; ambc_t ambc0={{{0}}}; ssat_t ssat0={0}; int i; trace(3,"rtkinit :\n"); rtk->sol=sol0; for (i=0;i<6;i++) rtk->rb[i]=0.0; rtk->nx=opt->mode<=PMODE_FIXED?NX(opt):pppnx(opt); rtk->na=opt->mode<=PMODE_FIXED?NR(opt):pppnx(opt); rtk->tt=0.0; rtk->x=zeros(rtk->nx,1); rtk->P=zeros(rtk->nx,rtk->nx); rtk->xa=zeros(rtk->na,1); rtk->Pa=zeros(rtk->na,rtk->na); rtk->nfix=rtk->neb=0; for (i=0;iambc[i]=ambc0; rtk->ssat[i]=ssat0; } rtk->holdamb=0; rtk->excsat=0; rtk->nb_ar=0; for (i=0;ierrbuf[i]=0; rtk->opt=*opt; rtk->initial_mode=rtk->opt.mode; rtk->sol.thres=(float)opt->thresar[0]; rtk->opt.insopt.gopt =&rtk->opt; /* ins */ if(opt->mode<=PMODE_INS_TGNSS){ init_tc(&rtk->opt.insopt,&rtk->ins,opt); rtk->ins.rtkp=rtk; } else{ rtk->ins.x = NULL; rtk->ins.P = NULL; rtk->ins.rtkp=NULL; } } /* free rtk control ------------------------------------------------------------ * free memory for rtk control struct * args : rtk_t *rtk IO rtk control/result struct * return : none *-----------------------------------------------------------------------------*/ extern void rtkfree(rtk_t *rtk) { trace(3,"rtkfree :\n"); rtk->nx=rtk->na=0; free(rtk->x ); rtk->x =NULL; free(rtk->P ); rtk->P =NULL; free(rtk->xa); rtk->xa=NULL; free(rtk->Pa); rtk->Pa=NULL; if (rtk->ins.x ) free(rtk->ins.x ); rtk->ins.x =NULL; if (rtk->ins.P ) free(rtk->ins.P ); rtk->ins.P =NULL; if (rtk->ins.F ) free(rtk->ins.F ); rtk->ins.F =NULL; if (rtk->ins.P0 ) free(rtk->ins.P0 ); rtk->ins.P0 =NULL; rtk->ins.nx=0; } /* precise positioning --------------------------------------------------------- * input observation data and navigation message, compute rover position by * precise positioning * args : rtk_t *rtk IO RTK control/result struct * rtk->sol IO solution * .time O solution time * .rr[] IO rover position/velocity * (I:fixed mode,O:single mode) * .dtr[0] O receiver clock bias (s) * .dtr[1-5] O receiver GLO/GAL/BDS/IRN/QZS-GPS time offset (s) * .Qr[] O rover position covarinace * .stat O solution status (SOLQ_???) * .ns O number of valid satellites * .age O age of differential (s) * .ratio O ratio factor for ambiguity validation * rtk->rb[] IO base station position/velocity * (I:relative mode,O:moving-base mode) * rtk->nx I number of all states * rtk->na I number of integer states * rtk->ns O number of valid satellites in use * rtk->tt O time difference between current and previous (s) * rtk->x[] IO float states pre-filter and post-filter * rtk->P[] IO float covariance pre-filter and post-filter * rtk->xa[] O fixed states after AR * rtk->Pa[] O fixed covariance after AR * rtk->ssat[s] IO satellite {s+1} status * .sys O system (SYS_???) * .az [r] O azimuth angle (rad) (r=0:rover,1:base) * .el [r] O elevation angle (rad) (r=0:rover,1:base) * .vs [r] O data valid single (r=0:rover,1:base) * .resp [f] O freq(f+1) pseudorange residual (m) * .resc [f] O freq(f+1) carrier-phase residual (m) * .vsat [f] O freq(f+1) data vaild (0:invalid,1:valid) * .fix [f] O freq(f+1) ambiguity flag * (0:nodata,1:float,2:fix,3:hold) * .slip [f] O freq(f+1) cycle slip flag * (bit8-7:rcv1 LLI, bit6-5:rcv2 LLI, * bit2:parity unknown, bit1:slip) * .lock [f] IO freq(f+1) carrier lock count * .outc [f] IO freq(f+1) carrier outage count * .slipc[f] IO freq(f+1) cycle slip count * .rejc [f] IO freq(f+1) data reject count * .gf IO geometry-free phase (L1-L2 or L1-L5) (m) * rtk->nfix IO number of continuous fixes of ambiguity * rtk->neb IO bytes of error message buffer * rtk->errbuf IO error message buffer * rtk->tstr O time string for debug * rtk->opt I processing options * obsd_t *obs I observation data for an epoch * obs[i].rcv=1:rover,2:reference * sorted by receiver and satellte * int n I number of observation data * nav_t *nav I navigation messages * return : status (0:no solution,1:valid solution) * notes : before calling function, base station position rtk->sol.rb[] should * be properly set for relative mode except for moving-baseline *-----------------------------------------------------------------------------*/ extern int rtkpos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { prcopt_t *opt=&rtk->opt; insopt_t *insopt=&opt->insopt; sol_t solb={{0}}; gtime_t time; int i,nu,nr,mode; char msg[128]=""; trace(3,"rtkpos : time=%s n=%d\n",time_str(obs[0].time,3),n); trace(4,"obs=\n"); traceobs(4,obs,n); /* check mode */ mode = opt->mode&&insopt->tc; /* set base station 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; } /* count rover/base station observations */ for (nu=0;nu sol.time; /* previous epoch */ /* rover position and time by single point positioning/calculate residual and kalman filter */ if (!tc_pntpos(obs,nu,nav,&rtk->opt,&rtk->sol,&rtk->ins,NULL,rtk->ssat,msg)) { errmsg(rtk,"point pos error (%s)\n",msg); if (!rtk->opt.dynamics) { outsolstat(rtk,nav); return 0; } } if (time.time!=0) rtk->tt=timediff(rtk->sol.time,time); /* return to static start if long delay without rover data */ if (fabs(rtk->tt)>300&&rtk->initial_mode==PMODE_STATIC_START) { rtk->opt.mode=PMODE_STATIC_START; for (i=0;i<3;i++) initx(rtk,rtk->sol.rr[i],VAR_POS,i); if (rtk->opt.dynamics) { for (i=3;i<6;i++) initx(rtk,1E-6,VAR_VEL,i); for (i=6;i<9;i++) initx(rtk,1E-6,VAR_ACC,i); } trace(3,"No data for > 5 min: switch back to static mode:\n"); } /* single point positioning or mode */ if (opt->mode==PMODE_SINGLE||mode) { outsolstat(rtk,nav); return 1; } /* suppress output of single solution */ if (!opt->outsingle) { rtk->sol.stat=SOLQ_NONE; } /* precise point positioning */ if (opt->mode>=PMODE_PPP_KINEMA) { pppos(rtk,obs,nu,nav); outsolstat(rtk,nav); return 1; } /* 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,nav); return 1; } 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)>MIN(TTOL_MOVEB,opt->maxtdiff)) { 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; trace(3,"base pos: "); tracemat(3,rtk->rb,1,3,13,4); } 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,nav); return 1; } } /* relative potitioning */ relpos(rtk,obs,nu,nr,nav); outsolstat(rtk,nav); return 1; }