1use bbqueue::{Consumer, GrantR};
3use embedded_sdmmc::Timestamp;
4use fugit::{ExtU64, MillisDuration};
5use ublox::{
6 AlignmentToReferenceTime, CfgMsgSinglePortBuilder, CfgNav5DynModel, CfgNav5FixMode,
7 CfgNav5Params, CfgNav5UtcStandard, FixedLinearBuffer, NavPosVelTime, ParserError,
8};
9
10pub const PARSER_BUFFER_LEN: usize = 1024;
12
13#[cfg(not(debug_assertions))]
15pub const ACTIVE_MESSAGE_RATE: u16 = 56;
16
17#[cfg(debug_assertions)]
19pub const ACTIVE_MESSAGE_RATE: u16 = 100;
20
21pub const STARTUP_MESSAGE_RATE: u16 = 1000;
23
24#[derive(Debug)]
25pub enum Error {
26 InvalidFix,
27 ParserError(ParserError),
28}
29
30impl From<ParserError> for Error {
31 fn from(item: ParserError) -> Error {
32 Error::ParserError(item)
33 }
34}
35
36#[derive(Clone)]
37pub struct HeaderData {
38 pub utc: embedded_sdmmc::Timestamp,
40 pub timestamp: MillisDuration<u64>,
42}
43
44impl core::default::Default for HeaderData {
45 fn default() -> Self {
46 HeaderData {
47 utc: Timestamp {
48 year_since_1970: 0,
49 zero_indexed_month: 0,
50 zero_indexed_day: 0,
51 hours: 0,
52 minutes: 0,
53 seconds: 0,
54 },
55 timestamp: 0.millis(),
56 }
57 }
58}
59
60pub trait UbloxDriver {
61 fn enable_gnss(&mut self);
63
64 fn disable_gnss(&mut self);
66
67 fn send_blocking(&mut self, packet: &[u8]);
69}
70
71pub struct Device<D: UbloxDriver, const LEN: usize> {
73 driver: D,
75 parser: ublox::Parser<FixedLinearBuffer<'static>>,
77 rx_cons: Consumer<'static, LEN>,
79 signal_acquired: bool,
81 header_data: Option<HeaderData>,
83}
84
85impl<D: UbloxDriver, const LEN: usize> Device<D, LEN> {
86 #[inline]
88 pub fn new(
89 driver: D,
90 rx_cons: Consumer<'static, LEN>,
91 buffer: &'static mut [u8; PARSER_BUFFER_LEN],
92 ) -> Self {
93 let buffer = FixedLinearBuffer::new(buffer);
94
95 let mut device = Device {
96 driver,
97 rx_cons,
98 parser: ublox::Parser::new(buffer),
99 signal_acquired: false,
100 header_data: None,
101 };
102
103 device.gnss_power(true);
104
105 device.driver.send_blocking(
107 &ublox::CfgNav5Builder {
108 mask: CfgNav5Params::DYN | CfgNav5Params::POS_FIX_MODE,
109 dyn_model: CfgNav5DynModel::AirborneWith4gAcceleration,
110 fix_mode: CfgNav5FixMode::Auto2D3D,
111 fixed_alt: 0.0,
112 fixed_alt_var: 1.0,
113 min_elev_degrees: 5,
114 dr_limit: 0,
115 pdop: 25.0,
116 tdop: 25.0,
117 pacc: 100,
118 tacc: 100,
119 static_hold_thresh: 0.0,
120 dgps_time_out: 60,
121 cno_thresh_num_svs: 0,
122 cno_thresh: 0,
123 reserved1: [0; 2],
124 static_hold_max_dist: 0,
125 utc_standard: CfgNav5UtcStandard::Automatic,
126 reserved2: [0; 5],
127 }
128 .into_packet_bytes(),
129 );
130
131 device.driver.send_blocking(
134 &CfgMsgSinglePortBuilder::set_rate_for::<NavPosVelTime>(1).into_packet_bytes(),
135 );
136
137 device
145 }
146
147 #[inline]
149 pub fn full_power(&mut self) {
150 let power_setup = cfg_pms::PowerSetupValue::FullPower;
151 self.driver.send_blocking(
152 &cfg_pms::CfgPms::new()
153 .with_power_setup_value(power_setup)
154 .into_bytes(),
155 );
156
157 self.driver.send_blocking(
161 &ublox::CfgRateBuilder {
162 measure_rate_ms: ACTIVE_MESSAGE_RATE,
163 nav_rate: 1,
164 time_ref: AlignmentToReferenceTime::Utc,
165 }
166 .into_packet_bytes(),
167 );
168 }
169
170 #[inline]
172 pub fn low_power_1hz(&mut self) {
173 let power_setup = cfg_pms::PowerSetupValue::Aggressive1Hz;
174 self.driver.send_blocking(
175 &cfg_pms::CfgPms::new()
176 .with_power_setup_value(power_setup)
177 .into_bytes(),
178 );
179 }
180
181 #[inline]
183 pub fn gnss_power(&mut self, enabled: bool) {
184 self.signal_acquired = false;
185 if enabled {
186 self.driver.enable_gnss();
187 } else {
188 self.driver.disable_gnss();
189 }
190 }
191
192 #[inline]
215 pub fn parse(
216 &mut self,
217 ) -> Result<
218 (
219 &mut ublox::Parser<FixedLinearBuffer<'static>>,
220 GrantR<'static, LEN>,
221 ),
222 bbqueue::Error,
223 > {
224 let mut grant = self.rx_cons.read()?;
225 grant.to_release(usize::MAX);
226 Ok((&mut self.parser, grant))
227 }
228
229 #[inline]
231 pub fn signal_acquired(&self) -> bool {
232 self.signal_acquired
233 }
234
235 #[inline]
237 pub fn acquire_signal(&mut self) {
238 self.signal_acquired = true;
239 }
240
241 #[inline]
243 pub fn set_header_data(&mut self, data: &HeaderData) {
244 self.header_data.replace(data.clone());
245 }
246
247 #[inline]
249 pub fn header_data(&self) -> Option<HeaderData> {
250 self.header_data.clone()
251 }
252
253 #[inline]
255 pub fn invalidate_header_data(&mut self) {
256 self.header_data = None;
257 }
258}
259
260#[derive(Debug)]
261pub struct GnssData {
262 pub lat: i32,
263 pub lon: i32,
264 pub ground_speed: u32,
265 pub vel_down: i32,
266 pub heading: Option<i32>,
267}
268
269pub mod cfg_pms {
270 use modular_bitfield::prelude::*;
271
272 #[bitfield]
273 pub struct CfgPms {
274 #[skip(setters)]
275 pub version: u8,
276 pub power_setup_value: PowerSetupValue,
277 pub period: u16,
278 pub on_time: u16,
279 #[skip]
280 _reserved: B16,
281 }
282
283 #[derive(BitfieldSpecifier)]
284 #[bits = 8]
285 pub enum PowerSetupValue {
286 FullPower = 0x00,
287 Balanced = 0x01,
288 Interval = 0x02,
289 Aggressive1Hz = 0x03,
290 Aggressive2Hz = 0x04,
291 Aggressive4Hz = 0x05,
292 Invalid = 0xFF,
293 }
294}
295
296pub mod cfg_prt_uart {
297 #![allow(clippy::identity_op)]
298 #![allow(unused_braces)]
299
300 use modular_bitfield::prelude::*;
301 #[bitfield]
302 #[repr(u16)]
303 pub struct TxReady {
304 pub en: bool,
305 pub pol: Polarity,
306 pub pin: B5,
307 pub thres: B9,
308 }
309
310 #[bitfield]
311 #[repr(u32)]
312 pub struct Mode {
313 #[skip]
314 _reserved: B6,
315 pub char_len: CharLen,
316 #[skip]
317 _reserved: B1,
318 pub parity: Parity,
319 pub n_stop_bits: NumStopBits,
320 #[skip]
321 _reserved: B18,
322 }
323
324 #[bitfield]
325 #[repr(u16)]
326 pub struct ProtoMask {
327 pub ubx: bool,
328 pub nmea: bool,
329 pub rtcm: bool,
330 #[skip]
331 _reserved: B2,
332 pub rtcm3: bool,
333 #[skip]
334 _reserved: B10,
335 }
336
337 #[bitfield]
338 #[repr(u16)]
339 pub struct Flags {
340 #[skip]
341 _reserved: B1,
342 pub extended_tx_timeout: bool,
343 #[skip]
344 _reserved: B14,
345 }
346
347 #[derive(BitfieldSpecifier)]
348 #[bits = 2]
349 pub enum NumStopBits {
350 Bits1 = 0b00,
351 Bits1p5 = 0b01,
352 Bits2 = 0b10,
353 Bits0p5 = 0b11,
354 }
355
356 #[derive(BitfieldSpecifier)]
357 #[bits = 2]
358 pub enum CharLen {
359 Bits7 = 0b10,
360 Bits8 = 0b11,
361 }
362
363 #[derive(BitfieldSpecifier)]
364 #[bits = 3]
365 pub enum Parity {
366 Even = 0b000,
367 Odd = 0b001,
368 None = 0b100,
369 }
370
371 #[derive(BitfieldSpecifier)]
372 #[bits = 1]
373 pub enum Polarity {
374 HighActive = 0,
375 LowActive = 1,
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use cfg_prt_uart::*;
383
384 use ublox::{
385 AlignmentToReferenceTime, CfgNav5DynModel, CfgNav5FixMode, CfgNav5Params,
386 CfgNav5UtcStandard,
387 };
388
389 #[test]
390 fn build_packet_prt() {
391 const BUS_SPEED: u32 = 9600_u32;
392
393 let packet = ublox::CfgPrtUartBuilder {
394 portid: ublox::UartPortId::Uart1,
395 reserved0: 0,
396 tx_ready: TxReady::new().with_en(false).into(),
398 mode: Mode::new()
399 .with_char_len(CharLen::Bits8)
400 .with_parity(Parity::None)
401 .with_n_stop_bits(NumStopBits::Bits1)
402 .into(),
403 baud_rate: BUS_SPEED,
404 in_proto_mask: ProtoMask::new().with_ubx(true).into(),
405 out_proto_mask: ProtoMask::new().with_ubx(true).with_nmea(true).into(),
406 flags: Flags::new().into(),
407 reserved5: 0,
408 }
409 .into_packet_bytes();
410
411 let frame = [
412 0xb5, 0x62, 0x6, 0x0, 0x14, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc0, 0x8, 0x0, 0x0, 0x80, 0x25,
413 0x0, 0x0, 0x1, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x85,
414 ];
415
416 assert_eq!(packet, frame);
417 }
418
419 #[test]
420 fn build_packet_nav5() {
421 let packet = ublox::CfgNav5Builder {
422 mask: CfgNav5Params::DYN | CfgNav5Params::POS_FIX_MODE,
423 dyn_model: CfgNav5DynModel::AirborneWith4gAcceleration,
424 fix_mode: CfgNav5FixMode::Auto2D3D,
425 fixed_alt: 0.0,
426 fixed_alt_var: 0.0,
427 min_elev_degrees: 0,
428 dr_limit: 0,
429 pdop: 0.0,
430 tdop: 0.0,
431 pacc: 0,
432 tacc: 0,
433 static_hold_thresh: 0.0,
434 dgps_time_out: 0,
435 cno_thresh_num_svs: 0,
436 cno_thresh: 0,
437 reserved1: [0; 2],
438 static_hold_max_dist: 0,
439 utc_standard: CfgNav5UtcStandard::Automatic,
440 reserved2: [0; 5],
441 }
442 .into_packet_bytes();
443
444 let frame = [
445 0xb5, 0x62, 0x6, 0x24, 0x24, 0x0, 0x5, 0x0, 0x8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
446 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
447 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5e, 0xeb,
448 ];
449
450 assert_eq!(packet, frame);
451 }
452
453 #[test]
454 fn build_packet_rate() {
455 let packet = ublox::CfgRateBuilder {
456 measure_rate_ms: 50,
457 nav_rate: 1,
458 time_ref: AlignmentToReferenceTime::Utc,
459 }
460 .into_packet_bytes();
461
462 let frame = [
463 0xb5, 0x62, 0x6, 0x8, 0x6, 0x0, 0x32, 0x0, 0x1, 0x0, 0x0, 0x0, 0x47, 0xe4,
464 ];
465
466 assert_eq!(packet, frame);
467 }
468
469 #[test]
472 fn send_partial_packets() {
473 let mut buffer = [0u8; 512];
474 let buffer = ublox::FixedLinearBuffer::new(&mut buffer);
475 let mut parser = ublox::Parser::new(buffer);
476
477 {
478 let packet_1 = [0xb5, 0x62, 0x05, 0x01, 0x02];
480 let mut iter = parser.consume(&packet_1);
481 println!("Print packet 1 (partial AckAck): ");
482
483 if iter.next().is_some() {
484 panic!("Returned some packet when only a partial packet was supplied");
485 }
486 }
487
488 {
489 let packet_2 = [0x00, 0x06, 0x00, 0x0e, 0x37, 0xb5, 0x62, 0x05, 0x00, 0x02];
491 let mut iter = parser.consume(&packet_2);
494 println!("Print packet 2 (finish AckAck, partial AckNak): ");
495 match iter.next() {
496 Some(Ok(ublox::PacketRef::AckAck(_))) => (),
497 _ => panic!("Did not return expected packet"),
498 }
499 }
500
501 {
502 let packet_3 = [0x00, 0x06, 0x00, 0x0d, 0x32];
504 let mut iter = parser.consume(&packet_3);
505 println!("Print packet 3 (finish AckNak): ");
506 match iter.next() {
507 Some(Ok(ublox::PacketRef::AckNak(_))) => (),
508 _ => panic!("Did not return expected packet"),
509 }
510 }
511 }
512}