1pub struct FieldReader<'a> {
11 fields: &'a [&'a str],
12 idx: usize,
13}
14
15impl<'a> FieldReader<'a> {
16 pub fn new(fields: &'a [&'a str]) -> Self {
17 Self { fields, idx: 0 }
18 }
19
20 pub fn f32(&mut self) -> Option<f32> {
22 let val = self.fields.get(self.idx).and_then(|f| {
23 if f.is_empty() {
24 None
25 } else {
26 f.parse::<f32>().ok()
27 }
28 });
29 self.idx += 1;
30 val
31 }
32
33 pub fn f64(&mut self) -> Option<f64> {
35 let val = self.fields.get(self.idx).and_then(|f| {
36 if f.is_empty() {
37 None
38 } else {
39 f.parse::<f64>().ok()
40 }
41 });
42 self.idx += 1;
43 val
44 }
45
46 pub fn u8(&mut self) -> Option<u8> {
48 let val = self.fields.get(self.idx).and_then(|f| {
49 if f.is_empty() {
50 None
51 } else {
52 f.parse::<u8>().ok()
53 }
54 });
55 self.idx += 1;
56 val
57 }
58
59 pub fn u32(&mut self) -> Option<u32> {
61 let val = self.fields.get(self.idx).and_then(|f| {
62 if f.is_empty() {
63 None
64 } else {
65 f.parse::<u32>().ok()
66 }
67 });
68 self.idx += 1;
69 val
70 }
71
72 pub fn i8(&mut self) -> Option<i8> {
74 let val = self.fields.get(self.idx).and_then(|f| {
75 if f.is_empty() {
76 None
77 } else {
78 f.parse::<i8>().ok()
79 }
80 });
81 self.idx += 1;
82 val
83 }
84
85 pub fn u16(&mut self) -> Option<u16> {
87 let val = self.fields.get(self.idx).and_then(|f| {
88 if f.is_empty() { None } else { f.parse::<u16>().ok() }
89 });
90 self.idx += 1;
91 val
92 }
93
94 pub fn i16(&mut self) -> Option<i16> {
96 let val = self.fields.get(self.idx).and_then(|f| {
97 if f.is_empty() { None } else { f.parse::<i16>().ok() }
98 });
99 self.idx += 1;
100 val
101 }
102
103 pub fn i32(&mut self) -> Option<i32> {
105 let val = self.fields.get(self.idx).and_then(|f| {
106 if f.is_empty() { None } else { f.parse::<i32>().ok() }
107 });
108 self.idx += 1;
109 val
110 }
111
112 pub fn char(&mut self) -> Option<char> {
114 let val = self
115 .fields
116 .get(self.idx)
117 .and_then(|f| f.chars().next().filter(|_| !f.is_empty()));
118 self.idx += 1;
119 val
120 }
121
122 pub fn string(&mut self) -> Option<String> {
124 let val = self.fields.get(self.idx).and_then(|f| {
125 if f.is_empty() {
126 None
127 } else {
128 Some((*f).to_string())
129 }
130 });
131 self.idx += 1;
132 val
133 }
134
135 pub fn skip(&mut self) {
137 self.idx += 1;
138 }
139}
140
141pub struct FieldWriter {
145 fields: Vec<String>,
146}
147
148impl FieldWriter {
149 pub fn new() -> Self {
150 Self { fields: Vec::new() }
151 }
152
153 pub fn f32(&mut self, value: Option<f32>) {
155 self.fields.push(match value {
156 Some(v) => format!("{v}"),
157 None => String::new(),
158 });
159 }
160
161 pub fn f64(&mut self, value: Option<f64>) {
163 self.fields.push(match value {
164 Some(v) => format!("{v}"),
165 None => String::new(),
166 });
167 }
168
169 pub fn u8(&mut self, value: Option<u8>) {
171 self.fields.push(match value {
172 Some(v) => v.to_string(),
173 None => String::new(),
174 });
175 }
176
177 pub fn i8(&mut self, value: Option<i8>) {
179 self.fields.push(match value {
180 Some(v) => v.to_string(),
181 None => String::new(),
182 });
183 }
184
185 pub fn u32(&mut self, value: Option<u32>) {
187 self.fields.push(match value {
188 Some(v) => v.to_string(),
189 None => String::new(),
190 });
191 }
192
193 pub fn u16(&mut self, value: Option<u16>) {
195 self.fields.push(match value {
196 Some(v) => v.to_string(),
197 None => String::new(),
198 });
199 }
200
201 pub fn i16(&mut self, value: Option<i16>) {
203 self.fields.push(match value {
204 Some(v) => v.to_string(),
205 None => String::new(),
206 });
207 }
208
209 pub fn i32(&mut self, value: Option<i32>) {
211 self.fields.push(match value {
212 Some(v) => v.to_string(),
213 None => String::new(),
214 });
215 }
216
217 pub fn char(&mut self, value: Option<char>) {
219 self.fields.push(match value {
220 Some(c) => c.to_string(),
221 None => String::new(),
222 });
223 }
224
225 pub fn fixed(&mut self, c: char) {
227 self.fields.push(c.to_string());
228 }
229
230 pub fn string(&mut self, value: Option<&str>) {
232 self.fields.push(value.unwrap_or("").to_string());
233 }
234
235 pub fn finish(self) -> Vec<String> {
237 self.fields
238 }
239}
240
241impl Default for FieldWriter {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247pub trait NmeaEncodable {
270 const SENTENCE_TYPE: &'static str;
272
273 const PROPRIETARY_ID: &'static str = "";
276
277 fn encode(&self) -> Vec<String>;
279
280 fn to_sentence(&self, talker: &str) -> String {
282 let fields = self.encode();
283 let field_refs: Vec<&str> = fields.iter().map(|s| s.as_str()).collect();
284 crate::encode_frame('$', talker, Self::SENTENCE_TYPE, &field_refs)
285 }
286
287 fn to_proprietary_sentence(&self) -> String {
292 let fields = self.encode();
293 let field_refs: Vec<&str> = fields.iter().map(|s| s.as_str()).collect();
294 crate::encode_frame('$', "", Self::PROPRIETARY_ID, &field_refs)
295 }
296}
297
298pub fn ddmm_to_decimal(ddmm: f64) -> f64 {
314 let degrees = (ddmm / 100.0).floor();
315 let minutes = ddmm - degrees * 100.0;
316 degrees + minutes / 60.0
317}
318
319pub fn decimal_to_ddmm(decimal: f64) -> f64 {
334 let degrees = decimal.floor();
335 let minutes = (decimal - degrees) * 60.0;
336 degrees * 100.0 + minutes
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn reader_char() {
345 let fields = &["T", "", "AB"];
346 let mut r = FieldReader::new(fields);
347 assert_eq!(r.char(), Some('T'));
348 assert_eq!(r.char(), None);
349 assert_eq!(r.char(), Some('A')); }
351
352 #[test]
353 fn reader_f32() {
354 let fields = &["270.0", "", "abc"];
355 let mut r = FieldReader::new(fields);
356 assert_eq!(r.f32(), Some(270.0));
357 assert_eq!(r.f32(), None);
358 assert_eq!(r.f32(), None); }
360
361 #[test]
362 fn reader_past_end() {
363 let fields: &[&str] = &[];
364 let mut r = FieldReader::new(fields);
365 assert_eq!(r.f32(), None);
366 assert_eq!(r.char(), None);
367 }
368
369 #[test]
370 fn reader_skip() {
371 let fields = &["10.0", "T", "20.0"];
372 let mut r = FieldReader::new(fields);
373 assert_eq!(r.f32(), Some(10.0));
374 r.skip();
375 assert_eq!(r.f32(), Some(20.0));
376 }
377
378 #[test]
379 fn reader_string() {
380 let fields = &["DEST", ""];
381 let mut r = FieldReader::new(fields);
382 assert_eq!(r.string(), Some("DEST".to_string()));
383 assert_eq!(r.string(), None);
384 }
385
386 #[test]
387 fn writer_roundtrip() {
388 let mut w = FieldWriter::new();
389 w.f32(Some(270.0));
390 w.fixed('T');
391 w.f32(None);
392 w.fixed('M');
393 let fields = w.finish();
394 assert_eq!(fields, vec!["270", "T", "", "M"]);
395 }
396
397 #[test]
398 fn ddmm_to_decimal_lat() {
399 let result = ddmm_to_decimal(4807.038);
401 assert!((result - 48.1173).abs() < 0.0001);
402 }
403
404 #[test]
405 fn ddmm_to_decimal_lon() {
406 let result = ddmm_to_decimal(1131.0);
408 assert!((result - 11.5167).abs() < 0.0001);
409 }
410
411 #[test]
412 fn decimal_to_ddmm_lat() {
413 let result = decimal_to_ddmm(48.1173);
415 assert!((result - 4807.038).abs() < 0.001);
416 }
417
418 #[test]
419 fn decimal_to_ddmm_roundtrip() {
420 let original = 5132.5200_f64;
421 let roundtrip = decimal_to_ddmm(ddmm_to_decimal(original));
422 assert!((roundtrip - original).abs() < 0.0001);
423 }
424}