Merge branch 'joebre-feat/monitor-pvt-used-satellites' into next

This commit is contained in:
Carles Fernandez
2026-08-29 12:59:45 +02:00
5 changed files with 150 additions and 0 deletions
+3
View File
@@ -387,6 +387,9 @@ All notable changes to GNSS-SDR will be documented in this file.
epochs now fall on the same integer-millisecond grid as the observables, as
they already did in configurations including GPS. RINEX files were not
affected.
- The PVT Monitor now reports per-signal details for satellites used in the
position solution, including PRN, constellation, signal, azimuth, elevation,
and whether multiple signals were combined. Contributed by @joebre.
See the definitions of concepts and metrics at
https://gnss-sdr.org/design-forces/
+15
View File
@@ -52,4 +52,19 @@ 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.
message UsedSatellite {
uint32 prn = 1;
string system = 2; // "G" GPS, "E" Galileo, "R" GLONASS, "C" BeiDou, "S" SBAS, "J" QZSS
string signal = 3;
double azimuth_deg = 4;
double elevation_deg = 5;
bool combined = 6;
}
repeated UsedSatellite used_satellites = 37;
}
+39
View File
@@ -18,8 +18,10 @@
#define GNSS_SDR_MONITOR_PVT_H
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/vector.hpp>
#include <cstdint>
#include <string>
#include <vector>
/** \addtogroup PVT
* \{ */
@@ -33,6 +35,42 @@
class Monitor_Pvt
{
public:
/*!
* \brief One satellite/signal that contributed to 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 -- see
* the "dual-frequency" branch of prange() in rtklib_pntpos.cc). Signals
* are listed individually (one entry per satellite per signal), not
* merged, so a combined satellite appears as two entries both flagged
* combined = true.
*/
class UsedSatelliteInfo
{
public:
uint32_t prn{};
char system{}; // 'G' GPS, 'E' Galileo, 'R' GLONASS, 'C' BeiDou, 'S' SBAS, 'J' QZSS
std::string signal;
double azimuth_deg{};
double elevation_deg{};
bool combined{};
template <class Archive>
void serialize(Archive& ar, const unsigned int version)
{
if (version)
{
};
ar& BOOST_SERIALIZATION_NVP(prn);
ar& BOOST_SERIALIZATION_NVP(system);
ar& BOOST_SERIALIZATION_NVP(signal);
ar& BOOST_SERIALIZATION_NVP(azimuth_deg);
ar& BOOST_SERIALIZATION_NVP(elevation_deg);
ar& BOOST_SERIALIZATION_NVP(combined);
}
};
std::vector<UsedSatelliteInfo> used_satellites;
// TOW
uint32_t TOW_at_current_symbol_ms;
// WEEK
@@ -155,6 +193,7 @@ public:
ar& BOOST_SERIALIZATION_NVP(cog);
ar& BOOST_SERIALIZATION_NVP(geohash);
ar& BOOST_SERIALIZATION_NVP(used_satellites);
}
};
+70
View File
@@ -2830,6 +2830,76 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
d_monitor_pvt.hdop = d_dop[2];
d_monitor_pvt.vdop = d_dop[3];
// USED SATELLITES: per-satellite azimuth/elevation and which
// signal(s) contributed to this fix. Signals are listed
// individually (one entry per satellite per signal); a
// satellite whose signals were combined (e.g. Galileo E1+E5a
// iono-free combination -- see the "dual-frequency" branch of
// prange() in rtklib_pntpos.cc) gets one entry per signal, all
// flagged combined = true.
d_monitor_pvt.used_satellites.clear();
for (int sat_idx = 0; sat_idx < MAXSAT; sat_idx++)
{
if (!pvt_ssat[sat_idx].vs)
{
continue;
}
int prn = 0;
char sys_char = '?';
switch (satsys(sat_idx + 1, &prn))
{
case SYS_GPS:
sys_char = 'G';
break;
case SYS_GAL:
sys_char = 'E';
break;
case SYS_GLO:
sys_char = 'R';
break;
case SYS_BDS:
sys_char = 'C';
break;
case SYS_SBS:
sys_char = 'S';
break;
case SYS_QZS:
sys_char = 'J';
break;
default:
break;
}
std::vector<const Gnss_Synchro *> contributing_signals;
for (const auto &observable_pair : gnss_observables_map)
{
const Gnss_Synchro &synchro = observable_pair.second;
if (synchro.System == sys_char && static_cast<int>(synchro.PRN) == prn)
{
contributing_signals.push_back(&synchro);
}
}
const bool combined = contributing_signals.size() > 1;
// satazel() (rtklib_rtkcmn.cc) returns azimuth in [0, 2*pi); wrap to
// (-180, 180] deg, the convention expected downstream (monitor sky plot).
double az_deg = pvt_ssat[sat_idx].azel[0] * R2D;
if (az_deg > 180.0)
{
az_deg -= 360.0;
}
for (const Gnss_Synchro *synchro : contributing_signals)
{
Monitor_Pvt::UsedSatelliteInfo info;
info.prn = static_cast<uint32_t>(prn);
info.system = sys_char;
info.signal = std::string(synchro->Signal, 2);
info.azimuth_deg = az_deg;
info.elevation_deg = pvt_ssat[sat_idx].azel[1] * R2D;
info.combined = combined;
d_monitor_pvt.used_satellites.push_back(info);
}
}
this->set_rx_vel({enuv[0], enuv[1], enuv[2]});
// ENU vel [m/s]
@@ -120,6 +120,17 @@ public:
monitor_.set_galhas_status(monitor->galhas_status);
monitor_.set_geohash(monitor->geohash);
for (const auto& sat : monitor->used_satellites)
{
gnss_sdr::MonitorPvt::UsedSatellite* pb_sat = monitor_.add_used_satellites();
pb_sat->set_prn(sat.prn);
pb_sat->set_system(std::string(1, sat.system));
pb_sat->set_signal(sat.signal);
pb_sat->set_azimuth_deg(sat.azimuth_deg);
pb_sat->set_elevation_deg(sat.elevation_deg);
pb_sat->set_combined(sat.combined);
}
if (!monitor_.SerializeToString(&data))
{
return {};
@@ -168,6 +179,18 @@ public:
monitor.galhas_status = mon.galhas_status();
monitor.geohash = mon.geohash();
for (const auto& pb_sat : mon.used_satellites())
{
Monitor_Pvt::UsedSatelliteInfo sat;
sat.prn = pb_sat.prn();
sat.system = pb_sat.system().empty() ? '\0' : pb_sat.system()[0];
sat.signal = pb_sat.signal();
sat.azimuth_deg = pb_sat.azimuth_deg();
sat.elevation_deg = pb_sat.elevation_deg();
sat.combined = pb_sat.combined();
monitor.used_satellites.push_back(sat);
}
return monitor;
}