stm_32/peripherals/ublox.rs
1// ******************************************************************************
2// * File : platforms/stm_32/src/peripherals/ublox.rs
3// * Date : June 28, 2026
4// ******************************************************************************
5// *
6// * Copyright (c) 2023, AeroVironment, Inc.
7// * All rights reserved.
8// *
9// * Redistribution and use in source and binary forms, with or without
10// * modification, are permitted provided that the following conditions are met:
11// *
12// * 1.Redistributions of source code must retain the above copyright notice, this
13// * list of conditions and the following disclaimer.
14// *
15// * 2.Redistributions in binary form must reproduce the above copyright notice,
16// * this list of conditions and the following disclaimer in the documentation
17// * and/or other materials provided with the distribution.
18// *
19// * 3.Neither the name of the copyright holder nor the names of its
20// * contributors may be used to endorse or promote products derived from
21// * this software without specific prior written permission.
22// *
23// * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24// * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25// * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26// * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
27// * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28// * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29// * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30// * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31// * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33// *
34// ******************************************************************************
35
36use embassy_stm32::mode::Async;
37use embassy_stm32::usart;
38use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
39use embassy_sync::signal::Signal;
40use embassy_time::Duration;
41use embassy_time::Instant;
42use embassy_time::with_timeout;
43
44use super::pps;
45use veloxity_core::errors;
46use veloxity_core::packets;
47
48const BUFFER_LEN: usize = 512;
49
50fn unix_seconds_from_utc(year: u16, month: u8, day: u8, hour: u8, min: u8, sec: u8) -> i64 {
51 let mut y = year as i32;
52 let m = month as i32;
53 y -= (m <= 2) as i32;
54 let era = if y >= 0 { y } else { y - 399 } / 400;
55 let yoe = y - era * 400;
56 let mp = m + if m > 2 { -3 } else { 9 };
57 let doy = (153 * mp + 2) / 5 + day as i32 - 1;
58 let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
59 let days = era * 146097 + doe - 719468;
60 days as i64 * 86_400 + hour as i64 * 3_600 + min as i64 * 60 + sec as i64
61}
62
63pub static GNSS_SIGNAL: Signal<
64 CriticalSectionRawMutex,
65 Result<packets::GNSSPacket, errors::SensorError>,
66> = Signal::<CriticalSectionRawMutex, Result<packets::GNSSPacket, errors::SensorError>>::new();
67
68fn publish_gnss(result: Result<packets::GNSSPacket, errors::SensorError>) {
69 #[cfg(feature = "runtime-diagnostics")]
70 crate::runtime_diagnostics::record_signal_publish(
71 crate::runtime_diagnostics::SensorKind::Gnss,
72 GNSS_SIGNAL.signaled(),
73 result.is_err(),
74 );
75 GNSS_SIGNAL.signal(result);
76}
77
78#[repr(C, packed)]
79#[derive(Copy, Clone)]
80pub struct PvtPayload {
81 pub i_tow: u32, // ms, GPS time of week
82 pub year: u16,
83 pub month: u8,
84 pub day: u8,
85 pub hour: u8,
86 pub min: u8,
87 pub sec: u8, // UTC
88
89 pub valid: u8, // validity flags
90 pub t_acc: u32, // ns, time accuracy estimate
91 pub nano: i32, // ns, Fraction of second -1e9 to 1e9 (UTC)
92 pub fix_type: u8, // 0 none, 1 dead reckoning, 2 2D, 3 3D, 4 GNS+dead reckoning combined, 5 time only fix
93
94 pub flags: u8,
95 pub flags2: u8,
96 pub num_sv: u8, // satellites used in solution
97 pub lon: i32,
98 pub lat: i32, // degx10^-7
99 pub height: i32,
100 pub h_msl: i32, // mm
101 pub h_acc: u32,
102 pub v_acc: u32, // mm
103 pub vel_n: i32,
104 pub vel_e: i32,
105 pub vel_d: i32,
106 pub g_speed: i32, // mm/s velocity
107 pub head_mot: i32, // degx10^-5
108 pub s_acc: u32, // mm/s speed accuracy estimate
109 pub head_acc: u32, // degx10^-5
110 pub p_dop: u16, // 0.01 (percent)
111
112 flags3: u16,
113 pub reserved1: u8,
114 pub head_veh: i32, // degx10^-5, vehicle heading
115 pub mag_dec: i16, // degx 10^-2
116 pub mag_acc: u32, // degx 10^-2
117}
118
119#[repr(C, packed)]
120#[derive(Copy, Clone)]
121pub union PvtUnion {
122 pub packet: PvtPayload,
123 pub payload: [u8; 92],
124}
125
126#[repr(u32)]
127#[derive(Clone, Copy)]
128pub enum Bitrate {
129 Baud9600 = 9600u32,
130 Baud38400 = 38400u32,
131 Baud57600 = 57600u32,
132 Baud115200 = 115200u32,
133 Baud230400 = 230400u32,
134}
135
136pub enum Protocol {
137 M8,
138 M9,
139}
140static UBX_MAX_PAYLOAD_BYTES: usize = 256;
141
142#[repr(C, packed)]
143#[derive(Copy, Clone)]
144pub struct UbxFrame {
145 cl: u8,
146 id: u8,
147 length: usize,
148 a: u8, // checksum
149 b: u8, // checksum
150 payload: [u8; UBX_MAX_PAYLOAD_BYTES],
151}
152
153pub struct UbloxSensor {
154 pub uart: usart::Uart<'static, Async>,
155 pub protocol: Protocol,
156 pub baudrate: Bitrate,
157 pub nav_period_ms: u16,
158}
159
160static PPS_PERIOD_US: u32 = 1000000u32;
161
162// checksum is over class, id, length, payload only
163fn checksum(buffer: &[u8]) -> (u8, u8) {
164 let mut ck_a: u8 = 0;
165 let mut ck_b: u8 = 0;
166
167 for &byte in buffer {
168 ck_a = ck_a.wrapping_add(byte);
169 ck_b = ck_b.wrapping_add(ck_a);
170 }
171
172 (ck_a, ck_b)
173}
174
175fn make_packet(class: u8, id: u8, payload: &[u8], buffer: &mut [u8]) -> bool {
176 let length = payload.len();
177
178 // Check for payload too big
179 if length + 8 > buffer.len() {
180 return false;
181 }
182
183 // header
184 buffer[0] = 0xB5;
185 buffer[1] = 0x62;
186 buffer[2] = class;
187 buffer[3] = id;
188 buffer[4..6].copy_from_slice(&(length as u16).to_le_bytes());
189
190 // payload
191 buffer[6..length + 6].copy_from_slice(payload);
192
193 // checksum
194 let (ck_a, ck_b) = checksum(&buffer[2..length + 6]);
195 buffer[length + 6] = ck_a;
196 buffer[length + 7] = ck_b;
197
198 true
199}
200
201impl UbloxSensor {
202 async fn tx(&mut self, class: u8, id: u8, payload: &[u8]) -> bool {
203 let mut buffer = [0u8; BUFFER_LEN]; // largest ubx packet length we will support
204 // Make packet
205 if make_packet(class, id, payload, &mut buffer) {
206 // Make the expected ack packet
207 let mut ack: [u8; 10] = [0u8; 10];
208 let _result = make_packet(0x05, 0x01, &[class, id], &mut ack); // ack packet is class=0x05 id=0x01
209
210 // send packet
211 let result = self.uart.write(&buffer[0..payload.len() + 8]).await;
212 if let Ok(_size) = result {
213 // check it it was successful
214 let result = self.look_for_ack(&ack).await;
215 return result;
216 }
217 }
218 false
219 }
220
221 async fn cfg_prt(&mut self, _baud: u32) -> bool {
222 match self.protocol {
223 Protocol::M8 => {
224 let mut payload = [0u8; 20]; // #define CFG_PRT_LENGTH 20
225 payload[0] = 0x01; // Port 1 is the UART
226 //payload[1] = 0x00; // Reserved
227 //payload[2] = 0x00; // txReady
228 //payload[3] = 0x00; // txReady
229 payload[4] = 0xC0; // mode 1100 0000 (8-bit character length)
230 payload[5] = 0x08; // mode 0000 1000 (No parity, 1 stop bit)
231 //payload[6] = 0x00; // mode
232 //payload[7] = 0x00; // mode
233 payload[8..12].copy_from_slice(&(self.baudrate as u32).to_le_bytes()); // meas rate
234 payload[12] = 0x01; // inProtoMask (ubx)
235 //payload[13] = 0x00; // inProtoMask
236 payload[14] = 0x01; // outProtoMask (ubx)
237 //payload[15] = 0x00; // outProtoMask
238 //payload[16] = 0x00; // flags
239 //payload[17] = 0x00; // flags
240 //payload[18] = 0x00; // reserved2
241 //payload[19] = 0x00; // reserved2
242
243 self.tx(0x06, 0x00, &payload).await
244 }
245 Protocol::M9 => {
246 let mut payload = [0u8; 34];
247 //payload[0] = 0x00; // Message Version (1 bytes)
248 payload[1] = 0x01; // Write to RAM bit 1 is ram, 2 is bbr layer, 3 is flash (1 bytes)
249 //payload[2] = 0x00; // transaction/action (1 bytes)
250 //payload[3] = 0x00; // reserved0 (1 bytes)
251
252 // Key-Value pairs
253 // baud rate (8 bytes)
254 payload[4..8].copy_from_slice(&0x40520001u32.to_le_bytes());
255 payload[8..12].copy_from_slice(&(self.baudrate as u32).to_le_bytes());
256
257 // output rate in milliseconds (6 bytes)
258 payload[12..16].copy_from_slice(&0x30210001u32.to_le_bytes());
259 payload[16..18].copy_from_slice(&self.nav_period_ms.to_le_bytes());
260
261 // 1 data output per nav measurement (6 bytes)
262 payload[18..22].copy_from_slice(&0x30210002u32.to_le_bytes());
263 payload[22..24].copy_from_slice(&1u16.to_le_bytes());
264
265 // CFG-NAVSPG-DYNMODEL 8 = 4G Airborne (5 bytes)
266 payload[24..28].copy_from_slice(&0x20110021u32.to_le_bytes());
267 payload[28] = 8;
268
269 // CFG-NAVSPG-FIXMODE 3 = Auto 2/3D (5 bytes)
270 payload[29..33].copy_from_slice(&0x20110011u32.to_le_bytes());
271 payload[33] = 3u8;
272
273 self.tx(0x06, 0x8A, &payload).await
274 }
275 }
276 }
277
278 async fn cfg_rate(&mut self) -> bool {
279 let mut payload = [0u8; 6]; // #define CFG_RATE_LENGTH 6
280 payload[0..2].copy_from_slice(&self.nav_period_ms.to_le_bytes()); // meas rate
281 payload[2..4].copy_from_slice(&0x0001u16.to_le_bytes()); // nav rate = meas rate
282 payload[4..6].copy_from_slice(&0x0000u16.to_le_bytes()); // UTC time reference
283
284 self.tx(0x06, 0x08, &payload).await
285 }
286
287 async fn cfg_tp5(&mut self) -> bool {
288 let mut payload = [0u8; 32]; // #define SFG_TP5_LENGTH 32
289 payload[0] = 0; // Timepulse pin 0
290 payload[1] = 1; // Version 1
291 //payload[2] = 0; // reserved
292 //payload[3] = 0; // reserved
293 //payload[4..6].copy_from_slice(& 0u16.to_le_bytes()); // antenna delay
294 //payload[6..8].copy_from_slice(& 0u16.to_le_bytes()); // rf group delay
295 let pps_period_us = PPS_PERIOD_US; //(pps_period_ms as u32)*1000u32;
296 payload[8..12].copy_from_slice(&pps_period_us.to_le_bytes()); // pulse period
297 payload[12..16].copy_from_slice(&pps_period_us.to_le_bytes()); // pulse period if locked other set
298 let pulse_len_us = 1000u32;
299 payload[16..20].copy_from_slice(&pulse_len_us.to_le_bytes()); // pulse high time
300 payload[20..24].copy_from_slice(&pulse_len_us.to_le_bytes()); // pulse high time
301 // payload[24..28].copy_from_slice(& 0u32.to_le_bytes()); // pulse high time
302 payload[28..32].copy_from_slice(&0x01F7u32.to_le_bytes()); // pulse high time
303
304 self.tx(0x06, 0x31, &payload).await
305 }
306
307 async fn cfg_nav5(&mut self) -> bool {
308 let mut payload = [0u8; 36]; // #define CFG_NAV5_LENGTH 36
309 payload[0] = 5; // Parameters bitmask
310 payload[1] = 8; // Airbourne navigatin < 4G's
311 payload[2] = 3; // Auto 2d/3d fix mode
312 self.tx(0x06, 0x24, &payload).await
313 }
314
315 async fn cfg_msg(&mut self, class: u8, id: u8, decimation_rate: u8) -> bool {
316 let payload = [class, id, decimation_rate];
317 self.tx(0x06, 0x01, &payload).await
318 }
319
320 async fn look_for_ack(&mut self, ack: &[u8]) -> bool {
321 let mut buffer = [0u8; 256];
322
323 // Expected ack packet [0xB5u8, 0x62u8, 0x05u8, 0x01u8, 0x02u8, 0x00u8, 0x06u8, 0x00u8, 0x0Eu8, 0x37u8,]
324 // Read data block with 2 second timeout
325 match with_timeout(
326 Duration::from_secs(2),
327 self.uart.read_until_idle(&mut buffer),
328 )
329 .await
330 {
331 Ok(Ok(_size)) => {
332 for subarray in buffer.windows(ack.len()) {
333 if ack == subarray {
334 return true;
335 }
336 }
337 }
338 Ok(Err(_read_err)) => {
339 // underlying UART/read error from self.uart.read(...)
340 }
341 Err(_timeout_err) => {
342 // with_timeout timed out
343 }
344 }
345 false
346 }
347
348 async fn sync_baudrate(&mut self) -> bool {
349 // Determine baud rate
350 let bauds = [
351 Bitrate::Baud9600 as u32,
352 Bitrate::Baud38400 as u32,
353 Bitrate::Baud57600 as u32,
354 Bitrate::Baud115200 as u32,
355 Bitrate::Baud230400 as u32,
356 ]; //, 460800u32, 921600u32];
357
358 for _retries in 0..30 {
359 for baud in bauds {
360 // try baud rate
361 // set stm32 baud rate
362 let _result: Result<(), usart::ConfigError> = self.uart.set_baudrate(baud);
363
364 // set ublox the desired baud rate
365 let result = self.cfg_prt(self.baudrate as u32).await;
366
367 if result {
368 let _result: Result<(), usart::ConfigError> =
369 self.uart.set_baudrate(self.baudrate as u32);
370 return true;
371 }
372 }
373 }
374 false
375 }
376
377 pub async fn run(&mut self) {
378 let _synced = self.sync_baudrate().await;
379
380 // Disable these messages
381 self.cfg_msg(0x0A, 0x09, 0).await; // MON-HW
382 self.cfg_msg(0x0A, 0x0B, 0).await; // MON-HW2
383 self.cfg_msg(0x01, 0x04, 0).await; // NAV-DOP
384 self.cfg_msg(0x01, 0x03, 0).await; // NAV-STATUS
385 self.cfg_msg(0x01, 0x35, 0).await; // NAV-SAT
386 self.cfg_msg(0x01, 0x20, 0).await; // NAV-TIMEGPS
387 self.cfg_msg(0x01, 0x01, 0).await; // NAV-POSECEF (length 20)
388 self.cfg_msg(0x01, 0x11, 0).await; // NAV-VELECEF (length 20)
389
390 // These are needed if you want ECEF, but disable for now
391 self.cfg_msg(0x01, 0x20, 0).await; // NAV-TIMEGPS (length 16)
392 self.cfg_msg(0x01, 0x01, 0).await; // NAV-POSECEF (length 20)
393 self.cfg_msg(0x01, 0x11, 0).await; // NAV-VELECEF (length 20)
394
395 // Enable this messages
396 self.cfg_msg(0x01, 0x07, 1).await; // NAV-PVT (length 92)
397
398 // Set GPS Configuration (already done in cfg_prt() for UBX_M9)
399 if let Protocol::M8 = self.protocol {
400 self.cfg_rate().await;
401 self.cfg_tp5().await;
402 self.cfg_nav5().await;
403 }
404 pub struct UbxFrame {
405 cl: u8,
406 id: u8,
407 length: usize,
408 a: u8, // checksum
409 b: u8, // checksum
410 payload: [u8; UBX_MAX_PAYLOAD_BYTES],
411 }
412
413 let mut p: UbxFrame = UbxFrame {
414 cl: 0u8,
415 id: 0u8,
416 length: 0usize,
417 a: 0u8,
418 b: 0u8,
419 payload: [0u8; UBX_MAX_PAYLOAD_BYTES],
420 };
421 let mut n = 0usize;
422 let mut pps_timestamp = 0;
423 loop {
424 // get most recent pps timestamp
425 match pps::PPS_SIGNAL.try_take() {
426 Some(packet) => {
427 pps_timestamp = packet.header.timestamp;
428 }
429 None => {}
430 }
431
432 let mut buffer = [0u8; BUFFER_LEN];
433
434 let result = self.uart.read_until_idle(&mut buffer).await;
435 if let Ok(size) = result {
436 // This could be a function, but might as well just chug through this here
437 for &c in buffer[0..size].iter() {
438 // special case where we get 0xB5 randomly duplicated at the start (DMA wierdness).
439
440 if (c == 0xB5) && (n == 1) {
441 n = 0;
442 }
443
444 if n == 0
445 // header byte 1 "mu" character
446 {
447 if c == 0xB5 {
448 n += 1;
449 } else {
450 n = 0;
451 }
452 } else if n == 1
453 // header byte 2
454 {
455 if c == 0x62 {
456 n += 1;
457 } else if c == 0xB5 {
458 n = 1;
459 }
460 // repeated 'mu'
461 else {
462 n = 0;
463 }
464 } else if n == 2
465 // Class
466 {
467 p.a = 0;
468 p.b = 0; // Reset the checksum calculation
469 p.cl = c;
470 n += 1;
471 p.a = p.a.wrapping_add(c);
472 p.b = p.b.wrapping_add(p.a);
473 } else if n == 3
474 // ID, allow all
475 {
476 p.id = c;
477 n += 1;
478 p.a = p.a.wrapping_add(c);
479 p.b = p.b.wrapping_add(p.a);
480 } else if n == 4
481 // length LSB
482 {
483 p.length = c as usize;
484 n += 1;
485 p.a = p.a.wrapping_add(c);
486 p.b = p.b.wrapping_add(p.a);
487 } else if n == 5
488 // length MSB
489 {
490 p.length |= (c as usize) << 8;
491 if p.length > UBX_MAX_PAYLOAD_BYTES {
492 n = 0;
493 } else {
494 n += 1;
495 p.a = p.a.wrapping_add(c);
496 p.b = p.b.wrapping_add(p.a);
497 }
498 } else if n < p.length + 6
499 // Packet Payload bytes and first byte of checksum.
500 {
501 p.payload[n - 6] = c;
502 n += 1;
503 p.a = p.a.wrapping_add(c);
504 p.b = p.b.wrapping_add(p.a);
505 } else if n == p.length + 6
506 // Checksum A
507 {
508 if p.a != c {
509 n = 0;
510 } else {
511 n += 1;
512 }
513 } else {
514 n = 0;
515 if p.b == c {
516 // we found a valid packet
517 if (p.cl == 0x01) || (p.id == 0x07)
518 // pvt packet
519 {
520 let end_of_packet_timestamp = Instant::now();
521
522 // map the payload into the pvt union
523 let mut payload = [0u8; 92];
524 payload.copy_from_slice(&p.payload[0..92]);
525 let pvt = PvtUnion { payload };
526
527 // build up the device specific status register.
528 // (from Bitfield valid) [0] = validDate, [1] = validTime, [2] = fullyResolved, [3] validMag, none ignored
529 // (from Bitfield flags) [4] = gnssFixOK , [5] = diffSoln, [6] = psmState, [7] = headVehValid, [8] = carrSoln, none ignored
530 // (from Bitfield flags2) [9] = confirmedAvai, [10] = confirmedDate, [11] = confirmedTime, none ignored
531 // (from Bitfield flags3) [12] = invalidLlh, [13] = lastCorrectionAge
532
533 let mut status = 0u16;
534 let valid = unsafe { pvt.packet }.valid;
535 let flags = unsafe { pvt.packet }.flags;
536 let flags2 = unsafe { pvt.packet }.flags2;
537 let flags3 = unsafe { pvt.packet }.flags3;
538 let utc_nanos = unsafe { pvt.packet }.nano;
539
540 if (valid & 0x01) != 0 {
541 status |= 0x0001
542 }; // validDate
543 if (valid & 0x02) != 0 {
544 status |= 0x0002
545 }; // validTime
546 if (valid & 0x04) != 0 {
547 status |= 0x0004
548 }; // fullyResolved
549 if (valid & 0x08) != 0 {
550 status |= 0x0008
551 }; // validMag
552
553 if (flags & 0x01) != 0 {
554 status |= 0x0010
555 }; // gnssFixOK
556 if (flags & 0x02) != 0 {
557 status |= 0x0020
558 }; // diffSoln
559 if (flags & 0x10) != 0 {
560 status |= 0x0020
561 }; // pmsState
562 if (flags & 0x20) != 0 {
563 status |= 0x0040
564 }; // headVehValid
565 if (flags & 0x80) != 0 {
566 status |= 0x0080
567 }; // carrSoln
568
569 if (flags2 & 0x20) != 0 {
570 status |= 0x0100
571 }; // confirmedAvai
572 if (flags2 & 0x40) != 0 {
573 status |= 0x0200
574 }; // confirmedDate
575 if (flags2 & 0x80) != 0 {
576 status |= 0x0400
577 }; // confirmedTime
578
579 if (flags3 & 0x0001) != 0 {
580 status |= 0x0800
581 }; // invalidLlh
582 if (flags3 & 0x0010) != 0 {
583 status |= 0x1000
584 }; // lastCorrectionAge
585
586 // PPS may arrive while the UART read is pending;
587 // consume it again here so the C-compatible timing
588 // calculation uses the latest edge.
589 if let Some(packet) = pps::PPS_SIGNAL.try_take() {
590 pps_timestamp = packet.header.timestamp;
591 }
592 let completion_timestamp_us = end_of_packet_timestamp.as_micros();
593 let time_and_fix_valid =
594 (valid & 0x07) == 0x07 && (flags & 0x01) == 0x01;
595 let timestamp_us = packets::rosflight_c_gnss_timestamp(
596 pps_timestamp as u64,
597 completion_timestamp_us,
598 utc_nanos,
599 time_and_fix_valid,
600 );
601
602 let header = packets::RosflightPacketHeader {
603 timestamp: timestamp_us,
604 status,
605 };
606
607 let _fix_type =
608 packets::GNSSFixType::from_u8(unsafe { pvt.packet }.fix_type);
609
610 let _pi = 3.141592654;
611 let mut unix_seconds = unix_seconds_from_utc(
612 unsafe { pvt.packet }.year,
613 unsafe { pvt.packet }.month,
614 unsafe { pvt.packet }.day,
615 unsafe { pvt.packet }.hour,
616 unsafe { pvt.packet }.min,
617 unsafe { pvt.packet }.sec,
618 );
619 let mut unix_nanos = utc_nanos;
620 if unix_nanos < 0 {
621 unix_seconds -= 1;
622 unix_nanos += 1_000_000_000;
623 }
624
625 let pvt_packet = packets::GNSSPacket {
626 header: header,
627 unix_seconds,
628 unix_nanos,
629 // UBX NAV-PVT and ROSFLIGHT_GNSS both use decimal degrees.
630 lat: (unsafe { pvt.packet }.lat as f64) * 1.0e-7,
631 lon: (unsafe { pvt.packet }.lon as f64) * 1.0e-7,
632 // Match ROSflight C: publish NAV-PVT hMSL, not ellipsoid height.
633 height_msl: (unsafe { pvt.packet }.h_msl as f32) / 1000.0,
634 vel_n: (unsafe { pvt.packet }.vel_n as f32) / 1000.0,
635 vel_e: (unsafe { pvt.packet }.vel_e as f32) / 1000.0,
636 vel_d: (unsafe { pvt.packet }.vel_d as f32) / 1000.0,
637 h_acc: (unsafe { pvt.packet }.h_acc as f32) / 1000.0,
638 v_acc: (unsafe { pvt.packet }.v_acc as f32) / 1000.0,
639 s_acc: (unsafe { pvt.packet }.s_acc as f32) / 1000.0,
640 month: unsafe { pvt.packet }.month,
641 day: unsafe { pvt.packet }.day,
642 year: unsafe { pvt.packet }.year,
643 hour: unsafe { pvt.packet }.hour,
644 min: unsafe { pvt.packet }.min,
645 sec: unsafe { pvt.packet }.sec,
646 nano: unsafe { pvt.packet }.nano,
647 fix_type: packets::GNSSFixType::from_u8(
648 unsafe { pvt.packet }.fix_type,
649 ),
650 num_sats: unsafe { pvt.packet }.num_sv,
651 mag_dec: (unsafe { pvt.packet }.mag_dec as f32)
652 * 1.7453292519943296e-4,
653 time_correction: 0,
654 };
655 publish_gnss(Ok(pvt_packet));
656 }
657 }
658 }
659 }
660 }
661 }
662 }
663}
664
665#[embassy_executor::task]
666pub async fn task(mut ublox: UbloxSensor) {
667 ublox.run().await;
668}