/*! * \file rtklib_solution.cc * \brief solution functions * \authors * * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #include #include "rtklib_solution.h" #include "rtklib_rtkcmn.h" #include "rtklib_rtksvr.h" /* constants and macros ------------------------------------------------------*/ #define SQR_SOL(x) ((x)<0.0?-(x)*(x):(x)*(x)) #define SQRT_SOL(x) ((x)<0.0?0.0:sqrt(x)) const int MAXFIELD = 64; /* max number of fields in a record */ const int MAXNMEA = 256; /* max length of nmea sentence */ const double KNOT2M = 0.514444444; /* m/knot */ static const int solq_nmea[] = { /* nmea quality flags to rtklib sol quality */ /* nmea 0183 v.2.3 quality flags: */ /* 0=invalid, 1=gps fix (sps), 2=dgps fix, 3=pps fix, 4=rtk, 5=float rtk */ /* 6=estimated (dead reckoning), 7=manual input, 8=simulation */ SOLQ_NONE ,SOLQ_SINGLE, SOLQ_DGPS, SOLQ_PPP , SOLQ_FIX, SOLQ_FLOAT,SOLQ_DR , SOLQ_NONE, SOLQ_NONE, SOLQ_NONE }; /* solution option to field separator ----------------------------------------*/ const char *opt2sep(const solopt_t *opt) { if (!*opt->sep) return " "; else if (!strcmp(opt->sep,"\\t")) return "\t"; return opt->sep; } /* separate fields -----------------------------------------------------------*/ int tonum(char *buff, const char *sep, double *v) { int n,len = (int)strlen(sep); char *p,*q; for (p = buff,n = 0;nqr[0]; /* xx or ee */ P[4] = sol->qr[1]; /* yy or nn */ P[8] = sol->qr[2]; /* zz or uu */ P[1] = P[3] = sol->qr[3]; /* xy or en */ P[5] = P[7] = sol->qr[4]; /* yz or nu */ P[2] = P[6] = sol->qr[5]; /* zx or ue */ } /* covariance to solution ----------------------------------------------------*/ void covtosol(const double *P, sol_t *sol) { sol->qr[0] = (float)P[0]; /* xx or ee */ sol->qr[1] = (float)P[4]; /* yy or nn */ sol->qr[2] = (float)P[8]; /* zz or uu */ sol->qr[3] = (float)P[1]; /* xy or en */ sol->qr[4] = (float)P[5]; /* yz or nu */ sol->qr[5] = (float)P[2]; /* zx or ue */ } /* decode nmea gprmc: recommended minumum data for gps -----------------------*/ int decode_nmearmc(char **val, int n, sol_t *sol) { double tod = 0.0,lat = 0.0,lon = 0.0,vel = 0.0,dir = 0.0,date = 0.0,ang = 0.0,ep[6]; double pos[3] = {0}; char act = ' ',ns = 'N',ew = 'E',mew = 'E',mode = 'A'; int i; trace(4,"decode_nmearmc: n=%d\n",n); for (i = 0;inmea 2) */ /* A=autonomous,D=differential */ /* E=estimated,N=not valid,S=simulator */ } } if ((act != 'A' && act != 'V') || (ns != 'N' && ns != 'S') || (ew != 'E' && ew != 'W')) { trace(2,"invalid nmea gprmc format\n"); return 0; } pos[0] = (ns == 'S'?-1.0:1.0)*dmm2deg(lat)*D2R; pos[1] = (ew == 'W'?-1.0:1.0)*dmm2deg(lon)*D2R; septime(date,ep+2,ep+1,ep); septime(tod,ep+3,ep+4,ep+5); ep[0] += ep[0]<80.0?2000.0:1900.0; sol->time = utc2gpst(epoch2time(ep)); pos2ecef(pos,sol->rr); sol->stat = mode == 'D'?SOLQ_DGPS:SOLQ_SINGLE; sol->ns = 0; sol->type = 0; /* postion type = xyz */ trace(5,"decode_nmearmc: %s rr=%.3f %.3f %.3f stat=%d ns=%d vel=%.2f dir=%.0f ang=%.0f mew=%c mode=%c\n", time_str(sol->time,0),sol->rr[0],sol->rr[1],sol->rr[2],sol->stat,sol->ns, vel,dir,ang,mew,mode); return 1; } /* decode nmea gpgga: fix information ----------------------------------------*/ int decode_nmeagga(char **val, int n, sol_t *sol) { gtime_t time; double tod = 0.0,lat = 0.0,lon = 0.0,hdop = 0.0,alt = 0.0,msl = 0.0,ep[6],tt; double pos[3] = {0}; char ns = 'N',ew = 'E',ua = ' ',um = ' '; int i,solq = 0,nrcv = 0; trace(4,"decode_nmeagga: n=%d\n",n); for (i = 0;itime.time == 0.0) { trace(2,"no date info for nmea gpgga\n"); return 0; } pos[0] = (ns == 'N'?1.0:-1.0)*dmm2deg(lat)*D2R; pos[1] = (ew == 'E'?1.0:-1.0)*dmm2deg(lon)*D2R; pos[2] = alt+msl; time2epoch(sol->time,ep); septime(tod,ep+3,ep+4,ep+5); time = utc2gpst(epoch2time(ep)); tt = timediff(time,sol->time); if (tt<-43200.0) sol->time = timeadd(time, 86400.0); else if (tt> 43200.0) sol->time = timeadd(time,-86400.0); else sol->time = time; pos2ecef(pos,sol->rr); sol->stat = 0 <= solq && solq <= 8?solq_nmea[solq]:SOLQ_NONE; sol->ns = nrcv; sol->type = 0; /* postion type = xyz */ trace(5,"decode_nmeagga: %s rr=%.3f %.3f %.3f stat=%d ns=%d hdop=%.1f ua=%c um=%c\n", time_str(sol->time,0),sol->rr[0],sol->rr[1],sol->rr[2],sol->stat,sol->ns, hdop,ua,um); return 1; } /* decode nmea ---------------------------------------------------------------*/ int decode_nmea(char *buff, sol_t *sol) { char *p, *q, *val[MAXFIELD] = {0}; int n = 0; trace(4,"decode_nmea: buff=%s\n",buff); /* parse fields */ for (p = buff;*p && nsep,"\\t")) strcpy(s,"\t"); else if (*opt->sep) strcpy(s,opt->sep); len = (int)strlen(s); /* yyyy/mm/dd hh:mm:ss or yyyy mm dd hh:mm:ss */ if (sscanf(buff,"%lf/%lf/%lf %lf:%lf:%lf",v,v+1,v+2,v+3,v+4,v+5) >= 6) { if (v[0]<100.0) { v[0] += v[0]<80.0?2000.0:1900.0; } *time = epoch2time(v); if (opt->times == TIMES_UTC) { *time = utc2gpst(*time); } else if (opt->times == TIMES_JST) { *time = utc2gpst(timeadd(*time,-9*3600.0)); } if (!(p = strchr(buff,':')) || !(p = strchr(p+1,':'))) return NULL; for (p++;isdigit((int)*p) || *p == '.';) p++; return p+len; } if (opt->posf == SOLF_GSIF) { if (sscanf(buff,"%lf %lf %lf %lf:%lf:%lf",v,v+1,v+2,v+3,v+4,v+5)<6) { return NULL; } *time = timeadd(epoch2time(v),-12.0*3600.0); if (!(p = strchr(buff,':')) || !(p = strchr(p+1,':'))) return NULL; for (p++;isdigit((int)*p) || *p == '.';) p++; return p+len; } /* wwww ssss */ for (p = buff,n = 0;n<2;p = q+len) { if ((q = strstr(p,s))) *q = '\0'; if (*p) v[n++] = atof(p); if (!q) break; } if (n >= 2 && 0.0 <= v[0] && v[0] <= 3000.0 && 0.0 <= v[1] && v[1]<604800.0) { *time = gpst2time((int)v[0],v[1]); return p; } return NULL; } /* decode x/y/z-ecef ---------------------------------------------------------*/ int decode_solxyz(char *buff, const solopt_t *opt, sol_t *sol) { double val[MAXFIELD],P[9] = {0}; int i = 0,j,n; const char *sep = opt2sep(opt); trace(4,"decode_solxyz:\n"); if ((n = tonum(buff,sep,val))<3) return 0; for (j = 0;j<3;j++) { sol->rr[j] = val[i++]; /* xyz */ } if (istat = (unsigned char)val[i++]; if (ins = (unsigned char)val[i++]; if (i+3age = (float)val[i++]; if (iratio = (float)val[i]; sol->type = 0; /* postion type = xyz */ if (MAXSOLQstat) sol->stat = SOLQ_NONE; return 1; } /* decode lat/lon/height -----------------------------------------------------*/ int decode_solllh(char *buff, const solopt_t *opt, sol_t *sol) { double val[MAXFIELD],pos[3],Q[9] = {0},P[9]; int i = 0,n; const char *sep = opt2sep(opt); trace(4,"decode_solllh:\n"); n = tonum(buff,sep,val); if (!opt->degf) { if (n<3) return 0; pos[0] = val[i++]*D2R; /* lat/lon/hgt (ddd.ddd) */ pos[1] = val[i++]*D2R; pos[2] = val[i++]; } else { if (n<7) return 0; pos[0] = dms2deg(val )*D2R; /* lat/lon/hgt (ddd mm ss) */ pos[1] = dms2deg(val+3)*D2R; pos[2] = val[6]; i += 7; } pos2ecef(pos,sol->rr); if (istat = (unsigned char)val[i++]; if (ins = (unsigned char)val[i++]; if (i+3age = (float)val[i++]; if (iratio = (float)val[i]; sol->type = 0; /* postion type = xyz */ if (MAXSOLQstat) sol->stat = SOLQ_NONE; return 1; } /* decode e/n/u-baseline -----------------------------------------------------*/ int decode_solenu(char *buff, const solopt_t *opt, sol_t *sol) { double val[MAXFIELD],Q[9] = {0}; int i = 0,j,n; const char *sep = opt2sep(opt); trace(4,"decode_solenu:\n"); if ((n = tonum(buff,sep,val))<3) return 0; for (j = 0;j<3;j++) { sol->rr[j] = val[i++]; /* enu */ } if (istat = (unsigned char)val[i++]; if (ins = (unsigned char)val[i++]; if (i+3age = (float)val[i++]; if (iratio = (float)val[i]; sol->type = 1; /* postion type = enu */ if (MAXSOLQstat) sol->stat = SOLQ_NONE; return 1; } /* decode gsi f solution -----------------------------------------------------*/ int decode_solgsi(char *buff, const solopt_t *opt __attribute((unused)), sol_t *sol) { double val[MAXFIELD]; int i = 0,j; trace(4,"decode_solgsi:\n"); if (tonum(buff," ",val)<3) return 0; for (j = 0;j<3;j++) { sol->rr[j] = val[i++]; /* xyz */ } sol->stat = SOLQ_FIX; return 1; } /* decode solution position --------------------------------------------------*/ int decode_solpos(char *buff, const solopt_t *opt, sol_t *sol) { sol_t sol0 = {{0,0}, {0,0,0,0,0,0}, {0,0,0,0,0,0}, {0,0,0,0,0,0}, '0', '0', '0', 0, 0, 0 }; char *p = buff; trace(4,"decode_solpos: buff=%s\n",buff); *sol = sol0; /* decode solution time */ if (!(p = decode_soltime(p,opt,&sol->time))) { return 0; } /* decode solution position */ switch (opt->posf) { case SOLF_XYZ : return decode_solxyz(p,opt,sol); case SOLF_LLH : return decode_solllh(p,opt,sol); case SOLF_ENU : return decode_solenu(p,opt,sol); case SOLF_GSIF: return decode_solgsi(p,opt,sol); } return 0; } /* decode reference position -------------------------------------------------*/ void decode_refpos(char *buff, const solopt_t *opt, double *rb) { double val[MAXFIELD],pos[3]; int i,n; const char *sep = opt2sep(opt); trace(3,"decode_refpos: buff=%s\n",buff); if ((n = tonum(buff,sep,val))<3) return; if (opt->posf == SOLF_XYZ) { /* xyz */ for (i = 0;i<3;i++) rb[i] = val[i]; } else if (opt->degf == 0) { /* lat/lon/hgt (ddd.ddd) */ pos[0] = val[0]*D2R; pos[1] = val[1]*D2R; pos[2] = val[2]; pos2ecef(pos,rb); } else if (opt->degf == 1 && n >= 7) { /* lat/lon/hgt (ddd mm ss) */ pos[0] = dms2deg(val )*D2R; pos[1] = dms2deg(val+3)*D2R; pos[2] = val[6]; pos2ecef(pos,rb); } } /* decode solution -----------------------------------------------------------*/ int decode_sol(char *buff, const solopt_t *opt, sol_t *sol, double *rb) { char *p; trace(4,"decode_sol: buff=%s\n",buff); if (!strncmp(buff,COMMENTH,1)) { /* reference position */ if (!strstr(buff,"ref pos") && !strstr(buff,"slave pos")) return 0; if (!(p = strchr(buff,':'))) return 0; decode_refpos(p+1,opt,rb); return 0; } if (!strncmp(buff,"$GP",3)) { /* decode nmea */ if (!decode_nmea(buff,sol)) return 0; /* for time update only */ if (opt->posf != SOLF_NMEA && !strncmp(buff,"$GPRMC",6)) return 2; } else { /* decode position record */ if (!decode_solpos(buff,opt,sol)) return 0; } return 1; } /* decode solution options ---------------------------------------------------*/ void decode_solopt(char *buff, solopt_t *opt) { char *p; trace(4,"decode_solhead: buff=%s\n",buff); if (strncmp(buff,COMMENTH,1) && strncmp(buff,"+",1)) return; if (strstr(buff,"GPST")) opt->times = TIMES_GPST; else if (strstr(buff,"UTC" )) opt->times = TIMES_UTC; else if (strstr(buff,"JST" )) opt->times = TIMES_JST; if ((p = strstr(buff,"x-ecef(m)"))) { opt->posf = SOLF_XYZ; opt->degf = 0; strncpy(opt->sep,p+9,1); opt->sep[1] = '\0'; } else if ((p = strstr(buff,"latitude(d'\")"))) { opt->posf = SOLF_LLH; opt->degf = 1; strncpy(opt->sep,p+14,1); opt->sep[1] = '\0'; } else if ((p = strstr(buff,"latitude(deg)"))) { opt->posf = SOLF_LLH; opt->degf = 0; strncpy(opt->sep,p+13,1); opt->sep[1] = '\0'; } else if ((p = strstr(buff,"e-baseline(m)"))) { opt->posf = SOLF_ENU; opt->degf = 0; strncpy(opt->sep,p+13,1); opt->sep[1] = '\0'; } else if ((p = strstr(buff,"+SITE/INF"))) { /* gsi f2/f3 solution */ opt->times = TIMES_GPST; opt->posf = SOLF_GSIF; opt->degf = 0; strcpy(opt->sep," "); } } /* read solution option ------------------------------------------------------*/ void readsolopt(FILE *fp, solopt_t *opt) { char buff[MAXSOLMSG+1]; int i; trace(3,"readsolopt:\n"); for (i = 0;fgets(buff,sizeof(buff),fp) && i<100;i++) { /* only 100 lines */ /* decode solution options */ decode_solopt(buff,opt); } } /* input solution data from stream --------------------------------------------- * input solution data from stream * args : unsigned char data I stream data * gtime_t ts I start time (ts.time == 0: from start) * gtime_t te I end time (te.time == 0: to end) * double tint I time interval (0: all) * int qflag I quality flag (0: all) * solbuf_t *solbuf IO solution buffer * return : status (1:solution received,0:no solution,-1:disconnect received) *-----------------------------------------------------------------------------*/ int inputsol(unsigned char data, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t *opt, solbuf_t *solbuf) { sol_t sol = {{0,0}, {0,0,0,0,0,0}, {0,0,0,0,0,0}, {0,0,0,0,0,0}, '0', '0', '0', 0, 0, 0 }; int stat; trace(4,"inputsol: data=0x%02x\n",data); sol.time = solbuf->time; if (data == '$' || (!isprint(data) && data != '\r' && data != '\n')) { /* sync header */ solbuf->nb = 0; } solbuf->buff[solbuf->nb++] = data; if (data != '\n' && solbuf->nbbuff[solbuf->nb] = '\0'; solbuf->nb = 0; /* check disconnect message */ if (!strcmp((char *)solbuf->buff,MSG_DISCONN)) { trace(3,"disconnect received\n"); return -1; } /* decode solution */ if ((stat = decode_sol((char *)solbuf->buff,opt,&sol,solbuf->rb))>0) { solbuf->time = sol.time; /* update current time */ } if (stat != 1 || !screent(sol.time,ts,te,tint) || (qflag && sol.stat != qflag)) { return 0; } /* add solution to solution buffer */ return addsol(solbuf,&sol); } /* read solution data --------------------------------------------------------*/ int readsoldata(FILE *fp, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t *opt, solbuf_t *solbuf) { int c; trace(3,"readsoldata:\n"); while ((c = fgetc(fp)) != EOF) { /* input solution */ inputsol((unsigned char)c,ts,te,tint,qflag,opt,solbuf); } return solbuf->n>0; } /* compare solution data -----------------------------------------------------*/ int cmpsol(const void *p1, const void *p2) { sol_t *q1 = (sol_t *)p1,*q2 = (sol_t *)p2; double tt = timediff(q1->time,q2->time); return tt<-0.0?-1:(tt>0.0?1:0); } /* sort solution data --------------------------------------------------------*/ int sort_solbuf(solbuf_t *solbuf) { sol_t *solbuf_data; trace(4,"sort_solbuf: n=%d\n",solbuf->n); if (solbuf->n <= 0) return 0; if (!(solbuf_data = (sol_t *)realloc(solbuf->data,sizeof(sol_t)*solbuf->n))) { trace(1,"sort_solbuf: memory allocation error\n"); free(solbuf->data); solbuf->data = NULL; solbuf->n = solbuf->nmax = 0; return 0; } solbuf->data = solbuf_data; qsort(solbuf->data,solbuf->n,sizeof(sol_t),cmpsol); solbuf->nmax = solbuf->n; solbuf->start = 0; solbuf->end = solbuf->n-1; return 1; } /* read solutions data from solution files ------------------------------------- * read solution data from soluiton files * args : char *files[] I solution files * int nfile I number of files * (gtime_t ts) I start time (ts.time == 0: from start) * (gtime_t te) I end time (te.time == 0: to end) * (double tint) I time interval (0: all) * (int qflag) I quality flag (0: all) * solbuf_t *solbuf O solution buffer * return : status (1:ok,0:no data or error) *-----------------------------------------------------------------------------*/ int readsolt(char *files[], int nfile, gtime_t ts, gtime_t te, double tint, int qflag, solbuf_t *solbuf) { FILE *fp; solopt_t opt = solopt_default; int i; trace(3,"readsolt: nfile=%d\n",nfile); initsolbuf(solbuf,0,0); for (i = 0;icyclic) { /* ring buffer */ if (solbuf->nmax <= 1) return 0; solbuf->data[solbuf->end] = *sol; if (++solbuf->end >= solbuf->nmax) solbuf->end = 0; if (solbuf->start == solbuf->end) { if (++solbuf->start >= solbuf->nmax) solbuf->start = 0; } else solbuf->n++; return 1; } if (solbuf->n >= solbuf->nmax) { solbuf->nmax = solbuf->nmax == 0?8192:solbuf->nmax*2; if (!(solbuf_data = (sol_t *)realloc(solbuf->data,sizeof(sol_t)*solbuf->nmax))) { trace(1,"addsol: memory allocation error\n"); free(solbuf->data); solbuf->data = NULL; solbuf->n = solbuf->nmax = 0; return 0; } solbuf->data = solbuf_data; } solbuf->data[solbuf->n++] = *sol; return 1; } /* get solution data from solution buffer -------------------------------------- * get solution data by index from solution buffer * args : solbuf_t *solbuf I solution buffer * int index I index of solution (0...) * return : solution data pointer (NULL: no solution, out of range) *-----------------------------------------------------------------------------*/ sol_t *getsol(solbuf_t *solbuf, int index) { trace(4,"getsol: index=%d\n",index); if (index<0 || solbuf->n <= index) return NULL; if ((index = solbuf->start+index) >= solbuf->nmax) { index -= solbuf->nmax; } return solbuf->data+index; } /* initialize solution buffer -------------------------------------------------- * initialize position solutions * args : solbuf_t *solbuf I solution buffer * int cyclic I solution data buffer type (0:linear,1:cyclic) * int nmax I initial number of solution data * return : status (1:ok,0:error) *-----------------------------------------------------------------------------*/ void initsolbuf(solbuf_t *solbuf, int cyclic, int nmax) { gtime_t time0 = {0, 0.0}; trace(3,"initsolbuf: cyclic=%d nmax=%d\n",cyclic,nmax); solbuf->n = solbuf->nmax = solbuf->start = solbuf->end = 0; solbuf->cyclic = cyclic; solbuf->time = time0; solbuf->data = NULL; if (cyclic) { if (nmax <= 2) nmax=2; if (!(solbuf->data = (sol_t*)malloc(sizeof(sol_t)*nmax))) { trace(1,"initsolbuf: memory allocation error\n"); return; } solbuf->nmax = nmax; } } /* free solution --------------------------------------------------------------- * free memory for solution buffer * args : solbuf_t *solbuf I solution buffer * return : none *-----------------------------------------------------------------------------*/ void freesolbuf(solbuf_t *solbuf) { trace(3,"freesolbuf: n=%d\n",solbuf->n); free(solbuf->data); solbuf->n = solbuf->nmax = solbuf->start = solbuf->end = 0; solbuf->data = NULL; } void freesolstatbuf(solstatbuf_t *solstatbuf) { trace(3,"freesolstatbuf: n=%d\n",solstatbuf->n); solstatbuf->n = solstatbuf->nmax = 0; free(solstatbuf->data); solstatbuf->data = NULL; } /* compare solution status ---------------------------------------------------*/ int cmpsolstat(const void *p1, const void *p2) { solstat_t *q1 = (solstat_t *)p1,*q2 = (solstat_t *)p2; double tt = timediff(q1->time,q2->time); return tt<-0.0?-1:(tt>0.0?1:0); } /* sort solution data --------------------------------------------------------*/ int sort_solstat(solstatbuf_t *statbuf) { solstat_t *statbuf_data; trace(4,"sort_solstat: n=%d\n",statbuf->n); if (statbuf->n <= 0) return 0; if (!(statbuf_data = (solstat_t*)realloc(statbuf->data,sizeof(solstat_t)*statbuf->n))) { trace(1,"sort_solstat: memory allocation error\n"); free(statbuf->data); statbuf->data = NULL; statbuf->n = statbuf->nmax = 0; return 0; } statbuf->data = statbuf_data; qsort(statbuf->data,statbuf->n,sizeof(solstat_t),cmpsolstat); statbuf->nmax = statbuf->n; return 1; } /* decode solution status ----------------------------------------------------*/ int decode_solstat(char *buff, solstat_t *stat) { static const solstat_t stat0 = {{0, 0.0}, '0', '0', 0, 0, 0, 0, '0', '0', 0, 0, 0, 0}; double tow,az,el,resp,resc; int n,week,sat,frq,vsat,snr,fix,slip,lock,outc,slipc,rejc; char id[32] = "",*p; trace(4,"decode_solstat: buff=%s\n",buff); if (strstr(buff,"$SAT") != buff) return 0; for (p = buff;*p;p++) if (*p == ',') *p = ' '; n = sscanf(buff,"$SAT%d%lf%s%d%lf%lf%lf%lf%d%d%d%d%d%d%d%d", &week,&tow,id,&frq,&az,&el,&resp,&resc,&vsat,&snr,&fix,&slip, &lock,&outc,&slipc,&rejc); if (n<15) { trace(2,"invalid format of solution status: %s\n",buff); return 0; } if ((sat = satid2no(id)) <= 0) { trace(2,"invalid satellite in solution status: %s\n",id); return 0; } *stat = stat0; stat->time = gpst2time(week,tow); stat->sat = (unsigned char)sat; stat->frq = (unsigned char)frq; stat->az = (float)(az*D2R); stat->el = (float)(el*D2R); stat->resp = (float)resp; stat->resc = (float)resc; stat->flag = (unsigned char)((vsat<<5)+(slip<<3)+fix); stat->snr = (unsigned char)(snr*4.0+0.5); stat->lock = (unsigned short)lock; stat->outc = (unsigned short)outc; stat->slipc = (unsigned short)slipc; stat->rejc = (unsigned short)rejc; return 1; } /* add solution status data --------------------------------------------------*/ void addsolstat(solstatbuf_t *statbuf, const solstat_t *stat) { solstat_t *statbuf_data; trace(4,"addsolstat:\n"); if (statbuf->n >= statbuf->nmax) { statbuf->nmax = statbuf->nmax == 0?8192:statbuf->nmax*2; if (!(statbuf_data = (solstat_t *)realloc(statbuf->data,sizeof(solstat_t)* statbuf->nmax))) { trace(1,"addsolstat: memory allocation error\n"); free(statbuf->data); statbuf->data = NULL; statbuf->n = statbuf->nmax = 0; return; } statbuf->data = statbuf_data; } statbuf->data[statbuf->n++] = *stat; } /* read solution status data -------------------------------------------------*/ int readsolstatdata(FILE *fp, gtime_t ts, gtime_t te, double tint, solstatbuf_t *statbuf) { solstat_t stat = {{0, 0.0}, '0', '0', 0, 0, 0, 0, '0', '0', 0, 0, 0, 0}; char buff[MAXSOLMSG+1]; trace(3,"readsolstatdata:\n"); while (fgets(buff,sizeof(buff),fp)) { /* decode solution status */ if (!decode_solstat(buff,&stat)) continue; /* add solution to solution buffer */ if (screent(stat.time,ts,te,tint)) { addsolstat(statbuf,&stat); } } return statbuf->n>0; } /* read solution status -------------------------------------------------------- * read solution status from solution status files * args : char *files[] I solution status files * int nfile I number of files * (gtime_t ts) I start time (ts.time == 0: from start) * (gtime_t te) I end time (te.time == 0: to end) * (double tint) I time interval (0: all) * solstatbuf_t *statbuf O solution status buffer * return : status (1:ok,0:no data or error) *-----------------------------------------------------------------------------*/ int readsolstatt(char *files[], int nfile, gtime_t ts, gtime_t te, double tint, solstatbuf_t *statbuf) { FILE *fp; char path[1024]; int i; trace(3,"readsolstatt: nfile=%d\n",nfile); statbuf->n = statbuf->nmax = 0; statbuf->data = NULL; for (i = 0;irr[0],sep,sol->rr[1],sep,sol->rr[2],sep,sol->stat,sep, sol->ns,sep,SQRT_SOL(sol->qr[0]),sep,SQRT_SOL(sol->qr[1]),sep,SQRT_SOL(sol->qr[2]), sep,sqvar(sol->qr[3]),sep,sqvar(sol->qr[4]),sep,sqvar(sol->qr[5]), sep,sol->age,sep,sol->ratio); return p-(char *)buff; } /* output solution as the form of lat/lon/height -----------------------------*/ int outpos(unsigned char *buff, const char *s, const sol_t *sol, const solopt_t *opt) { double pos[3],dms1[3],dms2[3],P[9],Q[9]; const char *sep = opt2sep(opt); char *p = (char *)buff; trace(3,"outpos :\n"); ecef2pos(sol->rr,pos); soltocov(sol,P); covenu(pos,P,Q); if (opt->height == 1) { /* geodetic height */ // pos[2] -= geoidh(pos); } if (opt->degf) { deg2dms(pos[0]*R2D,dms1); deg2dms(pos[1]*R2D,dms2); p += sprintf(p,"%s%s%4.0f%s%02.0f%s%08.5f%s%4.0f%s%02.0f%s%08.5f",s,sep, dms1[0],sep,dms1[1],sep,dms1[2],sep,dms2[0],sep,dms2[1],sep, dms2[2]); } else p += sprintf(p,"%s%s%14.9f%s%14.9f",s,sep,pos[0]*R2D,sep,pos[1]*R2D); p += sprintf(p,"%s%10.4f%s%3d%s%3d%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%6.2f%s%6.1f\n", sep,pos[2],sep,sol->stat,sep,sol->ns,sep,SQRT_SOL(Q[4]),sep, SQRT_SOL(Q[0]),sep,SQRT_SOL(Q[8]),sep,sqvar(Q[1]),sep,sqvar(Q[2]), sep,sqvar(Q[5]),sep,sol->age,sep,sol->ratio); return p-(char *)buff; } /* output solution as the form of e/n/u-baseline -----------------------------*/ int outenu(unsigned char *buff, const char *s, const sol_t *sol, const double *rb, const solopt_t *opt) { double pos[3],rr[3],enu[3],P[9],Q[9]; int i; const char *sep = opt2sep(opt); char *p = (char *)buff; trace(3,"outenu :\n"); for (i = 0;i<3;i++) rr[i] = sol->rr[i]-rb[i]; ecef2pos(rb,pos); soltocov(sol,P); covenu(pos,P,Q); ecef2enu(pos,rr,enu); p += sprintf(p,"%s%s%14.4f%s%14.4f%s%14.4f%s%3d%s%3d%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%6.2f%s%6.1f\n", s,sep,enu[0],sep,enu[1],sep,enu[2],sep,sol->stat,sep,sol->ns,sep, SQRT_SOL(Q[0]),sep,SQRT_SOL(Q[4]),sep,SQRT_SOL(Q[8]),sep,sqvar(Q[1]), sep,sqvar(Q[5]),sep,sqvar(Q[2]),sep,sol->age,sep,sol->ratio); return p-(char *)buff; } /* output solution in the form of nmea RMC sentence --------------------------*/ int outnmea_rmc(unsigned char *buff, const sol_t *sol) { static double dirp = 0.0; gtime_t time; double ep[6],pos[3],enuv[3],dms1[3],dms2[3],vel,dir,amag = 0.0; char *p = (char *)buff,*q,sum,*emag = (char*)"E"; trace(3,"outnmea_rmc:\n"); if (sol->stat <= SOLQ_NONE) { p += sprintf(p,"$GPRMC,,,,,,,,,,,,"); for (q=(char *)buff+1,sum = 0;*q;q++) sum ^= *q; p += sprintf(p,"*%02X%c%c",sum,0x0D,0x0A); return p-(char *)buff; } time = gpst2utc(sol->time); if (time.sec >= 0.995) {time.time++; time.sec = 0.0;} time2epoch(time,ep); ecef2pos(sol->rr,pos); ecef2enu(pos,sol->rr+3,enuv); vel = norm_rtk(enuv,3); if (vel >= 1.0) { dir = atan2(enuv[0],enuv[1])*R2D; if (dir<0.0) dir += 360.0; dirp = dir; } else dir = dirp; deg2dms(fabs(pos[0])*R2D,dms1); deg2dms(fabs(pos[1])*R2D,dms2); p += sprintf(p,"$GPRMC,%02.0f%02.0f%05.2f,A,%02.0f%010.7f,%s,%03.0f%010.7f,%s,%4.2f,%4.2f,%02.0f%02.0f%02d,%.1f,%s,%s", ep[3],ep[4],ep[5],dms1[0],dms1[1]+dms1[2]/60.0,pos[0] >= 0?"N":"S", dms2[0],dms2[1]+dms2[2]/60.0,pos[1] >= 0?"E":"W",vel/KNOT2M,dir, ep[2],ep[1],(int)ep[0]%100,amag,emag, sol->stat == SOLQ_DGPS || sol->stat == SOLQ_FLOAT || sol->stat == SOLQ_FIX?"D":"A"); for (q = (char *)buff+1,sum = 0;*q;q++) sum ^= *q; /* check-sum */ p += sprintf(p,"*%02X%c%c",sum,0x0D,0x0A); return p-(char *)buff; } /* output solution in the form of nmea GGA sentence --------------------------*/ int outnmea_gga(unsigned char *buff, const sol_t *sol) { gtime_t time; double h,ep[6],pos[3],dms1[3],dms2[3],dop = 1.0; int solq; char *p = (char *)buff,*q,sum; trace(3,"outnmea_gga:\n"); if (sol->stat <= SOLQ_NONE) { p += sprintf(p,"$GPGGA,,,,,,,,,,,,,,"); for (q = (char *)buff+1,sum = 0;*q;q++) sum ^= *q; p += sprintf(p,"*%02X%c%c",sum,0x0D,0x0A); return p-(char *)buff; } for (solq = 0;solq<8;solq++) if (solq_nmea[solq] == sol->stat) break; if (solq >= 8) solq = 0; time = gpst2utc(sol->time); if (time.sec >= 0.995) {time.time++; time.sec = 0.0;} time2epoch(time,ep); ecef2pos(sol->rr,pos); h = 0;//geoidh(pos); deg2dms(fabs(pos[0])*R2D,dms1); deg2dms(fabs(pos[1])*R2D,dms2); p += sprintf(p,"$GPGGA,%02.0f%02.0f%05.2f,%02.0f%010.7f,%s,%03.0f%010.7f,%s,%d,%02d,%.1f,%.3f,M,%.3f,M,%.1f,", ep[3],ep[4],ep[5],dms1[0],dms1[1]+dms1[2]/60.0,pos[0] >= 0?"N":"S", dms2[0],dms2[1]+dms2[2]/60.0,pos[1] >= 0?"E":"W",solq, sol->ns,dop,pos[2]-h,h,sol->age); for (q = (char *)buff+1,sum = 0;*q;q++) sum ^= *q; /* check-sum */ p += sprintf(p,"*%02X%c%c",sum,0x0D,0x0A); return p-(char *)buff; } /* output solution in the form of nmea GSA sentences -------------------------*/ int outnmea_gsa(unsigned char *buff, const sol_t *sol, const ssat_t *ssat) { double azel[MAXSAT*2],dop[4]; int i,sat,sys,nsat,prn[MAXSAT]; char *p = (char *)buff,*q,*s,sum; trace(3,"outnmea_gsa:\n"); if (sol->stat <= SOLQ_NONE) { p += sprintf(p,"$GPGSA,A,1,,,,,,,,,,,,,,,"); for (q = (char *)buff+1,sum = 0;*q;q++) sum ^= *q; p += sprintf(p,"*%02X%c%c",sum,0x0D,0x0A); return p-(char *)buff; } /* GPGSA: gps/sbas */ for (sat = 1,nsat = 0;sat <= MAXSAT&&nsat<12;sat++) { if (!ssat[sat-1].vs || ssat[sat-1].azel[1] <= 0.0) continue; sys = satsys(sat,prn+nsat); if (sys != SYS_GPS && sys != SYS_SBS) continue; if (sys == SYS_SBS) prn[nsat] += 33-MINPRNSBS; for (i = 0;i<2;i++) azel[i+nsat*2] = ssat[sat-1].azel[i]; nsat++; } if (nsat>0) { s = p; p += sprintf(p,"$GPGSA,A,%d",sol->stat <= 0?1:3); for (i = 0;i<12;i++) { if (i0) { s = p; p += sprintf(p,"$GLGSA,A,%d",sol->stat <= 0?1:3); for (i = 0;i<12;i++) { if (i0) { s = p; p += sprintf(p,"$GAGSA,A,%d",sol->stat <= 0?1:3); for (i = 0;i<12;i++) { if (istat <= SOLQ_NONE) { p += sprintf(p,"$GPGSV,1,1,0,,,,,,,,,,,,,,,,"); for (q = (char *)buff+1,sum = 0;*q;q++) sum ^= *q; p += sprintf(p,"*%02X%c%c",sum,0x0D,0x0A); return p-(char *)buff; } /* GPGSV: gps/sbas */ for (sat = 1,n = 0;sat0.0) sats[n++] = sat; } nmsg = n <= 0?0:(n-1)/4+1; for (i = k = 0;i0.0) sats[n++] = sat; } nmsg = n <= 0?0:(n-1)/4+1; for (i = k = 0;i0.0) sats[n++] = sat; } nmsg = n <= 0?0:(n-1)/4+1; for (i = k = 0;imode]); if (PMODE_DGPS <= opt->mode && opt->mode <= PMODE_FIXED) { p += sprintf(p,"%s freqs : %s\n",COMMENTH,s2[opt->nf-1]); } if (opt->mode>PMODE_SINGLE) { p += sprintf(p,"%s solution : %s\n",COMMENTH,s3[opt->soltype]); } p += sprintf(p,"%s elev mask : %.1f deg\n",COMMENTH,opt->elmin*R2D); if (opt->mode>PMODE_SINGLE) { p += sprintf(p,"%s dynamics : %s\n",COMMENTH,opt->dynamics?"on":"off"); p += sprintf(p,"%s tidecorr : %s\n",COMMENTH,opt->tidecorr?"on":"off"); } if (opt->mode <= PMODE_FIXED) { p += sprintf(p,"%s ionos opt : %s\n",COMMENTH,s4[opt->ionoopt]); } p += sprintf(p,"%s tropo opt : %s\n",COMMENTH,s5[opt->tropopt]); p += sprintf(p,"%s ephemeris : %s\n",COMMENTH,s6[opt->sateph]); if (opt->navsys != SYS_GPS) { p += sprintf(p,"%s navi sys :",COMMENTH); for (i = 0;sys[i];i++) { if (opt->navsys&sys[i]) p += sprintf(p," %s",s7[i]); } p += sprintf(p,"\n"); } if (PMODE_KINEMA <= opt->mode && opt->mode <= PMODE_FIXED) { p += sprintf(p,"%s amb res : %s\n",COMMENTH,s8[opt->modear]); if (opt->navsys&SYS_GLO) { p += sprintf(p,"%s amb glo : %s\n",COMMENTH,s9[opt->glomodear]); } if (opt->thresar[0]>0.0) { p += sprintf(p,"%s val thres : %.1f\n",COMMENTH,opt->thresar[0]); } } if (opt->mode == PMODE_MOVEB && opt->baseline[0]>0.0) { p += sprintf(p,"%s baseline : %.4f %.4f m\n",COMMENTH, opt->baseline[0],opt->baseline[1]); } for (i = 0;i<2;i++) { if (opt->mode == PMODE_SINGLE || (i >= 1 && opt->mode>PMODE_FIXED)) continue; p += sprintf(p,"%s antenna%d : %-21s (%7.4f %7.4f %7.4f)\n",COMMENTH, i+1,opt->anttype[i],opt->antdel[i][0],opt->antdel[i][1], opt->antdel[i][2]); } return p-(char *)buff; } /* output solution header ------------------------------------------------------ * output solution header to buffer * args : unsigned char *buff IO output buffer * solopt_t *opt I solution options * return : number of output bytes *-----------------------------------------------------------------------------*/ int outsolheads(unsigned char *buff, const solopt_t *opt) { const char *s1[] = {"WGS84","Tokyo"},*s2[] = {"ellipsoidal","geodetic"}; const char *s3[] = {"GPST","UTC ","JST "},*sep = opt2sep(opt); char *p = (char *)buff; int timeu = opt->timeu<0?0:(opt->timeu>20?20:opt->timeu); trace(3,"outsolheads:\n"); if (opt->posf == SOLF_NMEA) return 0; if (opt->outhead) { p += sprintf(p,"%s (",COMMENTH); if (opt->posf == SOLF_XYZ) p += sprintf(p,"x/y/z-ecef=WGS84"); else if (opt->posf == SOLF_ENU) p += sprintf(p,"e/n/u-baseline=WGS84"); else p += sprintf(p,"lat/lon/height=%s/%s",s1[opt->datum],s2[opt->height]); p += sprintf(p,",Q=1:fix,2:float,3:sbas,4:dgps,5:single,6:ppp,ns=# of satellites)\n"); } p += sprintf(p,"%s %-*s%s",COMMENTH,(opt->timef?16:8)+timeu+1,s3[opt->times],sep); if (opt->posf == SOLF_LLH) { /* lat/lon/hgt */ if (opt->degf) { p += sprintf(p,"%16s%s%16s%s%10s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s%s%6s%s%6s\n", "latitude(d'\")",sep,"longitude(d'\")",sep,"height(m)",sep, "Q",sep,"ns",sep,"sdn(m)",sep,"sde(m)",sep,"sdu(m)",sep, "sdne(m)",sep,"sdeu(m)",sep,"sdue(m)",sep,"age(s)",sep,"ratio"); } else { p += sprintf(p,"%14s%s%14s%s%10s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s%s%6s%s%6s\n", "latitude(deg)",sep,"longitude(deg)",sep,"height(m)",sep, "Q",sep,"ns",sep,"sdn(m)",sep,"sde(m)",sep,"sdu(m)",sep, "sdne(m)",sep,"sdeu(m)",sep,"sdun(m)",sep,"age(s)",sep,"ratio"); } } else if (opt->posf == SOLF_XYZ) { /* x/y/z-ecef */ p += sprintf(p,"%14s%s%14s%s%14s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s%s%6s%s%6s\n", "x-ecef(m)",sep,"y-ecef(m)",sep,"z-ecef(m)",sep,"Q",sep,"ns",sep, "sdx(m)",sep,"sdy(m)",sep,"sdz(m)",sep,"sdxy(m)",sep, "sdyz(m)",sep,"sdzx(m)",sep,"age(s)",sep,"ratio"); } else if (opt->posf == SOLF_ENU) { /* e/n/u-baseline */ p += sprintf(p,"%14s%s%14s%s%14s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s%s%6s%s%6s\n", "e-baseline(m)",sep,"n-baseline(m)",sep,"u-baseline(m)",sep, "Q",sep,"ns",sep,"sde(m)",sep,"sdn(m)",sep,"sdu(m)",sep, "sden(m)",sep,"sdnu(m)",sep,"sdue(m)",sep,"age(s)",sep,"ratio"); } return p-(char *)buff; } /* output solution body -------------------------------------------------------- * output solution body to buffer * args : unsigned char *buff IO output buffer * sol_t *sol I solution * double *rb I base station position {x,y,z} (ecef) (m) * solopt_t *opt I solution options * return : number of output bytes *-----------------------------------------------------------------------------*/ int outsols(unsigned char *buff, const sol_t *sol, const double *rb, const solopt_t *opt) { gtime_t time,ts = {0, 0.0}; double gpst; int week,timeu; const char *sep = opt2sep(opt); char s[255]; unsigned char *p = buff; trace(3,"outsols :\n"); if (opt->posf == SOLF_NMEA) { if (opt->nmeaintv[0] < 0.0) return 0; if (!screent(sol->time, ts, ts, opt->nmeaintv[0])) return 0; } if (sol->stat <= SOLQ_NONE || (opt->posf == SOLF_ENU && norm_rtk(rb,3) <= 0.0)) { return 0; } timeu = opt->timeu < 0 ? 0 : (opt->timeu > 20 ? 20 : opt->timeu); time = sol->time; if (opt->times >= TIMES_UTC) time = gpst2utc(time); if (opt->times == TIMES_JST) time = timeadd(time, 9*3600.0); if (opt->timef) time2str(time, s, timeu); else { gpst = time2gpst(time, &week); if (86400 * 7 - gpst < 0.5 / pow(10.0, timeu)) { week++; gpst = 0.0; } snprintf(s, 255, "%4d%s%*.*f", week, sep, 6 + (timeu <= 0 ? 0 : timeu+1), timeu, gpst); } switch (opt->posf) { case SOLF_LLH: p += outpos(p, s, sol, opt); break; case SOLF_XYZ: p += outecef(p, s, sol, opt); break; case SOLF_ENU: p += outenu(p, s, sol, rb, opt); break; case SOLF_NMEA: p += outnmea_rmc(p, sol); p += outnmea_gga(p, sol); break; } return p - buff; } /* output solution extended ---------------------------------------------------- * output solution exteneded infomation * args : unsigned char *buff IO output buffer * sol_t *sol I solution * ssat_t *ssat I satellite status * solopt_t *opt I solution options * return : number of output bytes * notes : only support nmea *-----------------------------------------------------------------------------*/ int outsolexs(unsigned char *buff, const sol_t *sol, const ssat_t *ssat, const solopt_t *opt) { gtime_t ts = {0, 0.0}; unsigned char *p = buff; trace(3,"outsolexs:\n"); if (opt->posf == SOLF_NMEA) { if (opt->nmeaintv[1]<0.0) return 0; if (!screent(sol->time,ts,ts,opt->nmeaintv[1])) return 0; } if (opt->posf == SOLF_NMEA) { p += outnmea_gsa(p,sol,ssat); p += outnmea_gsv(p,sol,ssat); } return p-buff; } /* output processing option ---------------------------------------------------- * output processing option to file * args : FILE *fp I output file pointer * prcopt_t *opt I processing options * return : none *-----------------------------------------------------------------------------*/ void outprcopt(FILE *fp, const prcopt_t *opt) { unsigned char buff[MAXSOLMSG+1]; int n; trace(3, "outprcopt:\n"); if ((n = outprcopts(buff, opt)) > 0) { fwrite(buff, n, 1, fp); } } /* output solution header ------------------------------------------------------ * output solution heade to file * args : FILE *fp I output file pointer * solopt_t *opt I solution options * return : none *-----------------------------------------------------------------------------*/ void outsolhead(FILE *fp, const solopt_t *opt) { unsigned char buff[MAXSOLMSG+1]; int n; trace(3, "outsolhead:\n"); if ((n = outsolheads(buff, opt)) > 0) { fwrite(buff, n, 1, fp); } } /* output solution body -------------------------------------------------------- * output solution body to file * args : FILE *fp I output file pointer * sol_t *sol I solution * double *rb I base station position {x,y,z} (ecef) (m) * solopt_t *opt I solution options * return : none *-----------------------------------------------------------------------------*/ void outsol(FILE *fp, const sol_t *sol, const double *rb, const solopt_t *opt) { unsigned char buff[MAXSOLMSG+1]; int n; trace(3, "outsol :\n"); if ((n = outsols(buff, sol, rb, opt)) > 0) { fwrite(buff, n, 1, fp); } } /* output solution extended ---------------------------------------------------- * output solution exteneded infomation to file * args : FILE *fp I output file pointer * sol_t *sol I solution * ssat_t *ssat I satellite status * solopt_t *opt I solution options * return : output size (bytes) * notes : only support nmea *-----------------------------------------------------------------------------*/ void outsolex(FILE *fp, const sol_t *sol, const ssat_t *ssat, const solopt_t *opt) { unsigned char buff[MAXSOLMSG+1]; int n; trace(3, "outsolex:\n"); if ((n = outsolexs(buff, sol, ssat, opt)) > 0) { fwrite(buff, n, 1, fp); } }