dis_rs/common/underwater_acoustic/
model.rs

1use crate::common::{BodyInfo, Interaction};
2use crate::constants::{EIGHT_OCTETS, FOUR_OCTETS, ONE_OCTET, TWENTY_OCTETS};
3use crate::enumerations::{
4    APAStatus, PduType, UAAcousticEmitterSystemFunction, UAAcousticSystemName,
5    UAActiveEmissionParameterIndex, UAAdditionalPassiveActivityParameterIndex,
6    UAPassiveParameterIndex, UAPropulsionPlantConfiguration, UAScanPattern,
7    UAStateChangeUpdateIndicator,
8};
9use crate::model::{EntityId, EventId, PduBody, VectorF32};
10use crate::underwater_acoustic::builder::UnderwaterAcousticBuilder;
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14const BASE_UA_BODY_LENGTH: u16 = 20;
15
16/// 5.7.5 Underwater Acoustic (UA) PDU
17///
18/// 7.6.4 Underwater Acoustic (UA) PDU
19#[derive(Clone, Debug, Default, PartialEq)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub struct UnderwaterAcoustic {
22    pub emitting_entity_id: EntityId,
23    pub event_id: EventId,
24    pub state_change_update_indicator: UAStateChangeUpdateIndicator,
25    pub passive_parameter_index: UAPassiveParameterIndex,
26    pub propulsion_plant_configuration: PropulsionPlantConfiguration,
27    pub shafts: Vec<Shaft>,
28    pub apas: Vec<APA>,
29    pub emitter_systems: Vec<UAEmitterSystem>,
30}
31
32impl UnderwaterAcoustic {
33    #[must_use]
34    pub fn builder() -> UnderwaterAcousticBuilder {
35        UnderwaterAcousticBuilder::new()
36    }
37
38    #[must_use]
39    pub fn into_builder(self) -> UnderwaterAcousticBuilder {
40        UnderwaterAcousticBuilder::new_from_body(self)
41    }
42
43    #[must_use]
44    pub fn into_pdu_body(self) -> PduBody {
45        PduBody::UnderwaterAcoustic(self)
46    }
47}
48
49impl BodyInfo for UnderwaterAcoustic {
50    fn body_length(&self) -> u16 {
51        BASE_UA_BODY_LENGTH
52            + self.shafts.iter().map(Shaft::record_length).sum::<u16>()
53            + self.apas.iter().map(APA::record_length).sum::<u16>()
54            + self
55                .emitter_systems
56                .iter()
57                .map(UAEmitterSystem::record_length)
58                .sum::<u16>()
59    }
60
61    fn body_type(&self) -> PduType {
62        PduType::UnderwaterAcoustic
63    }
64}
65
66impl Interaction for UnderwaterAcoustic {
67    fn originator(&self) -> Option<&EntityId> {
68        Some(&self.emitting_entity_id)
69    }
70
71    fn receiver(&self) -> Option<&EntityId> {
72        None
73    }
74}
75
76/// Implementation of UID 149
77#[derive(Clone, Debug, Default, PartialEq)]
78#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
79pub struct PropulsionPlantConfiguration {
80    pub configuration: UAPropulsionPlantConfiguration,
81    pub hull_mounted_masker: bool,
82}
83
84impl PropulsionPlantConfiguration {
85    #[must_use]
86    pub fn with_configuration(mut self, configuration: UAPropulsionPlantConfiguration) -> Self {
87        self.configuration = configuration;
88        self
89    }
90
91    #[must_use]
92    pub fn with_hull_mounted_masker(mut self, masker_on: bool) -> Self {
93        self.hull_mounted_masker = masker_on;
94        self
95    }
96
97    #[must_use]
98    pub fn record_length(&self) -> u16 {
99        ONE_OCTET as u16
100    }
101}
102
103/// 7.6.4 Underwater Acoustic (UA) PDU
104///
105/// Table 164—UA PDU
106#[derive(Clone, Debug, Default, PartialEq)]
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
108pub struct Shaft {
109    pub current_rpm: i16,
110    pub ordered_rpm: i16,
111    pub rpm_rate_of_change: i32,
112}
113
114impl Shaft {
115    #[must_use]
116    pub fn with_current_rpm(mut self, rpm: i16) -> Self {
117        self.current_rpm = rpm;
118        self
119    }
120
121    #[must_use]
122    pub fn with_ordered_rpm(mut self, rpm: i16) -> Self {
123        self.ordered_rpm = rpm;
124        self
125    }
126
127    #[must_use]
128    pub fn with_rpm_rate_of_change(mut self, rate_of_change: i32) -> Self {
129        self.rpm_rate_of_change = rate_of_change;
130        self
131    }
132
133    #[must_use]
134    pub fn record_length(&self) -> u16 {
135        EIGHT_OCTETS as u16
136    }
137}
138
139/// 7.6.4 Underwater Acoustic (UA) PDU
140///
141/// Table 163—APA Parameter Index record
142#[derive(Clone, Debug, Default, PartialEq)]
143#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
144pub struct APA {
145    pub parameter: UAAdditionalPassiveActivityParameterIndex,
146    pub status: APAStatus,
147    pub value: i16,
148}
149
150impl APA {
151    #[must_use]
152    pub fn with_parameter(mut self, parameter: UAAdditionalPassiveActivityParameterIndex) -> Self {
153        self.parameter = parameter;
154        self
155    }
156
157    #[must_use]
158    pub fn with_status(mut self, status: APAStatus) -> Self {
159        self.status = status;
160        self
161    }
162    #[must_use]
163    pub fn with_value(mut self, value: i16) -> Self {
164        self.value = value;
165        self
166    }
167
168    #[must_use]
169    pub fn record_length(&self) -> u16 {
170        FOUR_OCTETS as u16
171    }
172}
173
174/// Figure 50 — General form of emitter systems in the UA PDU
175#[derive(Clone, Debug, Default, PartialEq)]
176#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
177pub struct UAEmitterSystem {
178    pub acoustic_emitter_system: AcousticEmitterSystem,
179    pub location: VectorF32,
180    pub beams: Vec<UABeam>,
181}
182
183impl UAEmitterSystem {
184    #[must_use]
185    pub fn with_acoustic_emitter_system(
186        mut self,
187        acoustic_emitter_system: AcousticEmitterSystem,
188    ) -> Self {
189        self.acoustic_emitter_system = acoustic_emitter_system;
190        self
191    }
192
193    #[must_use]
194    pub fn with_location(mut self, location: VectorF32) -> Self {
195        self.location = location;
196        self
197    }
198
199    #[must_use]
200    pub fn with_beam(mut self, beam: UABeam) -> Self {
201        self.beams.push(beam);
202        self
203    }
204
205    #[must_use]
206    pub fn with_beams(mut self, beams: Vec<UABeam>) -> Self {
207        self.beams = beams;
208        self
209    }
210
211    #[must_use]
212    pub fn record_length(&self) -> u16 {
213        TWENTY_OCTETS as u16 + self.beams.iter().map(UABeam::record_length).sum::<u16>()
214    }
215}
216
217/// 6.2.2 Acoustic Emitter System record
218#[derive(Clone, Debug, Default, PartialEq)]
219#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
220pub struct AcousticEmitterSystem {
221    pub acoustic_system_name: UAAcousticSystemName,
222    pub function: UAAcousticEmitterSystemFunction,
223    pub acoustic_id_number: u8,
224}
225
226impl AcousticEmitterSystem {
227    #[must_use]
228    pub fn with_acoustic_system_name(mut self, acoustic_system_name: UAAcousticSystemName) -> Self {
229        self.acoustic_system_name = acoustic_system_name;
230        self
231    }
232
233    #[must_use]
234    pub fn with_function(mut self, function: UAAcousticEmitterSystemFunction) -> Self {
235        self.function = function;
236        self
237    }
238
239    #[must_use]
240    pub fn with_acoustic_id_number(mut self, acoustic_id_number: u8) -> Self {
241        self.acoustic_id_number = acoustic_id_number;
242        self
243    }
244
245    #[must_use]
246    pub fn record_length(&self) -> u16 {
247        FOUR_OCTETS as u16
248    }
249}
250
251/// Custom record for an UA Beam
252///
253/// 7.6.4 Underwater Acoustic (UA) PDU
254#[derive(Clone, Debug, Default, PartialEq)]
255#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
256pub struct UABeam {
257    pub beam_data_length: u8,
258    pub beam_id_number: u8,
259    pub fundamental_parameters: UAFundamentalParameterData,
260}
261
262impl UABeam {
263    #[must_use]
264    pub fn with_beam_data_length(mut self, beam_data_length: u8) -> Self {
265        self.beam_data_length = beam_data_length;
266        self
267    }
268
269    #[must_use]
270    pub fn with_beam_id_number(mut self, beam_id_number: u8) -> Self {
271        self.beam_id_number = beam_id_number;
272        self
273    }
274
275    #[must_use]
276    pub fn with_fundamental_parameters(
277        mut self,
278        fundamental_parameters: UAFundamentalParameterData,
279    ) -> Self {
280        self.fundamental_parameters = fundamental_parameters;
281        self
282    }
283
284    #[must_use]
285    pub fn record_length(&self) -> u16 {
286        FOUR_OCTETS as u16 + self.fundamental_parameters.record_length()
287    }
288}
289
290/// 6.2.91 UA Fundamental Parameter Data record
291#[derive(Clone, Debug, Default, PartialEq)]
292#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
293pub struct UAFundamentalParameterData {
294    pub active_emission_parameter_index: UAActiveEmissionParameterIndex,
295    pub scan_pattern: UAScanPattern,
296    pub beam_center_azimuth: f32,
297    pub azimuthal_beamwidth: f32,
298    pub beam_center_depression_elevation: f32,
299    pub depression_elevation_beamwidth: f32,
300}
301
302impl UAFundamentalParameterData {
303    #[must_use]
304    pub fn with_active_emission_parameter_index(
305        mut self,
306        active_emission_parameter_index: UAActiveEmissionParameterIndex,
307    ) -> Self {
308        self.active_emission_parameter_index = active_emission_parameter_index;
309        self
310    }
311
312    #[must_use]
313    pub fn with_scan_pattern(mut self, scan_pattern: UAScanPattern) -> Self {
314        self.scan_pattern = scan_pattern;
315        self
316    }
317
318    #[must_use]
319    pub fn with_beam_center_azimuth(mut self, beam_center_azimuth: f32) -> Self {
320        self.beam_center_azimuth = beam_center_azimuth;
321        self
322    }
323
324    #[must_use]
325    pub fn with_azimuthal_beamwidth(mut self, azimuthal_beamwidth: f32) -> Self {
326        self.azimuthal_beamwidth = azimuthal_beamwidth;
327        self
328    }
329
330    #[must_use]
331    pub fn with_beam_center_depression_elevation(
332        mut self,
333        beam_center_depression_elevation: f32,
334    ) -> Self {
335        self.beam_center_depression_elevation = beam_center_depression_elevation;
336        self
337    }
338
339    #[must_use]
340    pub fn with_depression_elevation_beamwidth(
341        mut self,
342        depression_elevation_beamwidth: f32,
343    ) -> Self {
344        self.depression_elevation_beamwidth = depression_elevation_beamwidth;
345        self
346    }
347    #[must_use]
348    pub fn record_length(&self) -> u16 {
349        TWENTY_OCTETS as u16
350    }
351}