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}