gnss_rtk/navigation/
solutions.rs

1//! PVT Solution
2use crate::{
3    navigation::{sv::SVContribution, DilutionOfPrecision, State},
4    prelude::{Epoch, TimeScale},
5};
6
7#[cfg(feature = "serde")]
8use serde::Serialize;
9
10/// Describes the navigation technique used to obtain this [PVTSolution].
11#[derive(Debug, Clone, PartialEq)]
12#[cfg_attr(feature = "serde", derive(Serialize))]
13pub enum PVTSolutionType {
14    /// [PVTSolutionType::PPP] obtained using absolute navigation technique.
15    /// This is true as long as this is not an RTK (differential) solution.
16    /// In PPP solutions, the clock state, drift and TDOP are updated.
17    PPP = 0,
18
19    /// [PVTSolutionType::RTK] obtained using differential navigation technique.
20    /// That means at minimum of one ground station was used to obtain
21    /// this [PVTSolution]. The clock state, drift and TDoP are not resolved in
22    /// RTK solutions. It is normal to obtain 0 for all these fields, for every single solution,
23    /// for a session that only uses RTK.
24    RTK = 1,
25}
26
27/// [PVTSolution] is solved by the navigation solver from a set of measurements,
28/// using either PPP or RTK navigation technique. Depending on the technique being used,
29/// the [PVTSolution] will differ. Mainly, RTK is not able to update and resolve the clock state.
30#[derive(Debug, Clone)]
31#[cfg_attr(feature = "serde", derive(Serialize))]
32pub struct PVTSolution {
33    /// Type of solution
34    pub solution_type: PVTSolutionType,
35
36    /// Measurement [Epoch] that led to this solution.
37    pub epoch: Epoch,
38
39    /// Position solution, expressed in meters (ECEF).
40    pub pos_m: (f64, f64, f64),
41
42    /// Velocity solution, expressed in meters.s⁻¹ (ECEF).
43    pub vel_m_s: (f64, f64, f64),
44
45    /// Latitude, longitude and altitude above mean sea level,
46    /// in degrees and meters.
47    pub lat_long_alt_deg_deg_m: (f64, f64, f64),
48
49    /// [TimeScale] of clock_offset [Duration] expression.
50    pub timescale: TimeScale,
51
52    /// Clock offset (in seconds).
53    pub clock_offset_s: f64,
54
55    /// Space Vehicles that helped form this solution
56    /// and data associated to each individual SV
57    pub sv: Vec<SVContribution>,
58
59    /// Geometric Dilution of Precision
60    pub gdop: f64,
61
62    /// Vertical Dilution of Precision
63    pub vdop: f64,
64
65    /// Horizontal Diultion of Precision
66    pub hdop: f64,
67
68    /// Temporal Dilution of Precision
69    pub tdop: f64,
70}
71
72impl PVTSolution {
73    pub(crate) fn new(
74        epoch: Epoch,
75        uses_rtk: bool,
76        state: &State,
77        dop: &DilutionOfPrecision,
78        contributions: &[SVContribution],
79    ) -> Self {
80        let pos_vel_ecef_m = state.to_position_velocity_ecef_m();
81        let (clock_offset_s, _) = state.clock_profile_s();
82
83        Self {
84            epoch,
85            solution_type: if uses_rtk {
86                PVTSolutionType::RTK
87            } else {
88                PVTSolutionType::PPP
89            },
90            gdop: dop.gdop,
91            tdop: dop.tdop,
92            vdop: dop.vdop,
93            hdop: dop.hdop,
94            clock_offset_s,
95            lat_long_alt_deg_deg_m: (
96                state.lat_long_alt_deg_deg_km.0,
97                state.lat_long_alt_deg_deg_km.1,
98                state.lat_long_alt_deg_deg_km.2 * 1.0E3,
99            ),
100            sv: contributions.to_vec(),
101            timescale: state.epoch.time_scale,
102            pos_m: (pos_vel_ecef_m[0], pos_vel_ecef_m[1], pos_vel_ecef_m[2]),
103            vel_m_s: (pos_vel_ecef_m[3], pos_vel_ecef_m[4], pos_vel_ecef_m[5]),
104        }
105    }
106}