Skip to main content

nmea_kit/ais/transmit/
class_b.rs

1use crate::EncodeError;
2use crate::ais::encode::{
3    BitWriter, encode_cog, encode_epfd, encode_heading, encode_latitude, encode_longitude,
4    encode_position_timestamp, encode_sog, encode_timestamp,
5};
6
7use super::{AisEncodable, AisTransmitOptions, PositionTimestamp, encode_payload};
8
9/// AIS Type 18 communication-state selector and its 19-bit state value.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ClassBCommunicationState {
12    Sotdma(u32),
13    Itdma(u32),
14}
15
16impl ClassBCommunicationState {
17    fn selector(self) -> bool {
18        matches!(self, Self::Itdma(_))
19    }
20
21    fn value(self) -> u32 {
22        match self {
23            Self::Sotdma(value) | Self::Itdma(value) => value,
24        }
25    }
26}
27
28/// AIS Type 18 standard Class B position report.
29#[derive(Debug, Clone, PartialEq)]
30pub struct ClassBPosition {
31    pub repeat_indicator: u8,
32    pub mmsi: u32,
33    pub sog: Option<f32>,
34    pub position_accuracy: bool,
35    pub longitude: Option<f64>,
36    pub latitude: Option<f64>,
37    pub cog: Option<f32>,
38    pub heading: Option<u16>,
39    pub timestamp: Option<u8>,
40    pub transmit_power_low: bool,
41    pub class_b_cs: bool,
42    pub display_available: bool,
43    pub dsc_capable: bool,
44    pub full_band_capable: bool,
45    pub message_22_capable: bool,
46    pub assigned_mode: bool,
47    pub raim: bool,
48    pub communication_state: ClassBCommunicationState,
49}
50
51/// AIS Type 19 Class B+ extended position report.
52#[derive(Debug, Clone, PartialEq)]
53pub struct ClassBExtendedPosition {
54    pub repeat_indicator: u8,
55    pub mmsi: u32,
56    pub sog: Option<f32>,
57    pub position_accuracy: bool,
58    pub longitude: Option<f64>,
59    pub latitude: Option<f64>,
60    pub cog: Option<f32>,
61    pub heading: Option<u16>,
62    pub timestamp: PositionTimestamp,
63    pub vessel_name: String,
64    pub ship_type: u8,
65    pub dimension_to_bow: u16,
66    pub dimension_to_stern: u16,
67    pub dimension_to_port: u8,
68    pub dimension_to_starboard: u8,
69    pub position_fixing_device: u8,
70    /// `false` means the DTE is available.
71    pub dte: bool,
72    pub assigned_mode: bool,
73    pub raim: bool,
74}
75
76/// AIS Type 24 Part A static data report.
77#[derive(Debug, Clone, PartialEq)]
78pub struct ClassBStaticPartA {
79    pub repeat_indicator: u8,
80    pub mmsi: u32,
81    pub vessel_name: String,
82}
83
84/// AIS Type 24 Part B static data report.
85#[derive(Debug, Clone, PartialEq)]
86pub struct ClassBStaticPartB {
87    pub repeat_indicator: u8,
88    pub mmsi: u32,
89    pub ship_type: u8,
90    /// Three-character AIS manufacturer mnemonic.
91    pub manufacturer_id: String,
92    pub model_code: u8,
93    pub serial_number: u32,
94    pub callsign: String,
95    pub dimension_to_bow: u16,
96    pub dimension_to_stern: u16,
97    pub dimension_to_port: u8,
98    pub dimension_to_starboard: u8,
99    pub position_fixing_device: u8,
100    /// VDES capability code from ITU-R M.1371-6 Table 77.
101    pub vdes_capabilities: u8,
102}
103
104impl AisEncodable for ClassBPosition {
105    fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
106        let mut writer = BitWriter::new();
107        writer.push_u32(18, 6, "message_type")?;
108        writer.push_u32(u32::from(self.repeat_indicator), 2, "repeat_indicator")?;
109        writer.push_u32(self.mmsi, 30, "mmsi")?;
110        writer.push_spare(8);
111        writer.push_u32(encode_sog(self.sog)?, 10, "sog")?;
112        writer.push_bool(self.position_accuracy);
113        writer.push_i32(encode_longitude(self.longitude)?, 28, "longitude")?;
114        writer.push_i32(encode_latitude(self.latitude)?, 27, "latitude")?;
115        writer.push_u32(encode_cog(self.cog)?, 12, "cog")?;
116        writer.push_u32(encode_heading(self.heading)?, 9, "heading")?;
117        writer.push_u32(encode_timestamp(self.timestamp)?, 6, "timestamp")?;
118        writer.push_bool(self.transmit_power_low);
119        writer.push_spare(1);
120        writer.push_bool(self.class_b_cs);
121        writer.push_bool(self.display_available);
122        writer.push_bool(self.dsc_capable);
123        writer.push_bool(self.full_band_capable);
124        writer.push_bool(self.message_22_capable);
125        writer.push_bool(self.assigned_mode);
126        writer.push_bool(self.raim);
127        writer.push_bool(self.communication_state.selector());
128        writer.push_u32(self.communication_state.value(), 19, "communication_state")?;
129        encode_payload(&writer.finish(), options)
130    }
131}
132
133impl AisEncodable for ClassBExtendedPosition {
134    fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
135        let mut writer = BitWriter::new();
136        writer.push_u32(19, 6, "message_type")?;
137        writer.push_u32(u32::from(self.repeat_indicator), 2, "repeat_indicator")?;
138        writer.push_u32(self.mmsi, 30, "mmsi")?;
139        writer.push_spare(8);
140        writer.push_u32(encode_sog(self.sog)?, 10, "sog")?;
141        writer.push_bool(self.position_accuracy);
142        writer.push_i32(encode_longitude(self.longitude)?, 28, "longitude")?;
143        writer.push_i32(encode_latitude(self.latitude)?, 27, "latitude")?;
144        writer.push_u32(encode_cog(self.cog)?, 12, "cog")?;
145        writer.push_u32(encode_heading(self.heading)?, 9, "heading")?;
146        writer.push_u32(encode_position_timestamp(self.timestamp)?, 6, "timestamp")?;
147        writer.push_spare(4);
148        writer.push_text(&self.vessel_name, 20, "vessel_name")?;
149        writer.push_u32(u32::from(self.ship_type), 8, "ship_type")?;
150        writer.push_u32(u32::from(self.dimension_to_bow), 9, "dimension_to_bow")?;
151        writer.push_u32(u32::from(self.dimension_to_stern), 9, "dimension_to_stern")?;
152        writer.push_u32(u32::from(self.dimension_to_port), 6, "dimension_to_port")?;
153        writer.push_u32(
154            u32::from(self.dimension_to_starboard),
155            6,
156            "dimension_to_starboard",
157        )?;
158        writer.push_u32(
159            encode_epfd(self.position_fixing_device)?,
160            4,
161            "position_fixing_device",
162        )?;
163        writer.push_bool(self.raim);
164        writer.push_bool(self.dte);
165        writer.push_bool(self.assigned_mode);
166        writer.push_spare(4);
167        encode_payload(&writer.finish(), options)
168    }
169}
170
171impl AisEncodable for ClassBStaticPartA {
172    fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
173        let mut writer = BitWriter::new();
174        writer.push_u32(24, 6, "message_type")?;
175        writer.push_u32(u32::from(self.repeat_indicator), 2, "repeat_indicator")?;
176        writer.push_u32(self.mmsi, 30, "mmsi")?;
177        writer.push_u32(0, 2, "part_number")?;
178        writer.push_text(&self.vessel_name, 20, "vessel_name")?;
179        encode_payload(&writer.finish(), options)
180    }
181}
182
183impl AisEncodable for ClassBStaticPartB {
184    fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
185        let mut writer = BitWriter::new();
186        writer.push_u32(24, 6, "message_type")?;
187        writer.push_u32(u32::from(self.repeat_indicator), 2, "repeat_indicator")?;
188        writer.push_u32(self.mmsi, 30, "mmsi")?;
189        writer.push_u32(1, 2, "part_number")?;
190        writer.push_u32(u32::from(self.ship_type), 8, "ship_type")?;
191        writer.push_text(&self.manufacturer_id, 3, "manufacturer_id")?;
192        writer.push_u32(u32::from(self.model_code), 4, "model_code")?;
193        writer.push_u32(self.serial_number, 20, "serial_number")?;
194        writer.push_text(&self.callsign, 7, "callsign")?;
195        writer.push_u32(u32::from(self.dimension_to_bow), 9, "dimension_to_bow")?;
196        writer.push_u32(u32::from(self.dimension_to_stern), 9, "dimension_to_stern")?;
197        writer.push_u32(u32::from(self.dimension_to_port), 6, "dimension_to_port")?;
198        writer.push_u32(
199            u32::from(self.dimension_to_starboard),
200            6,
201            "dimension_to_starboard",
202        )?;
203        writer.push_u32(
204            encode_epfd(self.position_fixing_device)?,
205            4,
206            "position_fixing_device",
207        )?;
208        writer.push_u32(u32::from(self.vdes_capabilities), 2, "vdes_capabilities")?;
209        encode_payload(&writer.finish(), options)
210    }
211}