nmea_parser/ais/
mod.rs

1/*
2Copyright 2020-2021 Timo Saarinen
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17//! AIS VDM/VDO data structures
18
19pub(crate) mod vdm_t1t2t3;
20pub(crate) mod vdm_t4;
21pub(crate) mod vdm_t5;
22pub(crate) mod vdm_t6;
23pub(crate) mod vdm_t9;
24pub(crate) mod vdm_t10;
25pub(crate) mod vdm_t11;
26pub(crate) mod vdm_t12;
27pub(crate) mod vdm_t13;
28pub(crate) mod vdm_t14;
29pub(crate) mod vdm_t15;
30pub(crate) mod vdm_t16;
31pub(crate) mod vdm_t17;
32pub(crate) mod vdm_t18;
33pub(crate) mod vdm_t19;
34pub(crate) mod vdm_t20;
35pub(crate) mod vdm_t21;
36pub(crate) mod vdm_t22;
37pub(crate) mod vdm_t23;
38pub(crate) mod vdm_t24;
39pub(crate) mod vdm_t25;
40pub(crate) mod vdm_t26;
41pub(crate) mod vdm_t27;
42
43use super::*;
44pub use vdm_t4::BaseStationReport;
45pub use vdm_t6::BinaryAddressedMessage;
46pub use vdm_t9::StandardSarAircraftPositionReport;
47pub use vdm_t10::UtcDateInquiry;
48pub use vdm_t12::AddressedSafetyRelatedMessage;
49pub use vdm_t13::SafetyRelatedAcknowledgement;
50pub use vdm_t14::SafetyRelatedBroadcastMessage;
51pub use vdm_t15::{Interrogation, InterrogationCase};
52pub use vdm_t16::AssignmentModeCommand;
53pub use vdm_t17::DgnssBroadcastBinaryMessage;
54pub use vdm_t20::{DataLinkManagementMessage};
55pub use vdm_t21::{AidToNavigationReport, NavAidType};
56pub use vdm_t22::{ChannelManagement};
57pub use vdm_t23::{GroupAssignmentCommand};
58pub use vdm_t25::{SingleSlotBinaryMessage};
59pub use vdm_t26::{MultipleSlotBinaryMessage};
60
61// -------------------------------------------------------------------------------------------------
62
63/// AIS station based on talker id
64#[derive(Clone, Copy, Debug, PartialEq)]
65pub enum Station {
66    BaseStation,             // !AB
67    DependentAisBaseStation, // !AD
68    MobileStation,           // !AI (the most common one)
69    AidToNavigationStation,  // !AN
70    AisReceivingStation,     // !AR
71    LimitedBaseStation,      // !AS
72    AisTransmittingStation,  // !AT
73    RepeaterStation,         // !AX
74    Other,                   // !BS, !SA, etc.
75}
76
77impl Default for Station {
78    fn default() -> Station {
79        Station::Other
80    }
81}
82
83impl core::fmt::Display for Station {
84    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85        match self {
86            Station::BaseStation => write!(f, "base station"),
87            Station::DependentAisBaseStation => write!(f, "dependent AIS base station"),
88            Station::MobileStation => write!(f, "mobile station"),
89            Station::AidToNavigationStation => write!(f, "aid to navigation station"),
90            Station::AisReceivingStation => write!(f, "ais receiving station"),
91            Station::LimitedBaseStation => write!(f, "limited base station"),
92            Station::AisTransmittingStation => write!(f, "AIS transmitting station"),
93            Station::RepeaterStation => write!(f, "repeater station"),
94            Station::Other => write!(f, "other"),
95        }
96    }
97}
98
99impl core::str::FromStr for Station {
100    type Err = ParseError;
101
102    fn from_str(talker_id: &str) -> Result<Self, Self::Err> {
103        if talker_id.len() < 2 {
104            return Err(ParseError::InvalidSentence(
105                "Invalid station identifier".to_string(),
106            ));
107        }
108        match &talker_id[0..2] {
109            "AB" => Ok(Self::BaseStation),
110            "AD" => Ok(Self::DependentAisBaseStation),
111            "AI" => Ok(Self::MobileStation),
112            "AN" => Ok(Self::AidToNavigationStation),
113            "AR" => Ok(Self::AisReceivingStation),
114            "AS" => Ok(Self::LimitedBaseStation),
115            "AT" => Ok(Self::AisTransmittingStation),
116            "AX" => Ok(Self::RepeaterStation),
117            _ => Ok(Self::Other),
118        }
119    }
120}
121
122// -------------------------------------------------------------------------------------------------
123
124/// Types 1, 2, 3 and 18: Position Report Class A, and Long Range AIS Broadcast message
125#[derive(Default, Clone, Debug, PartialEq)]
126pub struct VesselDynamicData {
127    /// True if the data is about own vessel, false if about other.
128    pub own_vessel: bool,
129
130    /// AIS station type.
131    pub station: Station,
132
133    /// Class A or Class B
134    pub ais_type: AisClass,
135
136    /// User ID (30 bits)
137    pub mmsi: u32,
138
139    // TODO: timestamp
140    /// Navigation status
141    pub nav_status: NavigationStatus,
142
143    /// Accurate ROT_sensor (±0..708°/min) if available.
144    pub rot: Option<f64>,
145
146    /// ROT direction when turn is more than 5°/30s.
147    pub rot_direction: Option<RotDirection>,
148
149    /// Speed over ground in knots
150    pub sog_knots: Option<f64>,
151
152    /// Position accuracy: true = high (<= 10 m), false = low (> 10 m)
153    pub high_position_accuracy: bool,
154
155    /// Latitude
156    pub latitude: Option<f64>,
157
158    /// Longitude
159    pub longitude: Option<f64>,
160
161    /// Course over ground
162    pub cog: Option<f64>,
163
164    /// True heading (0-359)
165    pub heading_true: Option<f64>,
166
167    /// Derived from UTC second (6 bits)
168    pub timestamp_seconds: u8,
169
170    /// Positioning system metadata (included in seconds in UTC timestamp)
171    pub positioning_system_meta: Option<PositioningSystemMeta>,
172
173    /// GNSS position status (Type 27):
174    ///  true = current GNSS position
175    ///  false = not GNSS position
176    pub current_gnss_position: Option<bool>,
177
178    /// Special manoeuvre indicator. false = not engaged in special manoeuvre,
179    /// true = engaged in special manouvre.
180    pub special_manoeuvre: Option<bool>,
181
182    /// Riverine And Inland Navigation systems blue sign:
183    /// RAIM (Receiver autonomous integrity monitoring) flag of electronic position
184    /// fixing device; false = RAIM not in use = default; true = RAIM in use
185    pub raim_flag: bool,
186
187    /// Class B unit flag: false = Class B SOTDMA unit, true = Class B "CS" unit.
188    pub class_b_unit_flag: Option<bool>,
189
190    /// Class B display:
191    /// false = No display available; not capable of displaying Message 12 and 14
192    /// true  = Equipped with integrated display displaying Message 12 and 14
193    pub class_b_display: Option<bool>,
194
195    /// Class B DSC:
196    /// false = Not equipped with DSC function
197    /// true  = Equipped with DSC function (dedicated or time-shared
198    pub class_b_dsc: Option<bool>,
199
200    /// Class B band flag:
201    /// false = Capable of operating over the upper 525 kHz band of the marine band
202    /// true  = Capable of operating over the whole marine band (irrelevant if
203    ///         “Class B Message 22 flag” is 0)
204    pub class_b_band_flag: Option<bool>,
205
206    /// Class B Message 22 flag:
207    /// false = No frequency management via Message 22 , operating on AIS1, AIS2 only
208    /// true  = Frequency management via Message 22
209    pub class_b_msg22_flag: Option<bool>,
210
211    /// Mode flag:
212    /// false = Station operating in autonomous and continuous mode = default
213    /// true  = Station operating in assigned mode
214    pub class_b_mode_flag: Option<bool>,
215
216    /// Communication state selector flag
217    /// false = SOTDMA communication state follows
218    /// true  = ITDMA communication state follows (always “1” for Class-B “CS”)
219    pub class_b_css_flag: Option<bool>,
220
221    /// Communication state
222    /// Diagnostic information for the radio system.
223    /// <https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1371-1-200108-S!!PDF-E.pdf>
224    pub radio_status: Option<u32>,
225}
226
227/// AIS class which is either Class A or Class B
228#[derive(Clone, Copy, Debug, PartialEq)]
229pub enum AisClass {
230    /// AIS class not known.
231    Unknown,
232
233    /// AIS class A.
234    ClassA, // Message types 1, 2, 3, 5
235
236    /// AIS class B.
237    ClassB, // Message types 14, 18, 19, 24
238}
239
240impl Default for AisClass {
241    fn default() -> AisClass {
242        AisClass::Unknown
243    }
244}
245
246impl core::fmt::Display for AisClass {
247    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
248        match self {
249            AisClass::Unknown => write!(f, "unknown"),
250            AisClass::ClassA => write!(f, "Class A"),
251            AisClass::ClassB => write!(f, "Class B"),
252        }
253    }
254}
255
256impl LatLon for VesselDynamicData {
257    fn latitude(&self) -> Option<f64> {
258        self.latitude
259    }
260
261    fn longitude(&self) -> Option<f64> {
262        self.longitude
263    }
264}
265
266/// Navigation status for VesselDynamicData
267#[derive(Clone, Copy, Debug, PartialEq)]
268pub enum NavigationStatus {
269    UnderWayUsingEngine = 0,        // 0
270    AtAnchor = 1,                   // 1
271    NotUnderCommand = 2,            // 2
272    RestrictedManoeuverability = 3, // 3
273    ConstrainedByDraught = 4,       // 4
274    Moored = 5,                     // 5
275    Aground = 6,                    // 6
276    EngagedInFishing = 7,           // 7
277    UnderWaySailing = 8,            // 8
278    Reserved9 = 9,                  // 9, may be renamed in the future
279    Reserved10 = 10,                // 10, may be renamed in the future
280    Reserved11 = 11,                // 11, may be renamed in the future
281    Reserved12 = 12,                // 12, may be renamed in the future
282    Reserved13 = 13,                // 13, may be renamed in the future
283    AisSartIsActive = 14,           // 14
284    NotDefined = 15,                // 15
285}
286impl NavigationStatus {
287    pub fn new(nav_status: u8) -> NavigationStatus {
288        match nav_status {
289            0 => NavigationStatus::UnderWayUsingEngine,
290            1 => NavigationStatus::AtAnchor,
291            2 => NavigationStatus::NotUnderCommand,
292            3 => NavigationStatus::RestrictedManoeuverability,
293            4 => NavigationStatus::ConstrainedByDraught,
294            5 => NavigationStatus::Moored,
295            6 => NavigationStatus::Aground,
296            7 => NavigationStatus::EngagedInFishing,
297            8 => NavigationStatus::UnderWaySailing,
298            9 => NavigationStatus::Reserved9,
299            10 => NavigationStatus::Reserved10,
300            11 => NavigationStatus::Reserved11,
301            12 => NavigationStatus::Reserved12,
302            13 => NavigationStatus::Reserved13,
303            14 => NavigationStatus::AisSartIsActive,
304            15 => NavigationStatus::NotDefined,
305            _ => NavigationStatus::NotDefined,
306        }
307    }
308
309    pub fn to_value(&self) -> u8 {
310        *self as u8
311    }
312}
313
314impl core::fmt::Display for NavigationStatus {
315    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
316        match self {
317            NavigationStatus::UnderWayUsingEngine => write!(f, "under way using engine"),
318            NavigationStatus::AtAnchor => write!(f, "at anchor"),
319            NavigationStatus::NotUnderCommand => write!(f, "not under command"),
320            NavigationStatus::RestrictedManoeuverability => {
321                write!(f, "restricted manoeuverability")
322            }
323            NavigationStatus::ConstrainedByDraught => write!(f, "constrained by draught"),
324            NavigationStatus::Moored => write!(f, "moored"),
325            NavigationStatus::Aground => write!(f, "aground"),
326            NavigationStatus::EngagedInFishing => write!(f, "engaged in fishing"),
327            NavigationStatus::UnderWaySailing => write!(f, "under way sailing"),
328            NavigationStatus::Reserved9 => write!(f, "(reserved9)"),
329            NavigationStatus::Reserved10 => write!(f, "(reserved10)"),
330            NavigationStatus::Reserved11 => write!(f, "(reserved11)"),
331            NavigationStatus::Reserved12 => write!(f, "(reserved12)"),
332            NavigationStatus::Reserved13 => write!(f, "(reserved13)"),
333            NavigationStatus::AisSartIsActive => write!(f, "ais sart is active"),
334            NavigationStatus::NotDefined => write!(f, "(notDefined)"),
335        }
336    }
337}
338
339impl Default for NavigationStatus {
340    fn default() -> NavigationStatus {
341        NavigationStatus::NotDefined
342    }
343}
344
345// -------------------------------------------------------------------------------------------------
346
347/// Location metadata about positioning system
348#[derive(Clone, Copy, Debug, PartialEq)]
349pub enum PositioningSystemMeta {
350    Operative, // When timestamp second is 0-59
351    ManualInputMode,
352    DeadReckoningMode,
353    Inoperative,
354}
355
356impl core::fmt::Display for PositioningSystemMeta {
357    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
358        match self {
359            PositioningSystemMeta::Operative => write!(f, "operative"),
360            PositioningSystemMeta::ManualInputMode => write!(f, "manual input mode"),
361            PositioningSystemMeta::DeadReckoningMode => write!(f, "dead reckoning mode"),
362            PositioningSystemMeta::Inoperative => write!(f, "inoperative"),
363        }
364    }
365}
366
367// -------------------------------------------------------------------------------------------------
368
369/// Vessel rotation direction
370#[derive(Clone, Copy, Debug, PartialEq)]
371pub enum RotDirection {
372    /// Turning port (left, when seen by an observer aboard the vessel looking forward)
373    Port,
374
375    /// Not turning
376    Center,
377
378    /// Turning starboard (right, when seen by an observer aboard the vessel looking forward)
379    Starboard,
380}
381
382impl Default for RotDirection {
383    fn default() -> RotDirection {
384        RotDirection::Center
385    }
386}
387
388impl core::fmt::Display for RotDirection {
389    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
390        match self {
391            RotDirection::Port => write!(f, "port"),
392            RotDirection::Center => write!(f, "center"),
393            RotDirection::Starboard => write!(f, "starboard"),
394        }
395    }
396}
397
398// -------------------------------------------------------------------------------------------------
399
400/// Types 5 and 24: Ship static voyage related data, and boat static data report.
401#[derive(Default, Clone, Debug, PartialEq)]
402pub struct VesselStaticData {
403    /// True if the data is about own vessel, false if about other vessel.
404    pub own_vessel: bool,
405
406    /// Class A or Class B
407    pub ais_type: AisClass,
408
409    /// User ID (30 bits)
410    pub mmsi: u32,
411
412    /// AIS version indicator (2 bits)
413    pub ais_version_indicator: u8,
414
415    /// IMO number (1-999999999; 30 bits).
416    pub imo_number: Option<u32>,
417
418    /// Call sign (7 ASCII characters)
419    pub call_sign: Option<String>,
420
421    /// Name (20 ASCII characters)
422    pub name: Option<String>,
423
424    /// Type of ship (first 4 of 8 bits)
425    pub ship_type: ShipType,
426
427    /// Type of ship and cargo (last 4 of 8 bits)
428    pub cargo_type: CargoType,
429
430    /// Class B Vendor ID
431    pub equipment_vendor_id: Option<String>,
432
433    /// Class B unite model code
434    pub equipment_model: Option<u8>,
435
436    /// Class B serial number
437    pub equipment_serial_number: Option<u32>,
438
439    /// Overall dimension / reference for position A (9 bits)
440    pub dimension_to_bow: Option<u16>,
441    /// Overall dimension / reference for position B (9 bits)
442    pub dimension_to_stern: Option<u16>,
443    /// Overall dimension / reference for position C (6 bits)
444    pub dimension_to_port: Option<u16>,
445    /// Overall dimension / reference for position C (6 bits)
446    pub dimension_to_starboard: Option<u16>,
447
448    // Type of electronic position fixing device.
449    pub position_fix_type: Option<PositionFixType>,
450
451    /// ETA (20 bits)
452    pub eta: Option<DateTime<Utc>>,
453
454    /// Maximum present static draught in decimetres (1-255; 8 bits)
455    pub draught10: Option<u8>,
456
457    /// Destination (120 ASCII characters)
458    pub destination: Option<String>,
459
460    /// Class B mothership MMSI
461    pub mothership_mmsi: Option<u32>,
462}
463
464// -------------------------------------------------------------------------------------------------
465
466/// Ship type derived from combined ship and cargo type field
467#[derive(Clone, Copy, Debug, PartialEq)]
468pub enum ShipType {
469    NotAvailable = 0,             // 0
470    Reserved1 = 10,               // 1x
471    WingInGround = 20,            // 2x
472    Fishing = 30,                 // 30
473    Towing = 31,                  // 31
474    TowingLong = 32,              // 32; Towing: length exceeds 200m or breadth exceeds 25m
475    DredgingOrUnderwaterOps = 33, // 33
476    DivingOps = 34,               // 34
477    MilitaryOps = 35,             // 35
478    Sailing = 36,                 // 36
479    PleasureCraft = 37,           // 37
480    Reserved38 = 38,              // 38
481    Reserved39 = 39,              // 39
482    HighSpeedCraft = 40,          // 4x
483    Pilot = 50,                   // 50
484    SearchAndRescue = 51,         // 51
485    Tug = 52,                     // 52
486    PortTender = 53,              // 53
487    AntiPollutionEquipment = 54,  // 54
488    LawEnforcement = 55,          // 55
489    SpareLocal56 = 56,            // 56
490    SpareLocal57 = 57,            // 57
491    MedicalTransport = 58,        // 58
492    Noncombatant = 59,            // 59; Noncombatant ship according to RR Resolution No. 18
493    Passenger = 60,               // 6x
494    Cargo = 70,                   // 7x
495    Tanker = 80,                  // 8x
496    Other = 90,                   // 9x
497}
498
499impl ShipType {
500    /// Construct a new `ShipType` using the higher bits of the ship and cargo type field of NMEA.
501    pub fn new(raw: u8) -> ShipType {
502        match raw {
503            0..=9 => ShipType::NotAvailable,
504            10..=19 => ShipType::Reserved1,
505            20..=29 => ShipType::WingInGround,
506
507            30 => ShipType::Fishing,
508            31 => ShipType::Towing,
509            32 => ShipType::TowingLong,
510            33 => ShipType::DredgingOrUnderwaterOps,
511            34 => ShipType::DivingOps,
512            35 => ShipType::MilitaryOps,
513            36 => ShipType::Sailing,
514            37 => ShipType::PleasureCraft,
515            38 => ShipType::Reserved38,
516            39 => ShipType::Reserved39,
517
518            40..=49 => ShipType::HighSpeedCraft,
519
520            50 => ShipType::Pilot,
521            51 => ShipType::SearchAndRescue,
522            52 => ShipType::Tug,
523            53 => ShipType::PortTender,
524            54 => ShipType::AntiPollutionEquipment,
525            55 => ShipType::LawEnforcement,
526            56 => ShipType::SpareLocal56,
527            57 => ShipType::SpareLocal57,
528            58 => ShipType::MedicalTransport,
529            59 => ShipType::Noncombatant,
530
531            60..=69 => ShipType::Passenger,
532            70..=79 => ShipType::Cargo,
533            80..=89 => ShipType::Tanker,
534            90..=99 => ShipType::Other,
535            _ => {
536                warn!("Unexpected ship and cargo type: {}", raw);
537                ShipType::NotAvailable
538            }
539        }
540    }
541
542    pub fn to_value(&self) -> u8 {
543        *self as u8
544    }
545}
546
547impl Default for ShipType {
548    fn default() -> ShipType {
549        ShipType::NotAvailable
550    }
551}
552
553impl core::fmt::Display for ShipType {
554    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
555        match self {
556            ShipType::NotAvailable => write!(f, "(not available)"),
557            ShipType::Reserved1 => write!(f, "(reserved)"),
558            ShipType::WingInGround => write!(f, "wing in ground"),
559            ShipType::Fishing => write!(f, "fishing"),
560            ShipType::Towing => write!(f, "towing"),
561            ShipType::TowingLong => write!(f, "towing, long"),
562            ShipType::DredgingOrUnderwaterOps => write!(f, "dredging or underwater ops"),
563            ShipType::DivingOps => write!(f, "diving ops"),
564            ShipType::MilitaryOps => write!(f, "military ops"),
565            ShipType::Sailing => write!(f, "sailing"),
566            ShipType::PleasureCraft => write!(f, "pleasure craft"),
567            ShipType::Reserved38 => write!(f, "(reserved)"),
568            ShipType::Reserved39 => write!(f, "(reserved)"),
569            ShipType::HighSpeedCraft => write!(f, "high-speed craft"),
570            ShipType::Pilot => write!(f, "pilot"),
571            ShipType::SearchAndRescue => write!(f, "search and rescue"),
572            ShipType::Tug => write!(f, "tug"),
573            ShipType::PortTender => write!(f, "port tender"),
574            ShipType::AntiPollutionEquipment => write!(f, "anti-pollution equipment"),
575            ShipType::LawEnforcement => write!(f, "law enforcement"),
576            ShipType::SpareLocal56 => write!(f, "(local)"),
577            ShipType::SpareLocal57 => write!(f, "(local)"),
578            ShipType::MedicalTransport => write!(f, "medical transport"),
579            ShipType::Noncombatant => write!(f, "noncombatant"),
580            ShipType::Passenger => write!(f, "passenger"),
581            ShipType::Cargo => write!(f, "cargo"),
582            ShipType::Tanker => write!(f, "tanker"),
583            ShipType::Other => write!(f, "other"),
584        }
585    }
586}
587
588// -------------------------------------------------------------------------------------------------
589
590/// Cargo type derived from combined ship and cargo type field
591#[derive(Clone, Copy, Debug, PartialEq)]
592pub enum CargoType {
593    Undefined = 10,          // x0
594    HazardousCategoryA = 11, // x1
595    HazardousCategoryB = 12, // x2
596    HazardousCategoryC = 13, // x3
597    HazardousCategoryD = 14, // x4
598    Reserved5 = 15,          // x5
599    Reserved6 = 16,          // x6
600    Reserved7 = 17,          // x7
601    Reserved8 = 18,          // x8
602    Reserved9 = 19,          // x9
603}
604
605impl CargoType {
606    /// Construct a new `CargoType` using the higher bits of the ship and cargo type field of NMEA.
607    pub fn new(raw: u8) -> CargoType {
608        match raw {
609            10 | 20 | 40 | 60 | 70 | 80 | 90 => CargoType::Undefined,
610            11 | 21 | 41 | 61 | 71 | 81 | 91 => CargoType::HazardousCategoryA,
611            12 | 22 | 42 | 62 | 72 | 82 | 92 => CargoType::HazardousCategoryB,
612            13 | 23 | 43 | 63 | 73 | 83 | 93 => CargoType::HazardousCategoryC,
613            14 | 24 | 44 | 64 | 74 | 84 | 94 => CargoType::HazardousCategoryD,
614            15 | 25 | 45 | 65 | 75 | 85 | 95 => CargoType::Reserved5,
615            16 | 26 | 46 | 66 | 76 | 86 | 96 => CargoType::Reserved6,
616            17 | 27 | 47 | 67 | 77 | 87 | 97 => CargoType::Reserved7,
617            18 | 28 | 48 | 68 | 78 | 88 | 98 => CargoType::Reserved8,
618            19 | 29 | 49 | 69 | 79 | 89 | 99 => CargoType::Reserved9,
619            _ => {
620                warn!("Unexpected ship and cargo type: {}", raw);
621                CargoType::Undefined
622            }
623        }
624    }
625
626    pub fn to_value(&self) -> u8 {
627        *self as u8
628    }
629}
630
631impl core::fmt::Display for CargoType {
632    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
633        match self {
634            CargoType::Undefined => write!(f, "undefined"),
635            CargoType::HazardousCategoryA => write!(f, "hazardous category A"),
636            CargoType::HazardousCategoryB => write!(f, "hazardous category B"),
637            CargoType::HazardousCategoryC => write!(f, "hazardous category C"),
638            CargoType::HazardousCategoryD => write!(f, "hazardous category D"),
639            CargoType::Reserved5 => write!(f, "(reserved)"),
640            CargoType::Reserved6 => write!(f, "(reserved)"),
641            CargoType::Reserved7 => write!(f, "(reserved)"),
642            CargoType::Reserved8 => write!(f, "(reserved)"),
643            CargoType::Reserved9 => write!(f, "(reserved)"),
644        }
645    }
646}
647
648impl Default for CargoType {
649    fn default() -> CargoType {
650        CargoType::Undefined
651    }
652}
653
654// -------------------------------------------------------------------------------------------------
655
656/// EPFD position fix types
657#[derive(Clone, Copy, Debug, PartialEq)]
658pub enum PositionFixType {
659    Undefined = 0,                  // 0
660    GPS = 1,                        // 1
661    GLONASS = 2,                    // 2
662    GPSGLONASS = 3,                 // 3
663    LoranC = 4,                     // 4
664    Chayka = 5,                     // 5
665    IntegratedNavigationSystem = 6, // 6
666    Surveyed = 7,                   // 7
667    Galileo = 8,                    // 8
668}
669
670impl PositionFixType {
671    pub fn new(raw: u8) -> PositionFixType {
672        match raw {
673            0 => PositionFixType::Undefined,
674            1 => PositionFixType::GPS,
675            2 => PositionFixType::GLONASS,
676            3 => PositionFixType::GPSGLONASS,
677            4 => PositionFixType::LoranC,
678            5 => PositionFixType::Chayka,
679            6 => PositionFixType::IntegratedNavigationSystem,
680            7 => PositionFixType::Surveyed,
681            8 => PositionFixType::Galileo,
682            _ => {
683                warn!("Unrecognized position fix type: {}", raw);
684                PositionFixType::Undefined
685            }
686        }
687    }
688
689    pub fn to_value(&self) -> u8 {
690        *self as u8
691    }
692}
693
694impl core::fmt::Display for PositionFixType {
695    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
696        match self {
697            PositionFixType::Undefined => write!(f, "undefined"),
698            PositionFixType::GPS => write!(f, "GPS"),
699            PositionFixType::GLONASS => write!(f, "GLONASS"),
700            PositionFixType::GPSGLONASS => write!(f, "GPS/GLONASS"),
701            PositionFixType::LoranC => write!(f, "Loran-C"),
702            PositionFixType::Chayka => write!(f, "Chayka"),
703            PositionFixType::IntegratedNavigationSystem => {
704                write!(f, "integrated navigation system")
705            }
706            PositionFixType::Surveyed => write!(f, "surveyed"),
707            PositionFixType::Galileo => write!(f, "Galileo"),
708        }
709    }
710}
711
712impl VesselStaticData {
713    /// Decode ISO 3166 country code from MID part of MMSI.
714    pub fn country(&self) -> Option<&'static str> {
715        match self.mmsi / 1000000 {
716            // Mapping generated with mid-to-iso3166.py
717            201 => Some("AL"), // Albania
718            202 => Some("AD"), // Andorra
719            203 => Some("AT"), // Austria
720            204 => Some("PT"), // Portugal
721            205 => Some("BE"), // Belgium
722            206 => Some("BY"), // Belarus
723            207 => Some("BG"), // Bulgaria
724            208 => Some("VA"), // Vatican City State
725            209 => Some("CY"), // Cyprus
726            210 => Some("CY"), // Cyprus
727            211 => Some("DE"), // Germany
728            212 => Some("CY"), // Cyprus
729            213 => Some("GE"), // Georgia
730            214 => Some("MD"), // Moldova
731            215 => Some("MT"), // Malta
732            216 => Some("AM"), // Armenia
733            218 => Some("DE"), // Germany
734            219 => Some("DK"), // Denmark
735            220 => Some("DK"), // Denmark
736            224 => Some("ES"), // Spain
737            225 => Some("ES"), // Spain
738            226 => Some("FR"), // France
739            227 => Some("FR"), // France
740            228 => Some("FR"), // France
741            229 => Some("MT"), // Malta
742            230 => Some("FI"), // Finland
743            231 => Some("FO"), // Faroe Islands
744            232 => Some("GB"), // United Kingdom of Great Britain and Northern Ireland
745            233 => Some("GB"), // United Kingdom of Great Britain and Northern Ireland
746            234 => Some("GB"), // United Kingdom of Great Britain and Northern Ireland
747            235 => Some("GB"), // United Kingdom of Great Britain and Northern Ireland
748            236 => Some("GI"), // Gibraltar
749            237 => Some("GR"), // Greece
750            238 => Some("HR"), // Croatia
751            239 => Some("GR"), // Greece
752            240 => Some("GR"), // Greece
753            241 => Some("GR"), // Greece
754            242 => Some("MA"), // Morocco
755            243 => Some("HU"), // Hungary
756            244 => Some("NL"), // Netherlands
757            245 => Some("NL"), // Netherlands
758            246 => Some("NL"), // Netherlands
759            247 => Some("IT"), // Italy
760            248 => Some("MT"), // Malta
761            249 => Some("MT"), // Malta
762            250 => Some("IE"), // Ireland
763            251 => Some("IS"), // Iceland
764            252 => Some("LI"), // Liechtenstein
765            253 => Some("LU"), // Luxembourg
766            254 => Some("MC"), // Monaco
767            255 => Some("PT"), // Portugal
768            256 => Some("MT"), // Malta
769            257 => Some("NO"), // Norway
770            258 => Some("NO"), // Norway
771            259 => Some("NO"), // Norway
772            261 => Some("PL"), // Poland
773            262 => Some("ME"), // Montenegro
774            263 => Some("PT"), // Portugal
775            264 => Some("RO"), // Romania
776            265 => Some("SE"), // Sweden
777            266 => Some("SE"), // Sweden
778            267 => Some("SK"), // Slovakia
779            268 => Some("SM"), // San Marino
780            269 => Some("CH"), // Switzerland
781            270 => Some("CZ"), // Czechia
782            271 => Some("TR"), // Turkey
783            272 => Some("UA"), // Ukraine
784            273 => Some("RU"), // Russian Federation
785            274 => Some("MK"), // Republic of North Macedonia
786            275 => Some("LV"), // Latvia
787            276 => Some("EE"), // Estonia
788            277 => Some("LT"), // Lithuania
789            278 => Some("SI"), // Slovenia
790            279 => Some("RS"), // Serbia
791            301 => Some("AI"), // Anguilla
792            303 => Some("US"), // United States of America
793            304 => Some("AG"), // Antigua and Barbuda
794            305 => Some("AG"), // Antigua and Barbuda
795            306 => Some("BQ"), // Bonaire, Sint Eustatius and Saba
796            //            306 => Some("CW"), // Curaçao
797            //            306 => Some("SX"), // Sint Maarten
798            307 => Some("AW"), // Aruba
799            308 => Some("BS"), // Bahamas
800            309 => Some("BS"), // Bahamas
801            310 => Some("BM"), // Bermuda
802            311 => Some("BS"), // Bahamas
803            312 => Some("BZ"), // Belize
804            314 => Some("BB"), // Barbados
805            316 => Some("CA"), // Canada
806            319 => Some("KY"), // Cayman Islands
807            321 => Some("CR"), // Costa Rica
808            323 => Some("CU"), // Cuba
809            325 => Some("DM"), // Dominica
810            327 => Some("DO"), // Dominican Republic
811            329 => Some("GP"), // Guadeloupe
812            330 => Some("GD"), // Grenada
813            331 => Some("GL"), // Greenland
814            332 => Some("GT"), // Guatemala
815            334 => Some("HN"), // Honduras
816            336 => Some("HT"), // Haiti
817            338 => Some("US"), // United States of America
818            339 => Some("JM"), // Jamaica
819            341 => Some("KN"), // Saint Kitts and Nevis
820            343 => Some("LC"), // Saint Lucia
821            345 => Some("MX"), // Mexico
822            347 => Some("MQ"), // Martinique
823            348 => Some("MS"), // Montserrat
824            350 => Some("NI"), // Nicaragua
825            351 => Some("PA"), // Panama
826            352 => Some("PA"), // Panama
827            353 => Some("PA"), // Panama
828            354 => Some("PA"), // Panama
829            355 => Some("PA"), // Panama
830            356 => Some("PA"), // Panama
831            357 => Some("PA"), // Panama
832            358 => Some("PR"), // Puerto Rico
833            359 => Some("SV"), // El Salvador
834            361 => Some("PM"), // Saint Pierre and Miquelon
835            362 => Some("TT"), // Trinidad and Tobago
836            364 => Some("TC"), // Turks and Caicos Islands
837            366 => Some("US"), // United States of America
838            367 => Some("US"), // United States of America
839            368 => Some("US"), // United States of America
840            369 => Some("US"), // United States of America
841            370 => Some("PA"), // Panama
842            371 => Some("PA"), // Panama
843            372 => Some("PA"), // Panama
844            373 => Some("PA"), // Panama
845            374 => Some("PA"), // Panama
846            375 => Some("VC"), // Saint Vincent and the Grenadines
847            376 => Some("VC"), // Saint Vincent and the Grenadines
848            377 => Some("VC"), // Saint Vincent and the Grenadines
849            378 => Some("VG"), // British Virgin Islands
850            379 => Some("VI"), // United States Virgin Islands
851            401 => Some("AF"), // Afghanistan
852            403 => Some("SA"), // Saudi Arabia
853            405 => Some("BD"), // Bangladesh
854            408 => Some("BH"), // Bahrain
855            410 => Some("BT"), // Bhutan
856            412 => Some("CN"), // China
857            413 => Some("CN"), // China
858            414 => Some("CN"), // China
859            416 => Some("TW"), // Taiwan
860            417 => Some("LK"), // Sri Lanka
861            419 => Some("IN"), // India
862            422 => Some("IR"), // Iran
863            423 => Some("AZ"), // Azerbaijan
864            425 => Some("IQ"), // Iraq
865            428 => Some("IL"), // Israel
866            431 => Some("JP"), // Japan
867            432 => Some("JP"), // Japan
868            434 => Some("TM"), // Turkmenistan
869            436 => Some("KZ"), // Kazakhstan
870            437 => Some("UZ"), // Uzbekistan
871            438 => Some("JO"), // Jordan
872            440 => Some("KR"), // Korea
873            441 => Some("KR"), // Korea
874            443 => Some("PS"), // Palestine, State of
875            445 => Some("KR"), // Korea
876            447 => Some("KW"), // Kuwait
877            450 => Some("LB"), // Lebanon
878            451 => Some("KG"), // Kyrgyzstan
879            453 => Some("MO"), // Macao
880            455 => Some("MV"), // Maldives
881            457 => Some("MN"), // Mongolia
882            459 => Some("NP"), // Nepal
883            461 => Some("OM"), // Oman
884            463 => Some("PK"), // Pakistan
885            466 => Some("QA"), // Qatar
886            468 => Some("SY"), // Syrian Arab Republic
887            470 => Some("AE"), // United Arab Emirates
888            471 => Some("AE"), // United Arab Emirates
889            472 => Some("TJ"), // Tajikistan
890            473 => Some("YE"), // Yemen
891            475 => Some("YE"), // Yemen
892            477 => Some("HK"), // Hong Kong
893            478 => Some("BA"), // Bosnia and Herzegovina
894            501 => Some("TF"), // French Southern Territories
895            503 => Some("AU"), // Australia
896            506 => Some("MM"), // Myanmar
897            508 => Some("BN"), // Brunei Darussalam
898            510 => Some("FM"), // Micronesia
899            511 => Some("PW"), // Palau
900            512 => Some("NZ"), // New Zealand
901            514 => Some("KH"), // Cambodia
902            515 => Some("KH"), // Cambodia
903            516 => Some("CX"), // Christmas Island
904            518 => Some("CK"), // Cook Islands
905            520 => Some("FJ"), // Fiji
906            523 => Some("CC"), // Cocos Islands
907            525 => Some("ID"), // Indonesia
908            529 => Some("KI"), // Kiribati
909            531 => Some("LA"), // Lao People's Democratic Republic
910            533 => Some("MY"), // Malaysia
911            536 => Some("MP"), // Northern Mariana Islands
912            538 => Some("MH"), // Marshall Islands
913            540 => Some("NC"), // New Caledonia
914            542 => Some("NU"), // Niue
915            544 => Some("NR"), // Nauru
916            546 => Some("PF"), // French Polynesia
917            548 => Some("PH"), // Philippines
918            550 => Some("TL"), // Timor-Leste
919            553 => Some("PG"), // Papua New Guinea
920            555 => Some("PN"), // Pitcairn
921            557 => Some("SB"), // Solomon Islands
922            559 => Some("AS"), // American Samoa
923            561 => Some("WS"), // Samoa
924            563 => Some("SG"), // Singapore
925            564 => Some("SG"), // Singapore
926            565 => Some("SG"), // Singapore
927            566 => Some("SG"), // Singapore
928            567 => Some("TH"), // Thailand
929            570 => Some("TO"), // Tonga
930            572 => Some("TV"), // Tuvalu
931            574 => Some("VN"), // Viet Nam
932            576 => Some("VU"), // Vanuatu
933            577 => Some("VU"), // Vanuatu
934            578 => Some("WF"), // Wallis and Futuna
935            601 => Some("ZA"), // South Africa
936            603 => Some("AO"), // Angola
937            605 => Some("DZ"), // Algeria
938            607 => Some("TF"), // French Southern Territories
939            608 => Some("SH"), // Saint Helena, Ascension and Tristan da Cunha
940            609 => Some("BI"), // Burundi
941            610 => Some("BJ"), // Benin
942            611 => Some("BW"), // Botswana
943            612 => Some("CF"), // Central African Republic
944            613 => Some("CM"), // Cameroon
945            615 => Some("CG"), // Congo
946            616 => Some("KM"), // Comoros
947            617 => Some("CV"), // Cabo Verde
948            618 => Some("TF"), // French Southern Territories
949            619 => Some("CI"), // Côte d'Ivoire
950            620 => Some("KM"), // Comoros
951            621 => Some("DJ"), // Djibouti
952            622 => Some("EG"), // Egypt
953            624 => Some("ET"), // Ethiopia
954            625 => Some("ER"), // Eritrea
955            626 => Some("GA"), // Gabon
956            627 => Some("GH"), // Ghana
957            629 => Some("GM"), // Gambia
958            630 => Some("GW"), // Guinea-Bissau
959            631 => Some("GQ"), // Equatorial Guinea
960            632 => Some("GN"), // Guinea
961            633 => Some("BF"), // Burkina Faso
962            634 => Some("KE"), // Kenya
963            635 => Some("TF"), // French Southern Territories
964            636 => Some("LR"), // Liberia
965            637 => Some("LR"), // Liberia
966            638 => Some("SS"), // South Sudan
967            642 => Some("LY"), // Libya
968            644 => Some("LS"), // Lesotho
969            645 => Some("MU"), // Mauritius
970            647 => Some("MG"), // Madagascar
971            649 => Some("ML"), // Mali
972            650 => Some("MZ"), // Mozambique
973            654 => Some("MR"), // Mauritania
974            655 => Some("MW"), // Malawi
975            656 => Some("NE"), // Niger
976            657 => Some("NG"), // Nigeria
977            659 => Some("NA"), // Namibia
978            660 => Some("TF"), // French Southern Territories
979            661 => Some("RW"), // Rwanda
980            662 => Some("SD"), // Sudan
981            663 => Some("SN"), // Senegal
982            664 => Some("SC"), // Seychelles
983            665 => Some("SH"), // Saint Helena, Ascension and Tristan da Cunha
984            666 => Some("SO"), // Somalia
985            667 => Some("SL"), // Sierra Leone
986            668 => Some("ST"), // Sao Tome and Principe
987            669 => Some("SZ"), // Eswatini
988            670 => Some("TD"), // Chad
989            671 => Some("TG"), // Togo
990            672 => Some("TN"), // Tunisia
991            674 => Some("TZ"), // Tanzania, United Republic of
992            675 => Some("UG"), // Uganda
993            676 => Some("CG"), // Congo
994            677 => Some("TZ"), // Tanzania, United Republic of
995            678 => Some("ZM"), // Zambia
996            679 => Some("ZW"), // Zimbabwe
997            701 => Some("AR"), // Argentina
998            710 => Some("BR"), // Brazil
999            720 => Some("BO"), // Bolivia
1000            725 => Some("CL"), // Chile
1001            730 => Some("CO"), // Colombia
1002            735 => Some("EC"), // Ecuador
1003            740 => Some("FK"), // Falkland Islands [Malvinas]
1004            745 => Some("GF"), // French Guiana
1005            750 => Some("GY"), // Guyana
1006            755 => Some("PY"), // Paraguay
1007            760 => Some("PE"), // Peru
1008            765 => Some("SR"), // Suriname
1009            770 => Some("UY"), // Uruguay
1010            775 => Some("VE"), // Venezuela
1011            _ => None,
1012        }
1013    }
1014}