/*------------------------------------------------------------------------------ * rtkpos.c : precise positioning * * Copyright (C) 2007-2018 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 functino 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 * 2018/01/29 1.19 unfix ambiguity between gps and qzss *-----------------------------------------------------------------------------*/ #include #include "rtklib.h" static const char rcsid[]="$Id:$"; /* constants/macros ----------------------------------------------------------*/ #define SQR(x) ((x)*(x)) #define SQRT(x) ((x)<=0.0?0.0:sqrt(x)) #define MIN(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_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_HWBIAS SQR(1.0) /* initial variance of h/w bias ((m/MHz)^2) */ #define VAR_GRA SQR(0.001) /* initial variance of gradient (m^2) */ #define INIT_ZWD 0.15 /* initial zwd (m) */ #define PRN_HWBIAS 1E-6 /* process noise of h/w bias (m/MHz/sqrt(s)) */ #define GAP_RESION 120 /* gap to reset ionosphere parameters (epochs) */ #define MAXACC 30.0 /* max accel for doppler slip detection (m/s^2) */ #define VAR_HOLDAMB 0.001 /* constraint to hold ambiguity (cycle^2) */ #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!=2?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)) /* 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) */ #ifdef EXTGSI extern int resamb_WLNL(rtk_t *rtk, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav, const double *azel); extern int resamb_TCAR(rtk_t *rtk, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav, const double *azel); #else extern int resamb_WLNL(rtk_t *rtk, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav, const double *azel) {return 0;} extern int resamb_TCAR(rtk_t *rtk, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav, const double *azel) {return 0;} #endif /* 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 * 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) * * $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 * 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 * *-----------------------------------------------------------------------------*/ 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; } /* 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 ----------------------------------------------------*/ static void outsolstat(rtk_t *rtk) { 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]; if (statlevel<=0||!fp_stat) return; trace(3,"outsolstat:\n"); /* swap solution status file */ swapsolstat(); 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; fprintf(fp_stat,"$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 { fprintf(fp_stat,"$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); fprintf(fp_stat,"$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); fprintf(fp_stat,"$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 */ fprintf(fp_stat,"$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; fprintf(fp_stat,"$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; fprintf(fp_stat,"$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==2) { for (i=0;iopt); xa[0]=jna?rtk->xa[j]:0.0; fprintf(fp_stat,"$HWBIAS,%d,%.3f,%d,%d,%.4f,%.4f\n",week,tow,rtk->sol.stat, i+1,rtk->x[j],xa[0]); } } if (rtk->sol.stat==SOLQ_NONE||statlevel<=1) return; /* residuals and status */ for (i=0;issat+i; if (!ssat->vs) continue; satno2id(i+1,id); for (j=0;jazel[0]*R2D,ssat->azel[1]*R2D, ssat->resp [j],ssat->resc[j], ssat->vsat[j],ssat->snr[j]*0.25, ssat->fix [j],ssat->slip[j]&3,ssat->lock[j],ssat->outc[j], ssat->slipc[j],ssat->rejc[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 f) { double pi=ferr[3]*bl/1E4,d=CLIGHT*opt->sclkstab*dt,fact=1.0; double sinel=sin(el); int i=sys==SYS_GLO?1:(sys==SYS_GAL?2:0),nf=NF(opt); /* extended error model */ if (f>=nf&&opt->exterr.ena[0]) { /* code */ a=opt->exterr.cerr[i][ (f-nf)*2]; b=opt->exterr.cerr[i][1+(f-nf)*2]; if (sys==SYS_SBS) {a*=EFACT_SBS; b*=EFACT_SBS;} } else if (fexterr.ena[1]) { /* phase */ a=opt->exterr.perr[i][ f*2]; b=opt->exterr.perr[i][1+f*2]; if (sys==SYS_SBS) {a*=EFACT_SBS; b*=EFACT_SBS;} } else { /* normal error model */ if (f>=nf) fact=opt->eratio[f-nf]; if (fact<=0.0) fact=opt->eratio[0]; fact*=sys==SYS_GLO?EFACT_GLO:(sys==SYS_SBS?EFACT_SBS:EFACT_GPS); a=fact*opt->err[1]; b=fact*opt->err[2]; } return 2.0*(opt->ionoopt==IONOOPT_IFLC?3.0:1.0)*(a*a+b*b/sinel/sinel+c*c)+d*d; } /* 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; rtk->x[i]=xi; for (j=0;jnx;j++) { rtk->P[i+j*rtk->nx]=rtk->P[j+i*rtk->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,*FP,*xp,pos[3],Q[9]={0},Qv[9],var=0.0; int i,j; 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],1E-8,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) 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 postion */ 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; } /* state transition of position/velocity/acceleration */ F=eye(rtk->nx); FP=mat(rtk->nx,rtk->nx); xp=mat(rtk->nx,1); for (i=0;i<6;i++) { F[i+(i+3)*rtk->nx]=tt; } /* x=F*x, P=F*P*F+Q */ matmul("NN",rtk->nx,1,rtk->nx,1.0,F,rtk->x,0.0,xp); matcpy(rtk->x,xp,rtk->nx,1); matmul("NN",rtk->nx,rtk->nx,rtk->nx,1.0,F,rtk->P,0.0,FP); matmul("NT",rtk->nx,rtk->nx,rtk->nx,1.0,FP,F,0.0,rtk->P); /* process noise added to only acceleration */ Q[0]=Q[4]=SQR(rtk->opt.prn[3]); Q[8]=SQR(rtk->opt.prn[4]); 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(F); free(FP); 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=%.1f 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)*tt; } } } /* temporal update of tropospheric parameters --------------------------------*/ static void udtrop(rtk_t *rtk, double tt, double bl) { int i,j,k; trace(3,"udtrop : tt=%.1f\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])*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(rtk->tt); } } } } } /* temporal update of receiver h/w biases ------------------------------------*/ static void udrcvbias(rtk_t *rtk, double tt) { int i,j; trace(3,"udrcvbias: tt=%.1f\n",tt); for (i=0;iopt); if (rtk->x[j]==0.0) { initx(rtk,1E-6,VAR_HWBIAS,j); } /* hold to fixed solution */ else if (rtk->nfix>=rtk->opt.minfix&&rtk->sol.ratio>rtk->opt.thresar[0]) { initx(rtk,rtk->xa[j],rtk->Pa[j+j*rtk->na],j); } else { rtk->P[j+j*rtk->nx]+=SQR(PRN_HWBIAS)*tt; } } } /* detect cycle slip by LLI --------------------------------------------------*/ static void detslp_ll(rtk_t *rtk, const obsd_t *obs, int i, int rcv) { unsigned char slip,LLI1,LLI2,LLI; int f,sat=obs[i].sat; trace(3,"detslp_ll: i=%d rcv=%d\n",i,rcv); for (f=0;fopt.nf;f++) { if (obs[i].L[f]==0.0) continue; /* restore previous LLI */ LLI1=(rtk->ssat[sat-1].slip[f]>>6)&3; LLI2=(rtk->ssat[sat-1].slip[f]>>4)&3; LLI=rcv==1?LLI1:LLI2; /* detect slip by cycle slip flag */ slip=(rtk->ssat[sat-1].slip[f]|obs[i].LLI[f])&3; if (obs[i].LLI[f]&1) { errmsg(rtk,"slip detected (sat=%2d rcv=%d LLI%d=%x)\n", sat,rcv,f+1,obs[i].LLI[f]); } /* detect slip by parity unknown flag transition */ if (((LLI&2)&&!(obs[i].LLI[f]&2))||(!(LLI&2)&&(obs[i].LLI[f]&2))) { errmsg(rtk,"slip detected (sat=%2d rcv=%d LLI%d=%x->%x)\n", sat,rcv,f+1,LLI,obs[i].LLI[f]); slip|=1; } /* save current LLI and slip flag */ if (rcv==1) rtk->ssat[sat-1].slip[f]=(obs[i].LLI[f]<<6)|(LLI2<<4)|slip; else rtk->ssat[sat-1].slip[f]=(obs[i].LLI[f]<<4)|(LLI1<<6)|slip; } } /* detect cycle slip by L1-L2 geometry free phase jump -----------------------*/ static void detslp_gf_L1L2(rtk_t *rtk, const obsd_t *obs, int i, int j, const nav_t *nav) { int sat=obs[i].sat; double g0,g1; trace(3,"detslp_gf_L1L2: i=%d j=%d\n",i,j); if (rtk->opt.nf<=1||(g1=gfobs_L1L2(obs,i,j,nav->lam[sat-1]))==0.0) return; g0=rtk->ssat[sat-1].gf; rtk->ssat[sat-1].gf=g1; if (g0!=0.0&&fabs(g1-g0)>rtk->opt.thresslip) { rtk->ssat[sat-1].slip[0]|=1; rtk->ssat[sat-1].slip[1]|=1; errmsg(rtk,"slip detected (sat=%2d GF_L1_L2=%.3f %.3f)\n",sat,g0,g1); } } /* detect cycle slip by L1-L5 geometry free phase jump -----------------------*/ static void detslp_gf_L1L5(rtk_t *rtk, const obsd_t *obs, int i, int j, const nav_t *nav) { int sat=obs[i].sat; double g0,g1; trace(3,"detslp_gf_L1L5: i=%d j=%d\n",i,j); if (rtk->opt.nf<=2||(g1=gfobs_L1L5(obs,i,j,nav->lam[sat-1]))==0.0) return; g0=rtk->ssat[sat-1].gf2; rtk->ssat[sat-1].gf2=g1; if (g0!=0.0&&fabs(g1-g0)>rtk->opt.thresslip) { rtk->ssat[sat-1].slip[0]|=1; rtk->ssat[sat-1].slip[2]|=1; errmsg(rtk,"slip detected (sat=%2d GF_L1_L5=%.3f %.3f)\n",sat,g0,g1); } } /* detect cycle slip by doppler and phase difference -------------------------*/ static void detslp_dop(rtk_t *rtk, const obsd_t *obs, int i, int rcv, const nav_t *nav) { /* detection with doppler disabled because of clock-jump issue (v.2.3.0) */ #if 0 int f,sat=obs[i].sat; double tt,dph,dpt,lam,thres; trace(3,"detslp_dop: i=%d rcv=%d\n",i,rcv); for (f=0;fopt.nf;f++) { if (obs[i].L[f]==0.0||obs[i].D[f]==0.0||rtk->ph[rcv-1][sat-1][f]==0.0) { continue; } if (fabs(tt=timediff(obs[i].time,rtk->pt[rcv-1][sat-1][f]))lam[sat-1][f])<=0.0) continue; /* cycle slip threshold (cycle) */ thres=MAXACC*tt*tt/2.0/lam+rtk->opt.err[4]*fabs(tt)*4.0; /* phase difference and doppler x time (cycle) */ dph=obs[i].L[f]-rtk->ph[rcv-1][sat-1][f]; dpt=-obs[i].D[f]*tt; if (fabs(dph-dpt)<=thres) continue; rtk->slip[sat-1][f]|=1; errmsg(rtk,"slip detected (sat=%2d rcv=%d L%d=%.3f %.3f thres=%.3f)\n", sat,rcv,f+1,dph,dpt,thres); } #endif } /* 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,lami,lam1,lam2,C1,C2; int i,j,f,slip,reset,nf=NF(&rtk->opt); trace(3,"udbias : tt=%.1f ns=%d\n",tt,ns); for (i=0;iopt.nf;f++) rtk->ssat[sat[i]-1].slip[f]&=0xFC; detslp_ll(rtk,obs,iu[i],1); detslp_ll(rtk,obs,ir[i],2); /* detect cycle slip by geometry-free phase jump */ detslp_gf_L1L2(rtk,obs,iu[i],ir[i],nav); detslp_gf_L1L5(rtk,obs,iu[i],ir[i],nav); /* detect cycle slip by doppler and phase difference */ detslp_dop(rtk,obs,iu[i],1,nav); detslp_dop(rtk,obs,ir[i],2,nav); } for (f=0;fssat[i-1].outc[f]>(unsigned int)rtk->opt.maxout; if (rtk->opt.modear==ARMODE_INST&&rtk->x[IB(i,f,&rtk->opt)]!=0.0) { initx(rtk,0.0,0.0,IB(i,f,&rtk->opt)); } else if (reset&&rtk->x[IB(i,f,&rtk->opt)]!=0.0) { initx(rtk,0.0,0.0,IB(i,f,&rtk->opt)); trace(3,"udbias : obs outage counter overflow (sat=%3d L%d n=%d)\n", i,f+1,rtk->ssat[i-1].outc[f]); } if (rtk->opt.modear!=ARMODE_INST&&reset) { rtk->ssat[i-1].lock[f]=-rtk->opt.minlock; } } /* reset phase-bias if detecting cycle slip */ for (i=0;iopt); rtk->P[j+j*rtk->nx]+=rtk->opt.prn[0]*rtk->opt.prn[0]*tt; slip=rtk->ssat[sat[i]-1].slip[f]; if (rtk->opt.ionoopt==IONOOPT_IFLC) slip|=rtk->ssat[sat[i]-1].slip[1]; if (rtk->opt.modear==ARMODE_INST||!(slip&1)) continue; rtk->x[j]=0.0; rtk->ssat[sat[i]-1].lock[f]=-rtk->opt.minlock; } bias=zeros(ns,1); /* estimate approximate phase-bias by phase - code */ for (i=j=0,offset=0.0;iopt.ionoopt!=IONOOPT_IFLC) { cp=sdobs(obs,iu[i],ir[i],f); /* cycle */ pr=sdobs(obs,iu[i],ir[i],f+NFREQ); lami=nav->lam[sat[i]-1][f]; if (cp==0.0||pr==0.0||lami<=0.0) continue; bias[i]=cp-pr/lami; } else { cp1=sdobs(obs,iu[i],ir[i],0); cp2=sdobs(obs,iu[i],ir[i],1); pr1=sdobs(obs,iu[i],ir[i],NFREQ); pr2=sdobs(obs,iu[i],ir[i],NFREQ+1); lam1=nav->lam[sat[i]-1][0]; lam2=nav->lam[sat[i]-1][1]; if (cp1==0.0||cp2==0.0||pr1==0.0||pr2==0.0||lam1<=0.0||lam2<=0.0) continue; C1= SQR(lam2)/(SQR(lam2)-SQR(lam1)); C2=-SQR(lam1)/(SQR(lam2)-SQR(lam1)); bias[i]=(C1*lam1*cp1+C2*lam2*cp2)-(C1*pr1+C2*pr2); } if (rtk->x[IB(sat[i],f,&rtk->opt)]!=0.0) { offset+=bias[i]-rtk->x[IB(sat[i],f,&rtk->opt)]; j++; } } /* correct phase-bias offset to enssure phase-code coherency */ if (j>0) { for (i=1;i<=MAXSAT;i++) { if (rtk->x[IB(i,f,&rtk->opt)]!=0.0) rtk->x[IB(i,f,&rtk->opt)]+=offset/j; } } /* set initial states of phase-bias */ for (i=0;ix[IB(sat[i],f,&rtk->opt)]!=0.0) continue; initx(rtk,bias[i],SQR(rtk->opt.std[0]),IB(sat[i],f,&rtk->opt)); } 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=fabs(rtk->tt),bl,dr[3]; trace(3,"udstate : ns=%d\n",ns); /* temporal update of position/velocity/acceleration */ 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 eceiver h/w bias */ if (rtk->opt.glomodear==2&&(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); } } /* 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) { const double *lam=nav->lam[obs->sat-1]; double f1,f2,C1,C2,dant_if; int i,nf=NF(opt); if (opt->ionoopt==IONOOPT_IFLC) { /* iono-free linear combination */ if (lam[0]==0.0||lam[1]==0.0) return; if (testsnr(base,0,azel[1],obs->SNR[0]*0.25,&opt->snrmask)|| testsnr(base,1,azel[1],obs->SNR[1]*0.25,&opt->snrmask)) return; f1=CLIGHT/lam[0]; f2=CLIGHT/lam[1]; C1= SQR(f1)/(SQR(f1)-SQR(f2)); C2=-SQR(f2)/(SQR(f1)-SQR(f2)); dant_if=C1*dant[0]+C2*dant[1]; if (obs->L[0]!=0.0&&obs->L[1]!=0.0) { y[0]=C1*obs->L[0]*lam[0]+C2*obs->L[1]*lam[1]-r-dant_if; } if (obs->P[0]!=0.0&&obs->P[1]!=0.0) { y[1]=C1*obs->P[0]+C2*obs->P[1]-r-dant_if; } } else { for (i=0;iSNR[i]*0.25,&opt->snrmask)) { continue; } /* residuals = observable - pseudorange */ if (obs->L[i]!=0.0) y[i ]=obs->L[i]*lam[i]-r-dant[i]; if (obs->P[i]!=0.0) y[i+nf]=obs->P[i] -r-dant[i]; } } } /* undifferenced phase/code residuals ----------------------------------------*/ static int zdres(int base, const obsd_t *obs, int n, const double *rs, const double *dts, const int *svh, const nav_t *nav, const double *rr, const prcopt_t *opt, int index, double *y, double *e, double *azel) { double r,rr_[3],pos[3],dant[NFREQ]={0},disp[3]; double zhd,zazel[]={0.0,90.0*D2R}; int i,nf=NF(opt); trace(3,"zdres : n=%d\n",n); 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]; } ecef2pos(rr_,pos); for (i=0;ielmin) continue; /* excluded satellite? */ if (satexclude(obs[i].sat,svh[i],opt)) continue; /* satellite clock-bias */ r+=-CLIGHT*dts[i*2]; /* troposphere delay model (hydrostatic) */ zhd=tropmodel(obs[0].time,pos,zazel,0.0); r+=tropmapf(obs[i].time,pos,azel+i*2,NULL)*zhd; /* receiver antenna phase center correction */ antmodel(opt->pcvr+index,opt->antdel[index],azel+i*2,opt->posopt[1], dant); /* undifferenced phase/code residual for satellite */ zdres_sat(base,r,obs+i,nav,azel+i*2,dant,opt,y+i*nf*2); } 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]+rtk->rb[i+3]*rtk->sol.age; 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>thres*thres*bb*bb) { trace(3,"constbl : equation nonlinear (bb=%.3f var=%.3f)\n",bb,var); return 0; } /* 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]; } /* glonass inter-channel bias correction -------------------------------------*/ static double gloicbcorr(int sat1, int sat2, const prcopt_t *opt, double lam1, double lam2, int f) { double dfreq; if (f>=NFREQGLO||f>=opt->nf||!opt->exterr.ena[2]) return 0.0; dfreq=(CLIGHT/lam1-CLIGHT/lam2)/(f==0?DFRQ1_GLO:DFRQ2_GLO); return opt->exterr.gloicb[f]*0.01*dfreq; /* (m) */ } /* test navi system (m=0:gps/sbs,1:glo,2:gal,3:bds,4:qzs) --------------------*/ static int test_sys(int sys, int m) { switch (sys) { case SYS_GPS: return m==0; case SYS_QZS: return m==4; case SYS_SBS: return m==0; case SYS_GLO: return m==1; case SYS_GAL: return m==2; case SYS_CMP: return m==3; } return 0; } /* double-differenced phase/code residuals -----------------------------------*/ static int ddres(rtk_t *rtk, const nav_t *nav, double dt, const double *x, const double *P, const int *sat, double *y, double *e, double *azel, 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; double *tropr,*tropu,*dtdxr,*dtdxu,*Ri,*Rj,lami,lamj,fi,fj,df,*Hi=NULL; int i,j,k,m,f,ff,nv=0,nb[NFREQ*4*2+2]={0},b=0,sysi,sysj,nf=NF(opt); trace(3,"ddres : dt=%.1f nx=%d ns=%d\n",dt,rtk->nx,ns); bl=baseline(x,rtk->rb,dr); ecef2pos(x,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); for (i=0;issat[i].resp[j]=rtk->ssat[i].resc[j]=0.0; } /* compute factors of ionospheric and tropospheric delay */ 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); } } for (m=0;m<5;m++) /* m=0:gps/sbs,1:glo,2:gal,3:bds,4:qzs */ for (f=opt->mode>PMODE_DGPS?0:nf;fssat[sat[j]-1].sys; if (!test_sys(sysi,m)) continue; if (!validobs(iu[j],ir[j],f,nf,y)) continue; if (i<0||azel[1+iu[j]*2]>=azel[1+iu[i]*2]) i=j; } if (i<0) continue; /* make double difference */ for (j=0;jssat[sat[i]-1].sys; sysj=rtk->ssat[sat[j]-1].sys; if (!test_sys(sysj,m)) continue; if (!validobs(iu[j],ir[j],f,nf,y)) continue; ff=f%nf; lami=nav->lam[sat[i]-1][ff]; lamj=nav->lam[sat[j]-1][ff]; if (lami<=0.0||lamj<=0.0) continue; if (H) Hi=H+nv*rtk->nx; /* double-differenced residual */ v[nv]=(y[f+iu[i]*nf*2]-y[f+ir[i]*nf*2])- (y[f+iu[j]*nf*2]-y[f+ir[j]*nf*2]); /* partial derivatives by rover position */ if (H) { for (k=0;k<3;k++) { Hi[k]=-e[k+iu[i]*3]+e[k+iu[j]*3]; } } /* double-differenced ionospheric delay term */ if (opt->ionoopt==IONOOPT_EST) { fi=lami/lam_carr[0]; fj=lamj/lam_carr[0]; didxi=(ftropopt==TROPOPT_EST||opt->tropopt==TROPOPT_ESTG) { v[nv]-=(tropu[i]-tropu[j])-(tropr[i]-tropr[j]); for (k=0;k<(opt->tropoptionoopt!=IONOOPT_IFLC) { v[nv]-=lami*x[IB(sat[i],f,opt)]-lamj*x[IB(sat[j],f,opt)]; if (H) { Hi[IB(sat[i],f,opt)]= lami; Hi[IB(sat[j],f,opt)]=-lamj; } } else { v[nv]-=x[IB(sat[i],f,opt)]-x[IB(sat[j],f,opt)]; if (H) { Hi[IB(sat[i],f,opt)]= 1.0; Hi[IB(sat[j],f,opt)]=-1.0; } } } /* glonass receiver h/w bias term */ if (rtk->opt.glomodear==2&&sysi==SYS_GLO&&sysj==SYS_GLO&&ffopt,lami,lamj,f); } if (fssat[sat[j]-1].resc[f ]=v[nv]; else rtk->ssat[sat[j]-1].resp[f-nf]=v[nv]; /* test innovation */ if (opt->maxinno>0.0&&fabs(v[nv])>opt->maxinno) { if (fssat[sat[i]-1].rejc[f]++; rtk->ssat[sat[j]-1].rejc[f]++; } errmsg(rtk,"outlier rejected (sat=%3d-%3d %s%d v=%.3f)\n", sat[i],sat[j],fmode>PMODE_DGPS) { if (fssat[sat[i]-1].vsat[f]=rtk->ssat[sat[j]-1].vsat[f]=1; } else { rtk->ssat[sat[i]-1].vsat[f-nf]=rtk->ssat[sat[j]-1].vsat[f-nf]=1; } trace(4,"sat=%3d-%3d %s%d v=%13.3f R=%8.6f %8.6f\n",sat[i], sat[j],fssat[j].resc[f]; s/=nb[b]+1; for (j=0;jssat[j].resc[f]!=0.0) rtk->ssat[j].resc[f]-=s; } } else { for (j=0,s=0.0;jssat[j].resp[f-nf]; s/=nb[b]+1; for (j=0;jssat[j].resp[f-nf]!=0.0) rtk->ssat[j].resp[f-nf]-=s; } } #endif 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,rtk->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-mission) ------------------------*/ 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]; 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); 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,svh,nav,rtk->rb,opt,1,yb,e,azel)) { 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; } /* single to double-difference transformation matrix (D') --------------------*/ static int ddmat(rtk_t *rtk, double *D) { int i,j,k,m,f,nb=0,nx=rtk->nx,na=rtk->na,nf=NF(&rtk->opt); trace(3,"ddmat :\n"); for (i=0;issat[i].fix[j]=0; } for (i=0;iopt.glomodear==0) continue; if (m==3&&rtk->opt.bdsmodear==0) continue; for (f=0,k=na;fx[i]==0.0||!test_sys(rtk->ssat[i-k].sys,m)|| !rtk->ssat[i-k].vsat[f]) { continue; } if (rtk->ssat[i-k].lock[f]>0&&!(rtk->ssat[i-k].slip[f]&2)&& rtk->ssat[i-k].azel[1]>=rtk->opt.elmaskar) { rtk->ssat[i-k].fix[f]=2; /* fix */ break; } else rtk->ssat[i-k].fix[f]=1; } for (j=k;jx[j]==0.0||!test_sys(rtk->ssat[j-k].sys,m)|| !rtk->ssat[j-k].vsat[f]) { continue; } if (rtk->ssat[j-k].lock[f]>0&&!(rtk->ssat[j-k].slip[f]&2)&& rtk->ssat[i-k].vsat[f]&& rtk->ssat[j-k].azel[1]>=rtk->opt.elmaskar) { D[i+(na+nb)*nx]= 1.0; D[j+(na+nb)*nx]=-1.0; nb++; rtk->ssat[j-k].fix[f]=2; /* fix */ } else rtk->ssat[j-k].fix[f]=1; } } } trace(5,"D=\n"); tracemat(5,D,nx,na+nb,2,0); return nb; } /* restore single-differenced ambiguity --------------------------------------*/ 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]; for (i=0;ina;i++) xa[i]=rtk->xa[i]; for (m=0;m<5;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); trace(3,"holdamb :\n"); v=mat(nb,1); H=zeros(nb,rtk->nx); for (m=0;m<5;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 */ } /* constraint to fixed ambiguity */ 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++; } } if (nv>0) { R=zeros(nv,nv); for (i=0;ix,rtk->P,H,v,R,rtk->nx,nv))) { errmsg(rtk,"filter error (info=%d)\n",info); } free(R); } free(v); free(H); } /* resolve integer ambiguity by LAMBDA ---------------------------------------*/ static int resamb_LAMBDA(rtk_t *rtk, double *bias, double *xa) { prcopt_t *opt=&rtk->opt; int i,j,ny,nb,info,nx=rtk->nx,na=rtk->na; double *D,*DP,*y,*Qy,*b,*db,*Qb,*Qab,*QQ,s[2]; trace(3,"resamb_LAMBDA : nx=%d\n",nx); rtk->sol.ratio=0.0; if (rtk->opt.mode<=PMODE_DGPS||rtk->opt.modear==ARMODE_OFF|| rtk->opt.thresar[0]<1.0) { return 0; } /* single to double-difference transformation matrix (D') */ D=zeros(nx,nx); if ((nb=ddmat(rtk,D))<=0) { errmsg(rtk,"no valid double-difference\n"); free(D); return 0; } ny=na+nb; y=mat(ny,1); Qy=mat(ny,ny); DP=mat(ny,nx); b=mat(nb,2); db=mat(nb,1); Qb=mat(nb,nb); Qab=mat(na,nb); QQ=mat(na,nb); /* transform single to double-differenced phase-bias (y=D'*x, Qy=D'*P*D) */ matmul("TN",ny, 1,nx,1.0,D ,rtk->x,0.0,y ); matmul("TN",ny,nx,nx,1.0,D ,rtk->P,0.0,DP); matmul("NN",ny,ny,nx,1.0,DP,D ,0.0,Qy); /* phase-bias covariance (Qb) and real-parameters to bias covariance (Qab) */ for (i=0;ixa); /* covariance of fixed solution (Qa=Qa-Qab*Qb^-1*Qab') */ matmul("NN",na,nb,nb, 1.0,Qab,Qb ,0.0,QQ); matmul("NT",na,na,nb,-1.0,QQ ,Qab,1.0,rtk->Pa); trace(3,"resamb : validation ok (nb=%d ratio=%.2f s=%.2f/%.2f)\n", nb,s[0]==0.0?0.0:s[1]/s[0],s[0],s[1]); /* restore single-differenced ambiguity */ restamb(rtk,bias,nb,xa); } else nb=0; } else { /* validation failed */ errmsg(rtk,"ambiguity validation failed (nb=%d ratio=%.2f s=%.2f/%.2f)\n", nb,s[1]/s[0],s[0],s[1]); nb=0; } } else { errmsg(rtk,"lambda error (info=%d)\n",info); } free(D); free(y); free(Qy); free(DP); free(b); free(db); free(Qb); free(Qab); free(QQ); return nb; /* number of ambiguities */ } /* validation of solution ----------------------------------------------------*/ static int valpos(rtk_t *rtk, const double *v, const double *R, const int *vflg, int nv, double thres) { #if 0 prcopt_t *opt=&rtk->opt; double vv=0.0; #endif 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?"L":"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])); } #if 0 /* omitted v.2.4.0 */ if (stat&&nv>NP(opt)) { /* chi-square validation */ for (i=0;ichisqr[nv-NP(opt)-1]) { errmsg(rtk,"residuals validation failed (nv=%d np=%d vv=%.2f cs=%.2f)\n", nv,NP(opt),vv,chisqr[nv-NP(opt)-1]); stat=0; } else { trace(3,"valpos : validation ok (%s nv=%d np=%d vv=%.2f cs=%.2f)\n", rtk->tstr,nv,NP(opt),vv,chisqr[nv-NP(opt)-1]); } } #endif return stat; } /* relative positioning ------------------------------------------------------*/ 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,*v,*H,*R,*xp,*Pp,*xa,*bias,dt; 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; trace(3,"relpos : nx=%d nu=%d nr=%d\n",rtk->nx,nu,nr); dt=timediff(time,obs[nu].time); rs=mat(6,n); dts=mat(2,n); var=mat(1,n); y=mat(nf*2,n); e=mat(3,n); azel=zeros(2,n); for (i=0;issat[i].sys=satsys(i+1,NULL); for (j=0;jssat[i].vsat[j]=rtk->ssat[i].snr[j]=0; } /* satellite positions/clocks */ satposs(time,obs,n,nav,opt->sateph,rs,dts,var,svh); /* undifferenced residuals for base station */ if (!zdres(1,obs+nu,nr,rs+nu*6,dts+nu*2,svh+nu,nav,rtk->rb,opt,1, y+nu*nf*2,e+nu*3,azel+nu*2)) { errmsg(rtk,"initial base station position error\n"); free(rs); free(dts); free(var); free(y); free(e); free(azel); 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); return 0; } /* temporal update of states */ udstate(rtk,obs,sat,iu,ir,ns,nav); trace(4,"x(0)="); tracemat(4,rtk->x,1,NR(opt),13,4); xp=mat(rtk->nx,1); Pp=zeros(rtk->nx,rtk->nx); xa=mat(rtk->nx,1); matcpy(xp,rtk->x,rtk->nx,1); ny=ns*nf*2+2; v=mat(ny,1); H=zeros(rtk->nx,ny); R=mat(ny,ny); bias=mat(rtk->nx,1); /* add 2 iterations for baseline-constraint moving-base */ niter=opt->niter+(opt->mode==PMODE_MOVEB&&opt->baseline[0]>0.0?2:0); for (i=0;iP,rtk->nx,rtk->nx); if ((info=filter(xp,Pp,H,v,R,rtk->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); } if (stat!=SOLQ_NONE&&zdres(0,obs,nu,rs,dts,svh,nav,xp,opt,0,y,e,azel)) { /* post-fit residuals for float solution */ nv=ddres(rtk,nav,dt,xp,Pp,sat,y,e,azel,iu,ir,ns,v,NULL,R,vflg); /* validation of float solution */ if (valpos(rtk,v,R,vflg,nv,4.0)) { /* update state and covariance matrix */ matcpy(rtk->x,xp,rtk->nx,1); matcpy(rtk->P,Pp,rtk->nx,rtk->nx); /* update ambiguity control struct */ rtk->sol.ns=0; for (i=0;issat[sat[i]-1].vsat[f]) continue; rtk->ssat[sat[i]-1].lock[f]++; rtk->ssat[sat[i]-1].outc[f]=0; if (f==0) rtk->sol.ns++; /* valid satellite count by L1 */ } /* lack of valid satellites */ if (rtk->sol.ns<4) stat=SOLQ_NONE; } else stat=SOLQ_NONE; } /* resolve integer ambiguity by WL-NL */ if (stat!=SOLQ_NONE&&rtk->opt.modear==ARMODE_WLNL) { if (resamb_WLNL(rtk,obs,sat,iu,ir,ns,nav,azel)) { stat=SOLQ_FIX; } } /* resolve integer ambiguity by TCAR */ else if (stat!=SOLQ_NONE&&rtk->opt.modear==ARMODE_TCAR) { if (resamb_TCAR(rtk,obs,sat,iu,ir,ns,nav,azel)) { stat=SOLQ_FIX; } } /* resolve integer ambiguity by LAMBDA */ else if (stat!=SOLQ_NONE&&resamb_LAMBDA(rtk,bias,xa)>1) { if (zdres(0,obs,nu,rs,dts,svh,nav,xa,opt,0,y,e,azel)) { /* post-fit reisiduals for fixed solution */ nv=ddres(rtk,nav,dt,xa,NULL,sat,y,e,azel,iu,ir,ns,v,NULL,R,vflg); /* validation of fixed solution */ if (valpos(rtk,v,R,vflg,nv,4.0)) { /* hold integer ambiguity */ if (++rtk->nfix>=rtk->opt.minfix&& rtk->opt.modear==ARMODE_FIXHOLD) { holdamb(rtk,xa); } stat=SOLQ_FIX; } } } /* save solution status */ 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]; } else { 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]; rtk->nfix=0; } 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[sat[i]-1].snr[j]=obs[iu[i]].SNR[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]++; } free(rs); free(dts); free(var); free(y); free(e); free(azel); 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; } /* initialize rtk control ------------------------------------------------------ * initialize rtk control struct * args : rtk_t *rtk IO rtk 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):0; 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; } for (i=0;ierrbuf[i]=0; rtk->opt=*opt; } /* 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; } /* 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] O receiver glonass-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 satellite * 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 sat(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) 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) (m) * .gf2 IO geometry-free phase (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; sol_t solb={{0}}; gtime_t time; int i,nu,nr; 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); /*trace(5,"nav=\n"); tracenav(5,nav);*/ /* set base staion position */ if (opt->refpos<=3&&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 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; } } if (time.time!=0) rtk->tt=timediff(rtk->sol.time,time); /* single point positioning */ if (opt->mode==PMODE_SINGLE) { outsolstat(rtk); return 1; } /* precise point positioning */ if (opt->mode>=PMODE_PPP_KINEMA) { pppos(rtk,obs,nu,nav); pppoutsolstat(rtk,statlevel,fp_stat); 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); 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)>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; } } /* relative potitioning */ relpos(rtk,obs,nu,nr,nav); outsolstat(rtk); return 1; }