use std::sync::{Mutex, Arc}; use super::GpsError; use nmea_parser; use nmea_parser::gnss::FaaMode; /// Data structure to store all relevant data collected from the gps #[derive(Default)] pub struct GpsData { /// Latitude of reported GPS location latitude: Option, /// Longitude of reported GPS location longitude: Option, /// Calculated speed from GPS reciever speed: Option, /// Altitude reported from GPS location altitude: Option, } impl GpsData { pub fn to_string(&self) -> String { format!("Latitude: {:.4?} \nLongitude: {:.4?} \nSpeed: {:.1?}mph \nAltitude: {:.1?}m", self.latitude.unwrap_or_default(), self.longitude.unwrap_or_default(), self.speed.unwrap_or_default(), self.altitude.unwrap_or_default() ) } /// Function to asynchronously go through and update GPS data from a nmea stream pub async fn update(&mut self, buf: &Vec) -> Result<(), GpsError> { let mut nmea_parser = nmea_parser::NmeaParser::new(); // Format the nmea buffer into a usable string let nmea_raw = String::from_utf8(buf.to_owned()) .map_err(|_| GpsError::ReadError())?; let nmea_vec: Vec<&str> = nmea_raw.split('$').collect(); // Loop through each sentence and use the information to update GPS data for nmea_line in nmea_vec { // Don't try and process / parse if the string is empty if nmea_line.is_empty() { continue; } // Construct string that is in the correct format for parsing let mut sentence = "$".to_string(); sentence.push_str(nmea_line); let nmea = match nmea_parser.parse_sentence(sentence.as_str()) { Ok(nmea) => nmea, // Don't continue processing a sentence if we know that it isn't supported Err(_) => { continue; } }; // print decoded gps data to serial match nmea { nmea_parser::ParsedMessage::Gga(gga) => { self.latitude = gga.latitude; self.longitude = gga.longitude; self.altitude = gga.altitude; } nmea_parser::ParsedMessage::Gll(gll) => { if gll.faa_mode.unwrap_or(FaaMode::NotValid) == FaaMode::Autonomous { self.latitude = gll.latitude; self.longitude = gll.longitude; } }, nmea_parser::ParsedMessage::Rmc(rms) => { self.latitude = rms.latitude; self.longitude = rms.longitude; self.speed = Some(knots_to_mph(rms.sog_knots.unwrap_or_default())); }, _ => {} } } Ok(()) } pub fn new() -> Arc> { return Arc::new(Mutex::new(GpsData::default())) } } /// Function to simply convert knots to mph fn knots_to_mph(knots: f64) -> f64{ knots * 1.150779 }