/*! * \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. * * SPDX-License-Identifier: BSD-2-Clause * * * ----------------------------------------------------------------------------- */ #include "rtklib_solution.h" #include "rtklib_rtkcmn.h" #include "rtklib_rtksvr.h" #include #include #include /* constants and macros ------------------------------------------------------*/ #define SQR_SOL(x) ((x) < 0.0 ? -(x) * (x) : (x) * (x)) #define SQRT_SOL(x) ((x) < 0.0 ? 0.0 : std::sqrt(x)) const int MAXFIELD = 64; /* max number of fields in a record */ 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 " "; } if (!strcmp(opt->sep, "\\t")) { return "\t"; } return opt->sep; } /* separate fields -----------------------------------------------------------*/ int tonum(char *buff, const char *sep, double *v) { int n; int len = static_cast(strlen(sep)); char *p; char *q; for (p = buff, n = 0; n < MAXFIELD; p = q + len) { if ((q = strstr(p, sep))) { *q = '\0'; } if (*p) { v[n++] = atof(p); } if (!q) { break; } } return n; } /* sqrt of covariance --------------------------------------------------------*/ double sqvar(double covar) { return covar < 0.0 ? -sqrt(-covar) : sqrt(covar); } /* convert ddmm.mm in nmea format to deg -------------------------------------*/ double dmm2deg(double dmm) { return floor(dmm / 100.0) + fmod(dmm, 100.0) / 60.0; } /* convert time in nmea format to time ---------------------------------------*/ void septime(double t, double *t1, double *t2, double *t3) { *t1 = floor(t / 10000.0); t -= *t1 * 10000.0; *t2 = floor(t / 100.0); *t3 = t - *t2 * 100.0; } /* solution to covariance ----------------------------------------------------*/ void soltocov(const sol_t *sol, double *P) { P[0] = sol->qr[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] = static_cast(P[0]); /* xx or ee */ sol->qr[1] = static_cast(P[4]); /* yy or nn */ sol->qr[2] = static_cast(P[8]); /* zz or uu */ sol->qr[3] = static_cast(P[1]); /* xy or en */ sol->qr[4] = static_cast(P[5]); /* yz or nu */ sol->qr[5] = static_cast(P[2]); /* zx or ue */ } /* decode nmea gprmc: recommended minimum data for gps -----------------------*/ int decode_nmearmc(char **val, int n, sol_t *sol) { double tod = 0.0; double lat = 0.0; double lon = 0.0; double vel = 0.0; double dir = 0.0; double date = 0.0; double ang = 0.0; double ep[6]; double pos[3] = {0}; char act = ' '; char ns = 'N'; char ew = 'E'; char mew = 'E'; char mode = 'A'; int i; trace(4, "decode_nmearmc: n=%d\n", n); for (i = 0; i < n; i++) { switch (i) { case 0: tod = atof(val[i]); break; /* time in utc (hhmmss) */ case 1: act = *val[i]; break; /* A=active,V=void */ case 2: lat = atof(val[i]); break; /* latitude (ddmm.mmm) */ case 3: ns = *val[i]; break; /* N=north,S=south */ case 4: lon = atof(val[i]); break; /* longitude (dddmm.mmm) */ case 5: ew = *val[i]; break; /* E=east,W=west */ case 6: vel = atof(val[i]); break; /* speed (knots) */ case 7: dir = atof(val[i]); break; /* track angle (deg) */ case 8: date = atof(val[i]); break; /* date (ddmmyy) */ case 9: ang = atof(val[i]); break; /* magnetic variation */ case 10: mew = *val[i]; break; /* E=east,W=west */ case 11: mode = *val[i]; break; /* mode indicator (>nmea 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; /* position 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; double lat = 0.0; double lon = 0.0; double hdop = 0.0; double alt = 0.0; double msl = 0.0; double ep[6]; double tt; double pos[3] = {0}; char ns = 'N'; char ew = 'E'; char ua = ' '; char um = ' '; int i; int solq = 0; int nrcv = 0; trace(4, "decode_nmeagga: n=%d\n", n); for (i = 0; i < n; i++) { switch (i) { case 0: tod = atof(val[i]); break; /* time in utc (hhmmss) */ case 1: lat = atof(val[i]); break; /* latitude (ddmm.mmm) */ case 2: ns = *val[i]; break; /* N=north,S=south */ case 3: lon = atof(val[i]); break; /* longitude (dddmm.mmm) */ case 4: ew = *val[i]; break; /* E=east,W=west */ case 5: solq = atoi(val[i]); break; /* fix quality */ case 6: nrcv = atoi(val[i]); break; /* # of satellite tracked */ case 7: hdop = atof(val[i]); break; /* hdop */ case 8: alt = atof(val[i]); break; /* altitude in msl */ case 9: ua = *val[i]; break; /* unit (M) */ case 10: msl = atof(val[i]); break; /* height of geoid */ case 11: um = *val[i]; break; /* unit (M) */ } } if ((ns != 'N' && ns != 'S') || (ew != 'E' && ew != 'W')) { trace(2, "invalid nmea gpgga format\n"); return 0; } if (sol->time.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; /* position 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; char *q; char *val[MAXFIELD] = {nullptr}; int n = 0; trace(4, "decode_nmea: buff=%s\n", buff); /* parse fields */ for (p = buff; *p && n < MAXFIELD; p = q + 1) { if ((q = strchr(p, ',')) || (q = strchr(p, '*'))) { val[n++] = p; *q = '\0'; } else { break; } } /* decode nmea sentence */ if (val[0]) { if (!strcmp(val[0], "$GPRMC")) { return decode_nmearmc(val + 1, n - 1, sol); } if (!strcmp(val[0], "$GPGGA")) { return decode_nmeagga(val + 1, n - 1, sol); } } return 0; } /* decode solution time ------------------------------------------------------*/ char *decode_soltime(char *buff, const solopt_t *opt, gtime_t *time) { double v[MAXFIELD]; char *p; char *q; char s[64] = " "; int n; int len; trace(4, "decode_soltime:\n"); if (!strcmp(opt->sep, "\\t")) { std::strncpy(s, "\t", 2); } else if (*opt->sep) { std::strncpy(s, opt->sep, 64); } len = static_cast(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 nullptr; } for (p++; isdigit(static_cast(*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 nullptr; } *time = timeadd(epoch2time(v), -12.0 * 3600.0); if (!(p = strchr(buff, ':')) || !(p = strchr(p + 1, ':'))) { return nullptr; } for (p++; isdigit(static_cast(*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(static_cast(v[0]), v[1]); return p; } return nullptr; } /* decode x/y/z-ecef ---------------------------------------------------------*/ int decode_solxyz(char *buff, const solopt_t *opt, sol_t *sol) { double val[MAXFIELD]; double P[9] = {0}; int i = 0; int j; int 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 (i < n) { sol->stat = static_cast(val[i++]); } if (i < n) { sol->ns = static_cast(val[i++]); } if (i + 3 < n) { P[0] = val[i] * val[i]; i++; /* sdx */ P[4] = val[i] * val[i]; i++; /* sdy */ P[8] = val[i] * val[i]; i++; /* sdz */ if (i + 3 < n) { P[1] = P[3] = SQR_SOL(val[i]); i++; /* sdxy */ P[5] = P[7] = SQR_SOL(val[i]); i++; /* sdyz */ P[2] = P[6] = SQR_SOL(val[i]); i++; /* sdzx */ } covtosol(P, sol); } if (i < n) { sol->age = static_cast(val[i++]); } if (i < n) { sol->ratio = static_cast(val[i]); } sol->type = 0; /* position type = xyz */ if (MAXSOLQ < sol->stat) { 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]; double pos[3]; double Q[9] = {0}; double P[9]; int i = 0; int 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 (i < n) { sol->stat = static_cast(val[i++]); } if (i < n) { sol->ns = static_cast(val[i++]); } if (i + 3 < n) { Q[4] = val[i] * val[i]; i++; /* sdn */ Q[0] = val[i] * val[i]; i++; /* sde */ Q[8] = val[i] * val[i]; i++; /* sdu */ if (i + 3 < n) { Q[1] = Q[3] = SQR_SOL(val[i]); i++; /* sdne */ Q[2] = Q[6] = SQR_SOL(val[i]); i++; /* sdeu */ Q[5] = Q[7] = SQR_SOL(val[i]); i++; /* sdun */ } covecef(pos, Q, P); covtosol(P, sol); } if (i < n) { sol->age = static_cast(val[i++]); } if (i < n) { sol->ratio = static_cast(val[i]); } sol->type = 0; /* position type = xyz */ if (MAXSOLQ < sol->stat) { 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]; double Q[9] = {0}; int i = 0; int j; int 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 (i < n) { sol->stat = static_cast(val[i++]); } if (i < n) { sol->ns = static_cast(val[i++]); } if (i + 3 < n) { Q[0] = val[i] * val[i]; i++; /* sde */ Q[4] = val[i] * val[i]; i++; /* sdn */ Q[8] = val[i] * val[i]; i++; /* sdu */ if (i + 3 < n) { Q[1] = Q[3] = SQR_SOL(val[i]); i++; /* sden */ Q[5] = Q[7] = SQR_SOL(val[i]); i++; /* sdnu */ Q[2] = Q[6] = SQR_SOL(val[i]); i++; /* sdue */ } covtosol(Q, sol); } if (i < n) { sol->age = static_cast(val[i++]); } if (i < n) { sol->ratio = static_cast(val[i]); } sol->type = 1; /* position type = enu */ if (MAXSOLQ < sol->stat) { 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; int 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]; double pos[3]; int i; int 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) != 0 && strncmp(buff, "+", 1) != 0) { 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; std::strncpy(opt->sep, " ", 2); } } /* 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->nb < MAXSOLMSG) { return 0; /* sync trailer */ } solbuf->buff[solbuf->nb] = '\0'; solbuf->nb = 0; /* check disconnect message */ if (!strcmp(reinterpret_cast(solbuf->buff), MSG_DISCONN)) { trace(3, "disconnect received\n"); return -1; } /* decode solution */ if ((stat = decode_sol(reinterpret_cast(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(static_cast(c), ts, te, tint, qflag, opt, solbuf); } return solbuf->n > 0; } /* compare solution data -----------------------------------------------------*/ int cmpsol(const void *p1, const void *p2) { const auto *q1 = static_cast(p1); const auto *q2 = static_cast(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 = static_cast(realloc(solbuf->data, sizeof(sol_t) * solbuf->n)))) { trace(1, "sort_solbuf: memory allocation error\n"); free(solbuf->data); solbuf->data = nullptr; 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; i < nfile; i++) { if (!(fp = fopen(files[i], "rbe"))) { trace(1, "readsolt: file open error %s\n", files[i]); continue; } /* read solution options in header */ readsolopt(fp, &opt); rewind(fp); /* read solution data */ if (!readsoldata(fp, ts, te, tint, qflag, &opt, solbuf)) { trace(1, "readsolt: no solution in %s\n", files[i]); } fclose(fp); } return sort_solbuf(solbuf); } int readsol(char *files[], int nfile, solbuf_t *sol) { gtime_t time = {0, 0.0}; trace(3, "readsol: nfile=%d\n", nfile); return readsolt(files, nfile, time, time, 0.0, 0, sol); } /* add solution data to solution buffer ---------------------------------------- * add solution data to solution buffer * args : solbuf_t *solbuf IO solution buffer * sol_t *sol I solution data * return : status (1:ok,0:error) *-----------------------------------------------------------------------------*/ int addsol(solbuf_t *solbuf, const sol_t *sol) { sol_t *solbuf_data; trace(4, "addsol:\n"); if (solbuf->cyclic) { /* 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 = static_cast(realloc(solbuf->data, sizeof(sol_t) * solbuf->nmax)))) { trace(1, "addsol: memory allocation error\n"); free(solbuf->data); solbuf->data = nullptr; 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 nullptr; } 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 = nullptr; if (cyclic) { if (nmax <= 2) { nmax = 2; } if (!(solbuf->data = static_cast(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 = nullptr; } void freesolstatbuf(solstatbuf_t *solstatbuf) { trace(3, "freesolstatbuf: n=%d\n", solstatbuf->n); solstatbuf->n = solstatbuf->nmax = 0; free(solstatbuf->data); solstatbuf->data = nullptr; } /* compare solution status ---------------------------------------------------*/ int cmpsolstat(const void *p1, const void *p2) { const auto *q1 = static_cast(p1); const auto *q2 = static_cast(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 = static_cast(realloc(statbuf->data, sizeof(solstat_t) * statbuf->n)))) { trace(1, "sort_solstat: memory allocation error\n"); free(statbuf->data); statbuf->data = nullptr; 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; double az; double el; double resp; double resc; int n; int week; int sat; int frq; int vsat; int snr; int fix; int slip; int lock; int outc; int slipc; int rejc; char id[32] = ""; char *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 = static_cast(sat); stat->frq = static_cast(frq); stat->az = static_cast(az * D2R); stat->el = static_cast(el * D2R); stat->resp = static_cast(resp); stat->resc = static_cast(resc); stat->flag = static_cast((vsat << 5) + (slip << 3) + fix); stat->snr = static_cast(std::lround(snr * 4.0)); stat->lock = static_cast(lock); stat->outc = static_cast(outc); stat->slipc = static_cast(slipc); stat->rejc = static_cast(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 = static_cast(realloc(statbuf->data, sizeof(solstat_t) * statbuf->nmax)))) { trace(1, "addsolstat: memory allocation error\n"); free(statbuf->data); statbuf->data = nullptr; 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 = nullptr; for (i = 0; i < nfile; i++) { std::snprintf(path, sizeof(path), "%s.stat", files[i]); if (!(fp = fopen(path, "re"))) { trace(1, "readsolstatt: file open error %s\n", path); continue; } /* read solution status data */ if (!readsolstatdata(fp, ts, te, tint, statbuf)) { trace(1, "readsolt: no solution in %s\n", path); } fclose(fp); } return sort_solstat(statbuf); } int readsolstat(char *files[], int nfile, solstatbuf_t *statbuf) { gtime_t time = {0, 0.0}; trace(3, "readsolstat: nfile=%d\n", nfile); return readsolstatt(files, nfile, time, time, 0.0, statbuf); } /* output solution as the form of x/y/z-ecef ---------------------------------*/ int outecef(unsigned char *buff, const char *s, const sol_t *sol, const solopt_t *opt) { const char *sep = opt2sep(opt); char *p = reinterpret_cast(buff); trace(3, "outecef:\n"); p += std::snprintf(p, MAXSOLBUF, "%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, sol->rr[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 - reinterpret_cast(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]; double dms1[3]; double dms2[3]; double P[9]; double Q[9]; const char *sep = opt2sep(opt); char *p = reinterpret_cast(buff); char *start; start = p; 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 += std::snprintf(p, MAXSOLMSG - (p - start), "%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 += std::snprintf(p, MAXSOLMSG - (p - start), "%s%s%14.9f%s%14.9f", s, sep, pos[0] * R2D, sep, pos[1] * R2D); } p += std::snprintf(p, MAXSOLMSG - (p - start), "%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 - reinterpret_cast(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]; double rr[3]; double enu[3]; double P[9]; double Q[9]; int i; const char *sep = opt2sep(opt); char *p = reinterpret_cast(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 += std::snprintf(p, MAXSOLMSG, "%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 - reinterpret_cast(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]; double pos[3]; double enuv[3]; double dms1[3]; double dms2[3]; double vel; double dir; double amag = 0.0; char *p = reinterpret_cast(buff); char *q; char sum; char *emag = const_cast("E"); const int MSG_TAIL = 6; trace(3, "outnmea_rmc:\n"); if (sol->stat <= SOLQ_NONE) { p += std::snprintf(p, MAXSOLBUF - MSG_TAIL, "$GPRMC,,,,,,,,,,,,"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(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 += std::snprintf(p, MAXSOLBUF - MSG_TAIL, "$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], static_cast(ep[0]) % 100, amag, emag, sol->stat == SOLQ_DGPS || sol->stat == SOLQ_FLOAT || sol->stat == SOLQ_FIX ? "D" : "A"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(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; double ep[6]; double pos[3]; double dms1[3]; double dms2[3]; double dop = 1.0; int solq; char *p = reinterpret_cast(buff); char *q; char sum; const int MSG_TAIL = 6; trace(3, "outnmea_gga:\n"); if (sol->stat <= SOLQ_NONE) { p += std::snprintf(p, MAXSOLBUF - MSG_TAIL, "$GPGGA,,,,,,,,,,,,,,"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(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 += std::snprintf(p, MAXSOLBUF - MSG_TAIL, "$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 = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(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]; double dop[4]; int i; int sat; int sys; int nsat; int prn[MAXSAT]; char *p = reinterpret_cast(buff); char *q; char *s; char sum; const int MSG_TAIL = 6; const int COMMA_LENGTH = 2; const int MAX_LENGTH_INT = 10; trace(3, "outnmea_gsa:\n"); if (sol->stat <= SOLQ_NONE) { p += std::snprintf(p, MAXSOLBUF - MSG_TAIL, "$GPGSA,A,1,,,,,,,,,,,,,,,"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(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 += std::snprintf(p, MAXSOLBUF, "$GPGSA,A,%d", sol->stat <= 0 ? 1 : 3); for (i = 0; i < 12; i++) { if (i < nsat) { p += std::snprintf(p, MAX_LENGTH_INT + 2, ",%02d", prn[i]); } else { p += std::snprintf(p, COMMA_LENGTH, ","); } } dops(nsat, azel, 0.0, dop); p += std::snprintf(p, MAXSOLBUF - (p - s), ",%3.1f,%3.1f,%3.1f,1", dop[1], dop[2], dop[3]); for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); } /* GLGSA: glonass */ for (sat = 1, nsat = 0; sat <= MAXSAT && nsat < 12; sat++) { if (!ssat[sat - 1].vs || ssat[sat - 1].azel[1] <= 0.0) { continue; } if (satsys(sat, prn + nsat) != SYS_GLO) { continue; } for (i = 0; i < 2; i++) { azel[i + nsat * 2] = ssat[sat - 1].azel[i]; } nsat++; } if (nsat > 0) { s = p; const int GLGSA_LENGTH = 11; p += std::snprintf(p, GLGSA_LENGTH, "$GLGSA,A,%d", sol->stat <= 0 ? 1 : 3); for (i = 0; i < 12; i++) { if (i < nsat) { p += std::snprintf(p, MAX_LENGTH_INT + 2, ",%02d", prn[i] + 64); } else { p += std::snprintf(p, COMMA_LENGTH, ","); } } dops(nsat, azel, 0.0, dop); p += std::snprintf(p, MAXSOLBUF - (p - s), ",%3.1f,%3.1f,%3.1f,2", dop[1], dop[2], dop[3]); for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); } /* GAGSA: galileo */ for (sat = 1, nsat = 0; sat <= MAXSAT && nsat < 12; sat++) { if (!ssat[sat - 1].vs || ssat[sat - 1].azel[1] <= 0.0) { continue; } if (satsys(sat, prn + nsat) != SYS_GAL) { continue; } for (i = 0; i < 2; i++) { azel[i + nsat * 2] = ssat[sat - 1].azel[i]; } nsat++; } if (nsat > 0) { s = p; p += std::snprintf(p, MAXSOLBUF, "$GAGSA,A,%d", sol->stat <= 0 ? 1 : 3); for (i = 0; i < 12; i++) { if (i < nsat) { p += std::snprintf(p, MAX_LENGTH_INT + 2, ",%02d", prn[i]); } else { p += std::snprintf(p, COMMA_LENGTH, ","); } } dops(nsat, azel, 0.0, dop); p += std::snprintf(p, MAXSOLBUF - (p - s), ",%3.1f,%3.1f,%3.1f,3", dop[1], dop[2], dop[3]); for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); } /* BDGSA: beidou */ for (sat = 1, nsat = 0; sat <= MAXSAT && nsat < 12; sat++) { if (!ssat[sat - 1].vs || ssat[sat - 1].azel[1] <= 0.0) { continue; } if (satsys(sat, prn + nsat) != SYS_BDS) { continue; } for (i = 0; i < 2; i++) { azel[i + nsat * 2] = ssat[sat - 1].azel[i]; } nsat++; } if (nsat > 0) { s = p; p += std::snprintf(p, MAXSOLBUF, "$BDGSA,A,%d", sol->stat <= 0 ? 1 : 3); for (i = 0; i < 12; i++) { if (i < nsat) { p += std::snprintf(p, MAX_LENGTH_INT + 2, ",%02d", prn[i]); } else { p += std::snprintf(p, COMMA_LENGTH, ","); } } dops(nsat, azel, 0.0, dop); p += std::snprintf(p, MAXSOLBUF - (p - s), ",%3.1f,%3.1f,%3.1f,3", dop[1], dop[2], dop[3]); for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); } return p - reinterpret_cast(buff); } /* output solution in the form of nmea GSV sentence --------------------------*/ int outnmea_gsv(unsigned char *buff, const sol_t *sol, const ssat_t *ssat) { double az; double el; double snr; int i; int j; int k; int n; int sat; int prn; int sys; int nmsg; int sats[MAXSAT]; char *p = reinterpret_cast(buff); char *q; char *s; char sum; const int MSG_TAIL = 6; trace(3, "outnmea_gsv:\n"); if (sol->stat <= SOLQ_NONE) { p += std::snprintf(p, MAXSOLBUF, "$GPGSV,1,1,0,,,,,,,,,,,,,,,,"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(buff); } /* GPGSV: gps/sbas */ for (sat = 1, n = 0; sat < MAXSAT && n < 12; sat++) { sys = satsys(sat, &prn); if (sys != SYS_GPS && sys != SYS_SBS) { continue; } if (ssat[sat - 1].vs && ssat[sat - 1].azel[1] > 0.0) { sats[n++] = sat; } } nmsg = n <= 0 ? 0 : (n - 1) / 4 + 1; for (i = k = 0; i < nmsg; i++) { s = p; p += std::snprintf(p, MAXSOLBUF, "$GPGSV,%d,%d,%02d", nmsg, i + 1, n); for (j = 0; j < 4; j++, k++) { if (k < n) { if (satsys(sats[k], &prn) == SYS_SBS) { prn += 33 - MINPRNSBS; } az = ssat[sats[k] - 1].azel[0] * R2D; if (az < 0.0) { az += 360.0; } el = ssat[sats[k] - 1].azel[1] * R2D; snr = ssat[sats[k] - 1].snr[0] * 0.25; p += std::snprintf(p, MAXSOLBUF - (s - p), ",%02d,%02.0f,%03.0f,%02.0f", prn, el, az, snr); } else { p += std::snprintf(p, MAXSOLBUF - (s - p), ",,,,"); } } p += std::snprintf(p, MAXSOLBUF - (s - p), ",1"); /* L1C/A */ for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); } /* GLGSV: glonass */ for (sat = 1, n = 0; sat < MAXSAT && n < 12; sat++) { if (satsys(sat, &prn) != SYS_GLO) { continue; } if (ssat[sat - 1].vs && ssat[sat - 1].azel[1] > 0.0) { sats[n++] = sat; } } nmsg = n <= 0 ? 0 : (n - 1) / 4 + 1; for (i = k = 0; i < nmsg; i++) { s = p; p += std::snprintf(p, MAXSOLBUF, "$GLGSV,%d,%d,%02d", nmsg, i + 1, n); for (j = 0; j < 4; j++, k++) { if (k < n) { satsys(sats[k], &prn); prn += 64; /* 65-99 */ az = ssat[sats[k] - 1].azel[0] * R2D; if (az < 0.0) { az += 360.0; } el = ssat[sats[k] - 1].azel[1] * R2D; snr = ssat[sats[k] - 1].snr[0] * 0.25; p += std::snprintf(p, MAXSOLBUF - (s - p), ",%02d,%02.0f,%03.0f,%02.0f", prn, el, az, snr); } else { p += std::snprintf(p, MAXSOLBUF - (s - p), ",,,,"); } } p += std::snprintf(p, MAXSOLBUF - (s - p), ",1"); /* L1C/A */ for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); } /* GAGSV: galileo */ for (sat = 1, n = 0; sat < MAXSAT && n < 12; sat++) { if (satsys(sat, &prn) != SYS_GAL) { continue; } if (ssat[sat - 1].vs && ssat[sat - 1].azel[1] > 0.0) { sats[n++] = sat; } } nmsg = n <= 0 ? 0 : (n - 1) / 4 + 1; for (i = k = 0; i < nmsg; i++) { s = p; p += std::snprintf(p, MAXSOLBUF, "$GAGSV,%d,%d,%02d", nmsg, i + 1, n); for (j = 0; j < 4; j++, k++) { if (k < n) { satsys(sats[k], &prn); /* 1-36 */ az = ssat[sats[k] - 1].azel[0] * R2D; if (az < 0.0) { az += 360.0; } el = ssat[sats[k] - 1].azel[1] * R2D; snr = ssat[sats[k] - 1].snr[0] * 0.25; p += std::snprintf(p, MAXSOLBUF - (s - p), ",%02d,%02.0f,%03.0f,%02.0f", prn, el, az, snr); } else { p += std::snprintf(p, MAXSOLBUF - (s - p), ",,,,"); } } p += std::snprintf(p, MAXSOLBUF - (s - p), ",7"); /* L1BC */ for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); } /* BDGSV: beidou */ for (sat = 1, n = 0; sat < MAXSAT && n < 12; sat++) { if (satsys(sat, &prn) != SYS_BDS) { continue; } if (ssat[sat - 1].vs && ssat[sat - 1].azel[1] > 0.0) { sats[n++] = sat; } } nmsg = n <= 0 ? 0 : (n - 1) / 4 + 1; for (i = k = 0; i < nmsg; i++) { s = p; p += std::snprintf(p, MAXSOLBUF, "$BDGSV,%d,%d,%02d", nmsg, i + 1, n); for (j = 0; j < 4; j++, k++) { if (k < n) { satsys(sats[k], &prn); /* 1-63 */ az = ssat[sats[k] - 1].azel[0] * R2D; if (az < 0.0) { az += 360.0; } el = ssat[sats[k] - 1].azel[1] * R2D; snr = ssat[sats[k] - 1].snr[0] * 0.25; p += std::snprintf(p, MAXSOLBUF - (s - p), ",%02d,%02.0f,%03.0f,%02.0f", prn, el, az, snr); } else { p += std::snprintf(p, MAXSOLBUF - (s - p), ",,,,"); } } p += std::snprintf(p, MAXSOLBUF - (s - p), ",1"); for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += std::snprintf(p, MSG_TAIL, "*%02X%c%c", sum, 0x0D, 0x0A); } return p - reinterpret_cast(buff); } /* output processing options --------------------------------------------------- * output processing options to buffer * args : unsigned char *buff IO output buffer * prcopt_t *opt I processign options * return : number of output bytes *-----------------------------------------------------------------------------*/ int outprcopts(unsigned char *buff, const prcopt_t *opt) { const int sys[] = {SYS_GPS, SYS_GLO, SYS_GAL, SYS_QZS, SYS_SBS, 0}; const char *s1[] = {"single", "dgps", "kinematic", "static", "moving-base", "fixed", "ppp-kinematic", "ppp-static", "ppp-fixed", ""}; const char *s2[] = {"L1", "L1+L2", "L1+L2+L5", "L1+L2+L5+L6", "L1+L2+L5+L6+L7", "L1+L2+L5+L6+L7+L8", ""}; const char *s3[] = {"forward", "backward", "combined"}; const char *s4[] = {"off", "broadcast", "sbas", "iono-free", "estimation", "ionex tec", "qzs", "lex", "vtec_sf", "vtec_ef", "gtec", ""}; const char *s5[] = {"off", "saastamoinen", "sbas", "est ztd", "est ztd+grad", ""}; const char *s6[] = {"broadcast", "precise", "broadcast+sbas", "broadcast+ssr apc", "broadcast+ssr com", "qzss lex", ""}; const char *s7[] = {"gps", "glonass", "galileo", "qzss", "sbas", ""}; const char *s8[] = {"off", "continuous", "instantaneous", "fix and hold", ""}; const char *s9[] = {"off", "on", "auto calib", "external calib", ""}; int i; char *p = reinterpret_cast(buff); char *s; s = p; trace(3, "outprcopts:\n"); p += std::snprintf(p, MAXSOLMSG, "%s pos mode : %s\n", COMMENTH, s1[opt->mode]); if (PMODE_DGPS <= opt->mode && opt->mode <= PMODE_FIXED) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%s freqs : %s\n", COMMENTH, s2[opt->nf - 1]); } if (opt->mode > PMODE_SINGLE) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%s solution : %s\n", COMMENTH, s3[opt->soltype]); } p += std::snprintf(p, MAXSOLMSG - (p - s), "%s elev mask : %.1f deg\n", COMMENTH, opt->elmin * R2D); if (opt->mode > PMODE_SINGLE) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%s dynamics : %s\n", COMMENTH, opt->dynamics ? "on" : "off"); p += std::snprintf(p, MAXSOLMSG - (p - s), "%s tidecorr : %s\n", COMMENTH, opt->tidecorr ? "on" : "off"); } if (opt->mode <= PMODE_FIXED) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%s ionos opt : %s\n", COMMENTH, s4[opt->ionoopt]); } p += std::snprintf(p, MAXSOLMSG - (p - s), "%s tropo opt : %s\n", COMMENTH, s5[opt->tropopt]); p += std::snprintf(p, MAXSOLMSG - (p - s), "%s ephemeris : %s\n", COMMENTH, s6[opt->sateph]); if (opt->navsys != SYS_GPS) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%s navi sys :", COMMENTH); for (i = 0; sys[i]; i++) { if (opt->navsys & sys[i]) { p += std::snprintf(p, MAXSOLMSG - (p - s), " %s", s7[i]); } } p += std::snprintf(p, MAXSOLMSG - (p - s), "\n"); } if (PMODE_KINEMA <= opt->mode && opt->mode <= PMODE_FIXED) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%s amb res : %s\n", COMMENTH, s8[opt->modear]); if (opt->navsys & SYS_GLO) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%s amb glo : %s\n", COMMENTH, s9[opt->glomodear]); } if (opt->thresar[0] > 0.0) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%s val thres : %.1f\n", COMMENTH, opt->thresar[0]); } } if (opt->mode == PMODE_MOVEB && opt->baseline[0] > 0.0) { p += std::snprintf(p, MAXSOLMSG - (p - s), "%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 += std::snprintf(p, MAXSOLMSG - (p - s), "%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 - reinterpret_cast(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"}; const char *s2[] = {"ellipsoidal", "geodetic"}; const char *s3[] = {"GPST", "UTC ", "JST "}; const char *sep = opt2sep(opt); char *p = reinterpret_cast(buff); int timeu = opt->timeu < 0 ? 0 : (opt->timeu > 20 ? 20 : opt->timeu); char *s; s = p; trace(3, "outsolheads:\n"); if (opt->posf == SOLF_NMEA) { return 0; } if (opt->outhead) { p += std::snprintf(p, MAXSOLMSG, "%s (", COMMENTH); if (opt->posf == SOLF_XYZ) { p += std::snprintf(p, MAXSOLMSG - (p - s), "x/y/z-ecef=WGS84"); } else if (opt->posf == SOLF_ENU) { p += std::snprintf(p, MAXSOLMSG - (p - s), "e/n/u-baseline=WGS84"); } else { p += std::snprintf(p, MAXSOLMSG - (p - s), "lat/lon/height=%s/%s", s1[opt->datum], s2[opt->height]); } p += std::snprintf(p, MAXSOLMSG - (p - s), ",Q=1:fix,2:float,3:sbas,4:dgps,5:single,6:ppp,ns=# of satellites)\n"); } p += std::snprintf(p, MAXSOLMSG - (p - s), "%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 += std::snprintf(p, MAXSOLMSG - (p - s), "%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 += std::snprintf(p, MAXSOLMSG - (p - s), "%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 += std::snprintf(p, MAXSOLMSG - (p - s), "%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 += std::snprintf(p, MAXSOLMSG - (p - s), "%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 - reinterpret_cast(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; gtime_t ts = {0, 0.0}; double gpst; int week; int 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; } std::snprintf(s, sizeof(s), "%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 extended information * 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 extended information 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); } }