dis_rs/common/electromagnetic_emission/
model.rs

1use crate::common::model::{BeamData, EntityId, EventId, PduBody, VectorF32};
2use crate::common::{BodyInfo, Interaction};
3use crate::electromagnetic_emission::builder::ElectromagneticEmissionBuilder;
4use crate::enumerations::{
5    BeamStatusBeamState, ElectromagneticEmissionBeamFunction,
6    ElectromagneticEmissionStateUpdateIndicator, EmitterName, EmitterSystemFunction,
7    HighDensityTrackJam, PduType,
8};
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12const EMISSION_BASE_BODY_LENGTH: u16 = 16;
13const EMITTER_SYSTEM_BASE_LENGTH: u16 = 20;
14const BEAM_BASE_LENGTH: u16 = 52;
15const TRACK_JAM_BASE_LENGTH: u16 = 8;
16
17/// 5.7.3 Electromagnetic Emission (EE) PDU
18///
19/// 7.6.2 Electromagnetic Emission (EE) PDU
20#[derive(Clone, Debug, Default, PartialEq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub struct ElectromagneticEmission {
23    pub emitting_entity_id: EntityId,
24    pub event_id: EventId,
25    pub state_update_indicator: ElectromagneticEmissionStateUpdateIndicator,
26    pub emitter_systems: Vec<EmitterSystem>,
27}
28
29impl ElectromagneticEmission {
30    #[must_use]
31    pub fn builder() -> ElectromagneticEmissionBuilder {
32        ElectromagneticEmissionBuilder::new()
33    }
34
35    #[must_use]
36    pub fn into_builder(self) -> ElectromagneticEmissionBuilder {
37        ElectromagneticEmissionBuilder::new_from_body(self)
38    }
39
40    #[must_use]
41    pub fn into_pdu_body(self) -> PduBody {
42        PduBody::ElectromagneticEmission(self)
43    }
44}
45
46impl BodyInfo for ElectromagneticEmission {
47    fn body_length(&self) -> u16 {
48        EMISSION_BASE_BODY_LENGTH
49            + self
50                .emitter_systems
51                .iter()
52                .map(EmitterSystem::system_data_length_bytes)
53                .sum::<u16>()
54    }
55
56    fn body_type(&self) -> PduType {
57        PduType::ElectromagneticEmission
58    }
59}
60
61impl Interaction for ElectromagneticEmission {
62    fn originator(&self) -> Option<&EntityId> {
63        Some(&self.emitting_entity_id)
64    }
65
66    fn receiver(&self) -> Option<&EntityId> {
67        // just selects the first track or jam
68        if let Some(emitter) = self.emitter_systems.first() {
69            if let Some(beam) = emitter.beams.first() {
70                if let Some(tracks) = beam.track_jam_data.first() {
71                    Some(&tracks.entity_id)
72                } else {
73                    None
74                }
75            } else {
76                None
77            }
78        } else {
79            None
80        }
81    }
82}
83
84/// 6.2.23 Emitter System record
85#[derive(Clone, Debug, PartialEq)]
86#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
87pub struct EmitterSystem {
88    pub name: EmitterName,
89    pub function: EmitterSystemFunction,
90    pub number: u8,
91    pub location: VectorF32,
92    pub beams: Vec<Beam>,
93}
94
95impl Default for EmitterSystem {
96    fn default() -> Self {
97        Self::new()
98    }
99}
100
101impl EmitterSystem {
102    #[must_use]
103    pub fn new() -> Self {
104        Self {
105            name: EmitterName::default(),
106            function: EmitterSystemFunction::default(),
107            number: 0,
108            location: VectorF32::default(),
109            beams: vec![],
110        }
111    }
112
113    #[must_use]
114    pub fn with_name(mut self, name: EmitterName) -> Self {
115        self.name = name;
116        self
117    }
118
119    #[must_use]
120    pub fn with_function(mut self, function: EmitterSystemFunction) -> Self {
121        self.function = function;
122        self
123    }
124
125    #[must_use]
126    pub fn with_number(mut self, number: u8) -> Self {
127        self.number = number;
128        self
129    }
130
131    #[must_use]
132    pub fn with_location(mut self, location: VectorF32) -> Self {
133        self.location = location;
134        self
135    }
136
137    #[allow(clippy::return_self_not_must_use)]
138    pub fn with_beams(mut self, beams: &mut Vec<Beam>) -> Self {
139        self.beams.append(beams);
140        self
141    }
142
143    #[must_use]
144    pub fn with_beam(mut self, beam: Beam) -> Self {
145        self.beams.push(beam);
146        self
147    }
148
149    #[must_use]
150    pub fn system_data_length_bytes(&self) -> u16 {
151        EMITTER_SYSTEM_BASE_LENGTH
152            + self
153                .beams
154                .iter()
155                .map(Beam::beam_data_length_bytes)
156                .sum::<u16>()
157    }
158}
159
160#[derive(Clone, Default, Debug, PartialEq)]
161#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
162pub struct Beam {
163    pub number: u8,
164    pub parameter_index: u16,
165    pub parameter_data: FundamentalParameterData,
166    pub beam_data: BeamData,
167    pub beam_function: ElectromagneticEmissionBeamFunction,
168    pub high_density_track_jam: HighDensityTrackJam,
169    pub beam_status: BeamStatusBeamState,
170    pub jamming_technique: JammingTechnique,
171    pub track_jam_data: Vec<TrackJam>,
172}
173
174impl Beam {
175    #[must_use]
176    pub fn new() -> Self {
177        Self {
178            number: 0,
179            parameter_index: 0,
180            parameter_data: FundamentalParameterData::default(),
181            beam_data: BeamData::default(),
182            beam_function: ElectromagneticEmissionBeamFunction::default(),
183            high_density_track_jam: HighDensityTrackJam::default(),
184            beam_status: BeamStatusBeamState::default(),
185            jamming_technique: JammingTechnique::default(),
186            track_jam_data: vec![],
187        }
188    }
189
190    #[must_use]
191    pub fn with_number(mut self, number: u8) -> Self {
192        self.number = number;
193        self
194    }
195
196    #[must_use]
197    pub fn with_parameter_index(mut self, parameter_index: u16) -> Self {
198        self.parameter_index = parameter_index;
199        self
200    }
201
202    #[must_use]
203    pub fn with_parameter_data(mut self, parameter_data: FundamentalParameterData) -> Self {
204        self.parameter_data = parameter_data;
205        self
206    }
207
208    #[must_use]
209    pub fn with_beam_data(mut self, beam_data: BeamData) -> Self {
210        self.beam_data = beam_data;
211        self
212    }
213
214    #[must_use]
215    pub fn with_beam_function(
216        mut self,
217        beam_function: ElectromagneticEmissionBeamFunction,
218    ) -> Self {
219        self.beam_function = beam_function;
220        self
221    }
222
223    #[must_use]
224    pub fn with_high_density_track_jam(
225        mut self,
226        high_density_track_jam: HighDensityTrackJam,
227    ) -> Self {
228        self.high_density_track_jam = high_density_track_jam;
229        self
230    }
231
232    #[must_use]
233    pub fn with_beam_status(mut self, beam_status: BeamStatusBeamState) -> Self {
234        self.beam_status = beam_status;
235        self
236    }
237
238    #[must_use]
239    pub fn with_jamming_technique(mut self, jamming_technique: JammingTechnique) -> Self {
240        self.jamming_technique = jamming_technique;
241        self
242    }
243
244    #[allow(clippy::return_self_not_must_use)]
245    pub fn with_track_jams(mut self, track_jam_data: &mut Vec<TrackJam>) -> Self {
246        self.track_jam_data.append(track_jam_data);
247        self
248    }
249
250    #[must_use]
251    pub fn with_track_jam(mut self, track_jam_data: TrackJam) -> Self {
252        self.track_jam_data.push(track_jam_data);
253        self
254    }
255
256    #[must_use]
257    pub fn beam_data_length_bytes(&self) -> u16 {
258        BEAM_BASE_LENGTH + (TRACK_JAM_BASE_LENGTH * self.track_jam_data.len() as u16)
259    }
260}
261
262/// 6.2.22 EE Fundamental Parameter Data record
263#[derive(Copy, Clone, Default, Debug, PartialEq)]
264#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
265pub struct FundamentalParameterData {
266    pub frequency: f32,
267    pub frequency_range: f32,
268    pub effective_power: f32,
269    pub pulse_repetition_frequency: f32,
270    pub pulse_width: f32,
271}
272
273impl FundamentalParameterData {
274    #[must_use]
275    pub fn new() -> Self {
276        Self {
277            frequency: 0.0,
278            frequency_range: 0.0,
279            effective_power: 0.0,
280            pulse_repetition_frequency: 0.0,
281            pulse_width: 0.0,
282        }
283    }
284
285    #[must_use]
286    pub fn with_frequency(mut self, frequency: f32) -> Self {
287        self.frequency = frequency;
288        self
289    }
290
291    #[must_use]
292    pub fn with_frequency_range(mut self, frequency_range: f32) -> Self {
293        self.frequency_range = frequency_range;
294        self
295    }
296
297    #[must_use]
298    pub fn with_effective_power(mut self, effective_power: f32) -> Self {
299        self.effective_power = effective_power;
300        self
301    }
302
303    #[must_use]
304    pub fn with_pulse_repetition_frequency(mut self, pulse_repetition_frequency: f32) -> Self {
305        self.pulse_repetition_frequency = pulse_repetition_frequency;
306        self
307    }
308
309    #[must_use]
310    pub fn with_pulse_width(mut self, pulse_width: f32) -> Self {
311        self.pulse_width = pulse_width;
312        self
313    }
314}
315
316/// 6.2.49 Jamming Technique record
317#[derive(Clone, Default, Debug, PartialEq)]
318#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
319pub struct JammingTechnique {
320    pub kind: u8,
321    pub category: u8,
322    pub subcategory: u8,
323    pub specific: u8,
324}
325
326impl JammingTechnique {
327    #[must_use]
328    pub fn new() -> Self {
329        Self {
330            kind: 0,
331            category: 0,
332            subcategory: 0,
333            specific: 0,
334        }
335    }
336
337    #[must_use]
338    pub fn with_kind(mut self, kind: u8) -> Self {
339        self.kind = kind;
340        self
341    }
342
343    #[must_use]
344    pub fn with_category(mut self, category: u8) -> Self {
345        self.category = category;
346        self
347    }
348
349    #[must_use]
350    pub fn with_subcategory(mut self, subcategory: u8) -> Self {
351        self.subcategory = subcategory;
352        self
353    }
354
355    #[must_use]
356    pub fn with_specific(mut self, specific: u8) -> Self {
357        self.specific = specific;
358        self
359    }
360}
361
362/// 6.2.90 Track/Jam Data record
363#[derive(Clone, Default, Debug, PartialEq)]
364#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
365pub struct TrackJam {
366    pub entity_id: EntityId,
367    pub emitter: u8,
368    pub beam: u8,
369}
370
371impl TrackJam {
372    #[must_use]
373    pub fn new() -> Self {
374        Self {
375            entity_id: EntityId::default(),
376            emitter: 0,
377            beam: 0,
378        }
379    }
380
381    #[must_use]
382    pub fn with_entity_id(mut self, entity_id: EntityId) -> Self {
383        self.entity_id = entity_id;
384        self
385    }
386
387    #[must_use]
388    pub fn with_emitter(mut self, emitter: u8) -> Self {
389        self.emitter = emitter;
390        self
391    }
392
393    #[must_use]
394    pub fn with_beam(mut self, beam: u8) -> Self {
395        self.beam = beam;
396        self
397    }
398}