1use crate::EncodeError;
2use crate::ais::encode::{
3 BitWriter, encode_cog, encode_epfd, encode_integer_sog, encode_latitude, encode_long_range_cog,
4 encode_long_range_latitude, encode_long_range_longitude, encode_long_range_sog,
5 encode_longitude, encode_position_timestamp, encode_sar_altitude, encode_utc_day,
6 encode_utc_hour, encode_utc_minute_or_second, encode_utc_month, encode_utc_year,
7};
8use crate::ais::messages::NavigationStatus;
9
10use super::{
11 AisEncodable, AisTransmitOptions, ClassBCommunicationState, PositionTimestamp, encode_payload,
12};
13
14#[derive(Debug, Clone, PartialEq)]
16pub struct BaseStation {
17 pub repeat_indicator: u8,
18 pub mmsi: u32,
19 pub year: Option<u16>,
20 pub month: Option<u8>,
21 pub day: Option<u8>,
22 pub hour: Option<u8>,
23 pub minute: Option<u8>,
24 pub second: Option<u8>,
25 pub position_accuracy: bool,
26 pub longitude: Option<f64>,
27 pub latitude: Option<f64>,
28 pub position_fixing_device: u8,
29 pub transmission_control: bool,
30 pub raim: bool,
31 pub communication_state: u32,
32}
33
34#[derive(Debug, Clone, PartialEq)]
36pub struct UtcDateResponse {
37 pub repeat_indicator: u8,
38 pub mmsi: u32,
39 pub year: Option<u16>,
40 pub month: Option<u8>,
41 pub day: Option<u8>,
42 pub hour: Option<u8>,
43 pub minute: Option<u8>,
44 pub second: Option<u8>,
45 pub position_accuracy: bool,
46 pub longitude: Option<f64>,
47 pub latitude: Option<f64>,
48 pub position_fixing_device: u8,
49 pub transmission_control: bool,
50 pub raim: bool,
51 pub communication_state: u32,
52}
53
54#[derive(Debug, Clone, PartialEq)]
56pub struct SarAircraft {
57 pub repeat_indicator: u8,
58 pub mmsi: u32,
59 pub altitude: Option<u16>,
60 pub sog: Option<u16>,
61 pub position_accuracy: bool,
62 pub longitude: Option<f64>,
63 pub latitude: Option<f64>,
64 pub cog: Option<f32>,
65 pub timestamp: PositionTimestamp,
66 pub regional_application: u8,
67 pub dte: bool,
69 pub assigned_mode: bool,
70 pub raim: bool,
71 pub communication_state: ClassBCommunicationState,
72}
73
74#[derive(Debug, Clone, PartialEq)]
76pub struct AidToNavigation {
77 pub repeat_indicator: u8,
78 pub mmsi: u32,
79 pub aid_type: u8,
80 pub name: String,
81 pub longitude: Option<f64>,
82 pub latitude: Option<f64>,
83 pub position_accuracy: bool,
84 pub dimension_to_bow: u16,
85 pub dimension_to_stern: u16,
86 pub dimension_to_port: u8,
87 pub dimension_to_starboard: u8,
88 pub position_fixing_device: u8,
89 pub timestamp: PositionTimestamp,
90 pub off_position: bool,
91 pub regional_application: u8,
92 pub raim: bool,
93 pub virtual_aid: bool,
94 pub assigned_mode: bool,
95 pub name_extension: Option<String>,
96}
97
98#[derive(Debug, Clone, PartialEq)]
100pub struct LongRangePosition {
101 pub mmsi: u32,
102 pub position_accuracy: bool,
103 pub raim: bool,
104 pub navigation_status: NavigationStatus,
105 pub longitude: Option<f64>,
106 pub latitude: Option<f64>,
107 pub sog: Option<u8>,
108 pub cog: Option<u16>,
109 pub gnss_position_status: bool,
110}
111
112struct UtcPositionInput {
113 repeat_indicator: u8,
114 mmsi: u32,
115 year: Option<u16>,
116 month: Option<u8>,
117 day: Option<u8>,
118 hour: Option<u8>,
119 minute: Option<u8>,
120 second: Option<u8>,
121 position_accuracy: bool,
122 longitude: Option<f64>,
123 latitude: Option<f64>,
124 position_fixing_device: u8,
125 transmission_control: bool,
126 raim: bool,
127 communication_state: u32,
128}
129
130impl AisEncodable for BaseStation {
131 fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
132 encode_utc_position(4, self.utc_position_input(), options)
133 }
134}
135
136impl AisEncodable for UtcDateResponse {
137 fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
138 encode_utc_position(11, self.utc_position_input(), options)
139 }
140}
141
142impl AisEncodable for SarAircraft {
143 fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
144 let mut writer = BitWriter::new();
145 let (selector, communication_state) = communication_state_parts(self.communication_state);
146 writer.push_u32(9, 6, "message_type")?;
147 writer.push_u32(u32::from(self.repeat_indicator), 2, "repeat_indicator")?;
148 writer.push_u32(self.mmsi, 30, "mmsi")?;
149 writer.push_u32(encode_sar_altitude(self.altitude)?, 12, "altitude")?;
150 writer.push_u32(encode_integer_sog(self.sog)?, 10, "sog")?;
151 writer.push_bool(self.position_accuracy);
152 writer.push_i32(encode_longitude(self.longitude)?, 28, "longitude")?;
153 writer.push_i32(encode_latitude(self.latitude)?, 27, "latitude")?;
154 writer.push_u32(encode_cog(self.cog)?, 12, "cog")?;
155 writer.push_u32(encode_position_timestamp(self.timestamp)?, 6, "timestamp")?;
156 writer.push_u32(
157 u32::from(self.regional_application),
158 8,
159 "regional_application",
160 )?;
161 writer.push_bool(self.dte);
162 writer.push_spare(3);
163 writer.push_bool(self.assigned_mode);
164 writer.push_bool(self.raim);
165 writer.push_bool(selector);
166 writer.push_u32(communication_state, 19, "communication_state")?;
167 encode_payload(&writer.finish(), options)
168 }
169}
170
171impl AisEncodable for AidToNavigation {
172 fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
173 let mut writer = BitWriter::new();
174 writer.push_u32(21, 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(u32::from(self.aid_type), 5, "aid_type")?;
178 writer.push_text(&self.name, 20, "name")?;
179 writer.push_bool(self.position_accuracy);
180 writer.push_i32(encode_longitude(self.longitude)?, 28, "longitude")?;
181 writer.push_i32(encode_latitude(self.latitude)?, 27, "latitude")?;
182 writer.push_u32(u32::from(self.dimension_to_bow), 9, "dimension_to_bow")?;
183 writer.push_u32(u32::from(self.dimension_to_stern), 9, "dimension_to_stern")?;
184 writer.push_u32(u32::from(self.dimension_to_port), 6, "dimension_to_port")?;
185 writer.push_u32(
186 u32::from(self.dimension_to_starboard),
187 6,
188 "dimension_to_starboard",
189 )?;
190 writer.push_u32(
191 encode_epfd(self.position_fixing_device)?,
192 4,
193 "position_fixing_device",
194 )?;
195 writer.push_u32(encode_position_timestamp(self.timestamp)?, 6, "timestamp")?;
196 writer.push_bool(self.off_position);
197 writer.push_u32(
198 u32::from(self.regional_application),
199 8,
200 "regional_application",
201 )?;
202 writer.push_bool(self.raim);
203 writer.push_bool(self.virtual_aid);
204 writer.push_bool(self.assigned_mode);
205 writer.push_spare(1);
206 if let Some(extension) = &self.name_extension {
207 let actual_chars = extension.chars().count();
208 if actual_chars > 14 {
209 return Err(EncodeError::AisTextTooLong {
210 field: "name_extension",
211 max_chars: 14,
212 actual_chars,
213 });
214 }
215 writer.push_text(extension, actual_chars, "name_extension")?;
216 }
217 encode_payload(&writer.finish(), options)
218 }
219}
220
221impl AisEncodable for LongRangePosition {
222 fn to_sentences(&self, options: AisTransmitOptions) -> Result<Vec<String>, EncodeError> {
223 let mut writer = BitWriter::new();
224 writer.push_u32(27, 6, "message_type")?;
225 writer.push_u32(3, 2, "repeat_indicator")?;
226 writer.push_u32(self.mmsi, 30, "mmsi")?;
227 writer.push_bool(self.position_accuracy);
228 writer.push_bool(self.raim);
229 writer.push_u32(
230 u32::from(u8::from(self.navigation_status)),
231 4,
232 "navigation_status",
233 )?;
234 writer.push_i32(
235 encode_long_range_longitude(self.longitude)?,
236 18,
237 "longitude",
238 )?;
239 writer.push_i32(encode_long_range_latitude(self.latitude)?, 17, "latitude")?;
240 writer.push_u32(encode_long_range_sog(self.sog)?, 6, "sog")?;
241 writer.push_u32(encode_long_range_cog(self.cog)?, 9, "cog")?;
242 writer.push_bool(self.gnss_position_status);
243 writer.push_spare(1);
244 encode_payload(&writer.finish(), options)
245 }
246}
247
248fn encode_utc_position(
249 message_type: u32,
250 input: UtcPositionInput,
251 options: AisTransmitOptions,
252) -> Result<Vec<String>, EncodeError> {
253 let mut writer = BitWriter::new();
254 writer.push_u32(message_type, 6, "message_type")?;
255 writer.push_u32(u32::from(input.repeat_indicator), 2, "repeat_indicator")?;
256 writer.push_u32(input.mmsi, 30, "mmsi")?;
257 writer.push_u32(encode_utc_year(input.year)?, 14, "year")?;
258 writer.push_u32(encode_utc_month(input.month)?, 4, "month")?;
259 writer.push_u32(encode_utc_day(input.day)?, 5, "day")?;
260 writer.push_u32(encode_utc_hour(input.hour)?, 5, "hour")?;
261 writer.push_u32(
262 encode_utc_minute_or_second(input.minute, "minute")?,
263 6,
264 "minute",
265 )?;
266 writer.push_u32(
267 encode_utc_minute_or_second(input.second, "second")?,
268 6,
269 "second",
270 )?;
271 writer.push_bool(input.position_accuracy);
272 writer.push_i32(encode_longitude(input.longitude)?, 28, "longitude")?;
273 writer.push_i32(encode_latitude(input.latitude)?, 27, "latitude")?;
274 writer.push_u32(
275 encode_epfd(input.position_fixing_device)?,
276 4,
277 "position_fixing_device",
278 )?;
279 writer.push_bool(input.transmission_control);
280 writer.push_spare(9);
281 writer.push_bool(input.raim);
282 writer.push_u32(input.communication_state, 19, "communication_state")?;
283 encode_payload(&writer.finish(), options)
284}
285
286trait UtcPositionSource {
287 fn utc_position_input(&self) -> UtcPositionInput;
288}
289
290impl UtcPositionSource for BaseStation {
291 fn utc_position_input(&self) -> UtcPositionInput {
292 UtcPositionInput {
293 repeat_indicator: self.repeat_indicator,
294 mmsi: self.mmsi,
295 year: self.year,
296 month: self.month,
297 day: self.day,
298 hour: self.hour,
299 minute: self.minute,
300 second: self.second,
301 position_accuracy: self.position_accuracy,
302 longitude: self.longitude,
303 latitude: self.latitude,
304 position_fixing_device: self.position_fixing_device,
305 transmission_control: self.transmission_control,
306 raim: self.raim,
307 communication_state: self.communication_state,
308 }
309 }
310}
311
312impl UtcPositionSource for UtcDateResponse {
313 fn utc_position_input(&self) -> UtcPositionInput {
314 UtcPositionInput {
315 repeat_indicator: self.repeat_indicator,
316 mmsi: self.mmsi,
317 year: self.year,
318 month: self.month,
319 day: self.day,
320 hour: self.hour,
321 minute: self.minute,
322 second: self.second,
323 position_accuracy: self.position_accuracy,
324 longitude: self.longitude,
325 latitude: self.latitude,
326 position_fixing_device: self.position_fixing_device,
327 transmission_control: self.transmission_control,
328 raim: self.raim,
329 communication_state: self.communication_state,
330 }
331 }
332}
333
334fn communication_state_parts(value: ClassBCommunicationState) -> (bool, u32) {
335 match value {
336 ClassBCommunicationState::Sotdma(value) => (false, value),
337 ClassBCommunicationState::Itdma(value) => (true, value),
338 }
339}