1use crate::stream::{crc24q, ubx_frame};
4use crate::ubx;
5
6pub fn nmea(body: &str) -> Vec<u8> {
8 let ck = body.bytes().fold(0u8, |a, c| a ^ c);
9 format!("${body}*{ck:02X}\r\n").into_bytes()
10}
11
12pub fn rtcm(msg: u16, extra: usize) -> Vec<u8> {
14 let mut payload = vec![(msg >> 4) as u8, ((msg & 0x0F) << 4) as u8];
15 payload.extend((0..extra).map(|i| i as u8));
16 let len = payload.len();
17 let mut f = vec![0xD3, (len >> 8) as u8 & 0x03, len as u8];
18 f.extend_from_slice(&payload);
19 let crc = crc24q(&f);
20 f.extend_from_slice(&[(crc >> 16) as u8, (crc >> 8) as u8, crc as u8]);
21 f
22}
23
24fn put_u16(p: &mut [u8], o: usize, v: u16) {
25 p[o..o + 2].copy_from_slice(&v.to_le_bytes());
26}
27fn put_u32(p: &mut [u8], o: usize, v: u32) {
28 p[o..o + 4].copy_from_slice(&v.to_le_bytes());
29}
30fn put_i32(p: &mut [u8], o: usize, v: i32) {
31 p[o..o + 4].copy_from_slice(&v.to_le_bytes());
32}
33
34#[derive(Debug, Clone, Copy)]
36pub struct Pvt {
37 pub fix_type: u8,
39 pub flags: u8,
41 pub num_sv: u8,
43 pub lat_e7: i32,
45 pub lon_e7: i32,
47 pub height_mm: i32,
49 pub hmsl_mm: i32,
51 pub pdop_centi: u16,
53 pub utc: Option<(u16, u8, u8, u8, u8, u8, i32)>,
55}
56
57impl Default for Pvt {
58 fn default() -> Self {
59 Self {
60 fix_type: 3,
61 flags: 0x01,
62 num_sv: 14,
63 lat_e7: 129_716_000,
64 lon_e7: 775_946_000,
65 height_mm: 920_400,
66 hmsl_mm: 1_006_100,
67 pdop_centi: 120,
68 utc: None,
69 }
70 }
71}
72
73pub fn nav_pvt(v: Pvt) -> Vec<u8> {
75 let mut p = vec![0u8; 92];
76 if let Some((y, mo, d, h, mi, s, nano)) = v.utc {
77 put_u16(&mut p, 4, y);
78 p[6] = mo;
79 p[7] = d;
80 p[8] = h;
81 p[9] = mi;
82 p[10] = s;
83 p[11] = 0x07;
84 put_i32(&mut p, 16, nano);
85 }
86 p[20] = v.fix_type;
87 p[21] = v.flags;
88 p[23] = v.num_sv;
89 put_i32(&mut p, 24, v.lon_e7);
90 put_i32(&mut p, 28, v.lat_e7);
91 put_i32(&mut p, 32, v.height_mm);
92 put_i32(&mut p, 36, v.hmsl_mm);
93 put_u16(&mut p, 76, v.pdop_centi);
94 ubx_frame(ubx::CLS_NAV, ubx::NAV_PVT, &p)
95}
96
97pub fn nav_dop(hdop_centi: u16) -> Vec<u8> {
99 let mut p = vec![0u8; 18];
100 put_u16(&mut p, 12, hdop_centi);
101 ubx_frame(ubx::CLS_NAV, ubx::NAV_DOP, &p)
102}
103
104pub fn nav_svin(
106 dur_s: u32,
107 mean_cm: [i32; 3],
108 acc_01mm: u32,
109 valid: bool,
110 active: bool,
111) -> Vec<u8> {
112 let mut p = vec![0u8; 40];
113 put_u32(&mut p, 8, dur_s);
114 put_i32(&mut p, 12, mean_cm[0]);
115 put_i32(&mut p, 16, mean_cm[1]);
116 put_i32(&mut p, 20, mean_cm[2]);
117 put_u32(&mut p, 28, acc_01mm);
118 p[36] = valid as u8;
119 p[37] = active as u8;
120 ubx_frame(ubx::CLS_NAV, ubx::NAV_SVIN, &p)
121}
122
123pub fn nav_relposned(heading_e5: i32, acc_e5: u32, length_cm: i32, flags: u32) -> Vec<u8> {
125 let mut p = vec![0u8; 64];
126 p[0] = 0x01;
127 put_i32(&mut p, 20, length_cm);
128 put_i32(&mut p, 24, heading_e5);
129 put_u32(&mut p, 52, acc_e5);
130 put_u32(&mut p, 60, flags);
131 ubx_frame(ubx::CLS_NAV, ubx::NAV_RELPOSNED, &p)
132}
133
134pub fn tim_tp(tow_ms: u32, sub_ms: u32, week: u16, flags: u8) -> Vec<u8> {
136 let mut p = vec![0u8; 16];
137 put_u32(&mut p, 0, tow_ms);
138 put_u32(&mut p, 4, sub_ms);
139 put_u16(&mut p, 12, week);
140 p[14] = flags;
141 ubx_frame(ubx::CLS_TIM, ubx::TIM_TP, &p)
142}
143
144pub fn mon_ver(hw: &str, extensions: &[&str]) -> Vec<u8> {
146 let mut p = vec![0u8; 40 + 30 * extensions.len()];
147 p[..9].copy_from_slice(b"EXT CORE ");
148 p[30..30 + hw.len()].copy_from_slice(hw.as_bytes());
149 for (i, e) in extensions.iter().enumerate() {
150 let o = 40 + 30 * i;
151 p[o..o + e.len()].copy_from_slice(e.as_bytes());
152 }
153 ubx_frame(ubx::CLS_MON, ubx::MON_VER, &p)
154}
155
156pub fn ack(ok: bool, class: u8, id: u8) -> Vec<u8> {
158 let msg = if ok { ubx::ACK_ACK } else { ubx::ACK_NAK };
159 ubx_frame(ubx::CLS_ACK, msg, &[class, id])
160}
161
162pub fn unframe(f: &[u8]) -> (u8, u8, Vec<u8>) {
164 let mut s = crate::stream::Splitter::new();
165 match s.push(f).pop() {
166 Some(crate::stream::Frame::Ubx { class, id, payload }) => (class, id, payload),
167 other => panic!("not one UBX frame: {other:?}"),
168 }
169}