From 7c8dfbbb7977ff4f663be52b4e40e1ab9d641987 Mon Sep 17 00:00:00 2001 From: joebre Date: Mon, 7 Sep 2026 18:11:12 +0200 Subject: [PATCH] Report az/el for satellites excluded from the PVT fix, not just used ones rescode() (rtklib_pntpos.cc) computes each satellite's azimuth/elevation via satazel() before checking it against PVT.elevation_mask, and pntpos() copies that azel into ssat[] unconditionally -- only ssat[].vs is gated by the mask (the same applies to a satellite dropped by RAIM FDE). The used_satellites loop in rtklib_solver.cc was keying its entire per-satellite entry on vs, so a tracked satellite excluded from the fix (below the elevation mask, or by RAIM) disappeared from MonitorPvt entirely -- indistinguishable from a genuine tracking problem. Key entries on whether azel was actually computed this epoch instead, and add a separate `used` flag (default true) reflecting vs. A satellite that is tracked but excluded from the fix now still reports its real az/el, with used = false. --- docs/protobuf/monitor_pvt.proto | 14 +++++++++----- src/algorithms/PVT/libs/monitor_pvt.h | 7 +++++++ src/algorithms/PVT/libs/rtklib_solver.cc | 17 ++++++++++++++++- src/algorithms/PVT/libs/serdes_monitor_pvt.h | 2 ++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/docs/protobuf/monitor_pvt.proto b/docs/protobuf/monitor_pvt.proto index 4461dd3f9..75fbfe9a5 100644 --- a/docs/protobuf/monitor_pvt.proto +++ b/docs/protobuf/monitor_pvt.proto @@ -53,11 +53,14 @@ message MonitorPvt { uint32 galhas_status = 35; // Galileo HAS status: 1- HAS messages decoded and applied, 0 - HAS not available string geohash = 36; // Encoded geographic location. See https://en.wikipedia.org/wiki/Geohash - // One satellite/signal used in this fix, with its azimuth/elevation and - // whether it was combined with another signal of the same satellite (e.g. - // Galileo E1+E5a iono-free combination). Signals are listed individually, - // not merged: a combined satellite appears as two entries, both with - // combined = true. + // One tracked satellite/signal with its azimuth/elevation, whether it was + // combined with another signal of the same satellite (e.g. Galileo E1+E5a + // iono-free combination), and whether it was actually used in this fix. + // Signals are listed individually, not merged: a combined satellite + // appears as two entries, both with combined = true. A satellite can have + // azimuth_deg/elevation_deg populated with used = false when it was + // tracked and had az/el computed but excluded from the fix itself (e.g. + // below PVT.elevation_mask, or by RAIM FDE). message UsedSatellite { uint32 prn = 1; string system = 2; // "G" GPS, "E" Galileo, "R" GLONASS, "C" BeiDou, "S" SBAS, "J" QZSS @@ -65,6 +68,7 @@ message MonitorPvt { double azimuth_deg = 4; double elevation_deg = 5; bool combined = 6; + bool used = 7; } repeated UsedSatellite used_satellites = 37; } diff --git a/src/algorithms/PVT/libs/monitor_pvt.h b/src/algorithms/PVT/libs/monitor_pvt.h index 6a3b278e4..739e62d5f 100644 --- a/src/algorithms/PVT/libs/monitor_pvt.h +++ b/src/algorithms/PVT/libs/monitor_pvt.h @@ -53,6 +53,12 @@ public: double azimuth_deg{}; double elevation_deg{}; bool combined{}; + // false when this satellite/signal was tracked and had azimuth/elevation + // computed but was excluded from the fix itself (e.g. below + // PVT.elevation_mask, or by RAIM FDE) -- azimuth_deg/elevation_deg are + // still valid in that case, only the position solve ignored this + // observation. + bool used{true}; template void serialize(Archive& ar, const unsigned int version) @@ -66,6 +72,7 @@ public: ar& BOOST_SERIALIZATION_NVP(azimuth_deg); ar& BOOST_SERIALIZATION_NVP(elevation_deg); ar& BOOST_SERIALIZATION_NVP(combined); + ar& BOOST_SERIALIZATION_NVP(used); } }; diff --git a/src/algorithms/PVT/libs/rtklib_solver.cc b/src/algorithms/PVT/libs/rtklib_solver.cc index a91e788e7..aa49c89ec 100644 --- a/src/algorithms/PVT/libs/rtklib_solver.cc +++ b/src/algorithms/PVT/libs/rtklib_solver.cc @@ -2837,13 +2837,27 @@ bool Rtklib_Solver::get_PVT(const std::map &gnss_observables_ // iono-free combination -- see the "dual-frequency" branch of // prange() in rtklib_pntpos.cc) gets one entry per signal, all // flagged combined = true. + // + // rescode() (rtklib_pntpos.cc) computes azel via satazel() + // *before* checking it against PVT.elevation_mask, and + // pntpos() copies that azel into ssat[] unconditionally -- + // only ssat[].vs is gated by the mask. So a satellite that + // is tracked and has a live observation this epoch, but + // falls below PVT.elevation_mask (or was excluded by RAIM + // FDE), still has a valid azel here; only vs is false. + // Report it anyway with used = false instead of dropping + // it, so an excluded satellite shows up in the monitor as + // "not used" rather than as missing/no-az-el (which + // otherwise looks identical to a tracking problem). d_monitor_pvt.used_satellites.clear(); for (int sat_idx = 0; sat_idx < MAXSAT; sat_idx++) { - if (!pvt_ssat[sat_idx].vs) + const bool has_azel = (pvt_ssat[sat_idx].azel[0] != 0.0) || (pvt_ssat[sat_idx].azel[1] != 0.0); + if (!has_azel) { continue; } + const bool used = pvt_ssat[sat_idx].vs != 0; int prn = 0; char sys_char = '?'; switch (satsys(sat_idx + 1, &prn)) @@ -2896,6 +2910,7 @@ bool Rtklib_Solver::get_PVT(const std::map &gnss_observables_ info.azimuth_deg = az_deg; info.elevation_deg = pvt_ssat[sat_idx].azel[1] * R2D; info.combined = combined; + info.used = used; d_monitor_pvt.used_satellites.push_back(info); } } diff --git a/src/algorithms/PVT/libs/serdes_monitor_pvt.h b/src/algorithms/PVT/libs/serdes_monitor_pvt.h index 62f32220b..ebc7e232f 100644 --- a/src/algorithms/PVT/libs/serdes_monitor_pvt.h +++ b/src/algorithms/PVT/libs/serdes_monitor_pvt.h @@ -129,6 +129,7 @@ public: pb_sat->set_azimuth_deg(sat.azimuth_deg); pb_sat->set_elevation_deg(sat.elevation_deg); pb_sat->set_combined(sat.combined); + pb_sat->set_used(sat.used); } if (!monitor_.SerializeToString(&data)) @@ -188,6 +189,7 @@ public: sat.azimuth_deg = pb_sat.azimuth_deg(); sat.elevation_deg = pb_sat.elevation_deg(); sat.combined = pb_sat.combined(); + sat.used = pb_sat.used(); monitor.used_satellites.push_back(sat); }