extern crate serialport;
#[allow(non_snake_case)]
pub mod PMTK;
pub mod nmea;
pub mod gps {
use std::io::Read;
use std::str;
use std::time::Duration;
use serialport::prelude::*;
use crate::nmea;
use crate::nmea::gsv::Satellites;
pub fn open_port(port_name: &str, baud_rate:u32) -> Box<dyn SerialPort> {
let settings = SerialPortSettings {
baud_rate,
data_bits: DataBits::Eight,
flow_control: FlowControl::None,
parity: Parity::None,
stop_bits: StopBits::One,
timeout: Duration::from_millis(1000),
};
match serialport::open_with_settings(port_name, &settings) {
Ok(port) => return port,
Err(_e) => panic!("Port not found: {} - {}", port_name, _e),
}
}
pub fn is_valid_checksum(s: &str) -> bool {
let s = s.trim();
let star = &s[s.len() - 3..s.len() - 2];
let checksum = &s[s.len() - 2..s.len()];
let body = &s[0..s.len() - 3];
if star != "*" {
return false;
}
match u8::from_str_radix(&checksum, 16) {
Ok(expected_checksum) => {
let mut actual: u8 = 0;
for i in body[1..].as_bytes() {
actual ^= *i;
}
if actual == expected_checksum {
return true;
} else {
return false;
}
}
Err(_e) => return false,
}
}
#[derive(Debug)]
#[derive(Default)]
pub struct GpsData {
pub utc: f64,
pub latitude: Option<f32>,
pub longitude: Option<f32>,
pub altitude: Option<f32>,
pub true_course: Option<f32>,
pub mag_course: Option<f32>,
pub speed_knots: Option<f32>,
pub speed_kph: Option<f32>,
pub geoidal_spe: Option<f32>,
pub age_diff_corr: Option<f32>,
pub sats_used: i32,
pub pdop: Option<f32>,
pub hdop: Option<f32>,
pub vdop: Option<f32>,
pub fix_quality: nmea::gga::SatFix,
pub satellites: Vec<Satellites>,
}
pub struct Gps {
pub port: Box<dyn SerialPort>,
pub satellite_data: bool,
pub naviagtion_data:bool,
}
pub trait GetGpsData {
fn update(&mut self) -> GpsData;
fn read_line(&mut self) -> String;
}
impl GetGpsData for Gps {
fn update(&mut self) -> GpsData {
let mut gga = self.naviagtion_data;
let mut vtg = self.naviagtion_data;
let mut gsa = self.satellite_data;
let mut gsv = self.satellite_data;
let mut values = GpsData::default();
while (gga == true) || (vtg == true) || (gsa == true) || (gsv == true) {
let line = self.read_line();
let sentence = nmea::nmea::parse_sentence(line.as_str());
if sentence.is_some() {
let sentence = sentence.unwrap();
if &sentence.get(0).unwrap()[3..5] == "GG" {
let gga_values = nmea::gga::parse_gga(sentence);
values.utc = gga_values.utc;
values.latitude = gga_values.lat;
values.longitude = gga_values.long;
values.sats_used = gga_values.satellites_used;
values.altitude = gga_values.msl_alt;
values.geoidal_spe = gga_values.geoidal_sep;
values.age_diff_corr = gga_values.age_diff_corr;
values.fix_quality = gga_values.sat_fix;
gga = false;
} else if &sentence.get(0).unwrap()[3..6] == "VTG" {
let vtg_values = nmea::vtg::parse_vtg(sentence);
values.true_course = vtg_values.true_course;
values.mag_course = vtg_values.magnetic_course;
values.speed_knots = vtg_values.speed_knots;
values.speed_kph = vtg_values.speed_kph;
vtg = false;
} else if &sentence.get(0).unwrap()[3..6] == "GSA" {
let gsa_values = nmea::gsa::parse_gsa(sentence);
values.hdop = gsa_values.hdop;
values.vdop = gsa_values.vdop;
values.pdop = gsa_values.pdop;
gsa = false;
} else if &sentence.get(0).unwrap()[3..6] == "GSV" {
let number_of_messages: i32 = sentence.get(1).unwrap().parse().unwrap();
let v = if number_of_messages == 1 {
nmea::gsv::parse_gsv(sentence)
} else {
let mut gsv_values: Vec<Satellites> = nmea::gsv::parse_gsv(sentence);
for _message in 1..number_of_messages {
let line = self.read_line();
let sentence = nmea::nmea::parse_sentence(line.as_str());
let sentence = sentence.unwrap();
gsv_values.append(nmea::gsv::parse_gsv(sentence).as_mut())
}
gsv_values
};
values.satellites = v;
gsv = false;
}
}
}
values
}
fn read_line(&mut self) -> String {
let mut buffer: Vec<u8> = vec![0; 1];
let mut output: Vec<u8> = Vec::new();
let p = &mut self.port;
let mut cont = true;
while cont {
match p.read(buffer.as_mut_slice()) {
Ok(buffer_size) => {
output.extend_from_slice(&buffer[..buffer_size]);
if output.get(output.len() - 1).unwrap() == &10u8 {
cont = false;
}
}
Err(_e) => (),
}
}
let string: String = str::from_utf8(&output).unwrap_or("Invalid bytes given").to_string();
return string;
}
}
}
#[cfg(test)]
mod gps_test {
use super::gps;
#[test]
fn is_valid_sentence() {
assert_eq!(gps::is_valid_checksum("$PMTK220,100*2F"), true);
assert_eq!(gps::is_valid_checksum("$GPGSV,4,3,14,12,12,100,,04,11,331,,16,06,282,,05,05,074,22*75"), true);
assert_eq!(gps::is_valid_checksum("$GPGSV,4,4,14,32,01,215,,41,,,*4F"), true);
assert_eq!(gps::is_valid_checksum("$GNGGA,131613.000,5132.7314,N,00005.9099,W,1,9,1.17,42.4,M,47.0,M,,*60\r\n"), true);
assert_eq!(gps::is_valid_checksum("$GPGSA,A,3,29,02,26,25,31,14,,,,,,,1.42,1.17,0.80*07\r\n"), true);
assert_eq!(gps::is_valid_checksum("$GPGSA,A,3,29,02,26,25,31,14,,,,,,,1.42,1.17,0.80*A7\r\n"), false);
}
}