1use crate::receiver::Fix;
4
5#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct Gga {
8 pub fix: Fix,
10 pub sats: u8,
12 pub hdop: Option<f64>,
14 pub lat_deg: Option<f64>,
16 pub lon_deg: Option<f64>,
18 pub h_msl_m: Option<f64>,
20 pub h_ellipsoid_m: Option<f64>,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq)]
26pub struct Rmc {
27 pub fix: Fix,
29 pub lat_deg: Option<f64>,
31 pub lon_deg: Option<f64>,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq)]
37pub enum Sentence {
38 Gga(Gga),
40 Rmc(Rmc),
42}
43
44pub fn parse(line: &str) -> Option<Sentence> {
46 let body = line.strip_prefix('$')?;
47 let body = body.rsplit_once('*').map_or(body, |(b, _)| b);
48 let f: Vec<&str> = body.split(',').collect();
49 let kind = f.first()?.get(2..)?;
50 match kind {
51 "GGA" if f.len() >= 12 => Some(Sentence::Gga(gga(&f))),
52 "RMC" if f.len() >= 7 => Some(Sentence::Rmc(rmc(&f))),
53 _ => None,
54 }
55}
56
57fn num(s: &str) -> Option<f64> {
58 s.parse().ok().filter(|v: &f64| v.is_finite())
59}
60
61fn coord(v: &str, hemi: &str) -> Option<f64> {
63 let raw = num(v)?;
64 let deg = (raw / 100.0).trunc();
65 let val = deg + (raw - deg * 100.0) / 60.0;
66 match hemi {
67 "N" | "E" => Some(val),
68 "S" | "W" => Some(-val),
69 _ => None,
70 }
71}
72
73fn gga(f: &[&str]) -> Gga {
74 let fix = match f[6] {
75 "1" => Fix::ThreeD,
76 "2" => Fix::Dgps,
77 "4" => Fix::RtkFixed,
78 "5" => Fix::RtkFloat,
79 _ => Fix::None,
80 };
81 let h_msl = num(f[9]);
82 let sep = num(f[11]);
83 let has_fix = fix != Fix::None;
84 Gga {
85 fix,
86 sats: f[7].parse().unwrap_or(0),
87 hdop: num(f[8]),
88 lat_deg: coord(f[2], f[3]).filter(|_| has_fix),
89 lon_deg: coord(f[4], f[5]).filter(|_| has_fix),
90 h_msl_m: h_msl.filter(|_| has_fix),
91 h_ellipsoid_m: h_msl.zip(sep).map(|(h, s)| h + s).filter(|_| has_fix),
92 }
93}
94
95fn rmc(f: &[&str]) -> Rmc {
96 let mode = f.get(12).copied().unwrap_or("A");
98 let fix = match (f[2], mode) {
99 ("A", "D") => Fix::Dgps,
100 ("A", "R") => Fix::RtkFixed,
101 ("A", "F") => Fix::RtkFloat,
102 ("A", "N") => Fix::None,
103 ("A", _) => Fix::TwoD,
104 _ => Fix::None,
105 };
106 let has_fix = fix != Fix::None;
107 Rmc {
108 fix,
109 lat_deg: coord(f[3], f[4]).filter(|_| has_fix),
110 lon_deg: coord(f[5], f[6]).filter(|_| has_fix),
111 }
112}