1use crate::common::entity_state::model::{
2 DrEulerAngles, DrOtherParameters, DrWorldOrientationQuaternion, EntityAppearance,
3};
4use crate::common::entity_state::model::{DrParameters, EntityMarking, EntityState};
5use crate::common::model::EntityType;
6use crate::common::{Serialize, SerializePdu, SupportedVersion};
7use crate::enumerations::{DrParametersType, ForceId};
8use crate::v6::entity_state::model::EntityCapabilities;
9use bytes::{BufMut, BytesMut};
10
11impl SerializePdu for EntityState {
12 fn serialize_pdu(&self, version: SupportedVersion, buf: &mut BytesMut) -> u16 {
13 let entity_id_bytes = self.entity_id.serialize(buf);
14 let force_id_bytes = self.force_id.serialize(buf);
15 buf.put_u8(self.variable_parameters.len() as u8);
16
17 let entity_type_bytes = self.entity_type.serialize(buf);
18 let alt_entity_type_bytes = self.alternative_entity_type.serialize(buf);
19
20 let linear_velocity_bytes = self.entity_linear_velocity.serialize(buf);
21 let location_bytes = self.entity_location.serialize(buf);
22 let orientation_bytes = self.entity_orientation.serialize(buf);
23
24 let appearance_bytes = self.entity_appearance.serialize(buf);
25 let dr_params_bytes = self.dead_reckoning_parameters.serialize(buf);
26
27 let marking_bytes = self.entity_marking.serialize(buf);
28 let capabilities_bytes = match version {
29 SupportedVersion::V6 => {
30 let capabilities: EntityCapabilities = self.entity_capabilities.into();
31 capabilities.serialize(buf)
32 }
33 SupportedVersion::V7 => {
34 buf.put_u32(self.entity_capabilities.into());
35 4
36 }
37 SupportedVersion::Unsupported => {
38 buf.put_u32(0u32);
39 4
40 }
41 };
42
43 let variable_params_bytes: u16 = self
44 .variable_parameters
45 .iter()
46 .map(|param| param.serialize(buf))
47 .sum();
48
49 entity_id_bytes
50 + force_id_bytes
51 + 1
52 + entity_type_bytes
53 + alt_entity_type_bytes
54 + linear_velocity_bytes
55 + location_bytes
56 + orientation_bytes
57 + appearance_bytes
58 + dr_params_bytes
59 + capabilities_bytes
60 + marking_bytes
61 + variable_params_bytes
62 }
63}
64
65impl Serialize for EntityAppearance {
66 fn serialize(&self, buf: &mut BytesMut) -> u16 {
67 let appearance: u32 = u32::from(self);
68 buf.put_u32(appearance);
69 4
70 }
71}
72
73impl Serialize for DrParameters {
74 fn serialize(&self, buf: &mut BytesMut) -> u16 {
75 buf.put_u8(self.algorithm.into());
76 let other_parameters_bytes = self.other_parameters.serialize(buf);
77 let lin_acc_bytes = self.linear_acceleration.serialize(buf);
78 let ang_vel_bytes = self.angular_velocity.serialize(buf);
79 1 + other_parameters_bytes + lin_acc_bytes + ang_vel_bytes
80 }
81}
82
83impl Serialize for DrOtherParameters {
84 fn serialize(&self, buf: &mut BytesMut) -> u16 {
85 match self {
86 DrOtherParameters::None(bytes) => {
87 for x in bytes {
88 buf.put_u8(*x);
89 }
90 15
91 }
92 DrOtherParameters::LocalEulerAngles(params) => params.serialize(buf),
93 DrOtherParameters::WorldOrientationQuaternion(params) => params.serialize(buf),
94 }
95 }
96}
97
98impl Serialize for DrEulerAngles {
99 fn serialize(&self, buf: &mut BytesMut) -> u16 {
100 buf.put_u8(DrParametersType::LocalEulerAngles_Yaw_Pitch_Roll_.into());
101 buf.put_u16(0u16);
102 buf.put_f32(self.local_yaw);
103 buf.put_f32(self.local_pitch);
104 buf.put_f32(self.local_roll);
105 15
106 }
107}
108
109impl Serialize for DrWorldOrientationQuaternion {
110 fn serialize(&self, buf: &mut BytesMut) -> u16 {
111 buf.put_u8(DrParametersType::WorldOrientationQuaternion.into());
112 buf.put_u16(self.nil);
113 buf.put_f32(self.x);
114 buf.put_f32(self.y);
115 buf.put_f32(self.z);
116 15
117 }
118}
119
120impl Serialize for ForceId {
121 fn serialize(&self, buf: &mut BytesMut) -> u16 {
122 let force_id = *self;
123 buf.put_u8(force_id.into());
124 1
125 }
126}
127
128impl Serialize for EntityType {
129 fn serialize(&self, buf: &mut BytesMut) -> u16 {
130 buf.put_u8(self.kind.into());
131 buf.put_u8(self.domain.into());
132 buf.put_u16(self.country.into());
133 buf.put_u8(self.category);
134 buf.put_u8(self.subcategory);
135 buf.put_u8(self.specific);
136 buf.put_u8(self.extra);
137 8
138 }
139}
140
141impl Serialize for EntityMarking {
142 fn serialize(&self, buf: &mut BytesMut) -> u16 {
143 buf.put_u8(self.marking_character_set.into());
144 let num_pad = 11 - self.marking_string.len();
145 let marking = self.marking_string.clone(); buf.put_slice(&marking.into_bytes()[..]);
148 (0..num_pad).for_each(|_i| buf.put_u8(0x20));
149 12
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use crate::BodyRaw;
156 use crate::common::Serialize;
157 use crate::common::entity_state::model::{
158 DrOtherParameters, DrParameters, EntityAppearance, EntityMarking, EntityState,
159 };
160 use crate::common::model::{
161 ArticulatedPart, EntityId, EntityType, Location, Orientation, Pdu, PduHeader, TimeUnits,
162 Timestamp, VariableParameter, VectorF32,
163 };
164 use crate::enumerations::{
165 AirPlatformAppearance, AppearanceAntiCollisionDayNight, AppearanceCanopy, AppearanceDamage,
166 AppearanceEntityOrObjectState, AppearanceNVGMode, AppearanceNavigationPositionBrightness,
167 AppearancePaintScheme, AppearanceTrailingEffects,
168 };
169 use crate::enumerations::{
170 ArticulatedPartsTypeClass, ArticulatedPartsTypeMetric, Country, DeadReckoningAlgorithm,
171 EntityKind, EntityMarkingCharacterSet, ForceId, PduType, PlatformDomain,
172 };
173 use alloc::string::ToString;
174 use bytes::BytesMut;
175
176 #[test]
177 fn entity_marking() {
178 let marking = EntityMarking {
179 marking_character_set: EntityMarkingCharacterSet::ASCII,
180 marking_string: "EYE 10".to_string(),
181 };
182 let mut buf = BytesMut::with_capacity(11);
183
184 marking.serialize(&mut buf);
185
186 let expected: [u8; 12] = [
187 0x01, 0x45, 0x59, 0x45, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20,
188 ];
189 assert_eq!(buf.as_ref(), expected.as_ref());
190 }
191
192 #[test]
193 fn articulated_part() {
194 let articulated_part = VariableParameter::Articulated(ArticulatedPart {
195 change_indicator: 0,
196 attachment_id: 0,
197 type_class: ArticulatedPartsTypeClass::LandingGear,
198 type_metric: ArticulatedPartsTypeMetric::Position,
199 parameter_value: 1.0,
200 });
201
202 let mut buf = BytesMut::with_capacity(11);
203
204 articulated_part.serialize(&mut buf);
205
206 let expected: [u8; 16] = [
207 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00,
208 0x00, 0x00,
209 ];
210 assert_eq!(buf.as_ref(), expected.as_ref());
211 }
212
213 #[test]
214 #[allow(clippy::too_many_lines)]
215 fn entity_state_pdu() {
216 let header = PduHeader::new_v6(1, PduType::EntityState);
217
218 let body = EntityState::builder()
219 .with_entity_id(EntityId::new(500, 900, 14))
220 .with_force_id(ForceId::Friendly)
221 .with_entity_type(EntityType {
222 kind: EntityKind::Platform, domain: PlatformDomain::Air, country: Country::Netherlands_NLD_, category: 50, subcategory: 4, specific: 4, extra: 0
223 })
224 .with_alternative_entity_type(EntityType {
225 kind: EntityKind::Platform, domain: PlatformDomain::Air, country: Country::Netherlands_NLD_, category: 50, subcategory: 4, specific: 4, extra: 0
226 })
227 .with_velocity(VectorF32 {
228 first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
229 })
230 .with_location(Location {
231 x_coordinate: 0f64, y_coordinate : 0f64, z_coordinate: 0f64
232 })
233 .with_orientation(Orientation {
234 psi: 0f32, theta: 0f32, phi: 0f32
235 })
236 .with_appearance(EntityAppearance::AirPlatform(AirPlatformAppearance {
237 paint_scheme: AppearancePaintScheme::Camouflage,
238 propulsion_killed: false,
239 nvg_mode: AppearanceNVGMode::default(),
240 damage: AppearanceDamage::default(),
241 is_smoke_emanating: true,
242 is_engine_emitting_smoke: false,
243 trailing_effects: AppearanceTrailingEffects::Medium,
244 canopy_troop_door: AppearanceCanopy::NotApplicable,
245 landing_lights_on: false,
246 navigation_lights_on: false,
247 anticollision_lights_on: true,
248 is_flaming: false,
249 afterburner_on: true,
250 lower_anticollision_light_on: false,
251 upper_anticollision_light_on: false,
252 anticollision_light_day_night: AppearanceAntiCollisionDayNight::Night,
253 is_blinking: false,
254 is_frozen: false,
255 power_plant_on: false,
256 state: AppearanceEntityOrObjectState::Deactivated,
257 formation_lights_on: false,
258 landing_gear_extended: false,
259 cargo_doors_opened: true,
260 navigation_position_brightness: AppearanceNavigationPositionBrightness::Dim,
261 spot_search_light_1_on: false,
262 interior_lights_on: false,
263 reverse_thrust_engaged: false,
264 weightonwheels: true,
265 }))
266 .with_dead_reckoning_parameters(DrParameters {
267 algorithm: DeadReckoningAlgorithm::DRM_RVW_HighSpeedOrManeuveringEntityWithExtrapolationOfOrientation,
268 other_parameters: DrOtherParameters::None([0u8;15]),
269 linear_acceleration: VectorF32 {
270 first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
271 },
272 angular_velocity: VectorF32 {
273 first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
274 }
275 })
276 .with_marking(EntityMarking {
277 marking_character_set: EntityMarkingCharacterSet::ASCII,
278 marking_string: "EYE 10".to_string()
279 })
280 .with_capabilities_flags(false, false, false, false)
281 .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
282 change_indicator: 0,
283 attachment_id: 0,
284 type_class: ArticulatedPartsTypeClass::LandingGear,
285 type_metric: ArticulatedPartsTypeMetric::Position,
286 parameter_value: 1.0
287 }))
288 .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
289 change_indicator: 0,
290 attachment_id: 0,
291 type_class: ArticulatedPartsTypeClass::PrimaryTurretNumber1,
292 type_metric: ArticulatedPartsTypeMetric::Azimuth,
293 parameter_value: 2.0
294 }))
295 .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
296 change_indicator: 0,
297 attachment_id: 0,
298 type_class: ArticulatedPartsTypeClass::PrimaryTurretNumber1,
299 type_metric: ArticulatedPartsTypeMetric::AzimuthRate,
300 parameter_value: 3.0
301 }))
302 .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
303 change_indicator: 0,
304 attachment_id: 0,
305 type_class: ArticulatedPartsTypeClass::PrimaryGunNumber1,
306 type_metric: ArticulatedPartsTypeMetric::Elevation,
307 parameter_value: 4.0
308 }))
309 .build()
310 .into_pdu_body();
311 let pdu = Pdu::finalize_from_parts(
312 header,
313 body,
314 Timestamp::Absolute(TimeUnits::new(35_791_394).unwrap()),
315 );
316
317 let mut buf = BytesMut::with_capacity(208);
318
319 pdu.serialize(&mut buf).unwrap();
320
321 let expected: [u8; 208] = [
322 0x06, 0x01, 0x01, 0x01, 0x04, 0x44, 0x44, 0x45, 0x00, 0xd0, 0x00, 0x00, 0x01, 0xf4,
323 0x03, 0x84, 0x00, 0x0e, 0x01, 0x04, 0x01, 0x02, 0x00, 0x99, 0x32, 0x04, 0x04, 0x00,
324 0x01, 0x02, 0x00, 0x99, 0x32, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328 0x84, 0x89, 0x41, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
330 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331 0x00, 0x00, 0x01, 0x45, 0x59, 0x45, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20,
332 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x3f, 0x80,
333 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0b,
334 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335 0x10, 0x0c, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336 0x00, 0x00, 0x11, 0x4d, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337 ];
338
339 assert_eq!(buf.as_ref(), expected.as_ref());
340 }
341}