nmea_parser/gnss/
gsv.rs

1/*
2Copyright 2020 Timo Saarinen
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16use super::*;
17
18/// GSV - satellite information
19#[derive(Clone, Debug, PartialEq, Serialize)]
20pub struct GsvData {
21    /// Navigation system
22    pub source: NavigationSystem,
23
24    /// Satellite PRN number
25    pub prn_number: u8,
26
27    /// Elevation in degrees (max 90°)
28    pub elevation: Option<f32>,
29
30    /// Azimuth in degrees from True north (0°-359°)
31    pub azimuth: Option<f32>,
32
33    /// SNR, 0-99 dB, None when not tracking
34    pub snr: Option<f32>,
35}
36
37// -------------------------------------------------------------------------------------------------
38
39/// xxGSV: GPS Satellites in view
40pub(crate) fn handle(
41    sentence: &str,
42    nav_system: NavigationSystem,
43    store: &mut NmeaParser,
44) -> Result<ParsedMessage, ParseError> {
45    let split: Vec<&str> = sentence.split(',').collect();
46
47    let msg_type = split.first().unwrap_or(&"");
48    let msg_count = pick_number_field(&split, 1)?.unwrap_or(0);
49    let msg_num = pick_number_field(&split, 2)?.unwrap_or(0);
50    store.push_string(make_gsv_key(msg_type, msg_count, msg_num), sentence.into());
51
52    let mut found_count = 0;
53    for i in 1..(msg_count + 1) {
54        if store.contains_key(make_gsv_key(msg_type, msg_count, i)) {
55            found_count += 1;
56        }
57    }
58
59    if found_count == msg_count {
60        let mut v = Vec::new();
61        for i in 1..(msg_count + 1) {
62            if let Some(sentence) = store.pull_string(make_gsv_key(msg_type, msg_count, i)) {
63                let split: Vec<&str> = sentence.split(',').collect();
64                for j in 0..4 {
65                    if let Some(prn) = pick_number_field(&split, 4 + 4 * j as usize)
66                        .ok()
67                        .unwrap_or(None)
68                    {
69                        v.push(GsvData {
70                            source: nav_system,
71                            prn_number: prn,
72                            elevation: pick_number_field(&split, 4 + 4 * j as usize + 1)
73                                .ok()
74                                .unwrap_or(None),
75                            azimuth: pick_number_field(&split, 4 + 4 * j as usize + 2)
76                                .ok()
77                                .unwrap_or(None),
78                            snr: pick_number_field(&split, 4 + 4 * j as usize + 3)
79                                .ok()
80                                .unwrap_or(None),
81                        });
82                    }
83                }
84            }
85        }
86
87        Ok(ParsedMessage::Gsv(v))
88    } else {
89        Ok(ParsedMessage::Incomplete)
90    }
91}
92
93/// Make key for store
94fn make_gsv_key(sentence_type: &str, msg_count: u32, msg_num: u32) -> String {
95    format!("{},{},{}", sentence_type, msg_count, msg_num)
96}
97
98// -------------------------------------------------------------------------------------------------
99
100#[cfg(test)]
101mod test {
102    use super::*;
103
104    //    fn init() {
105    //        let _ = env_logger::builder().is_test(true).try_init();
106    //    }
107
108    #[test]
109    fn test_parse_cpgsv() {
110        let mut p = NmeaParser::new();
111
112        match p
113            .parse_sentence("$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74")
114        {
115            Ok(ps) => match ps {
116                ParsedMessage::Incomplete => {}
117                _ => {
118                    assert!(false);
119                }
120            },
121            Err(e) => {
122                assert_eq!(e.to_string(), "OK");
123            }
124        }
125        assert_eq!(p.strings_count(), 1);
126
127        match p
128            .parse_sentence("$GPGSV,3,2,11,14,25,170,00,16,57,208,39,18,67,296,40,19,40,246,00*74")
129        {
130            Ok(ps) => match ps {
131                ParsedMessage::Incomplete => {}
132                _ => {
133                    assert!(false);
134                }
135            },
136            Err(e) => {
137                assert_eq!(e.to_string(), "OK");
138            }
139        }
140        assert_eq!(p.strings_count(), 2);
141
142        match p.parse_sentence("$GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D") {
143            Ok(ps) => {
144                match ps {
145                    // The expected result
146                    ParsedMessage::Gsv(v) => {
147                        assert_eq!(v.len(), 11);
148
149                        // 2nd satellite
150                        let s2 = v.get(1).unwrap();
151                        assert_eq!(s2.elevation, Some(15.0));
152                        assert_eq!(s2.azimuth, Some(270.0));
153                        assert_eq!(s2.snr, Some(0.0));
154
155                        // 5th satellite
156                        let s5 = v.get(4).unwrap();
157                        assert_eq!(s5.elevation, Some(25.0));
158                        assert_eq!(s5.azimuth, Some(170.0));
159                        assert_eq!(s5.snr, Some(0.0));
160
161                        // 11th satellite
162                        let s11 = v.get(10).unwrap();
163                        assert_eq!(s11.elevation, Some(5.0));
164                        assert_eq!(s11.azimuth, Some(244.0));
165                        assert_eq!(s11.snr, Some(0.0));
166                    }
167                    _ => {
168                        assert_eq!(p.strings_count(), 3);
169                        assert!(false);
170                    }
171                }
172            }
173            Err(e) => {
174                assert_eq!(e.to_string(), "OK");
175            }
176        }
177        assert_eq!(p.strings_count(), 0);
178    }
179}