diff --git a/src/algorithms/PVT/libs/rtklib_solver.cc b/src/algorithms/PVT/libs/rtklib_solver.cc index b0b0a5445..f4a3a4c57 100644 --- a/src/algorithms/PVT/libs/rtklib_solver.cc +++ b/src/algorithms/PVT/libs/rtklib_solver.cc @@ -2850,20 +2850,20 @@ bool Rtklib_Solver::get_PVT(const std::map &gnss_observables_ // "not used" rather than as missing/no-az-el (which // otherwise looks identical to a tracking problem). // - // std::isfinite() guards against corrupt ephemeris/almanac - // (e.g. an out-of-range eccentricity) propagating NaN through - // eph2pos()/alm2pos()/satazel() -- none of RTKLIB's own - // internal sanity checks (A <= 0, Kepler iteration overflow, - // geodist()'s Earth-radius check, the elevation mask) catch a - // NaN, since ordinary numeric comparisons are vacuously false - // against it. Without this, a NaN azel would actually pass - // the old != 0.0 check (NaN != 0.0 is true) and get reported - // into the monitor as a literal NaN azimuth/elevation. + // Deliberately NOT filtered for NaN here: a corrupt ephemeris/ + // almanac (e.g. an out-of-range eccentricity) can propagate NaN + // through eph2pos()/alm2pos()/satazel(), and that NaN is reported + // as-is rather than silently dropping the satellite from the + // monitor -- consumers (gnss-sdr-CtrlApp) are expected to handle + // a NaN azimuth_deg/elevation_deg explicitly (e.g. no sky plot + // entry, "NaN" printed in the signal table) rather than have it + // hidden here. What NaN must NOT do is influence the position fix + // itself -- see rescode()'s prange() isfinite guard in + // rtklib_pntpos.cc for where that's actually enforced. d_monitor_pvt.tracked_satellites.clear(); for (int sat_idx = 0; sat_idx < MAXSAT; sat_idx++) { - const bool has_azel = std::isfinite(pvt_ssat[sat_idx].azel[0]) && std::isfinite(pvt_ssat[sat_idx].azel[1]) && - ((pvt_ssat[sat_idx].azel[0] != 0.0) || (pvt_ssat[sat_idx].azel[1] != 0.0)); + const bool has_azel = (pvt_ssat[sat_idx].azel[0] != 0.0) || (pvt_ssat[sat_idx].azel[1] != 0.0); if (!has_azel) { continue; diff --git a/src/algorithms/libs/rtklib/rtklib_pntpos.cc b/src/algorithms/libs/rtklib/rtklib_pntpos.cc index f1cb6626b..f47eafecb 100644 --- a/src/algorithms/libs/rtklib/rtklib_pntpos.cc +++ b/src/algorithms/libs/rtklib/rtklib_pntpos.cc @@ -736,14 +736,26 @@ int rescode(int iter, const obsd_t *obs, int n, const double *rs, continue; } double elaux = satazel(pos, e, azel + i * 2); - if (elaux < opt->elmin) + /* !isfinite(elaux): corrupt ephemeris/almanac (e.g. an out-of-range + * eccentricity) can propagate NaN through eph2pos()/alm2pos() into + * azel -- azel itself is intentionally left as computed (including + * NaN) for callers that report it (e.g. the monitor's per-satellite + * az/el), but a NaN elevation must not be allowed into the position + * solve below: elaux < opt->elmin is a no-op against NaN (every + * relational comparison against NaN is false), so it would + * otherwise fall through as if elmin had been satisfied. */ + if (elaux < opt->elmin || !std::isfinite(elaux)) { trace(4, "satazel error. el = %lf , elmin = %lf\n", elaux, opt->elmin); continue; } /* psudorange with code bias correction */ double iono_scale = 1.0; - if ((P = prange(obs + i, nav, azel + i * 2, iter, opt, &vmeas, &iono_scale)) == 0.0) + P = prange(obs + i, nav, azel + i * 2, iter, opt, &vmeas, &iono_scale); + /* Same NaN concern as above, for prange()'s own corrections (iono/ + * tropo/code bias) -- P == 0.0 is prange()'s own "no valid pseudorange" + * sentinel and doesn't catch NaN either. */ + if (P == 0.0 || !std::isfinite(P)) { trace(4, "prange error\n"); continue;