/*! * \file rtklib_ppp.cc * \brief Precise Point Positioning * \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-2008, 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 "rtklib_ppp.h" #include "rtklib_rtkcmn.h" #include "rtklib_sbas.h" #include "rtklib_ephemeris.h" #include "rtklib_ionex.h" #include "rtklib_tides.h" #include "rtklib_lambda.h" /* wave length of LC (m) -----------------------------------------------------*/ double lam_LC(int i, int j, int k) { const double f1 = FREQ1, f2 = FREQ2, f5 = FREQ5; return SPEED_OF_LIGHT / (i * f1 + j * f2 + k * f5); } /* carrier-phase LC (m) ------------------------------------------------------*/ double L_LC(int i, int j, int k, const double *L) { const double f1 = FREQ1, f2 = FREQ2, f5 = FREQ5; double L1, L2, L5; if ((i && !L[0]) || (j && !L[1]) || (k && !L[2])) { return 0.0; } L1 = SPEED_OF_LIGHT / f1 * L[0]; L2 = SPEED_OF_LIGHT / f2 * L[1]; L5 = SPEED_OF_LIGHT / f5 * L[2]; return (i * f1 * L1 + j * f2 * L2 + k * f5 * L5) / (i * f1 + j * f2 + k * f5); } /* pseudorange LC (m) --------------------------------------------------------*/ double P_LC(int i, int j, int k, const double *P) { const double f1 = FREQ1, f2 = FREQ2, f5 = FREQ5; double P1, P2, P5; if ((i && !P[0]) || (j && !P[1]) || (k && !P[2])) { return 0.0; } P1 = P[0]; P2 = P[1]; P5 = P[2]; return (i * f1 * P1 + j * f2 * P2 + k * f5 * P5) / (i * f1 + j * f2 + k * f5); } /* noise variance of LC (m) --------------------------------------------------*/ double var_LC(int i, int j, int k, double sig) { const double f1 = FREQ1, f2 = FREQ2, f5 = FREQ5; return (std::pow(i * f1, 2.0) + std::pow(j * f2, 2.0) + std::pow(k * f5, 2.0)) / std::pow(i * f1 + j * f2 + k * f5, 2.0) * std::pow(sig, 2.0); } /* complementaty error function (ref [1] p.227-229) --------------------------*/ double p_gamma(double a, double x, double log_gamma_a) { double y, w; int i; if (x == 0.0) return 0.0; if (x >= a + 1.0) return 1.0 - q_gamma(a, x, log_gamma_a); y = w = exp(a * log(x) - x - log_gamma_a) / a; for (i = 1; i < 100; i++) { w *= x / (a + i); y += w; if (fabs(w) < 1E-15) break; } return y; } double q_gamma(double a, double x, double log_gamma_a) { double y, w, la = 1.0, lb = x + 1.0 - a, lc; int i; if (x < a + 1.0) return 1.0 - p_gamma(a, x, log_gamma_a); w = exp(-x + a * log(x) - log_gamma_a); y = w / lb; for (i = 2; i < 100; i++) { lc = ((i - 1 - a) * (lb - la) + (i + x) * lb) / i; la = lb; lb = lc; w *= (i - 1 - a) / i; y += w / la / lb; if (fabs(w / la / lb) < 1E-15) break; } return y; } double f_erfc(double x) { return x >= 0.0 ? q_gamma(0.5, x * x, LOG_PI / 2.0) : 1.0 + p_gamma(0.5, x * x, LOG_PI / 2.0); } /* confidence function of integer ambiguity ----------------------------------*/ double conffunc(int N, double B, double sig) { double x, p = 1.0; int i; x = fabs(B - N); for (i = 1; i < 8; i++) { p -= f_erfc((i - x) / (SQRT2 * sig)) - f_erfc((i + x) / (SQRT2 * sig)); } return p; } /* average LC ----------------------------------------------------------------*/ void average_LC(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav __attribute__((unused)), const double *azel) { ambc_t *amb; double LC1, LC2, LC3, var1, var2, var3, sig; int i, j, sat; for (i = 0; i < n; i++) { sat = obs[i].sat; if (azel[1 + 2 * i] < rtk->opt.elmin) continue; if (satsys(sat, NULL) != SYS_GPS) continue; /* triple-freq carrier and code LC (m) */ LC1 = L_LC(1, -1, 0, obs[i].L) - P_LC(1, 1, 0, obs[i].P); LC2 = L_LC(0, 1, -1, obs[i].L) - P_LC(0, 1, 1, obs[i].P); LC3 = L_LC(1, -6, 5, obs[i].L) - P_LC(1, 1, 0, obs[i].P); sig = std::sqrt(std::pow(rtk->opt.err[1], 2.0) + std::pow(rtk->opt.err[2] / sin(azel[1 + 2 * i]), 2.0)); /* measurement noise variance (m) */ var1 = var_LC(1, 1, 0, sig * rtk->opt.eratio[0]); var2 = var_LC(0, 1, 1, sig * rtk->opt.eratio[0]); var3 = var_LC(1, 1, 0, sig * rtk->opt.eratio[0]); amb = rtk->ambc + sat - 1; if (rtk->ssat[sat - 1].slip[0] || rtk->ssat[sat - 1].slip[1] || rtk->ssat[sat - 1].slip[2] || amb->n[0] == 0.0 || fabs(timediff(amb->epoch[0], obs[0].time)) > MIN_ARC_GAP) { amb->n[0] = amb->n[1] = amb->n[2] = 0.0; amb->LC[0] = amb->LC[1] = amb->LC[2] = 0.0; amb->LCv[0] = amb->LCv[1] = amb->LCv[2] = 0.0; amb->fixcnt = 0; for (j = 0; j < MAXSAT; j++) amb->flags[j] = 0; } /* averaging */ if (LC1) { amb->n[0] += 1.0; amb->LC[0] += (LC1 - amb->LC[0]) / amb->n[0]; amb->LCv[0] += (var1 - amb->LCv[0]) / amb->n[0]; } if (LC2) { amb->n[1] += 1.0; amb->LC[1] += (LC2 - amb->LC[1]) / amb->n[1]; amb->LCv[1] += (var2 - amb->LCv[1]) / amb->n[1]; } if (LC3) { amb->n[2] += 1.0; amb->LC[2] += (LC3 - amb->LC[2]) / amb->n[2]; amb->LCv[2] += (var3 - amb->LCv[2]) / amb->n[2]; } amb->epoch[0] = obs[0].time; } } /* fix wide-lane ambiguity ---------------------------------------------------*/ int fix_amb_WL(rtk_t *rtk, const nav_t *nav, int sat1, int sat2, int *NW) { ambc_t *amb1, *amb2; double BW, vW, lam_WL = lam_LC(1, -1, 0); amb1 = rtk->ambc + sat1 - 1; amb2 = rtk->ambc + sat2 - 1; if (!amb1->n[0] || !amb2->n[0]) return 0; /* wide-lane ambiguity */ #ifndef REV_WL_FCB BW = (amb1->LC[0] - amb2->LC[0]) / lam_WL + nav->wlbias[sat1 - 1] - nav->wlbias[sat2 - 1]; #else BW = (amb1->LC[0] - amb2->LC[0]) / lam_WL - nav->wlbias[sat1 - 1] + nav->wlbias[sat2 - 1]; #endif *NW = ROUND_PPP(BW); /* variance of wide-lane ambiguity */ vW = (amb1->LCv[0] / amb1->n[0] + amb2->LCv[0] / amb2->n[0]) / std::pow(lam_WL, 2.0); /* validation of integer wide-lane ambigyity */ return fabs(*NW - BW) <= rtk->opt.thresar[2] && conffunc(*NW, BW, sqrt(vW)) >= rtk->opt.thresar[1]; } /* linear dependency check ---------------------------------------------------*/ int is_depend(int sat1, int sat2, int *flgs, int *max_flg) { int i; if (flgs[sat1 - 1] == 0 && flgs[sat2 - 1] == 0) { flgs[sat1 - 1] = flgs[sat2 - 1] = ++(*max_flg); } else if (flgs[sat1 - 1] == 0 && flgs[sat2 - 1] != 0) { flgs[sat1 - 1] = flgs[sat2 - 1]; } else if (flgs[sat1 - 1] != 0 && flgs[sat2 - 1] == 0) { flgs[sat2 - 1] = flgs[sat1 - 1]; } else if (flgs[sat1 - 1] > flgs[sat2 - 1]) { for (i = 0; i < MAXSAT; i++) if (flgs[i] == flgs[sat2 - 1]) flgs[i] = flgs[sat1 - 1]; } else if (flgs[sat1 - 1] < flgs[sat2 - 1]) { for (i = 0; i < MAXSAT; i++) if (flgs[i] == flgs[sat1 - 1]) flgs[i] = flgs[sat2 - 1]; } else return 0; /* linear depenent */ return 1; } /* select fixed ambiguities --------------------------------------------------*/ int sel_amb(int *sat1, int *sat2, double *N, double *var, int n) { int i, j, flgs[MAXSAT] = {0}, max_flg = 0; /* sort by variance */ for (i = 0; i < n; i++) for (j = 1; j < n - i; j++) { if (var[j] >= var[j - 1]) continue; SWAP_I(sat1[j], sat1[j - 1]); SWAP_I(sat2[j], sat2[j - 1]); SWAP_D(N[j], N[j - 1]); SWAP_D(var[j], var[j - 1]); } /* select linearly independent satellite pair */ for (i = j = 0; i < n; i++) { if (!is_depend(sat1[i], sat2[i], flgs, &max_flg)) continue; sat1[j] = sat1[i]; sat2[j] = sat2[i]; N[j] = N[i]; var[j++] = var[i]; } return j; } /* fixed solution ------------------------------------------------------------*/ int fix_sol(rtk_t *rtk, const int *sat1, const int *sat2, const double *NC, int n) { double *v, *H, *R; int i, j, k, info; if (n <= 0) return 0; v = zeros(n, 1); H = zeros(rtk->nx, n); R = zeros(n, n); /* constraints to fixed ambiguities */ for (i = 0; i < n; i++) { j = IB_PPP(sat1[i], &rtk->opt); k = IB_PPP(sat2[i], &rtk->opt); v[i] = NC[i] - (rtk->x[j] - rtk->x[k]); H[j + i * rtk->nx] = 1.0; H[k + i * rtk->nx] = -1.0; R[i + i * n] = std::pow(CONST_AMB, 2.0); } /* update states with constraints */ if ((info = filter(rtk->x, rtk->P, H, v, R, rtk->nx, n))) { trace(1, "filter error (info=%d)\n", info); free(v); free(H); free(R); return 0; } /* set solution */ for (i = 0; i < rtk->na; i++) { rtk->xa[i] = rtk->x[i]; for (j = 0; j < rtk->na; j++) { rtk->Pa[i + j * rtk->na] = rtk->Pa[j + i * rtk->na] = rtk->P[i + j * rtk->nx]; } } /* set flags */ for (i = 0; i < n; i++) { rtk->ambc[sat1[i] - 1].flags[sat2[i] - 1] = 1; rtk->ambc[sat2[i] - 1].flags[sat1[i] - 1] = 1; } free(v); free(H); free(R); return 1; } /* fix narrow-lane ambiguity by rounding -------------------------------------*/ int fix_amb_ROUND(rtk_t *rtk, int *sat1, int *sat2, const int *NW, int n) { double C1, C2, B1, v1, BC, v, vc, *NC, *var, lam_NL = lam_LC(1, 1, 0), lam1, lam2; int i, j, k, m = 0, N1, stat; lam1 = lam_carr[0]; lam2 = lam_carr[1]; C1 = std::pow(lam2, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); C2 = -std::pow(lam1, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); NC = zeros(n, 1); var = zeros(n, 1); for (i = 0; i < n; i++) { j = IB_PPP(sat1[i], &rtk->opt); k = IB_PPP(sat2[i], &rtk->opt); /* narrow-lane ambiguity */ B1 = (rtk->x[j] - rtk->x[k] + C2 * lam2 * NW[i]) / lam_NL; N1 = ROUND_PPP(B1); /* variance of narrow-lane ambiguity */ var[m] = rtk->P[j + j * rtk->nx] + rtk->P[k + k * rtk->nx] - 2.0 * rtk->P[j + k * rtk->nx]; v1 = var[m] / std::pow(lam_NL, 2.0); /* validation of narrow-lane ambiguity */ if (fabs(N1 - B1) > rtk->opt.thresar[2] || conffunc(N1, B1, sqrt(v1)) < rtk->opt.thresar[1]) { continue; } /* iono-free ambiguity (m) */ BC = C1 * lam1 * N1 + C2 * lam2 * (N1 - NW[i]); /* check residuals */ v = rtk->ssat[sat1[i] - 1].resc[0] - rtk->ssat[sat2[i] - 1].resc[0]; vc = v + (BC - (rtk->x[j] - rtk->x[k])); if (fabs(vc) > THRES_RES) continue; sat1[m] = sat1[i]; sat2[m] = sat2[i]; NC[m++] = BC; } /* select fixed ambiguities by dependancy check */ m = sel_amb(sat1, sat2, NC, var, m); /* fixed solution */ stat = fix_sol(rtk, sat1, sat2, NC, m); free(NC); free(var); return stat && m >= 3; } /* fix narrow-lane ambiguity by ILS ------------------------------------------*/ int fix_amb_ILS(rtk_t *rtk, int *sat1, int *sat2, int *NW, int n) { double C1, C2, *B1, *N1, *NC, *D, *E, *Q, s[2], lam_NL = lam_LC(1, 1, 0), lam1, lam2; int i, j, k, m = 0, info, stat, flgs[MAXSAT] = {0}, max_flg = 0; lam1 = lam_carr[0]; lam2 = lam_carr[1]; C1 = std::pow(lam2, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); C2 = -std::pow(lam1, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); B1 = zeros(n, 1); N1 = zeros(n, 2); D = zeros(rtk->nx, n); E = mat(n, rtk->nx); Q = mat(n, n); NC = mat(n, 1); for (i = 0; i < n; i++) { /* check linear independency */ if (!is_depend(sat1[i], sat2[i], flgs, &max_flg)) continue; j = IB_PPP(sat1[i], &rtk->opt); k = IB_PPP(sat2[i], &rtk->opt); /* float narrow-lane ambiguity (cycle) */ B1[m] = (rtk->x[j] - rtk->x[k] + C2 * lam2 * NW[i]) / lam_NL; N1[m] = ROUND_PPP(B1[m]); /* validation of narrow-lane ambiguity */ if (fabs(N1[m] - B1[m]) > rtk->opt.thresar[2]) continue; /* narrow-lane ambiguity transformation matrix */ D[j + m * rtk->nx] = 1.0 / lam_NL; D[k + m * rtk->nx] = -1.0 / lam_NL; sat1[m] = sat1[i]; sat2[m] = sat2[i]; NW[m++] = NW[i]; } if (m < 3) { free(B1); free(N1); free(D); free(E); free(Q); free(NC); return 0; } /* covariance of narrow-lane ambiguities */ matmul("TN", m, rtk->nx, rtk->nx, 1.0, D, rtk->P, 0.0, E); matmul("NN", m, m, rtk->nx, 1.0, E, D, 0.0, Q); /* integer least square */ if ((info = lambda(m, 2, B1, Q, N1, s))) { trace(2, "lambda error: info=%d\n", info); free(B1); free(N1); free(D); free(E); free(Q); free(NC); return 0; } if (s[0] <= 0.0) { free(B1); free(N1); free(D); free(E); free(Q); free(NC); return 0; } rtk->sol.ratio = (float)(MIN_PPP(s[1] / s[0], 999.9)); /* varidation by ratio-test */ if (rtk->opt.thresar[0] > 0.0 && rtk->sol.ratio < rtk->opt.thresar[0]) { trace(2, "varidation error: n=%2d ratio=%8.3f\n", m, rtk->sol.ratio); free(B1); free(N1); free(D); free(E); free(Q); free(NC); return 0; } trace(2, "varidation ok: %s n=%2d ratio=%8.3f\n", time_str(rtk->sol.time, 0), m, rtk->sol.ratio); /* narrow-lane to iono-free ambiguity */ for (i = 0; i < m; i++) { NC[i] = C1 * lam1 * N1[i] + C2 * lam2 * (N1[i] - NW[i]); } /* fixed solution */ stat = fix_sol(rtk, sat1, sat2, NC, m); free(B1); free(N1); free(D); free(E); free(Q); free(NC); return stat; } /* resolve integer ambiguity for ppp -----------------------------------------*/ int pppamb(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav, const double *azel) { double elmask; int i, j, m = 0, stat = 0, *NW, *sat1, *sat2; if (n <= 0 || rtk->opt.ionoopt != IONOOPT_IFLC || rtk->opt.nf < 2) return 0; trace(3, "pppamb: time=%s n=%d\n", time_str(obs[0].time, 0), n); elmask = rtk->opt.elmaskar > 0.0 ? rtk->opt.elmaskar : rtk->opt.elmin; sat1 = imat(n * n, 1); sat2 = imat(n * n, 1); NW = imat(n * n, 1); /* average LC */ average_LC(rtk, obs, n, nav, azel); /* fix wide-lane ambiguity */ for (i = 0; i < n - 1; i++) for (j = i + 1; j < n; j++) { if (!rtk->ssat[obs[i].sat - 1].vsat[0] || !rtk->ssat[obs[j].sat - 1].vsat[0] || azel[1 + i * 2] < elmask || azel[1 + j * 2] < elmask) continue; #if 0 /* test already fixed */ if (rtk->ambc[obs[i].sat-1].flags[obs[j].sat-1] && rtk->ambc[obs[j].sat-1].flags[obs[i].sat-1]) continue; #endif sat1[m] = obs[i].sat; sat2[m] = obs[j].sat; if (fix_amb_WL(rtk, nav, sat1[m], sat2[m], NW + m)) m++; } /* fix narrow-lane ambiguity */ if (rtk->opt.modear == ARMODE_PPPAR) { stat = fix_amb_ROUND(rtk, sat1, sat2, NW, m); } else if (rtk->opt.modear == ARMODE_PPPAR_ILS) { stat = fix_amb_ILS(rtk, sat1, sat2, NW, m); } free(sat1); free(sat2); free(NW); return stat; } void pppoutsolstat(rtk_t *rtk, int level, FILE *fp) { ssat_t *ssat; double tow, pos[3], vel[3], acc[3]; int i, j, week, nfreq = 1; char id[32]; if (level <= 0 || !fp) return; trace(3, "pppoutsolstat:\n"); tow = time2gpst(rtk->sol.time, &week); /* receiver position */ fprintf(fp, "$POS,%d,%.3f,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n", week, tow, rtk->sol.stat, rtk->x[0], rtk->x[1], rtk->x[2], 0.0, 0.0, 0.0); /* receiver velocity and acceleration */ if (rtk->opt.dynamics) { ecef2pos(rtk->sol.rr, pos); ecef2enu(pos, rtk->x + 3, vel); ecef2enu(pos, rtk->x + 6, acc); fprintf(fp, "$VELACC,%d,%.3f,%d,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f\n", week, tow, rtk->sol.stat, vel[0], vel[1], vel[2], acc[0], acc[1], acc[2], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); } /* receiver clocks */ i = IC_PPP(0, &rtk->opt); fprintf(fp, "$CLK,%d,%.3f,%d,%d,%.3f,%.3f,%.3f,%.3f\n", week, tow, rtk->sol.stat, 1, rtk->x[i] * 1E9 / SPEED_OF_LIGHT, rtk->x[i + 1] * 1E9 / SPEED_OF_LIGHT, 0.0, 0.0); /* tropospheric parameters */ if (rtk->opt.tropopt == TROPOPT_EST || rtk->opt.tropopt == TROPOPT_ESTG) { i = IT_PPP(&rtk->opt); fprintf(fp, "$TROP,%d,%.3f,%d,%d,%.4f,%.4f\n", week, tow, rtk->sol.stat, 1, rtk->x[i], 0.0); } if (rtk->opt.tropopt == TROPOPT_ESTG) { i = IT_PPP(&rtk->opt); fprintf(fp, "$TRPG,%d,%.3f,%d,%d,%.5f,%.5f,%.5f,%.5f\n", week, tow, rtk->sol.stat, 1, rtk->x[i + 1], rtk->x[i + 2], 0.0, 0.0); } if (rtk->sol.stat == SOLQ_NONE || level <= 1) return; /* residuals and status */ for (i = 0; i < MAXSAT; i++) { ssat = rtk->ssat + i; if (!ssat->vs) continue; satno2id(i + 1, id); for (j = 0; j < nfreq; j++) { fprintf(fp, "$SAT,%d,%.3f,%s,%d,%.1f,%.1f,%.4f,%.4f,%d,%.0f,%d,%d,%d,%d,%d,%d\n", week, tow, id, j + 1, ssat->azel[0] * R2D, ssat->azel[1] * R2D, ssat->resp[j], ssat->resc[j], ssat->vsat[j], ssat->snr[j] * 0.25, ssat->fix[j], ssat->slip[j] & 3, ssat->lock[j], ssat->outc[j], ssat->slipc[j], ssat->rejc[j]); } } } /* solar/lunar tides (ref [2] 7) ---------------------------------------------*/ void tide_pl(const double *eu, const double *rp, double GMp, const double *pos, double *dr) { const double H3 = 0.292, L3 = 0.015; double r, ep[3], latp, lonp, p, K2, K3, a, H2, L2, dp, du, cosp, sinl, cosl; int i; trace(4, "tide_pl : pos=%.3f %.3f\n", pos[0] * R2D, pos[1] * R2D); if ((r = norm_rtk(rp, 3)) <= 0.0) return; for (i = 0; i < 3; i++) ep[i] = rp[i] / r; K2 = GMp / GME * std::pow(RE_WGS84, 2.0) * std::pow(RE_WGS84, 2.0) / (r * r * r); K3 = K2 * RE_WGS84 / r; latp = asin(ep[2]); lonp = atan2(ep[1], ep[0]); cosp = cos(latp); sinl = sin(pos[0]); cosl = cos(pos[0]); /* step1 in phase (degree 2) */ p = (3.0 * sinl * sinl - 1.0) / 2.0; H2 = 0.6078 - 0.0006 * p; L2 = 0.0847 + 0.0002 * p; a = dot(ep, eu, 3); dp = K2 * 3.0 * L2 * a; du = K2 * (H2 * (1.5 * a * a - 0.5) - 3.0 * L2 * a * a); /* step1 in phase (degree 3) */ dp += K3 * L3 * (7.5 * a * a - 1.5); du += K3 * (H3 * (2.5 * a * a * a - 1.5 * a) - L3 * (7.5 * a * a - 1.5) * a); /* step1 out-of-phase (only radial) */ du += 3.0 / 4.0 * 0.0025 * K2 * sin(2.0 * latp) * sin(2.0 * pos[0]) * sin(pos[1] - lonp); du += 3.0 / 4.0 * 0.0022 * K2 * cosp * cosp * cosl * cosl * sin(2.0 * (pos[1] - lonp)); dr[0] = dp * ep[0] + du * eu[0]; dr[1] = dp * ep[1] + du * eu[1]; dr[2] = dp * ep[2] + du * eu[2]; trace(5, "tide_pl : dr=%.3f %.3f %.3f\n", dr[0], dr[1], dr[2]); } /* displacement by solid earth tide (ref [2] 7) ------------------------------*/ void tide_solid(const double *rsun, const double *rmoon, const double *pos, const double *E, double gmst, int opt, double *dr) { double dr1[3], dr2[3], eu[3], du, dn, sinl, sin2l; trace(3, "tide_solid: pos=%.3f %.3f opt=%d\n", pos[0] * R2D, pos[1] * R2D, opt); /* step1: time domain */ eu[0] = E[2]; eu[1] = E[5]; eu[2] = E[8]; tide_pl(eu, rsun, GMS, pos, dr1); tide_pl(eu, rmoon, GMM, pos, dr2); /* step2: frequency domain, only K1 radial */ sin2l = sin(2.0 * pos[0]); du = -0.012 * sin2l * sin(gmst + pos[1]); dr[0] = dr1[0] + dr2[0] + du * E[2]; dr[1] = dr1[1] + dr2[1] + du * E[5]; dr[2] = dr1[2] + dr2[2] + du * E[8]; /* eliminate permanent deformation */ if (opt & 8) { sinl = sin(pos[0]); du = 0.1196 * (1.5 * sinl * sinl - 0.5); dn = 0.0247 * sin2l; dr[0] += du * E[2] + dn * E[1]; dr[1] += du * E[5] + dn * E[4]; dr[2] += du * E[8] + dn * E[7]; } trace(5, "tide_solid: dr=%.3f %.3f %.3f\n", dr[0], dr[1], dr[2]); } /* displacement by ocean tide loading (ref [2] 7) ----------------------------*/ void tide_oload(gtime_t tut, const double *odisp, double *denu) { const double args[][5] = { {1.40519E-4, 2.0, -2.0, 0.0, 0.00}, /* M2 */ {1.45444E-4, 0.0, 0.0, 0.0, 0.00}, /* S2 */ {1.37880E-4, 2.0, -3.0, 1.0, 0.00}, /* N2 */ {1.45842E-4, 2.0, 0.0, 0.0, 0.00}, /* K2 */ {0.72921E-4, 1.0, 0.0, 0.0, 0.25}, /* K1 */ {0.67598E-4, 1.0, -2.0, 0.0, -0.25}, /* O1 */ {0.72523E-4, -1.0, 0.0, 0.0, -0.25}, /* P1 */ {0.64959E-4, 1.0, -3.0, 1.0, -0.25}, /* Q1 */ {0.53234E-5, 0.0, 2.0, 0.0, 0.00}, /* Mf */ {0.26392E-5, 0.0, 1.0, -1.0, 0.00}, /* Mm */ {0.03982E-5, 2.0, 0.0, 0.0, 0.00} /* Ssa */ }; const double ep1975[] = {1975, 1, 1, 0, 0, 0}; double ep[6], fday, days, t, t2, t3, a[5], ang, dp[3] = {0}; int i, j; trace(3, "tide_oload:\n"); /* angular argument: see subroutine arg.f for reference [1] */ time2epoch(tut, ep); fday = ep[3] * 3600.0 + ep[4] * 60.0 + ep[5]; ep[3] = ep[4] = ep[5] = 0.0; days = timediff(epoch2time(ep), epoch2time(ep1975)) / 86400.0; t = (27392.500528 + 1.000000035 * days) / 36525.0; t2 = t * t; t3 = t2 * t; a[0] = fday; a[1] = (279.69668 + 36000.768930485 * t + 3.03E-4 * t2) * D2R; /* H0 */ a[2] = (270.434358 + 481267.88314137 * t - 0.001133 * t2 + 1.9E-6 * t3) * D2R; /* S0 */ a[3] = (334.329653 + 4069.0340329577 * t - 0.010325 * t2 - 1.2E-5 * t3) * D2R; /* P0 */ a[4] = 2.0 * PI; /* displacements by 11 constituents */ for (i = 0; i < 11; i++) { ang = 0.0; for (j = 0; j < 5; j++) ang += a[j] * args[i][j]; for (j = 0; j < 3; j++) dp[j] += odisp[j + i * 6] * cos(ang - odisp[j + 3 + i * 6] * D2R); } denu[0] = -dp[1]; denu[1] = -dp[2]; denu[2] = dp[0]; trace(5, "tide_oload: denu=%.3f %.3f %.3f\n", denu[0], denu[1], denu[2]); } /* iers mean pole (ref [7] eq.7.25) ------------------------------------------*/ void iers_mean_pole(gtime_t tut, double *xp_bar, double *yp_bar) { const double ep2000[] = {2000, 1, 1, 0, 0, 0}; double y, y2, y3; y = timediff(tut, epoch2time(ep2000)) / 86400.0 / 365.25; if (y < 3653.0 / 365.25) { /* until 2010.0 */ y2 = y * y; y3 = y2 * y; *xp_bar = 55.974 + 1.8243 * y + 0.18413 * y2 + 0.007024 * y3; /* (mas) */ *yp_bar = 346.346 + 1.7896 * y - 0.10729 * y2 - 0.000908 * y3; } else { /* after 2010.0 */ *xp_bar = 23.513 + 7.6141 * y; /* (mas) */ *yp_bar = 358.891 - 0.6287 * y; } } /* displacement by pole tide (ref [7] eq.7.26) --------------------------------*/ void tide_pole(gtime_t tut, const double *pos, const double *erpv, double *denu) { double xp_bar, yp_bar, m1, m2, cosl, sinl; trace(3, "tide_pole: pos=%.3f %.3f\n", pos[0] * R2D, pos[1] * R2D); /* iers mean pole (mas) */ iers_mean_pole(tut, &xp_bar, &yp_bar); /* ref [7] eq.7.24 */ m1 = erpv[0] / AS2R - xp_bar * 1E-3; /* (as) */ m2 = -erpv[1] / AS2R + yp_bar * 1E-3; /* sin(2*theta) = sin(2*phi), cos(2*theta)=-cos(2*phi) */ cosl = cos(pos[1]); sinl = sin(pos[1]); denu[0] = 9E-3 * sin(pos[0]) * (m1 * sinl - m2 * cosl); /* de= Slambda (m) */ denu[1] = -9E-3 * cos(2.0 * pos[0]) * (m1 * cosl + m2 * sinl); /* dn=-Stheta (m) */ denu[2] = -33E-3 * sin(2.0 * pos[0]) * (m1 * cosl + m2 * sinl); /* du= Sr (m) */ trace(5, "tide_pole : denu=%.3f %.3f %.3f\n", denu[0], denu[1], denu[2]); } /* tidal displacement ---------------------------------------------------------- * displacements by earth tides * args : gtime_t tutc I time in utc * double *rr I site position (ecef) (m) * int opt I options (or of the followings) * 1: solid earth tide * 2: ocean tide loading * 4: pole tide * 8: elimate permanent deformation * double *erp I earth rotation parameters (NULL: not used) * double *odisp I ocean loading parameters (NULL: not used) * odisp[0+i*6]: consituent i amplitude radial(m) * odisp[1+i*6]: consituent i amplitude west (m) * odisp[2+i*6]: consituent i amplitude south (m) * odisp[3+i*6]: consituent i phase radial (deg) * odisp[4+i*6]: consituent i phase west (deg) * odisp[5+i*6]: consituent i phase south (deg) * (i=0:M2,1:S2,2:N2,3:K2,4:K1,5:O1,6:P1,7:Q1, * 8:Mf,9:Mm,10:Ssa) * double *dr O displacement by earth tides (ecef) (m) * return : none * notes : see ref [1], [2] chap 7 * see ref [4] 5.2.1, 5.2.2, 5.2.3 * ver.2.4.0 does not use ocean loading and pole tide corrections *-----------------------------------------------------------------------------*/ void tidedisp(gtime_t tutc, const double *rr, int opt, const erp_t *erp, const double *odisp, double *dr) { gtime_t tut; double pos[2], E[9], drt[3], denu[3], rs[3], rm[3], gmst, erpv[5] = {0}; int i; #ifdef IERS_MODEL double ep[6], fhr; int year, mon, day; #endif trace(3, "tidedisp: tutc=%s\n", time_str(tutc, 0)); if (erp) geterp(erp, tutc, erpv); tut = timeadd(tutc, erpv[2]); dr[0] = dr[1] = dr[2] = 0.0; if (norm_rtk(rr, 3) <= 0.0) return; pos[0] = asin(rr[2] / norm_rtk(rr, 3)); pos[1] = atan2(rr[1], rr[0]); xyz2enu(pos, E); if (opt & 1) { /* solid earth tides */ /* sun and moon position in ecef */ sunmoonpos(tutc, erpv, rs, rm, &gmst); #ifdef IERS_MODEL time2epoch(tutc, ep); year = (int)ep[0]; mon = (int)ep[1]; day = (int)ep[2]; fhr = ep[3] + ep[4] / 60.0 + ep[5] / 3600.0; /* call DEHANTTIDEINEL */ dehanttideinel_((double *)rr, &year, &mon, &day, &fhr, rs, rm, drt); #else tide_solid(rs, rm, pos, E, gmst, opt, drt); #endif for (i = 0; i < 3; i++) dr[i] += drt[i]; } if ((opt & 2) && odisp) { /* ocean tide loading */ tide_oload(tut, odisp, denu); matmul("TN", 3, 1, 3, 1.0, E, denu, 0.0, drt); for (i = 0; i < 3; i++) dr[i] += drt[i]; } if ((opt & 4) && erp) { /* pole tide */ tide_pole(tut, pos, erpv, denu); matmul("TN", 3, 1, 3, 1.0, E, denu, 0.0, drt); for (i = 0; i < 3; i++) dr[i] += drt[i]; } trace(5, "tidedisp: dr=%.3f %.3f %.3f\n", dr[0], dr[1], dr[2]); } /* exclude meas of eclipsing satellite (block IIA) ---------------------------*/ void testeclipse(const obsd_t *obs, int n, const nav_t *nav, double *rs) { double rsun[3], esun[3], r, ang, erpv[5] = {0}, cosa; int i, j; const char *type; trace(3, "testeclipse:\n"); /* unit vector of sun direction (ecef) */ sunmoonpos(gpst2utc(obs[0].time), erpv, rsun, NULL, NULL); if (normv3(rsun, esun) == 0) trace(1, "Error computing the norm"); for (i = 0; i < n; i++) { type = nav->pcvs[obs[i].sat - 1].type; if ((r = norm_rtk(rs + i * 6, 3)) <= 0.0) continue; #if 1 /* only block IIA */ if (*type && !strstr(type, "BLOCK IIA")) continue; #endif /* sun-earth-satellite angle */ cosa = dot(rs + i * 6, esun, 3) / r; cosa = cosa < -1.0 ? -1.0 : (cosa > 1.0 ? 1.0 : cosa); ang = acos(cosa); /* test eclipse */ if (ang < PI / 2.0 || r * sin(ang) > RE_WGS84) continue; trace(2, "eclipsing sat excluded %s sat=%2d\n", time_str(obs[0].time, 0), obs[i].sat); for (j = 0; j < 3; j++) rs[j + i * 6] = 0.0; } } /* measurement error variance ------------------------------------------------*/ double varerr(int sat __attribute__((unused)), int sys, double el, int type, const prcopt_t *opt) { double a, b, a2, b2, fact = 1.0; double sinel = sin(el); int i = sys == SYS_GLO ? 1 : (sys == SYS_GAL ? 2 : 0); /* extended error model */ if (type == 1 && opt->exterr.ena[0]) { /* code */ a = opt->exterr.cerr[i][0]; b = opt->exterr.cerr[i][1]; if (opt->ionoopt == IONOOPT_IFLC) { a2 = opt->exterr.cerr[i][2]; b2 = opt->exterr.cerr[i][3]; a = std::sqrt(std::pow(2.55, 2.0) * a * a + std::pow(1.55, 2.0) * a2 * a2); b = std::sqrt(std::pow(2.55, 2.0) * b * b + std::pow(1.55, 2.0) * b2 * b2); } } else if (type == 0 && opt->exterr.ena[1]) { /* phase */ a = opt->exterr.perr[i][0]; b = opt->exterr.perr[i][1]; if (opt->ionoopt == IONOOPT_IFLC) { a2 = opt->exterr.perr[i][2]; b2 = opt->exterr.perr[i][3]; a = std::sqrt(std::pow(2.55, 2.0) * a * a + std::pow(1.55, 2.0) * a2 * a2); b = std::sqrt(std::pow(2.55, 2.0) * b * b + std::pow(1.55, 2.0) * b2 * b2); } } else { /* normal error model */ if (type == 1) fact *= opt->eratio[0]; fact *= sys == SYS_GLO ? EFACT_GLO : (sys == SYS_SBS ? EFACT_SBS : EFACT_GPS); if (opt->ionoopt == IONOOPT_IFLC) fact *= 3.0; a = fact * opt->err[1]; b = fact * opt->err[2]; } return a * a + b * b / sinel / sinel; } /* initialize state and covariance -------------------------------------------*/ void initx(rtk_t *rtk, double xi, double var, int i) { int j; rtk->x[i] = xi; for (j = 0; j < rtk->nx; j++) { rtk->P[i + j * rtk->nx] = rtk->P[j + i * rtk->nx] = i == j ? var : 0.0; } } /* dual-frequency iono-free measurements -------------------------------------*/ int ifmeas(const obsd_t *obs, const nav_t *nav, const double *azel, const prcopt_t *opt, const double *dantr, const double *dants, double phw, double *meas, double *var) { const double *lam = nav->lam[obs->sat - 1]; double c1, c2, L1, L2, P1, P2, P1_C1, P2_C2, gamma; int i = 0, j = 1, k; trace(4, "ifmeas :\n"); /* L1-L2 for GPS/GLO/QZS, L1-L5 for GAL/SBS */ if (NFREQ >= 3 && (satsys(obs->sat, NULL) & (SYS_GAL | SYS_SBS))) j = 2; if (NFREQ < 2 || lam[i] == 0.0 || lam[j] == 0.0) return 0; /* test snr mask */ if (testsnr(0, i, azel[1], obs->SNR[i] * 0.25, &opt->snrmask) || testsnr(0, j, azel[1], obs->SNR[j] * 0.25, &opt->snrmask)) { return 0; } gamma = std::pow(lam[j], 2.0) / std::pow(lam[i], 2.0); /* f1^2/f2^2 */ c1 = gamma / (gamma - 1.0); /* f1^2/(f1^2-f2^2) */ c2 = -1.0 / (gamma - 1.0); /* -f2^2/(f1^2-f2^2) */ L1 = obs->L[i] * lam[i]; /* cycle -> m */ L2 = obs->L[j] * lam[j]; P1 = obs->P[i]; P2 = obs->P[j]; P1_C1 = nav->cbias[obs->sat - 1][1]; P2_C2 = nav->cbias[obs->sat - 1][2]; if (opt->sateph == EPHOPT_LEX) { P1_C1 = nav->lexeph[obs->sat - 1].isc[0] * SPEED_OF_LIGHT; /* ISC_L1C/A */ } if (L1 == 0.0 || L2 == 0.0 || P1 == 0.0 || P2 == 0.0) return 0; /* iono-free phase with windup correction */ meas[0] = c1 * L1 + c2 * L2 - (c1 * lam[i] + c2 * lam[j]) * phw; /* iono-free code with dcb correction */ if (obs->code[i] == CODE_L1C) P1 += P1_C1; /* C1->P1 */ if (obs->code[j] == CODE_L2C) P2 += P2_C2; /* C2->P2 */ meas[1] = c1 * P1 + c2 * P2; var[1] = std::pow(ERR_CBIAS, 2.0); if (opt->sateph == EPHOPT_SBAS) meas[1] -= P1_C1; /* sbas clock based C1 */ /* gps-glonass h/w bias correction for code */ if (opt->exterr.ena[3] && satsys(obs->sat, NULL) == SYS_GLO) { meas[1] += c1 * opt->exterr.gpsglob[0] + c2 * opt->exterr.gpsglob[1]; } /* antenna phase center variation correction */ for (k = 0; k < 2; k++) { if (dants) meas[k] -= c1 * dants[i] + c2 * dants[j]; if (dantr) meas[k] -= c1 * dantr[i] + c2 * dantr[j]; } return 1; } /* get tgd parameter (m) -----------------------------------------------------*/ double gettgd_ppp(int sat, const nav_t *nav) { int i; for (i = 0; i < nav->n; i++) { if (nav->eph[i].sat != sat) continue; return SPEED_OF_LIGHT * nav->eph[i].tgd[0]; } return 0.0; } /* slant ionospheric delay ---------------------------------------------------*/ int corr_ion(gtime_t time, const nav_t *nav, int sat __attribute__((unused)), const double *pos, const double *azel, int ionoopt, double *ion, double *var, int *brk __attribute__((unused))) { #ifdef EXTSTEC double rate; #endif /* sbas ionosphere model */ if (ionoopt == IONOOPT_SBAS) { return sbsioncorr(time, nav, pos, azel, ion, var); } /* ionex tec model */ if (ionoopt == IONOOPT_TEC) { return iontec(time, nav, pos, azel, 1, ion, var); } #ifdef EXTSTEC /* slant tec model */ if (ionoopt == IONOOPT_STEC) { return stec_ion(time, nav, sat, pos, azel, ion, &rate, var, brk); } #endif /* broadcast model */ if (ionoopt == IONOOPT_BRDC) { *ion = ionmodel(time, nav->ion_gps, pos, azel); *var = std::pow(*ion * ERR_BRDCI, 2.0); return 1; } /* ionosphere model off */ *ion = 0.0; *var = VAR_IONO_OFF; return 1; } /* ionosphere and antenna corrected measurements -----------------------------*/ int corrmeas(const obsd_t *obs, const nav_t *nav, const double *pos, const double *azel, const prcopt_t *opt, const double *dantr, const double *dants, double phw, double *meas, double *var, int *brk) { const double *lam = nav->lam[obs->sat - 1]; double ion = 0.0, L1, P1, PC, P1_P2, P1_C1, vari, gamma; int i; trace(4, "corrmeas:\n"); meas[0] = meas[1] = var[0] = var[1] = 0.0; /* iono-free LC */ if (opt->ionoopt == IONOOPT_IFLC) { return ifmeas(obs, nav, azel, opt, dantr, dants, phw, meas, var); } if (lam[0] == 0.0 || obs->L[0] == 0.0 || obs->P[0] == 0.0) return 0; if (testsnr(0, 0, azel[1], obs->SNR[0] * 0.25, &opt->snrmask)) return 0; L1 = obs->L[0] * lam[0]; P1 = obs->P[0]; /* dcb correction */ gamma = std::pow(lam[1] / lam[0], 2.0); /* f1^2/f2^2 */ P1_P2 = nav->cbias[obs->sat - 1][0]; P1_C1 = nav->cbias[obs->sat - 1][1]; if (P1_P2 == 0.0 && (satsys(obs->sat, NULL) & (SYS_GPS | SYS_GAL | SYS_QZS))) { P1_P2 = (1.0 - gamma) * gettgd_ppp(obs->sat, nav); } if (obs->code[0] == CODE_L1C) P1 += P1_C1; /* C1->P1 */ PC = P1 - P1_P2 / (1.0 - gamma); /* P1->PC */ /* slant ionospheric delay L1 (m) */ if (!corr_ion(obs->time, nav, obs->sat, pos, azel, opt->ionoopt, &ion, &vari, brk)) { trace(2, "iono correction error: time=%s sat=%2d ionoopt=%d\n", time_str(obs->time, 2), obs->sat, opt->ionoopt); return 0; } /* ionosphere and windup corrected phase and code */ meas[0] = L1 + ion - lam[0] * phw; meas[1] = PC - ion; var[0] += vari; var[1] += vari + std::pow(ERR_CBIAS, 2.0); /* antenna phase center variation correction */ for (i = 0; i < 2; i++) { if (dants) meas[i] -= dants[0]; if (dantr) meas[i] -= dantr[0]; } return 1; } /* L1/L2 geometry-free phase measurement -------------------------------------*/ double gfmeas(const obsd_t *obs, const nav_t *nav) { const double *lam = nav->lam[obs->sat - 1]; if (lam[0] == 0.0 || lam[1] == 0.0 || obs->L[0] == 0.0 || obs->L[1] == 0.0) return 0.0; return lam[0] * obs->L[0] - lam[1] * obs->L[1]; } /* temporal update of position -----------------------------------------------*/ void udpos_ppp(rtk_t *rtk) { int i; trace(3, "udpos_ppp:\n"); /* fixed mode */ if (rtk->opt.mode == PMODE_PPP_FIXED) { for (i = 0; i < 3; i++) initx(rtk, rtk->opt.ru[i], 1E-8, i); return; } /* initialize position for first epoch */ if (norm_rtk(rtk->x, 3) <= 0.0) { for (i = 0; i < 3; i++) initx(rtk, rtk->sol.rr[i], VAR_POS_PPP, i); } /* static ppp mode */ if (rtk->opt.mode == PMODE_PPP_STATIC) return; /* kinmatic mode without dynamics */ for (i = 0; i < 3; i++) { initx(rtk, rtk->sol.rr[i], VAR_POS_PPP, i); } } /* temporal update of clock --------------------------------------------------*/ void udclk_ppp(rtk_t *rtk) { double dtr; int i; trace(3, "udclk_ppp:\n"); /* initialize every epoch for clock (white noise) */ for (i = 0; i < NSYS; i++) { if (rtk->opt.sateph == EPHOPT_PREC) { /* time of prec ephemeris is based gpst */ /* negelect receiver inter-system bias */ dtr = rtk->sol.dtr[0]; } else { dtr = i == 0 ? rtk->sol.dtr[0] : rtk->sol.dtr[0] + rtk->sol.dtr[i]; } initx(rtk, SPEED_OF_LIGHT * dtr, VAR_CLK, IC_PPP(i, &rtk->opt)); } } /* temporal update of tropospheric parameters --------------------------------*/ void udtrop_ppp(rtk_t *rtk) { double pos[3], azel[] = {0.0, PI / 2.0}, ztd, var; int i = IT_PPP(&rtk->opt), j; trace(3, "udtrop_ppp:\n"); if (rtk->x[i] == 0.0) { ecef2pos(rtk->sol.rr, pos); ztd = sbstropcorr(rtk->sol.time, pos, azel, &var); initx(rtk, ztd, var, i); if (rtk->opt.tropopt >= TROPOPT_ESTG) { for (j = 0; j < 2; j++) initx(rtk, 1E-6, VAR_GRA_PPP, ++i); } } else { rtk->P[i * (1 + rtk->nx)] += std::pow(rtk->opt.prn[2], 2.0) * fabs(rtk->tt); if (rtk->opt.tropopt >= TROPOPT_ESTG) { for (j = 0; j < 2; j++) { rtk->P[++i * (1 + rtk->nx)] += std::pow(rtk->opt.prn[2] * 0.1, 2.0) * fabs(rtk->tt); } } } } /* detect cycle slip by LLI --------------------------------------------------*/ void detslp_ll(rtk_t *rtk, const obsd_t *obs, int n) { int i, j; trace(3, "detslp_ll: n=%d\n", n); for (i = 0; i < n && i < MAXOBS; i++) for (j = 0; j < rtk->opt.nf; j++) { if (obs[i].L[j] == 0.0 || !(obs[i].LLI[j] & 3)) continue; trace(3, "detslp_ll: slip detected sat=%2d f=%d\n", obs[i].sat, j + 1); rtk->ssat[obs[i].sat - 1].slip[j] = 1; } } /* detect cycle slip by geometry free phase jump -----------------------------*/ void detslp_gf(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { double g0, g1; int i, j; trace(3, "detslp_gf: n=%d\n", n); for (i = 0; i < n && i < MAXOBS; i++) { if ((g1 = gfmeas(obs + i, nav)) == 0.0) continue; g0 = rtk->ssat[obs[i].sat - 1].gf; rtk->ssat[obs[i].sat - 1].gf = g1; trace(4, "detslip_gf: sat=%2d gf0=%8.3f gf1=%8.3f\n", obs[i].sat, g0, g1); if (g0 != 0.0 && fabs(g1 - g0) > rtk->opt.thresslip) { trace(3, "detslip_gf: slip detected sat=%2d gf=%8.3f->%8.3f\n", obs[i].sat, g0, g1); for (j = 0; j < rtk->opt.nf; j++) rtk->ssat[obs[i].sat - 1].slip[j] |= 1; } } } /* temporal update of phase biases -------------------------------------------*/ void udbias_ppp(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { double meas[2], var[2], bias[MAXOBS] = {0}, offset = 0.0, pos[3] = {0}; int i, j, k, sat, brk = 0; trace(3, "udbias : n=%d\n", n); for (i = 0; i < MAXSAT; i++) for (j = 0; j < rtk->opt.nf; j++) { rtk->ssat[i].slip[j] = 0; } /* detect cycle slip by LLI */ detslp_ll(rtk, obs, n); /* detect cycle slip by geometry-free phase jump */ detslp_gf(rtk, obs, n, nav); /* reset phase-bias if expire obs outage counter */ for (i = 0; i < MAXSAT; i++) { if (++rtk->ssat[i].outc[0] > (unsigned int)rtk->opt.maxout) { initx(rtk, 0.0, 0.0, IB_PPP(i + 1, &rtk->opt)); } } ecef2pos(rtk->sol.rr, pos); for (i = k = 0; i < n && i < MAXOBS; i++) { sat = obs[i].sat; j = IB_PPP(sat, &rtk->opt); if (!corrmeas(obs + i, nav, pos, rtk->ssat[sat - 1].azel, &rtk->opt, NULL, NULL, 0.0, meas, var, &brk)) continue; if (brk) { rtk->ssat[sat - 1].slip[0] = 1; trace(2, "%s: sat=%2d correction break\n", time_str(obs[i].time, 0), sat); } bias[i] = meas[0] - meas[1]; if (rtk->x[j] == 0.0 || rtk->ssat[sat - 1].slip[0] || rtk->ssat[sat - 1].slip[1]) continue; offset += bias[i] - rtk->x[j]; k++; } /* correct phase-code jump to enssure phase-code coherency */ if (k >= 2 && fabs(offset / k) > 0.0005 * SPEED_OF_LIGHT) { for (i = 0; i < MAXSAT; i++) { j = IB_PPP(i + 1, &rtk->opt); if (rtk->x[j] != 0.0) rtk->x[j] += offset / k; } trace(2, "phase-code jump corrected: %s n=%2d dt=%12.9fs\n", time_str(rtk->sol.time, 0), k, offset / k / SPEED_OF_LIGHT); } for (i = 0; i < n && i < MAXOBS; i++) { sat = obs[i].sat; j = IB_PPP(sat, &rtk->opt); rtk->P[j + j * rtk->nx] += std::pow(rtk->opt.prn[0], 2.0) * fabs(rtk->tt); if (rtk->x[j] != 0.0 && !rtk->ssat[sat - 1].slip[0] && !rtk->ssat[sat - 1].slip[1]) continue; if (bias[i] == 0.0) continue; /* reinitialize phase-bias if detecting cycle slip */ initx(rtk, bias[i], VAR_BIAS, IB_PPP(sat, &rtk->opt)); trace(5, "udbias_ppp: sat=%2d bias=%.3f\n", sat, meas[0] - meas[1]); } } /* temporal update of states --------------------------------------------------*/ void udstate_ppp(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { trace(3, "udstate_ppp: n=%d\n", n); /* temporal update of position */ udpos_ppp(rtk); /* temporal update of clock */ udclk_ppp(rtk); /* temporal update of tropospheric parameters */ if (rtk->opt.tropopt >= TROPOPT_EST) { udtrop_ppp(rtk); } /* temporal update of phase-bias */ udbias_ppp(rtk, obs, n, nav); } /* satellite antenna phase center variation ----------------------------------*/ void satantpcv(const double *rs, const double *rr, const pcv_t *pcv, double *dant) { double ru[3], rz[3], eu[3], ez[3], nadir, cosa; int i; for (i = 0; i < 3; i++) { ru[i] = rr[i] - rs[i]; rz[i] = -rs[i]; } if (!normv3(ru, eu) || !normv3(rz, ez)) return; cosa = dot(eu, ez, 3); cosa = cosa < -1.0 ? -1.0 : (cosa > 1.0 ? 1.0 : cosa); nadir = acos(cosa); antmodel_s(pcv, nadir, dant); } /* precise tropospheric model ------------------------------------------------*/ double prectrop(gtime_t time, const double *pos, const double *azel, const prcopt_t *opt, const double *x, double *dtdx, double *var) { const double zazel[] = {0.0, PI / 2.0}; double zhd, m_h, m_w, cotz, grad_n, grad_e; /* zenith hydrostatic delay */ zhd = tropmodel(time, pos, zazel, 0.0); /* mapping function */ m_h = tropmapf(time, pos, azel, &m_w); if ((opt->tropopt == TROPOPT_ESTG || opt->tropopt == TROPOPT_CORG) && azel[1] > 0.0) { /* m_w=m_0+m_0*cot(el)*(Gn*cos(az)+Ge*sin(az)): ref [6] */ cotz = 1.0 / tan(azel[1]); grad_n = m_w * cotz * cos(azel[0]); grad_e = m_w * cotz * sin(azel[0]); m_w += grad_n * x[1] + grad_e * x[2]; dtdx[1] = grad_n * (x[0] - zhd); dtdx[2] = grad_e * (x[0] - zhd); } dtdx[0] = m_w; *var = std::pow(0.01, 2.0); return m_h * zhd + m_w * (x[0] - zhd); } /* phase and code residuals --------------------------------------------------*/ int res_ppp(int iter __attribute__((unused)), const obsd_t *obs, int n, const double *rs, const double *dts, const double *vare, const int *svh, const nav_t *nav, const double *x, rtk_t *rtk, double *v, double *H, double *R, double *azel) { prcopt_t *opt = &rtk->opt; double r, rr[3], disp[3], pos[3], e[3], meas[2], dtdx[3], dantr[NFREQ] = {0}; double dants[NFREQ] = {0}, var[MAXOBS * 2], dtrp = 0.0, vart = 0.0, varm[2] = {0}; int i, j, k, sat, sys, nv = 0, nx = rtk->nx, brk, tideopt; trace(3, "res_ppp : n=%d nx=%d\n", n, nx); for (i = 0; i < MAXSAT; i++) rtk->ssat[i].vsat[0] = 0; for (i = 0; i < 3; i++) rr[i] = x[i]; /* earth tides correction */ if (opt->tidecorr) { tideopt = opt->tidecorr == 1 ? 1 : 7; /* 1:solid, 2:solid+otl+pole */ tidedisp(gpst2utc(obs[0].time), rr, tideopt, &nav->erp, opt->odisp[0], disp); for (i = 0; i < 3; i++) rr[i] += disp[i]; } ecef2pos(rr, pos); for (i = 0; i < n && i < MAXOBS; i++) { sat = obs[i].sat; if (!(sys = satsys(sat, NULL)) || !rtk->ssat[sat - 1].vs) continue; /* geometric distance/azimuth/elevation angle */ if ((r = geodist(rs + i * 6, rr, e)) <= 0.0 || satazel(pos, e, azel + i * 2) < opt->elmin) continue; /* excluded satellite? */ if (satexclude(obs[i].sat, svh[i], opt)) continue; /* tropospheric delay correction */ if (opt->tropopt == TROPOPT_SAAS) { dtrp = tropmodel(obs[i].time, pos, azel + i * 2, REL_HUMI); vart = std::pow(ERR_SAAS, 2.0); } else if (opt->tropopt == TROPOPT_SBAS) { dtrp = sbstropcorr(obs[i].time, pos, azel + i * 2, &vart); } else if (opt->tropopt == TROPOPT_EST || opt->tropopt == TROPOPT_ESTG) { dtrp = prectrop(obs[i].time, pos, azel + i * 2, opt, x + IT_PPP(opt), dtdx, &vart); } else if (opt->tropopt == TROPOPT_COR || opt->tropopt == TROPOPT_CORG) { dtrp = prectrop(obs[i].time, pos, azel + i * 2, opt, x, dtdx, &vart); } /* satellite antenna model */ if (opt->posopt[0]) { satantpcv(rs + i * 6, rr, nav->pcvs + sat - 1, dants); } /* receiver antenna model */ antmodel(opt->pcvr, opt->antdel[0], azel + i * 2, opt->posopt[1], dantr); /* phase windup correction */ if (opt->posopt[2]) { windupcorr(rtk->sol.time, rs + i * 6, rr, &rtk->ssat[sat - 1].phw); } /* ionosphere and antenna phase corrected measurements */ if (!corrmeas(obs + i, nav, pos, azel + i * 2, &rtk->opt, dantr, dants, rtk->ssat[sat - 1].phw, meas, varm, &brk)) { continue; } /* satellite clock and tropospheric delay */ r += -SPEED_OF_LIGHT * dts[i * 2] + dtrp; trace(5, "sat=%2d azel=%6.1f %5.1f dtrp=%.3f dantr=%6.3f %6.3f dants=%6.3f %6.3f phw=%6.3f\n", sat, azel[i * 2] * R2D, azel[1 + i * 2] * R2D, dtrp, dantr[0], dantr[1], dants[0], dants[1], rtk->ssat[sat - 1].phw); for (j = 0; j < 2; j++) { /* for phase and code */ if (meas[j] == 0.0) continue; for (k = 0; k < nx; k++) H[k + nx * nv] = 0.0; v[nv] = meas[j] - r; for (k = 0; k < 3; k++) H[k + nx * nv] = -e[k]; if (sys != SYS_GLO) { v[nv] -= x[IC_PPP(0, opt)]; H[IC_PPP(0, opt) + nx * nv] = 1.0; } else { v[nv] -= x[IC_PPP(1, opt)]; H[IC_PPP(1, opt) + nx * nv] = 1.0; } if (opt->tropopt >= TROPOPT_EST) { for (k = 0; k < (opt->tropopt >= TROPOPT_ESTG ? 3 : 1); k++) { H[IT_PPP(opt) + k + nx * nv] = dtdx[k]; } } if (j == 0) { v[nv] -= x[IB_PPP(obs[i].sat, opt)]; H[IB_PPP(obs[i].sat, opt) + nx * nv] = 1.0; } var[nv] = varerr(obs[i].sat, sys, azel[1 + i * 2], j, opt) + varm[j] + vare[i] + vart; if (j == 0) rtk->ssat[sat - 1].resc[0] = v[nv]; else rtk->ssat[sat - 1].resp[0] = v[nv]; /* test innovation */ #if 0 if (opt->maxinno>0.0 && fabs(v[nv])>opt->maxinno) { #else if (opt->maxinno > 0.0 && fabs(v[nv]) > opt->maxinno && sys != SYS_GLO) { #endif trace(2, "ppp outlier rejected %s sat=%2d type=%d v=%.3f\n", time_str(obs[i].time, 0), sat, j, v[nv]); rtk->ssat[sat - 1].rejc[0]++; continue; } if (j == 0) rtk->ssat[sat - 1].vsat[0] = 1; nv++; } } for (i = 0; i < nv; i++) for (j = 0; j < nv; j++) { R[i + j * nv] = i == j ? var[i] : 0.0; } trace(5, "x=\n"); tracemat(5, x, 1, nx, 8, 3); trace(5, "v=\n"); tracemat(5, v, 1, nv, 8, 3); trace(5, "H=\n"); tracemat(5, H, nx, nv, 8, 3); trace(5, "R=\n"); tracemat(5, R, nv, nv, 8, 5); return nv; } /* number of estimated states ------------------------------------------------*/ int pppnx(const prcopt_t *opt) { return NX_PPP(opt); } /* precise point positioning -------------------------------------------------*/ void pppos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { const prcopt_t *opt = &rtk->opt; double *rs, *dts, *var, *v, *H, *R, *azel, *xp, *Pp; int i, nv, info, svh[MAXOBS], stat = SOLQ_SINGLE; trace(3, "pppos : nx=%d n=%d\n", rtk->nx, n); rs = mat(6, n); dts = mat(2, n); var = mat(1, n); azel = zeros(2, n); for (i = 0; i < MAXSAT; i++) rtk->ssat[i].fix[0] = 0; /* temporal update of states */ udstate_ppp(rtk, obs, n, nav); trace(4, "x(0)="); tracemat(4, rtk->x, 1, NR_PPP(opt), 13, 4); /* satellite positions and clocks */ satposs(obs[0].time, obs, n, nav, rtk->opt.sateph, rs, dts, var, svh); /* exclude measurements of eclipsing satellite */ if (rtk->opt.posopt[3]) { testeclipse(obs, n, nav, rs); } xp = mat(rtk->nx, 1); Pp = zeros(rtk->nx, rtk->nx); matcpy(xp, rtk->x, rtk->nx, 1); nv = n * rtk->opt.nf * 2; v = mat(nv, 1); H = mat(rtk->nx, nv); R = mat(nv, nv); for (i = 0; i < rtk->opt.niter; i++) { /* phase and code residuals */ if ((nv = res_ppp(i, obs, n, rs, dts, var, svh, nav, xp, rtk, v, H, R, azel)) <= 0) break; /* measurement update */ matcpy(Pp, rtk->P, rtk->nx, rtk->nx); if ((info = filter(xp, Pp, H, v, R, rtk->nx, nv))) { trace(2, "ppp filter error %s info=%d\n", time_str(rtk->sol.time, 0), info); break; } trace(4, "x(%d)=", i + 1); tracemat(4, xp, 1, NR_PPP(opt), 13, 4); stat = SOLQ_PPP; } if (stat == SOLQ_PPP) { /* postfit residuals */ res_ppp(1, obs, n, rs, dts, var, svh, nav, xp, rtk, v, H, R, azel); /* update state and covariance matrix */ matcpy(rtk->x, xp, rtk->nx, 1); matcpy(rtk->P, Pp, rtk->nx, rtk->nx); /* ambiguity resolution in ppp */ if (opt->modear == ARMODE_PPPAR || opt->modear == ARMODE_PPPAR_ILS) { if (pppamb(rtk, obs, n, nav, azel)) stat = SOLQ_FIX; } /* update solution status */ rtk->sol.ns = 0; for (i = 0; i < n && i < MAXOBS; i++) { if (!rtk->ssat[obs[i].sat - 1].vsat[0]) continue; rtk->ssat[obs[i].sat - 1].lock[0]++; rtk->ssat[obs[i].sat - 1].outc[0] = 0; rtk->ssat[obs[i].sat - 1].fix[0] = 4; rtk->sol.ns++; } rtk->sol.stat = stat; for (i = 0; i < 3; i++) { rtk->sol.rr[i] = rtk->x[i]; rtk->sol.qr[i] = (float)rtk->P[i + i * rtk->nx]; } rtk->sol.qr[3] = (float)rtk->P[1]; rtk->sol.qr[4] = (float)rtk->P[2 + rtk->nx]; rtk->sol.qr[5] = (float)rtk->P[2]; rtk->sol.dtr[0] = rtk->x[IC_PPP(0, opt)]; rtk->sol.dtr[1] = rtk->x[IC_PPP(1, opt)] - rtk->x[IC_PPP(0, opt)]; for (i = 0; i < n && i < MAXOBS; i++) { rtk->ssat[obs[i].sat - 1].snr[0] = MIN_PPP(obs[i].SNR[0], obs[i].SNR[1]); } for (i = 0; i < MAXSAT; i++) { if (rtk->ssat[i].slip[0] & 3) rtk->ssat[i].slipc[0]++; } } free(rs); free(dts); free(var); free(azel); free(xp); free(Pp); free(v); free(H); free(R); }