#![allow(dead_code)]
#[macro_use] extern crate log;
extern crate env_logger;
extern crate chrono;
mod ais_vdm_t1t2t3;
mod ais_vdm_t5;
mod ais_vdm_t18;
mod ais_vdm_t19;
mod ais_vdm_t24;
mod gnss_gga;
mod gnss_gsa;
mod gnss_gsv;
mod gnss_rmc;
mod gnss_vtg;
mod gnss_gll;
mod types;
mod util;
pub use types::*;
use util::*;
use std::collections::{HashMap};
use bitvec::prelude::*;
use chrono::{DateTime};
use chrono::prelude::*;
pub fn decode_sentence(sentence: &str, nmea_store: &mut NmeaStore) -> Result<ParsedSentence, String> {
let mut checksum = 0;
let (sentence, checksum_hex_given) = {
if let Some(pos) = sentence.rfind('*') {
(sentence[0..pos].to_string(), sentence[(pos+1)..sentence.len()].to_string())
} else {
debug!("No checksum found for sentence: {}", sentence);
(sentence.to_string(), "".to_string())
}
};
for c in sentence.as_str().chars().skip(1) {
checksum = checksum ^ (c as u8);
}
let checksum_hex_calculated = format!("{:02X?}", checksum);
if checksum_hex_calculated != checksum_hex_given && checksum_hex_given != "" {
return Err(format!("Corrupted NMEA sentence: {:02X?} != {:02X?}",
checksum_hex_calculated, checksum_hex_given));
}
let mut sentence_type: String = {
if let Some(i) = sentence.find(',') {
sentence[0..i].into()
} else {
return Err(format!("Invalid NMEA sentence: {}", sentence));
}
};
let nav_system = {
if &sentence_type[0..1] == "$" {
match &sentence_type[1..3] {
"GN" => Some(NavigationSystem::Combination),
"GP" => Some(NavigationSystem::Gps),
"GL" => Some(NavigationSystem::Glonass),
"GA" => Some(NavigationSystem::Galileo),
"BD" => Some(NavigationSystem::Beidou),
"GI" => Some(NavigationSystem::Navic),
"QZ" => Some(NavigationSystem::Qzss),
_ => Some(NavigationSystem::Other),
}
} else {
None
}
};
if nav_system != None {
if sentence_type.len() <= 6 {
sentence_type = format!("${}", &sentence_type[3..6]);
}
}
let station = {
if &sentence_type[0..1] == "!" {
match &sentence_type[1..3] {
"AB" => Some(Station::BaseAisStation),
"AD" => Some(Station::DependentAisBaseStation),
"AI" => Some(Station::MobileAisStation),
"AN" => Some(Station::AidToNavigationAisStation),
"AR" => Some(Station::AisReceivingStation),
"AS" => Some(Station::LimitedBaseStation),
"AT" => Some(Station::AisTransmittingStation),
"AX" => Some(Station::RepeaterAisStation),
_ => Some(Station::Other),
}
} else {
None
}
};
if station != None {
if sentence_type.len() <= 6 {
sentence_type = format!("!{}", &sentence_type[3..6]);
}
}
match sentence_type.as_str() {
"$GGA" => {
return gnss_gga::handle(sentence.as_str(), nav_system.unwrap_or(NavigationSystem::Other));
},
"$RMC" => {
return gnss_rmc::handle(sentence.as_str(), nav_system.unwrap_or(NavigationSystem::Other));
},
"$GSA" => {
return gnss_gsa::handle(sentence.as_str(), nav_system.unwrap_or(NavigationSystem::Other));
},
"$GSV" => {
return gnss_gsv::handle(sentence.as_str(), nav_system.unwrap_or(NavigationSystem::Other),
nmea_store);
},
"$VTG" => {
return gnss_vtg::handle(sentence.as_str(), nav_system.unwrap_or(NavigationSystem::Other),
nmea_store);
},
"$GLL" => {
return gnss_gll::handle(sentence.as_str(), nav_system.unwrap_or(NavigationSystem::Other),
nmea_store);
},
"$ALM" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$HDT" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$TRF" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$STN" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$VBW" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$XTC" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$XTE" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$ZDA" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$BOD" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"$RMA" => {
return Err(format!("Unimplemented NMEA sentence: {}", sentence_type));
},
"!VDM" | "!VDO" => {
let own_vessel = sentence_type.as_str() == "!VDO";
let mut num = 0;
let mut fragment_count = 0;
let mut fragment_number = 0;
let mut message_id = None;
let mut radio_channel_code = None;
let mut payload_string: String = "".into();
for s in sentence.split(",") {
match num {
1 => {
match s.parse::<u8>() {
Ok(i) => { fragment_count = i; },
Err(_) => { return Err(format!("Failed to parse fragment count: {}", s)); }
};
},
2 => {
match s.parse::<u8>() {
Ok(i) => { fragment_number = i; },
Err(_) => { return Err(format!("Failed to parse fragment count: {}", s)); }
};
},
3 => {
message_id = s.parse::<u64>().ok();
},
4 => {
radio_channel_code = Some(s);
},
5 => {
payload_string = s.to_string();
},
6 => {
},
_ => {
}
}
num += 1;
}
let mut bv: Option<BitVec> = None;
if fragment_count == 1 {
bv = parse_payload(&payload_string).ok();
} else if fragment_count == 2 {
if let Some(msg_id) = message_id {
let key1 = make_fragment_key(&sentence_type.to_string(), msg_id, fragment_count,
1, radio_channel_code.unwrap_or(""));
let key2 = make_fragment_key(&sentence_type.to_string(), msg_id, fragment_count,
2, radio_channel_code.unwrap_or(""));
if fragment_number == 1 {
if let Some(p) = nmea_store.pull_string(key2.into()) {
let mut payload_string_combined = payload_string;
payload_string_combined.push_str(p.as_str());
bv = parse_payload(&payload_string_combined). ok();
} else {
nmea_store.push_string(key1.into(), payload_string);
}
} else if fragment_number == 2 {
if let Some(p) = nmea_store.pull_string(key1.into()) {
let mut payload_string_combined = p.clone();
payload_string_combined.push_str(payload_string.as_str());
bv = parse_payload(&payload_string_combined).ok();
} else {
nmea_store.push_string(key2.into(), payload_string);
}
} else {
warn!("Unexpected NMEA fragment number: {}/{}", fragment_number, fragment_count);
}
} else {
warn!("NMEA message_id missing from {} than supported 2", sentence_type);
}
} else {
warn!("NMEA sentence fragment count greater ({}) than supported 2", fragment_count);
}
if let Some(bv) = bv {
let message_type = pick_u64(&bv, 0, 6);
match message_type {
1 | 2 | 3 => {
return ais_vdm_t1t2t3::handle(&bv, station.unwrap_or(Station::Other),
own_vessel);
},
4 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
5 => {
return ais_vdm_t5::handle(&bv, station.unwrap_or(Station::Other),
own_vessel);
},
6 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
7 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
8 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
9 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
10 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
11 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
12 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
13 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
14 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
15 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
16 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
17 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
18 => {
return ais_vdm_t18::handle(&bv, station.unwrap_or(Station::Other),
own_vessel);
},
19 => {
return ais_vdm_t19::handle(&bv, station.unwrap_or(Station::Other),
own_vessel);
},
20 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
21 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
22 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
23 => {
return Err(format!("Unsupported {} message type: {}",
sentence_type, message_type));
},
24 => {
return ais_vdm_t24::handle(&bv, station.unwrap_or(Station::Other),
nmea_store, own_vessel);
},
_ => {
return Err(format!("Unrecognized {} message type: {}",
sentence_type, message_type));
}
}
} else {
Ok(ParsedSentence::Incomplete)
}
},
_ => {
return Err(format!("Unsupported sentence: {}", sentence_type));
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_corrupted() {
assert!(decode_sentence("!AIVDM,1,1,,A,38Id705000rRVJhE7cl9n;160000,0*41",
&mut NmeaStore::new()).ok().is_none());
}
#[test]
fn test_parse_missing_checksum() {
assert!(decode_sentence("!AIVDM,1,1,,A,38Id705000rRVJhE7cl9n;160000,0",
&mut NmeaStore::new()).ok().is_some());
}
}