mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2026-10-17 04:52:36 +00:00
Move the NaN guard from monitor reporting to the position solve
The previous commit filtered NaN azel out of tracked_satellites entirely, which was the wrong place: NaN there is a legitimate, informative result (ephemeris/almanac was corrupt enough to produce one) that the monitor and its consumers should see and handle explicitly, not have silently hidden. What must never happen is a NaN satellite influencing the actual position fix. Add that guard where it belongs: rescode() in rtklib_pntpos.cc, right after satazel() and prange(), using std::isfinite() (neither elaux < opt->elmin nor prange()'s P == 0.0 sentinel catches NaN, since ordinary relational comparisons are vacuously false against it). This is the single gatekeeper for vsat[]/nv, so the guard transitively protects everything downstream that filters on vsat[] too: DOP computation, the chi-square residual check, RAIM FDE, and the velocity/Doppler solve (resdop()/estvel()) -- not just the position solve directly. tracked_satellites reporting goes back to reporting azel as computed, NaN included.
This commit is contained in:
@@ -2850,20 +2850,20 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user