remote_id/codec/
encode.rs

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    // Track Direction
71    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    // Speed
78    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    // Vertical Speed
87    target[4] = (msg.vertical_speed / 0.5) as u8;
88
89    // Latitude
90    let lat = (msg.latidute * f32::powf(10., 7.)) as u32;
91    target[5..9].clone_from_slice(&lat.to_le_bytes());
92
93    // Longitude
94    let lon = (msg.longitude * f32::powf(10., 7.)) as u32;
95    target[9..13].clone_from_slice(&lon.to_le_bytes());
96
97    // Pressure Altitude
98    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    // Geodetic Altitude
102    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    // Height
106    let height = ((msg.height + 1000.) / 0.5) as u16;
107    target[17..19].clone_from_slice(&height.to_le_bytes());
108
109    // Vertical / Horizontal Accuracy
110    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    // Baro Altitude Accuracy / Speed Accuracy
115    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    // Timestamp
120    let timestamp = (msg.timestamp * 10.) as u16;
121    target[21..23].clone_from_slice(&timestamp.to_le_bytes());
122
123    // Reserved / Timestamp Accuracy
124    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    // Reserved
132    target[24] = 0;
133}
134
135fn encode_operator_id(msg: &OperatorId, target: &mut [u8]) {
136    // OperatorIdType
137    target[1] = Into::<u8>::into(msg.id_type);
138
139    // Operator ID
140    target[2..(MAX_ID_BYTE_SIZE + 2)].clone_from_slice(&msg.operator_id);
141}
142
143fn encode_system(msg: &System, target: &mut [u8]) {
144    // Classification Type: Bits [4..2]
145    // Operator Location/Altitude source type: Bits [1..0]
146    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    // Operator Latitude
151    let lat = (msg.operator_latidute * f32::powf(10., 7.)) as u32;
152    target[2..6].clone_from_slice(&lat.to_le_bytes());
153
154    // Operator Longitude
155    let lon = (msg.operator_longitude * f32::powf(10., 7.)) as u32;
156    target[6..10].clone_from_slice(&lon.to_le_bytes());
157
158    // Area Count
159    target[10..12].clone_from_slice(&msg.area_count.to_le_bytes());
160
161    // Area Radius
162    let area_radius = (msg.area_radius / 10.) as u8;
163    target[12] = area_radius;
164
165    // Area Ceiling
166    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    // Area Floor
170    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    // UA Classification
174    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    // Operator Altitude
186    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    // Timestamp
190    let unix_timestamp = (msg.timestamp.timestamp() - 1546300800) as u32;
191    target[20..24].clone_from_slice(&unix_timestamp.to_le_bytes());
192
193    // Reserved
194    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        // DroneTag Mini
217        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        // DroneTag BS
234        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}