r_d_inclita_sofware/sensors/
gps.rs1use crate::telemetry::data::{
2 DATA_CHANNEL, GnggaMessage, GpsFix, LATEST_TELEMETRY, LogEvent, UtcTime,
3};
4use defmt::{error, info, warn};
5use embassy_stm32::usart::Uart;
6
7#[embassy_executor::task]
8pub async fn gps_task(mut uart: Uart<'static, embassy_stm32::mode::Async>) {
9 let mut dma_buf = [0u8; 400];
11
12 loop {
13 match uart.read_until_idle(&mut dma_buf).await {
16 Ok(bytes_read) => {
17 if let Ok(burst_str) = core::str::from_utf8(&dma_buf[..bytes_read]) {
19 for line in burst_str.lines() {
21 if line.starts_with("$GNGGA") || line.starts_with("$GPGGA") {
22 parse_and_send_gngga(line).await;
23 }
24 }
25 }
26 }
27 Err(_) => error!("GPS UART Read Error"),
28 }
29 }
30}
31
32async fn parse_and_send_gngga(line: &str) {
33 let get_part = |idx: usize| -> Option<&str> { line.split(',').nth(idx) };
34
35 let raw_fix_num = get_part(6).unwrap_or("0").parse::<u8>().unwrap_or(0);
37 let current_fix = GpsFix::from_u8(raw_fix_num);
38
39 if current_fix != GpsFix::NoFix && current_fix != GpsFix::Unknown {
41 let raw_time = get_part(1).unwrap_or("000000.00");
42 let utc_time = parse_utc_time(raw_time);
43 let raw_lat = get_part(2).unwrap_or("0.0");
44 let lat_dir = get_part(3).unwrap_or("N");
45 let raw_lon = get_part(4).unwrap_or("0.0");
46 let lon_dir = get_part(5).unwrap_or("E");
47 let altitude = get_part(9).unwrap_or("0.0").parse::<f32>().unwrap_or(0.0);
48
49 let latitude = nmea_to_decimal(raw_lat, lat_dir);
50 let longitude = nmea_to_decimal(raw_lon, lon_dir);
51
52 let data = GnggaMessage {
53 utc_time,
54 latitude,
55 longitude,
56 altitude,
57 fix: current_fix,
58 timestamp_ms: embassy_time::Instant::now().as_millis() as u32,
59 };
60 info!("GPS {:?}", data);
61
62 DATA_CHANNEL.send(LogEvent::GPS(data.clone())).await;
63 {
64 let mut guard = LATEST_TELEMETRY.lock().await;
65
66 guard.gps = Some(data.into());
67 }
68 } else {
69 warn!("GPS: Waiting for Satellites (No Fix)");
70 }
71}
72fn nmea_to_decimal(raw: &str, direction: &str) -> f64 {
74 let val = match raw.parse::<f64>() {
76 Ok(v) => v,
77 Err(_) => return 0.0, };
79
80 let degrees = (val / 100.0) as i32 as f64;
87 let minutes = val - (degrees * 100.0);
88
89 let mut decimal = degrees + (minutes / 60.0);
90
91 if direction == "S" || direction == "W" {
93 decimal = -decimal;
94 }
95
96 decimal
97}
98
99fn parse_utc_time(raw: &str) -> UtcTime {
101 let hours = raw
105 .get(0..2)
106 .and_then(|c| c.parse::<u8>().ok())
107 .unwrap_or(0);
108 let minutes = raw
109 .get(2..4)
110 .and_then(|c| c.parse::<u8>().ok())
111 .unwrap_or(0);
112 let seconds = raw
113 .get(4..)
114 .and_then(|c| c.parse::<f32>().ok())
115 .unwrap_or(0.0);
116
117 UtcTime {
118 hours,
119 minutes,
120 seconds,
121 }
122}