use approx::assert_relative_eq;
use chrono::NaiveTime;
use helpers::format_satellites;
use nmea::{sentences::fix_type::FixType, *};
mod helpers;
#[test]
fn test_invalid_datetime() {
let mut nmea = Nmea::default();
let res = nmea.parse("$,GRMC,,A,,,,,,,290290GLCR*40");
println!("parse result {:?}", res);
assert!(matches!(res, Err(Error::ParsingError(_))));
}
#[test]
fn test_gga_north_west() {
use chrono::Timelike;
let mut nmea = Nmea::default();
nmea.parse("$GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76")
.unwrap();
assert_eq!(nmea.fix_timestamp().unwrap().second(), 50);
assert_eq!(nmea.fix_timestamp().unwrap().minute(), 27);
assert_eq!(nmea.fix_timestamp().unwrap().hour(), 9);
assert_eq!(nmea.latitude().unwrap(), 53. + 21.6802 / 60.);
assert_eq!(nmea.longitude().unwrap(), -(6. + 30.3372 / 60.));
assert_eq!(nmea.fix_type().unwrap(), FixType::Gps);
assert_eq!(nmea.fix_satellites().unwrap(), 8);
assert_eq!(nmea.hdop().unwrap(), 1.03);
assert_relative_eq!(nmea.geoid_altitude().unwrap(), (61.7 + 55.2));
}
#[test]
fn test_gga_north_east() {
let mut nmea = Nmea::default();
nmea.parse("$GPGGA,092750.000,5321.6802,N,00630.3372,E,1,8,1.03,61.7,M,55.2,M,,*64")
.unwrap();
assert_eq!(nmea.latitude().unwrap(), 53. + 21.6802 / 60.);
assert_eq!(nmea.longitude().unwrap(), 6. + 30.3372 / 60.);
}
#[test]
fn test_gga_south_west() {
let mut nmea = Nmea::default();
nmea.parse("$GPGGA,092750.000,5321.6802,S,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*6B")
.unwrap();
assert_eq!(nmea.latitude().unwrap(), -(53. + 21.6802 / 60.));
assert_eq!(nmea.longitude().unwrap(), -(6. + 30.3372 / 60.));
}
#[test]
fn test_gga_south_east() {
let mut nmea = Nmea::default();
nmea.parse("$GPGGA,092750.000,5321.6802,S,00630.3372,E,1,8,1.03,61.7,M,55.2,M,,*79")
.unwrap();
assert_eq!(nmea.latitude().unwrap(), -(53. + 21.6802 / 60.));
assert_eq!(nmea.longitude().unwrap(), 6. + 30.3372 / 60.);
}
#[test]
fn test_gga_invalid() {
let mut nmea = Nmea::default();
nmea.parse("$GPGGA,092750.000,5321.6802,S,00630.3372,E,0,8,1.03,61.7,M,55.2,M,,*7B")
.unwrap_err();
assert_eq!(nmea.fix_type(), None);
}
#[test]
fn test_gga_gps() {
use chrono::Timelike;
let mut nmea = Nmea::default();
nmea.parse("$GPGGA,092750.000,5321.6802,S,00630.3372,E,1,8,1.03,61.7,M,55.2,M,,*79")
.unwrap();
assert_eq!(nmea.fix_timestamp().unwrap().second(), 50);
assert_eq!(nmea.fix_timestamp().unwrap().minute(), 27);
assert_eq!(nmea.fix_timestamp().unwrap().hour(), 9);
assert_eq!(-(53. + 21.6802 / 60.), nmea.latitude.unwrap());
assert_eq!(6. + 30.3372 / 60., nmea.longitude.unwrap());
assert_eq!(nmea.fix_type(), Some(FixType::Gps));
assert_eq!(8, nmea.num_of_fix_satellites.unwrap());
assert_eq!(1.03, nmea.hdop.unwrap());
assert_eq!(61.7, nmea.altitude.unwrap());
assert_eq!(55.2, nmea.geoid_separation.unwrap());
}
#[test]
fn test_gsv() {
let mut nmea = Nmea::default();
nmea.parse("$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70")
.unwrap();
pretty_assertions::assert_eq!(
vec![
"{Gps 5 Some(59.0) Some(290.0) Some(20.0)}",
"{Gps 7 Some(61.0) Some(98.0) Some(15.0)}",
"{Gps 8 Some(54.0) Some(157.0) Some(30.0)}",
"{Gps 10 Some(63.0) Some(137.0) Some(17.0)}",
],
format_satellites(nmea.satellites())
);
nmea.parse("$GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79")
.unwrap();
pretty_assertions::assert_eq!(
vec![
"{Gps 2 Some(39.0) Some(223.0) Some(19.0)}",
"{Gps 4 Some(14.0) Some(186.0) Some(14.0)}",
"{Gps 5 Some(59.0) Some(290.0) Some(20.0)}",
"{Gps 7 Some(61.0) Some(98.0) Some(15.0)}",
"{Gps 8 Some(54.0) Some(157.0) Some(30.0)}",
"{Gps 10 Some(63.0) Some(137.0) Some(17.0)}",
"{Gps 13 Some(28.0) Some(70.0) Some(17.0)}",
"{Gps 26 Some(23.0) Some(252.0) None}",
],
format_satellites(nmea.satellites())
);
nmea.parse("$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76")
.unwrap();
pretty_assertions::assert_eq!(
vec![
"{Gps 2 Some(39.0) Some(223.0) Some(19.0)}",
"{Gps 4 Some(14.0) Some(186.0) Some(14.0)}",
"{Gps 5 Some(59.0) Some(290.0) Some(20.0)}",
"{Gps 7 Some(61.0) Some(98.0) Some(15.0)}",
"{Gps 8 Some(54.0) Some(157.0) Some(30.0)}",
"{Gps 10 Some(63.0) Some(137.0) Some(17.0)}",
"{Gps 13 Some(28.0) Some(70.0) Some(17.0)}",
"{Gps 16 Some(9.0) Some(20.0) None}",
"{Gps 26 Some(23.0) Some(252.0) None}",
"{Gps 29 Some(9.0) Some(301.0) Some(24.0)}",
"{Gps 36 None None None}"
],
format_satellites(nmea.satellites())
);
}
#[test]
fn test_gsv_real_data() {
let mut nmea = Nmea::default();
let real_data = [
"$GPGSV,3,1,12,01,49,196,41,03,71,278,32,06,02,323,27,11,21,196,39*72",
"$GPGSV,3,2,12,14,39,063,33,17,21,292,30,19,20,310,31,22,82,181,36*73",
"$GPGSV,3,3,12,23,34,232,42,25,11,045,33,31,45,092,38,32,14,061,39*75",
"$GLGSV,3,1,10,74,40,078,43,66,23,275,31,82,10,347,36,73,15,015,38*6B",
"$GLGSV,3,2,10,75,19,135,36,65,76,333,31,88,32,233,33,81,40,302,38*6A",
"$GLGSV,3,3,10,72,40,075,43,87,00,000,*6F",
"$GPGSV,4,4,15,26,02,112,,31,45,071,,32,01,066,*4C",
];
for line in &real_data {
pretty_assertions::assert_eq!(nmea.parse(line).unwrap(), SentenceType::GSV);
}
pretty_assertions::assert_eq!(
vec![
"{Gps 1 Some(49.0) Some(196.0) Some(41.0)}",
"{Gps 3 Some(71.0) Some(278.0) Some(32.0)}",
"{Gps 6 Some(2.0) Some(323.0) Some(27.0)}",
"{Gps 11 Some(21.0) Some(196.0) Some(39.0)}",
"{Gps 14 Some(39.0) Some(63.0) Some(33.0)}",
"{Gps 17 Some(21.0) Some(292.0) Some(30.0)}",
"{Gps 19 Some(20.0) Some(310.0) Some(31.0)}",
"{Gps 22 Some(82.0) Some(181.0) Some(36.0)}",
"{Gps 23 Some(34.0) Some(232.0) Some(42.0)}",
"{Gps 25 Some(11.0) Some(45.0) Some(33.0)}",
"{Gps 26 Some(2.0) Some(112.0) None}",
"{Gps 31 Some(45.0) Some(71.0) None}",
"{Gps 32 Some(1.0) Some(66.0) None}",
"{Glonass 65 Some(76.0) Some(333.0) Some(31.0)}",
"{Glonass 66 Some(23.0) Some(275.0) Some(31.0)}",
"{Glonass 72 Some(40.0) Some(75.0) Some(43.0)}",
"{Glonass 73 Some(15.0) Some(15.0) Some(38.0)}",
"{Glonass 74 Some(40.0) Some(78.0) Some(43.0)}",
"{Glonass 75 Some(19.0) Some(135.0) Some(36.0)}",
"{Glonass 81 Some(40.0) Some(302.0) Some(38.0)}",
"{Glonass 82 Some(10.0) Some(347.0) Some(36.0)}",
"{Glonass 87 Some(0.0) Some(0.0) None}",
"{Glonass 88 Some(32.0) Some(233.0) Some(33.0)}",
],
format_satellites(nmea.satellites())
);
}
#[test]
fn test_gsv_order() {
let mut nmea = Nmea::default();
nmea.parse("$GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79")
.unwrap();
nmea.parse("$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76")
.unwrap();
nmea.parse("$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70")
.unwrap();
assert_eq!(
vec![
"{Gps 2 Some(39.0) Some(223.0) Some(19.0)}",
"{Gps 4 Some(14.0) Some(186.0) Some(14.0)}",
"{Gps 5 Some(59.0) Some(290.0) Some(20.0)}",
"{Gps 7 Some(61.0) Some(98.0) Some(15.0)}",
"{Gps 8 Some(54.0) Some(157.0) Some(30.0)}",
"{Gps 10 Some(63.0) Some(137.0) Some(17.0)}",
"{Gps 13 Some(28.0) Some(70.0) Some(17.0)}",
"{Gps 16 Some(9.0) Some(20.0) None}",
"{Gps 26 Some(23.0) Some(252.0) None}",
"{Gps 29 Some(9.0) Some(301.0) Some(24.0)}",
"{Gps 36 None None None}",
],
format_satellites(nmea.satellites())
);
}
#[test]
fn test_gsv_two_of_three() {
let mut nmea = Nmea::default();
nmea.parse("$GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79")
.unwrap();
nmea.parse("$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76")
.unwrap();
assert_eq!(
vec![
"{Gps 2 Some(39.0) Some(223.0) Some(19.0)}",
"{Gps 4 Some(14.0) Some(186.0) Some(14.0)}",
"{Gps 13 Some(28.0) Some(70.0) Some(17.0)}",
"{Gps 16 Some(9.0) Some(20.0) None}",
"{Gps 26 Some(23.0) Some(252.0) None}",
"{Gps 29 Some(9.0) Some(301.0) Some(24.0)}",
"{Gps 36 None None None}",
],
format_satellites(nmea.satellites())
);
}
#[test]
fn test_nmea_parse() {
let sentences = [
"$GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76",
"$GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A",
"$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70",
"$GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79",
"$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76",
"$GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43",
];
let mut nmea = Nmea::default();
for s in &sentences {
let res = nmea.parse(s).unwrap();
println!("test_parse res {:?}", res);
}
assert_eq!(nmea.latitude().unwrap(), 53. + 21.6802 / 60.);
assert_eq!(nmea.longitude().unwrap(), -(6. + 30.3372 / 60.));
assert_eq!(nmea.altitude().unwrap(), 61.7);
pretty_assertions::assert_eq!(
vec![
"{Gps 2 Some(39.0) Some(223.0) Some(19.0)}",
"{Gps 4 Some(14.0) Some(186.0) Some(14.0)}",
"{Gps 5 Some(59.0) Some(290.0) Some(20.0)}",
"{Gps 7 Some(61.0) Some(98.0) Some(15.0)}",
"{Gps 8 Some(54.0) Some(157.0) Some(30.0)}",
"{Gps 10 Some(63.0) Some(137.0) Some(17.0)}",
"{Gps 13 Some(28.0) Some(70.0) Some(17.0)}",
"{Gps 16 Some(9.0) Some(20.0) None}",
"{Gps 26 Some(23.0) Some(252.0) None}",
"{Gps 29 Some(9.0) Some(301.0) Some(24.0)}",
"{Gps 36 None None None}",
],
format_satellites(nmea.satellites())
);
}
#[test]
fn test_nmea_parse_for_fix() {
{
let mut nmea =
Nmea::create_for_navigation(&[SentenceType::RMC, SentenceType::GGA]).unwrap();
let log = [
(
"$GPRMC,123308.2,A,5521.76474,N,03731.92553,E,000.48,071.9,090317,010.2,E,A*3B",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 200).expect("invalid time")),
),
(
"$GPGGA,123308.2,5521.76474,N,03731.92553,E,1,08,2.2,211.5,M,13.1,M,,*52",
FixType::Gps,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 200).expect("invalid time")),
),
(
"$GPVTG,071.9,T,061.7,M,000.48,N,0000.88,K,A*10",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 200).expect("invalid time")),
),
(
"$GPRMC,123308.3,A,5521.76474,N,03731.92553,E,000.51,071.9,090317,010.2,E,A*32",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 300).expect("invalid time")),
),
(
"$GPGGA,123308.3,5521.76474,N,03731.92553,E,1,08,2.2,211.5,M,13.1,M,,*53",
FixType::Gps,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 300).expect("invalid time")),
),
(
"$GPVTG,071.9,T,061.7,M,000.51,N,0000.94,K,A*15",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 300).expect("invalid time")),
),
(
"$GPRMC,123308.4,A,5521.76474,N,03731.92553,E,000.54,071.9,090317,010.2,E,A*30",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 400).expect("invalid time")),
),
(
"$GPGGA,123308.4,5521.76474,N,03731.92553,E,1,08,2.2,211.5,M,13.1,M,,*54",
FixType::Gps,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 400).expect("invalid time")),
),
(
"$GPVTG,071.9,T,061.7,M,000.54,N,0001.00,K,A*1C",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 400).expect("invalid time")),
),
(
"$GPRMC,123308.5,A,5521.76474,N,03731.92553,E,000.57,071.9,090317,010.2,E,A*32",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 500).expect("invalid time")),
),
(
"$GPGGA,123308.5,5521.76474,N,03731.92553,E,1,08,2.2,211.5,M,13.1,M,,*55",
FixType::Gps,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 500).expect("invalid time")),
),
(
"$GPVTG,071.9,T,061.7,M,000.57,N,0001.05,K,A*1A",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 500).expect("invalid time")),
),
(
"$GPRMC,123308.6,A,5521.76474,N,03731.92553,E,000.58,071.9,090317,010.2,E,A*3E",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 600).expect("invalid time")),
),
(
"$GPGGA,123308.6,5521.76474,N,03731.92553,E,1,08,2.2,211.5,M,13.1,M,,*56",
FixType::Gps,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 600).expect("invalid time")),
),
(
"$GPVTG,071.9,T,061.7,M,000.58,N,0001.08,K,A*18",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 600).expect("invalid time")),
),
(
"$GPRMC,123308.7,A,5521.76474,N,03731.92553,E,000.59,071.9,090317,010.2,E,A*3E",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 700).expect("invalid time")),
),
(
"$GPGGA,123308.7,5521.76474,N,03731.92553,E,1,08,2.2,211.5,M,13.1,M,,*57",
FixType::Gps,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 700).expect("invalid time")),
),
(
"$GPVTG,071.9,T,061.7,M,000.59,N,0001.09,K,A*18",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 700).expect("invalid time")),
),
];
for (i, item) in log.iter().enumerate() {
let res = nmea.parse_for_fix(item.0).unwrap();
println!("parse result({}): {:?}, {:?}", i, res, nmea.fix_time);
assert_eq!((&res, &nmea.fix_time), (&item.1, &item.2));
}
}
{
let mut nmea =
Nmea::create_for_navigation(&[SentenceType::RMC, SentenceType::GGA]).unwrap();
let log = [
(
"$GPRMC,123308.2,A,5521.76474,N,03731.92553,E,000.48,071.9,090317,010.2,E,A*3B",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 200).expect("invalid time")),
),
(
"$GPRMC,123308.3,A,5521.76474,N,03731.92553,E,000.51,071.9,090317,010.2,E,A*32",
FixType::Invalid,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 300).expect("invalid time")),
),
(
"$GPGGA,123308.3,5521.76474,N,03731.92553,E,1,08,2.2,211.5,M,13.1,M,,*53",
FixType::Gps,
Some(NaiveTime::from_hms_milli_opt(12, 33, 8, 300).expect("invalid time")),
),
];
for (i, item) in log.iter().enumerate() {
let res = nmea.parse_for_fix(item.0).unwrap();
println!("parse result({}): {:?}, {:?}", i, res, nmea.fix_time);
assert_eq!((&res, &nmea.fix_time), (&item.1, &item.2));
}
}
}
#[test]
#[cfg(all(feature = "RMC", feature = "GGA", feature = "GSA", feature = "ZDA"))]
fn test_some_receiver() {
let lines = [
"$GPRMC,171724.000,A,6847.2474,N,03245.8351,E,0.26,140.74,250317,,*02",
"$GPGGA,171725.000,6847.2473,N,03245.8351,E,1,08,1.0,87.7,M,18.5,M,,0000*66",
"$GPGSA,A,3,02,25,29,12,31,06,23,14,,,,,2.0,1.0,1.7*3A",
"$GPRMC,171725.000,A,6847.2473,N,03245.8351,E,0.15,136.12,250317,,*05",
"$GPGGA,171726.000,6847.2473,N,03245.8352,E,1,08,1.0,87.8,M,18.5,M,,0000*69",
"$GPGSA,A,3,02,25,29,12,31,06,23,14,,,,,2.0,1.0,1.7*3A",
"$GPRMC,171726.000,A,6847.2473,N,03245.8352,E,0.16,103.49,250317,,*0E",
"$GPGGA,171727.000,6847.2474,N,03245.8353,E,1,08,1.0,87.9,M,18.5,M,,0000*6F",
"$GPGSA,A,3,02,25,29,12,31,06,23,14,,,,,2.0,1.0,1.7*3A",
"$GPRMC,171727.000,A,6847.2474,N,03245.8353,E,0.49,42.80,250317,,*32",
"$GPZDA,160012.71,11,03,2004,-1,00*7D",
];
let mut nmea = Nmea::create_for_navigation(&[SentenceType::RMC, SentenceType::GGA]).unwrap();
println!("start test");
let mut nfixes = 0_usize;
for line in &lines {
match nmea.parse_for_fix(line) {
Ok(FixType::Invalid) => {
println!("invalid");
continue;
}
Err(msg) => {
println!("update_gnss_info_nmea: parse_for_fix failed: {:?}", msg);
continue;
}
Ok(_) => nfixes += 1,
}
}
assert_eq!(nfixes, 3);
}
#[test]
fn test_gll() {
use chrono::Timelike;
let mut nmea = Nmea::default();
nmea.parse("$GPGLL,5107.0013414,N,11402.3279144,W,205412.00,A,A*73")
.unwrap();
assert_eq!(51. + 7.0013414 / 60., nmea.latitude().unwrap());
assert_eq!(-(114. + 2.3279144 / 60.), nmea.longitude().unwrap());
assert_eq!(20, nmea.fix_timestamp().unwrap().hour());
assert_eq!(54, nmea.fix_timestamp().unwrap().minute());
assert_eq!(12, nmea.fix_timestamp().unwrap().second());
nmea.parse("$GPGLL,4916.45,N,12311.12,W,225444,A,*1D")
.unwrap();
assert_eq!(49. + 16.45 / 60., nmea.latitude().unwrap());
assert_eq!(-(123. + 11.12 / 60.), nmea.longitude().unwrap());
assert_eq!(22, nmea.fix_timestamp().unwrap().hour());
assert_eq!(54, nmea.fix_timestamp().unwrap().minute());
assert_eq!(44, nmea.fix_timestamp().unwrap().second());
}