1use basic_id::BasicId;
2use location::HeightType;
3use location::Location;
4use location::OperationalStatus;
5
6extern crate std;
7
8use crate::data::operator_id::OperatorId;
9use crate::data::system::ClassificationType;
10use crate::data::system::System;
11use crate::data::*;
12use crate::MAX_ID_BYTE_SIZE;
13use crate::OPEN_DRONE_ID_AD_CODE;
14
15const VERSION: u8 = 2;
16
17pub fn to_service_data(msg: &RemoteIDMessage, message_counter: u8) -> [u8; 27] {
18 let mut data = [0u8; 27];
19
20 data[0] = OPEN_DRONE_ID_AD_CODE;
21 data[1] = message_counter;
22
23 to_message_buffer(msg, &mut data[2..]);
24
25 data
26}
27
28pub fn to_message_buffer(msg: &RemoteIDMessage, data: &mut [u8]) {
29 match msg {
30 RemoteIDMessage::BasicID(basic_id) => {
31 data[0] = (basic_id::MESSAGE_TYPE << 4) | VERSION;
32 encode_basic_id(basic_id, data);
33 }
34
35 RemoteIDMessage::Location(location) => {
36 data[0] = (location::MESSAGE_TYPE << 4) | VERSION;
37 encode_location(location, data);
38 }
39
40 RemoteIDMessage::OperatorId(operator_id) => {
41 data[0] = (operator_id::MESSAGE_TYPE << 4) | VERSION;
42 encode_operator_id(operator_id, data);
43 }
44
45 RemoteIDMessage::System(system) => {
46 data[0] = (system::MESSAGE_TYPE << 4) | VERSION;
47 encode_system(system, data);
48 }
49 }
50}
51
52fn encode_basic_id(msg: &BasicId, target: &mut [u8]) {
53 let first_nibble: u8 = msg.id_type.into();
54 let last_nibble: u8 = msg.ua_type.into();
55 target[1] = (first_nibble << 4) | last_nibble;
56
57 target[2..(MAX_ID_BYTE_SIZE + 2)].clone_from_slice(&msg.uas_id);
58}
59
60fn encode_location(msg: &Location, target: &mut [u8]) {
61 let operational_status: u8 = OperationalStatus::into(msg.operational_status);
62 let height_type: u8 = HeightType::into(msg.height_type);
63 let ew_direction_segment: u8 = if msg.track_direction > 180 { 1 } else { 0 };
64
65 let speed_multiplier: u8 = if msg.speed > 255. * 0.25 { 1 } else { 0 };
66
67 target[1] =
68 operational_status << 3 | height_type << 2 | ew_direction_segment << 1 | speed_multiplier;
69
70 target[2] = if msg.track_direction > 180 {
72 (msg.track_direction - 180) as u8
73 } else {
74 msg.track_direction as u8
75 };
76
77 target[3] = if msg.speed <= 255. * 0.25 {
79 (msg.speed / 0.25) as u8
80 } else if msg.speed > 255. * 0.25 && msg.speed < 254.25 {
81 ((msg.speed - (255. * 0.25)) / 0.75) as u8
82 } else {
83 254
84 };
85
86 target[4] = (msg.vertical_speed / 0.5) as u8;
88
89 let lat = (msg.latidute * f32::powf(10., 7.)) as u32;
91 target[5..9].clone_from_slice(&lat.to_le_bytes());
92
93 let lon = (msg.longitude * f32::powf(10., 7.)) as u32;
95 target[9..13].clone_from_slice(&lon.to_le_bytes());
96
97 let pressure_altitude = ((msg.pressure_altitude + 1000.) / 0.5) as u16;
99 target[13..15].clone_from_slice(&pressure_altitude.to_le_bytes());
100
101 let geodetic_altitude = ((msg.geodetic_altitude + 1000.) / 0.5) as u16;
103 target[15..17].clone_from_slice(&geodetic_altitude.to_le_bytes());
104
105 let height = ((msg.height + 1000.) / 0.5) as u16;
107 target[17..19].clone_from_slice(&height.to_le_bytes());
108
109 let vertical_accuracy: u8 = msg.vertical_accuracy.into();
111 let horizontal_accuracy: u8 = msg.horizontal_accuracy.into();
112 target[19] = vertical_accuracy << 4 | horizontal_accuracy;
113
114 let baro_altitude_accuracy: u8 = msg.baro_altitude_accuracy.into();
116 let speed_accuracy: u8 = msg.speed_accuracy.into();
117 target[20] = baro_altitude_accuracy << 4 | speed_accuracy;
118
119 let timestamp = (msg.timestamp * 10.) as u16;
121 target[21..23].clone_from_slice(×tamp.to_le_bytes());
122
123 let timestamp_accuracy = if let Some(acc) = msg.timestamp_accuracy {
125 (acc.as_secs_f32() / 0.1) as u8
126 } else {
127 0
128 };
129 target[23] = timestamp_accuracy;
130
131 target[24] = 0;
133}
134
135fn encode_operator_id(msg: &OperatorId, target: &mut [u8]) {
136 target[1] = Into::<u8>::into(msg.id_type);
138
139 target[2..(MAX_ID_BYTE_SIZE + 2)].clone_from_slice(&msg.operator_id);
141}
142
143fn encode_system(msg: &System, target: &mut [u8]) {
144 let classification_type: u8 = msg.classification_type as u8;
147 let operator_location_type: u8 = msg.operator_location_type as u8;
148 target[1] = (classification_type << 2) | operator_location_type;
149
150 let lat = (msg.operator_latidute * f32::powf(10., 7.)) as u32;
152 target[2..6].clone_from_slice(&lat.to_le_bytes());
153
154 let lon = (msg.operator_longitude * f32::powf(10., 7.)) as u32;
156 target[6..10].clone_from_slice(&lon.to_le_bytes());
157
158 target[10..12].clone_from_slice(&msg.area_count.to_le_bytes());
160
161 let area_radius = (msg.area_radius / 10.) as u8;
163 target[12] = area_radius;
164
165 let area_ceiling = ((msg.area_ceiling + 1000.) / 0.5) as u16;
167 target[13..15].clone_from_slice(&area_ceiling.to_le_bytes());
168
169 let area_floor = ((msg.area_floor + 1000.) / 0.5) as u16;
171 target[15..17].clone_from_slice(&area_floor.to_le_bytes());
172
173 target[17] = if msg.classification_type == ClassificationType::EuropeanUnion {
175 let (cat, class): (u8, u8) = (
176 msg.ua_classification.category.into(),
177 msg.ua_classification.class.into(),
178 );
179
180 cat << 3 | class
181 } else {
182 0
183 };
184
185 let operator_altitude = ((msg.operator_altitude + 1000.) / 0.5) as u16;
187 target[18..20].clone_from_slice(&operator_altitude.to_le_bytes());
188
189 let unix_timestamp = (msg.timestamp.timestamp() - 1546300800) as u32;
191 target[20..24].clone_from_slice(&unix_timestamp.to_le_bytes());
192
193 target[24] = 0;
195}
196
197#[cfg(test)]
198mod test {
199 extern crate std;
200
201 use chrono::DateTime;
202
203 use super::basic_id::{IdType, UAType};
204 use super::location::{HeightType, Location, OperationalStatus};
205 use crate::codec::copy_to_id;
206 use crate::codec::encode::to_service_data;
207 use crate::data::basic_id::BasicId;
208 use crate::data::operator_id::{OperatorId, OperatorIdType};
209 use crate::data::system::{
210 ClassificationType, OperatorLocationType, System, UaCategory, UaClass, UaClassification,
211 };
212 use crate::data::RemoteIDMessage;
213
214 #[test]
215 fn encode_basic_id() {
216 let basic_id = RemoteIDMessage::BasicID(BasicId {
218 id_type: IdType::SerialNumber,
219 ua_type: UAType::None,
220 uas_id: copy_to_id("1596F359746167260749".as_bytes()),
221 });
222
223 let service_data = [
224 13, 1, 2, 16, 49, 53, 57, 54, 70, 51, 53, 57, 55, 52, 54, 49, 54, 55, 50, 54, 48, 55,
225 52, 57, 0, 0, 0,
226 ];
227
228 assert_eq!(service_data, to_service_data(&basic_id, 1));
229 }
230
231 #[test]
232 fn encode_basic_id_2() {
233 let basic_id = RemoteIDMessage::BasicID(BasicId {
235 id_type: IdType::SerialNumber,
236 ua_type: UAType::None,
237 uas_id: copy_to_id("1596F3170CE908F55122".as_bytes()),
238 });
239
240 let expected = [
241 13, 1, 2, 16, 49, 53, 57, 54, 70, 51, 49, 55, 48, 67, 69, 57, 48, 56, 70, 53, 53, 49,
242 50, 50, 0, 0, 0,
243 ];
244
245 assert_eq!(expected, to_service_data(&basic_id, 1));
246 }
247
248 #[test]
249 fn encode_location_1() {
250 let location = RemoteIDMessage::Location(Location {
251 height_type: HeightType::AboveTakeoff,
252 operational_status: OperationalStatus::RemoteIdSystemFailure,
253 speed: 10.,
254 vertical_speed: 10.,
255 pressure_altitude: 190.5,
256 geodetic_altitude: 210.0,
257 baro_altitude_accuracy: crate::data::location::VerticalAccuracy::Unknown,
258 horizontal_accuracy: crate::data::location::HorizontalAccuracy::LessThan_3_m,
259 speed_accuracy: crate::data::location::SpeedAccuracy::LessThan_third_mps,
260 vertical_accuracy: crate::data::location::VerticalAccuracy::LessThan_3_m,
261 track_direction: 77,
262 latidute: 49.874855,
263 longitude: 8.912173,
264 height: 0.,
265 timestamp: 361.0,
266 timestamp_accuracy: None,
267 });
268 let expected = [
269 13, 1, 18, 32, 77, 40, 20, 128, 76, 186, 29, 200, 227, 79, 5, 77, 9, 116, 9, 208, 7,
270 91, 4, 26, 14, 0, 0,
271 ];
272 assert_eq!(expected, to_service_data(&location, 1));
273 }
274
275 #[test]
276 fn encode_system() {
277 let system = RemoteIDMessage::System(System {
278 classification_type: ClassificationType::EuropeanUnion,
279 operator_location_type: OperatorLocationType::TakeOff,
280 operator_latidute: 49.874855,
281 operator_longitude: 8.912173,
282 operator_altitude: 210.,
283 area_ceiling: -1000.,
284 area_count: 1,
285 area_floor: -1000.,
286 area_radius: 250.,
287 ua_classification: UaClassification {
288 category: UaCategory::Specific,
289 class: UaClass::Undefined,
290 },
291 timestamp: DateTime::parse_from_rfc3339(&"2024-07-04T14:05:54Z")
292 .unwrap()
293 .to_utc(),
294 });
295
296 let service_data = [
297 13, 3, 66, 4, 128, 76, 186, 29, 200, 227, 79, 5, 1, 0, 25, 0, 0, 0, 0, 16, 116, 9, 194,
298 254, 91, 10, 0,
299 ];
300 assert_eq!(service_data, to_service_data(&system, 3));
301 }
302
303 #[test]
304 fn encode_operator_id() {
305 let operator_id = RemoteIDMessage::OperatorId(OperatorId {
306 id_type: OperatorIdType::OperatorId,
307 operator_id: copy_to_id("NULL".as_bytes()),
308 });
309
310 let service_data = [
311 13, 3, 82, 0, 78, 85, 76, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
312 ];
313 assert_eq!(service_data, to_service_data(&operator_id, 3));
314 }
315
316 #[test]
317 fn encode_operator_id_2() {
318 let operator_id = RemoteIDMessage::OperatorId(OperatorId {
319 id_type: OperatorIdType::OperatorId,
320 operator_id: copy_to_id("FIN87astrdge12k8".as_bytes()),
321 });
322
323 let service_data = [
324 13, 3, 82, 0, 70, 73, 78, 56, 55, 97, 115, 116, 114, 100, 103, 101, 49, 50, 107, 56, 0,
325 0, 0, 0, 0, 0, 0,
326 ];
327 assert_eq!(service_data, to_service_data(&operator_id, 3));
328 }
329}