use super::*;
#[derive(Clone, Debug, PartialEq)]
pub enum ParsedSentence {
Incomplete,
VesselDynamicData(VesselDynamicData),
VesselStaticData(VesselStaticData),
Gga(PositionTimeSatelites),
Rmc(PositionVelocityTime),
Gsa(PositionPrecision),
Gsv(Vec<SatelliteInformation>),
Vtg(VelocityMadeGood),
Gll(Position),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Station {
BaseAisStation,
DependentAisBaseStation,
MobileAisStation,
AidToNavigationAisStation,
AisReceivingStation,
LimitedBaseStation,
AisTransmittingStation,
RepeaterAisStation,
Other,
}
impl Default for Station {
fn default() -> Station {
Station::Other
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum NavigationSystem {
Combination,
Gps,
Glonass,
Galileo,
Beidou,
Navic,
Qzss,
Other,
}
impl std::fmt::Display for NavigationSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NavigationSystem::Combination => { write!(f, "combination") }
NavigationSystem::Gps => { write!(f, "GPS") }
NavigationSystem::Glonass => { write!(f, "GLONASS") }
NavigationSystem::Galileo => { write!(f, "Galileo") }
NavigationSystem::Beidou => { write!(f, "BeiDou") }
NavigationSystem::Navic => { write!(f, "Navic") }
NavigationSystem::Qzss => { write!(f, "QZSS") }
NavigationSystem::Other => { write!(f, "other") }
}
}
}
#[derive(Default, Clone, Debug, PartialEq)]
pub struct VesselDynamicData {
pub own_vessel: bool,
pub station: Station,
pub ais_type: AisClass,
pub mmsi: u32,
pub nav_status: NavigationStatus,
pub rot: Option<f64>,
pub rot_direction: Option<i8>,
pub sog_knots: Option<f64>,
pub high_position_accuracy: bool,
pub latitude: Option<f64>,
pub longitude: Option<f64>,
pub cog: Option<f64>,
pub heading_true: Option<f64>,
pub timestamp_seconds: u8,
pub positioning_system_meta: Option<PositioningSystemMeta>,
pub special_manoeuvre: Option<bool>,
pub raim_flag: bool,
pub class_b_unit_flag: Option<bool>,
pub class_b_display: Option<bool>,
pub class_b_dsc: Option<bool>,
pub class_b_band_flag: Option<bool>,
pub class_b_msg22_flag: Option<bool>,
pub class_b_mode_flag: Option<bool>,
pub class_b_css_flag: Option<bool>,
pub radio_status: u32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AisClass {
Unknown,
ClassA,
ClassB,
}
impl Default for AisClass {
fn default() -> AisClass {
AisClass::Unknown
}
}
impl std::fmt::Display for AisClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AisClass::Unknown => { write!(f, "?") }
AisClass::ClassA => { write!(f, "A") }
AisClass::ClassB => { write!(f, "B") }
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum NavigationStatus {
UnderWayUsingEngine,
AtAnchor,
NotUnderCommand,
RestrictedManoeuverability,
ConstrainedByDraught,
Moored,
Aground,
EngagedInFishing,
UnderWaySailing,
Reserved9,
Reserved10,
Reserved11,
Reserved12,
Reserved13,
AisSartIsActive,
NotDefined,
}
impl NavigationStatus {
pub fn new(nav_status: u8) -> NavigationStatus {
match nav_status {
0 => { NavigationStatus::UnderWayUsingEngine },
1 => { NavigationStatus::AtAnchor },
2 => { NavigationStatus::NotUnderCommand },
3 => { NavigationStatus::RestrictedManoeuverability },
4 => { NavigationStatus::ConstrainedByDraught },
5 => { NavigationStatus::Moored },
6 => { NavigationStatus::Aground },
7 => { NavigationStatus::EngagedInFishing },
8 => { NavigationStatus::UnderWaySailing },
9 => { NavigationStatus::Reserved9 },
10 => { NavigationStatus::Reserved10 },
11 => { NavigationStatus::Reserved11 },
12 => { NavigationStatus::Reserved12 },
13 => { NavigationStatus::Reserved13 },
14 => { NavigationStatus::AisSartIsActive },
15 => { NavigationStatus::NotDefined },
_ => { NavigationStatus::NotDefined }
}
}
}
impl Default for NavigationStatus {
fn default() -> NavigationStatus {
NavigationStatus::NotDefined
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PositioningSystemMeta {
Operative,
ManualInputMode,
DeadReckoningMode,
Inoperative,
}
#[derive(Default, Clone, Debug, PartialEq)]
pub struct VesselStaticData {
pub own_vessel: bool,
pub ais_type: AisClass,
pub mmsi: u32,
pub ais_version_indicator: u8,
pub imo_number: Option<u32>,
pub call_sign: Option<String>,
pub name: Option<String>,
pub ship_type: ShipType,
pub cargo_type: CargoType,
pub equipment_vendor_id: Option<String>,
pub equipment_model: Option<u8>,
pub equipment_serial_number: Option<u32>,
pub dimension_to_bow: Option<u16>,
pub dimension_to_stern: Option<u16>,
pub dimension_to_port: Option<u16>,
pub dimension_to_starboard: Option<u16>,
pub position_fix_type: Option<PositionFixType>,
pub eta: Option<DateTime<Utc>>,
pub draught10: Option<u8>,
pub destination: Option<String>,
pub mothership_mmsi: Option<u32>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ShipType {
NotAvailable,
Reserved1,
WingInGround,
Fishing,
Towing,
TowingLong,
DredgingOrUnderwaterOps,
DivingOps,
MilitaryOps,
Sailing,
PleasureCraft,
Reserved38,
Reserved39,
HighSpeedCraft,
Pilot,
SearchAndRescue,
Tug,
PortTender,
AntiPollutionEquipment,
LawEnforcement,
SpareLocal56,
SpareLocal57,
MedicalTransport,
Noncombatant,
Passenger,
Cargo,
Tanker,
Other,
}
impl ShipType {
pub fn new(raw: u8) -> ShipType {
match raw {
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 => {
ShipType::NotAvailable
},
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 => {
ShipType::Reserved1
},
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 => {
ShipType::WingInGround
},
30 => { ShipType::Fishing }
31 => { ShipType::Towing }
32 => { ShipType::TowingLong }
33 => { ShipType::DredgingOrUnderwaterOps }
34 => { ShipType::DivingOps }
35 => { ShipType::MilitaryOps }
36 => { ShipType::Sailing }
37 => { ShipType::PleasureCraft }
38 => { ShipType::Reserved38 }
39 => { ShipType::Reserved39 }
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 => {
ShipType::HighSpeedCraft
},
50 => { ShipType::Pilot }
51 => { ShipType::SearchAndRescue }
52 => { ShipType::Tug }
53 => { ShipType::PortTender }
54 => { ShipType::AntiPollutionEquipment }
55 => { ShipType::LawEnforcement }
56 => { ShipType::SpareLocal56 }
57 => { ShipType::SpareLocal57 }
58 => { ShipType::MedicalTransport }
59 => { ShipType::Noncombatant }
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 => {
ShipType::Passenger
},
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 => {
ShipType::Cargo
},
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 => {
ShipType::Cargo
},
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 => {
ShipType::Cargo
},
_ => {
warn!("Unexpected ship and cargo type: {}", raw);
ShipType::NotAvailable
}
}
}
}
impl Default for ShipType {
fn default() -> ShipType {
ShipType::NotAvailable
}
}
impl std::fmt::Display for ShipType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ShipType::NotAvailable => { write!(f,"(not available)") },
ShipType::Reserved1 => { write!(f,"(reserved)") },
ShipType::WingInGround => { write!(f,"wing in ground") },
ShipType::Fishing => { write!(f,"fishing") },
ShipType::Towing => { write!(f,"towing") },
ShipType::TowingLong => { write!(f,"towing, long") },
ShipType::DredgingOrUnderwaterOps => { write!(f,"dredging or underwater ops") },
ShipType::DivingOps => { write!(f,"diving ops") },
ShipType::MilitaryOps => { write!(f,"military ops") },
ShipType::Sailing => { write!(f,"sailing") },
ShipType::PleasureCraft => { write!(f,"pleasure craft") },
ShipType::Reserved38 => { write!(f,"(reserved)") },
ShipType::Reserved39 => { write!(f,"(reserved)") },
ShipType::HighSpeedCraft => { write!(f,"high-speed craft") },
ShipType::Pilot => { write!(f,"pilot") },
ShipType::SearchAndRescue => { write!(f,"search and rescue") },
ShipType::Tug => { write!(f,"tug") },
ShipType::PortTender => { write!(f,"port tender") },
ShipType::AntiPollutionEquipment => { write!(f,"anti-pollution equipment") },
ShipType::LawEnforcement => { write!(f,"law enforcement") },
ShipType::SpareLocal56 => { write!(f,"(local)") },
ShipType::SpareLocal57 => { write!(f,"(local)") },
ShipType::MedicalTransport => { write!(f,"medical transport") },
ShipType::Noncombatant => { write!(f,"noncombatant") },
ShipType::Passenger => { write!(f,"passenger") },
ShipType::Cargo => { write!(f,"cargo") },
ShipType::Tanker => { write!(f,"tanker") },
ShipType::Other => { write!(f,"other") },
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CargoType {
Undefined,
HazardousCategoryA,
HazardousCategoryB,
HazardousCategoryC,
HazardousCategoryD,
Reserved5,
Reserved6,
Reserved7,
Reserved8,
Reserved9,
}
impl CargoType {
pub fn new(raw: u8) -> CargoType {
match raw {
10 | 20 | 40 | 60 | 70 | 80 | 90 => {
CargoType::Undefined
},
11 | 21 | 41 | 61 | 71 | 81 | 91 => {
CargoType::HazardousCategoryA
},
12 | 22 | 42 | 62 | 72 | 82 | 92 => {
CargoType::HazardousCategoryB
},
13 | 23 | 43 | 63 | 73 | 83 | 93 => {
CargoType::HazardousCategoryC
},
14 | 24 | 44 | 64 | 74 | 84 | 94 => {
CargoType::HazardousCategoryD
},
15 | 25 | 45 | 65 | 75 | 85 | 95 => {
CargoType::Reserved5
},
16 | 26 | 46 | 66 | 76 | 86 | 96 => {
CargoType::Reserved6
},
17 | 27 | 47 | 67 | 77 | 87 | 97 => {
CargoType::Reserved7
},
18 | 28 | 48 | 68 | 78 | 88 | 98 => {
CargoType::Reserved8
},
19 | 29 | 49 | 69 | 79 | 89 | 99 => {
CargoType::Reserved9
},
_ => {
warn!("Unexpected ship and cargo type: {}", raw);
CargoType::Undefined
}
}
}
}
impl std::fmt::Display for CargoType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CargoType::Undefined => { write!(f,"undefined") },
CargoType::HazardousCategoryA => { write!(f,"hazardous category A") },
CargoType::HazardousCategoryB => { write!(f,"hazardous category B") },
CargoType::HazardousCategoryC => { write!(f,"hazardous category C") },
CargoType::HazardousCategoryD => { write!(f,"hazardous category D") },
CargoType::Reserved5 => { write!(f,"(reserved)") },
CargoType::Reserved6 => { write!(f,"(reserved)") },
CargoType::Reserved7 => { write!(f,"(reserved)") },
CargoType::Reserved8 => { write!(f,"(reserved)") },
CargoType::Reserved9 => { write!(f,"(reserved)") },
}
}
}
impl Default for CargoType {
fn default() -> CargoType {
CargoType::Undefined
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PositionFixType {
Undefined,
GPS,
GLONASS,
GPSGLONASS,
LoranC,
Chayka,
IntegratedNavigationSystem,
Surveyed,
Galileo,
}
impl PositionFixType {
pub fn new(raw: u8) -> PositionFixType {
match raw {
0 => PositionFixType::Undefined,
1 => PositionFixType::GPS,
2 => PositionFixType::GLONASS,
3 => PositionFixType::GPSGLONASS,
4 => PositionFixType::LoranC,
5 => PositionFixType::Chayka,
6 => PositionFixType::IntegratedNavigationSystem,
7 => PositionFixType::Surveyed,
8 => PositionFixType::Galileo,
_ => {
warn!("Unrecognized position fix type: {}", raw);
PositionFixType::Undefined
},
}
}
}
impl VesselStaticData {
pub fn country(&self)-> Option<&'static str> {
match self.mmsi / 1000000 {
201 => Some("AL"),
202 => Some("AD"),
203 => Some("AT"),
204 => Some("PT"),
205 => Some("BE"),
206 => Some("BY"),
207 => Some("BG"),
208 => Some("VA"),
209 => Some("CY"),
210 => Some("CY"),
211 => Some("DE"),
212 => Some("CY"),
213 => Some("GE"),
214 => Some("MD"),
215 => Some("MT"),
216 => Some("AM"),
218 => Some("DE"),
219 => Some("DK"),
220 => Some("DK"),
224 => Some("ES"),
225 => Some("ES"),
226 => Some("FR"),
227 => Some("FR"),
228 => Some("FR"),
229 => Some("MT"),
230 => Some("FI"),
231 => Some("FO"),
232 => Some("GB"),
233 => Some("GB"),
234 => Some("GB"),
235 => Some("GB"),
236 => Some("GI"),
237 => Some("GR"),
238 => Some("HR"),
239 => Some("GR"),
240 => Some("GR"),
241 => Some("GR"),
242 => Some("MA"),
243 => Some("HU"),
244 => Some("NL"),
245 => Some("NL"),
246 => Some("NL"),
247 => Some("IT"),
248 => Some("MT"),
249 => Some("MT"),
250 => Some("IE"),
251 => Some("IS"),
252 => Some("LI"),
253 => Some("LU"),
254 => Some("MC"),
255 => Some("PT"),
256 => Some("MT"),
257 => Some("NO"),
258 => Some("NO"),
259 => Some("NO"),
261 => Some("PL"),
262 => Some("ME"),
263 => Some("PT"),
264 => Some("RO"),
265 => Some("SE"),
266 => Some("SE"),
267 => Some("SK"),
268 => Some("SM"),
269 => Some("CH"),
270 => Some("CZ"),
271 => Some("TR"),
272 => Some("UA"),
273 => Some("RU"),
274 => Some("MK"),
275 => Some("LV"),
276 => Some("EE"),
277 => Some("LT"),
278 => Some("SI"),
279 => Some("RS"),
301 => Some("AI"),
303 => Some("US"),
304 => Some("AG"),
305 => Some("AG"),
306 => Some("BQ"),
307 => Some("AW"),
308 => Some("BS"),
309 => Some("BS"),
310 => Some("BM"),
311 => Some("BS"),
312 => Some("BZ"),
314 => Some("BB"),
316 => Some("CA"),
319 => Some("KY"),
321 => Some("CR"),
323 => Some("CU"),
325 => Some("DM"),
327 => Some("DO"),
329 => Some("GP"),
330 => Some("GD"),
331 => Some("GL"),
332 => Some("GT"),
334 => Some("HN"),
336 => Some("HT"),
338 => Some("US"),
339 => Some("JM"),
341 => Some("KN"),
343 => Some("LC"),
345 => Some("MX"),
347 => Some("MQ"),
348 => Some("MS"),
350 => Some("NI"),
351 => Some("PA"),
352 => Some("PA"),
353 => Some("PA"),
354 => Some("PA"),
355 => Some("PA"),
356 => Some("PA"),
357 => Some("PA"),
358 => Some("PR"),
359 => Some("SV"),
361 => Some("PM"),
362 => Some("TT"),
364 => Some("TC"),
366 => Some("US"),
367 => Some("US"),
368 => Some("US"),
369 => Some("US"),
370 => Some("PA"),
371 => Some("PA"),
372 => Some("PA"),
373 => Some("PA"),
374 => Some("PA"),
375 => Some("VC"),
376 => Some("VC"),
377 => Some("VC"),
378 => Some("VG"),
379 => Some("VI"),
401 => Some("AF"),
403 => Some("SA"),
405 => Some("BD"),
408 => Some("BH"),
410 => Some("BT"),
412 => Some("CN"),
413 => Some("CN"),
414 => Some("CN"),
416 => Some("TW"),
417 => Some("LK"),
419 => Some("IN"),
422 => Some("IR"),
423 => Some("AZ"),
425 => Some("IQ"),
428 => Some("IL"),
431 => Some("JP"),
432 => Some("JP"),
434 => Some("TM"),
436 => Some("KZ"),
437 => Some("UZ"),
438 => Some("JO"),
440 => Some("KR"),
441 => Some("KR"),
443 => Some("PS"),
445 => Some("KR"),
447 => Some("KW"),
450 => Some("LB"),
451 => Some("KG"),
453 => Some("MO"),
455 => Some("MV"),
457 => Some("MN"),
459 => Some("NP"),
461 => Some("OM"),
463 => Some("PK"),
466 => Some("QA"),
468 => Some("SY"),
470 => Some("AE"),
471 => Some("AE"),
472 => Some("TJ"),
473 => Some("YE"),
475 => Some("YE"),
477 => Some("HK"),
478 => Some("BA"),
501 => Some("TF"),
503 => Some("AU"),
506 => Some("MM"),
508 => Some("BN"),
510 => Some("FM"),
511 => Some("PW"),
512 => Some("NZ"),
514 => Some("KH"),
515 => Some("KH"),
516 => Some("CX"),
518 => Some("CK"),
520 => Some("FJ"),
523 => Some("CC"),
525 => Some("ID"),
529 => Some("KI"),
531 => Some("LA"),
533 => Some("MY"),
536 => Some("MP"),
538 => Some("MH"),
540 => Some("NC"),
542 => Some("NU"),
544 => Some("NR"),
546 => Some("PF"),
548 => Some("PH"),
550 => Some("TL"),
553 => Some("PG"),
555 => Some("PN"),
557 => Some("SB"),
559 => Some("AS"),
561 => Some("WS"),
563 => Some("SG"),
564 => Some("SG"),
565 => Some("SG"),
566 => Some("SG"),
567 => Some("TH"),
570 => Some("TO"),
572 => Some("TV"),
574 => Some("VN"),
576 => Some("VU"),
577 => Some("VU"),
578 => Some("WF"),
601 => Some("ZA"),
603 => Some("AO"),
605 => Some("DZ"),
607 => Some("TF"),
608 => Some("SH"),
609 => Some("BI"),
610 => Some("BJ"),
611 => Some("BW"),
612 => Some("CF"),
613 => Some("CM"),
615 => Some("CG"),
616 => Some("KM"),
617 => Some("CV"),
618 => Some("TF"),
619 => Some("CI"),
620 => Some("KM"),
621 => Some("DJ"),
622 => Some("EG"),
624 => Some("ET"),
625 => Some("ER"),
626 => Some("GA"),
627 => Some("GH"),
629 => Some("GM"),
630 => Some("GW"),
631 => Some("GQ"),
632 => Some("GN"),
633 => Some("BF"),
634 => Some("KE"),
635 => Some("TF"),
636 => Some("LR"),
637 => Some("LR"),
638 => Some("SS"),
642 => Some("LY"),
644 => Some("LS"),
645 => Some("MU"),
647 => Some("MG"),
649 => Some("ML"),
650 => Some("MZ"),
654 => Some("MR"),
655 => Some("MW"),
656 => Some("NE"),
657 => Some("NG"),
659 => Some("NA"),
660 => Some("TF"),
661 => Some("RW"),
662 => Some("SD"),
663 => Some("SN"),
664 => Some("SC"),
665 => Some("SH"),
666 => Some("SO"),
667 => Some("SL"),
668 => Some("ST"),
669 => Some("SZ"),
670 => Some("TD"),
671 => Some("TG"),
672 => Some("TN"),
674 => Some("TZ"),
675 => Some("UG"),
676 => Some("CG"),
677 => Some("TZ"),
678 => Some("ZM"),
679 => Some("ZW"),
701 => Some("AR"),
710 => Some("BR"),
720 => Some("BO"),
725 => Some("CL"),
730 => Some("CO"),
735 => Some("EC"),
740 => Some("FK"),
745 => Some("GF"),
750 => Some("GY"),
755 => Some("PY"),
760 => Some("PE"),
765 => Some("SR"),
770 => Some("UY"),
775 => Some("VE"),
_ => { None }
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PositionTimeSatelites {
pub system: NavigationSystem,
pub timestamp: Option<DateTime<Utc>>,
pub latitude: Option<f64>,
pub longitude: Option<f64>,
pub quality: GpsQualityIndicator,
pub satellite_count: Option<u8>,
pub hdop: Option<f64>,
pub altitude: Option<f64>,
pub geoid_separation: Option<f64>,
pub age_of_dgps: Option<f64>,
pub ref_station_id: Option<u16>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum GpsQualityIndicator {
Invalid,
GpsFix,
DGpsFix,
PpsFix,
RealTimeKinematic,
RealTimeKinematicFloat,
DeadReckoning,
ManualInputMode,
SimulationMode,
}
impl GpsQualityIndicator {
pub fn new(a: u8) -> GpsQualityIndicator {
match a {
0 => GpsQualityIndicator::Invalid,
1 => GpsQualityIndicator::GpsFix,
2 => GpsQualityIndicator::DGpsFix,
3 => GpsQualityIndicator::PpsFix,
4 => GpsQualityIndicator::RealTimeKinematic,
5 => GpsQualityIndicator::RealTimeKinematicFloat,
6 => GpsQualityIndicator::DeadReckoning,
7 => GpsQualityIndicator::ManualInputMode,
8 => GpsQualityIndicator::SimulationMode,
_ => GpsQualityIndicator::Invalid,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PositionVelocityTime {
pub system: NavigationSystem,
pub timestamp: Option<DateTime<Utc>>,
pub status_active: Option<bool>,
pub latitude: Option<f64>,
pub longitude: Option<f64>,
pub speed_knots: Option<f64>,
pub bearing: Option<f64>,
pub variation: Option<f64>
}
#[derive(Clone, Debug, PartialEq)]
pub struct PositionPrecision {
pub system: NavigationSystem,
pub mode1_automatic: Option<bool>,
pub mode2_3d: Option<FixMode>,
pub prn_numbers: Vec<u8>,
pub pdop: Option<f64>,
pub hdop: Option<f64>,
pub vdop: Option<f64>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FixMode {
NotAvailable,
Fix2D,
Fix3D,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SatelliteInformation {
pub system: NavigationSystem,
pub prn_number: u8,
pub elevation: Option<u8>,
pub azimuth: Option<u16>,
pub snr: Option<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct VelocityMadeGood {
pub system: NavigationSystem,
pub cog_true: Option<f64>,
pub cog_magnetic: Option<f64>,
pub sog_knots: Option<f64>,
pub sog_kph: Option<f64>,
pub faa_mode: Option<FaaMode>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FaaMode {
Autonomous,
Differential,
Estimated,
NotValid,
Simulator,
}
impl FaaMode {
pub fn new(val: &str) -> Result<FaaMode, String> {
match val {
"A" => Ok(FaaMode::Autonomous),
"D" => Ok(FaaMode::Differential),
"E" => Ok(FaaMode::Estimated),
"N" => Ok(FaaMode::NotValid),
_ => { Err(format!("Unrecognized FAA information value: {}", val)) }
}
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FaaMode::Autonomous => { write!(f, "A") }
FaaMode::Differential => { write!(f, "D") }
FaaMode::Estimated => { write!(f, "E") }
FaaMode::NotValid => { write!(f, "N") }
_ => { write!(f, "?") }
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Position {
pub system: NavigationSystem,
pub latitude: Option<f64>,
pub longitude: Option<f64>,
pub timestamp: Option<DateTime<Utc>>,
pub data_valid: Option<bool>,
pub faa_mode: Option<FaaMode>,
}
pub struct NmeaStore {
saved_fragments: HashMap<String, String>,
saved_vsds: HashMap<u32, VesselStaticData>,
}
impl NmeaStore {
pub fn new() -> NmeaStore {
NmeaStore {
saved_fragments: HashMap::new(),
saved_vsds: HashMap::new(),
}
}
pub fn push_string(&mut self, key: String, value: String) {
self.saved_fragments.insert(key, value);
}
pub fn pull_string(&mut self, key: String) -> Option<String> {
self.saved_fragments.remove(&key)
}
pub fn contains_key(&mut self, key: String) -> bool {
self.saved_fragments.contains_key(&key)
}
pub fn strings_count(&self) -> usize {
self.saved_fragments.len()
}
pub fn push_vsd(&mut self, mmsi: u32, vsd: VesselStaticData) {
self.saved_vsds.insert(mmsi, vsd);
}
pub fn pull_vsd(&mut self, mmsi: u32) -> Option<VesselStaticData> {
self.saved_vsds.remove(&mmsi)
}
pub fn vsds_count(&self) -> usize {
self.saved_vsds.len()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_nmea_store() {
let mut store = NmeaStore::new();
store.push_string("a".into(), "b".into());
assert_eq!(store.strings_count(), 1);
store.push_string("c".into(), "d".into());
assert_eq!(store.strings_count(), 2);
store.pull_string("a".into());
assert_eq!(store.strings_count(), 1);
store.pull_string("c".into());
assert_eq!(store.strings_count(), 0);
store.push_vsd(1, Default::default());
assert_eq!(store.vsds_count(), 1);
store.push_vsd(2, Default::default());
assert_eq!(store.vsds_count(), 2);
store.pull_vsd(1);
assert_eq!(store.vsds_count(), 1);
store.pull_vsd(2);
assert_eq!(store.vsds_count(), 0);
}
#[test]
fn test_mmsi_to_country_code_conversion() {
let mut vsd = VesselStaticData::default();
vsd.mmsi = 230992580; assert_eq!(vsd.country().unwrap(), "FI");
vsd.mmsi = 276009860; assert_eq!(vsd.country().unwrap(), "EE");
vsd.mmsi = 265803690; assert_eq!(vsd.country().unwrap(), "SE");
vsd.mmsi = 273353180; assert_eq!(vsd.country().unwrap(), "RU");
vsd.mmsi = 211805060; assert_eq!(vsd.country().unwrap(), "DE");
vsd.mmsi = 257037270; assert_eq!(vsd.country().unwrap(), "NO");
vsd.mmsi = 227232370; assert_eq!(vsd.country().unwrap(), "FR");
vsd.mmsi = 248221000; assert_eq!(vsd.country().unwrap(), "MT");
vsd.mmsi = 374190000; assert_eq!(vsd.country().unwrap(), "PA");
vsd.mmsi = 412511368; assert_eq!(vsd.country().unwrap(), "CN");
vsd.mmsi = 512003200; assert_eq!(vsd.country().unwrap(), "NZ");
vsd.mmsi = 995126020; assert_eq!(vsd.country(), None);
vsd.mmsi = 2300049; assert_eq!(vsd.country(), None);
}
}