1use serde::{Deserialize, Serialize};
4
5#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
7pub enum FixStatus {
8 Unknown,
10
11 NotFix,
13
14 Fix2D,
16
17 Fix3D,
19}
20impl TryFrom<&str> for FixStatus {
21 type Error = String;
22
23 fn try_from(value: &str) -> Result<Self, Self::Error> {
24 match value.trim() {
25 "Location Unknown" | "Unknown" => Ok(FixStatus::Unknown),
26 "Location Not Fix" | "Not Fix" => Ok(FixStatus::NotFix),
27 "Location 2D Fix" | "2D Fix" => Ok(FixStatus::Fix2D),
28 "Location 3D Fix" | "3D Fix" => Ok(FixStatus::Fix3D),
29 _ => Err(format!("Invalid GNSS fix status: '{value}'")),
30 }
31 }
32}
33impl From<u8> for FixStatus {
34 fn from(value: u8) -> Self {
35 match value {
36 0 => FixStatus::NotFix,
37 1 => FixStatus::Fix2D,
38 2 => FixStatus::Fix3D,
39 _ => FixStatus::Unknown,
40 }
41 }
42}
43
44#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
46pub struct PositionReport {
47 pub run_status: bool,
49
50 pub fix_status: bool,
52
53 pub utc_time: String,
55
56 pub latitude: Option<f64>,
58
59 pub longitude: Option<f64>,
61
62 pub msl_altitude: Option<f64>,
64
65 pub ground_speed: Option<f32>,
67
68 pub ground_course: Option<f32>,
70
71 pub fix_mode: FixStatus,
73
74 pub hdop: Option<f32>,
76
77 pub pdop: Option<f32>,
79
80 pub vdop: Option<f32>,
82
83 pub gps_in_view: Option<u8>,
85
86 pub gnss_used: Option<u8>,
88
89 pub glonass_in_view: Option<u8>,
91}
92impl TryFrom<Vec<&str>> for PositionReport {
93 type Error = String;
94
95 fn try_from(fields: Vec<&str>) -> Result<Self, Self::Error> {
96 if fields.len() < 15 {
97 return Err(format!(
98 "Insufficient GNSS data fields got {}",
99 fields.len()
100 ));
101 }
102
103 Ok(Self {
105 run_status: fields[0] == "1",
106 fix_status: fields[1] == "1",
107 utc_time: fields[2].to_string(),
108 latitude: fields[3].parse().ok(),
109 longitude: fields[4].parse().ok(),
110 msl_altitude: fields[5].parse().ok(),
111 ground_speed: fields[6].parse().ok(),
112 ground_course: fields[7].parse().ok(),
113 fix_mode: FixStatus::from(fields[8].parse::<u8>().unwrap_or(0)),
114 hdop: fields[10].parse().ok(),
116 pdop: fields[11].parse().ok(),
117 vdop: fields[12].parse().ok(),
118 gps_in_view: fields[14].parse().ok(),
120 gnss_used: fields[15].parse().ok(),
121 glonass_in_view: fields[16].parse().ok(),
122 })
123 }
124}
125impl std::fmt::Display for PositionReport {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 fn convert_opt<T: std::fmt::Display>(opt: Option<&T>) -> String {
128 match opt {
129 Some(value) => value.to_string(),
130 None => "None".to_string(),
131 }
132 }
133
134 write!(
135 f,
136 "Lat: {}, Lon: {}, Alt: {}, Speed: {}, Course: {}",
137 convert_opt(self.latitude.as_ref()),
138 convert_opt(self.longitude.as_ref()),
139 convert_opt(self.msl_altitude.as_ref()),
140 convert_opt(self.ground_speed.as_ref()),
141 convert_opt(self.ground_course.as_ref())
142 )
143 }
144}