1use crate::wire::WireError;
32use byteorder::{BigEndian, ByteOrder};
33use core::fmt;
34use core::mem;
35use static_assertions::const_assert_eq;
36
37const_assert_eq!(WireDataId::<&[u8]>::WIRE_SIZE, mem::size_of::<u16>());
38
39enum_with_unknown! {
40 #[derive(Default)]
41 pub enum Precision(u8) {
42 #[default]
44 Float32 = 0x0,
45 Fp1220 = 0x1,
47 Fp1632 = 0x2,
49 Float64 = 0x3,
51 }
52}
53
54enum_with_unknown! {
55 #[derive(Default)]
56 pub enum CoordinateSystem(u8) {
57 #[default]
59 Enu = 0x0,
60 Ned = 0x4,
62 Nwu = 0x8,
64 }
65}
66
67enum_with_unknown! {
68 pub enum DataType(u16) {
69 Temperature = 0x0810, UtcTime = 0x1010,
74 PacketCounter = 0x1020,
75 SampleTimeFine = 0x1060,
76 SampleTimeCoarse = 0x1070,
77
78 Quaternion = 0x2010,
80 EulerAngles = 0x2030, DeltaV = 0x4010, Acceleration = 0x4020, FreeAcceleration = 0x4030, AccelerationHR = 0x4040, AltitudeEllipsoid = 0x5020, PositionEcef = 0x5030, LatLon = 0x5040, GnssPvtData = 0x7010,
95 GnssSatInfo = 0x7020,
96 GnssPvtPulse = 0x7030, RateOfTurn = 0x8020, DeltaQ = 0x8030,
101 RateOfTurnHr = 0x8040, MagneticField = 0xC020, VelocityXYZ = 0xD010,
108
109 StatusByte = 0xE010,
111 StatusWord = 0xE020,
112 DeviceId = 0xE080,
113 LocationId = 0xE090,
114 }
115}
116
117impl DataType {
118 const MASK: u16 = 0b1111_1000_1111_0000;
121}
122
123impl fmt::Display for DataType {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 match self {
126 DataType::Unknown(t) => write!(f, "Unknown(0x{:04X})", t),
127 _ => write!(f, "{:?}", self),
128 }
129 }
130}
131
132#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
133pub struct DataId {
134 pub data_type: DataType,
135 pub precision: Precision,
136 pub coordinate_system: CoordinateSystem,
137}
138
139impl DataId {
140 pub fn new(
141 data_type: DataType,
142 precision: Precision,
143 coordinate_system: CoordinateSystem,
144 ) -> Self {
145 DataId {
146 data_type,
147 precision,
148 coordinate_system,
149 }
150 }
151
152 pub fn to_wire(self) -> u16 {
153 let group_type = DataType::MASK & u16::from(self.data_type);
154 let precision = 0b11 & u8::from(self.precision) as u16;
155 let coordinate_system = 0b1100 & u8::from(self.coordinate_system) as u16;
156 group_type | coordinate_system | precision
157 }
158
159 pub fn from_wire(value: u16) -> Self {
160 let precision = value & 0b11;
161 let coordinate_system = value & 0b1100;
162 let group_type = value & DataType::MASK;
163 DataId {
164 data_type: DataType::from(group_type),
165 precision: Precision::from(precision as u8),
166 coordinate_system: CoordinateSystem::from(coordinate_system as u8),
167 }
168 }
169
170 pub fn from_data_type(data_type: DataType) -> Self {
171 DataId {
172 data_type,
173 precision: Precision::default(),
174 coordinate_system: CoordinateSystem::default(),
175 }
176 }
177
178 pub fn data_type(&self) -> DataType {
179 self.data_type
180 }
181
182 pub fn precision(&self) -> Precision {
183 self.precision
184 }
185
186 pub fn coordinate_system(&self) -> CoordinateSystem {
187 self.coordinate_system
188 }
189}
190
191impl From<u16> for DataId {
192 fn from(value: u16) -> Self {
193 DataId::from_wire(value)
194 }
195}
196
197impl From<DataId> for u16 {
198 fn from(value: DataId) -> Self {
199 value.to_wire()
200 }
201}
202
203impl fmt::Display for DataId {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 write!(
206 f,
207 "DataId(0x{:04X}, {}, {:?}, {:?})",
208 self.to_wire(),
209 self.data_type(),
210 self.precision(),
211 self.coordinate_system(),
212 )
213 }
214}
215
216#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
217pub struct WireDataId<T: AsRef<[u8]>> {
218 buffer: T,
219}
220
221mod field {
222 use crate::wire::Field;
223
224 pub const DATA_ID: Field = 0..2;
225}
226
227impl<T: AsRef<[u8]>> WireDataId<T> {
228 pub const WIRE_SIZE: usize = mem::size_of::<u16>();
229
230 pub fn new_unchecked(buffer: T) -> WireDataId<T> {
231 WireDataId { buffer }
232 }
233
234 pub fn new(buffer: T) -> Result<WireDataId<T>, WireError> {
235 let f = Self::new_unchecked(buffer);
236 f.check_len()?;
237 Ok(f)
238 }
239
240 pub fn check_len(&self) -> Result<(), WireError> {
241 let len = self.buffer.as_ref().len();
242 if len < Self::WIRE_SIZE {
243 Err(WireError::MissingBytes)
244 } else {
245 Ok(())
246 }
247 }
248
249 pub fn into_inner(self) -> T {
250 self.buffer
251 }
252
253 #[inline]
254 pub fn buffer_len() -> usize {
255 Self::WIRE_SIZE
256 }
257
258 #[inline]
259 pub fn data_id(&self) -> DataId {
260 let data = self.buffer.as_ref();
261 let value = BigEndian::read_u16(&data[field::DATA_ID]);
262 DataId::from(value)
263 }
264}
265
266impl<T: AsRef<[u8]> + AsMut<[u8]>> WireDataId<T> {
267 #[inline]
268 pub fn set_data_id(&mut self, value: DataId) {
269 let data = self.buffer.as_mut();
270 BigEndian::write_u16(&mut data[field::DATA_ID], u16::from(value));
271 }
272}
273
274impl<T: AsRef<[u8]>> AsRef<[u8]> for WireDataId<T> {
275 fn as_ref(&self) -> &[u8] {
276 self.buffer.as_ref()
277 }
278}
279
280#[cfg(test)]
281pub(crate) mod propt {
282 use super::*;
283 use proptest::{
284 arbitrary::Arbitrary,
285 num,
286 prelude::{any, RngCore},
287 prop_compose,
288 strategy::{NewTree, Strategy, ValueTree},
289 test_runner::TestRunner,
290 };
291
292 impl Precision {
293 const MASK: u8 = Precision::Float32.into_inner()
294 | Precision::Fp1220.into_inner()
295 | Precision::Fp1632.into_inner()
296 | Precision::Float64.into_inner();
297 }
298
299 pub struct PrecisionBinarySearch(num::u8::BinarySearch);
300
301 impl ValueTree for PrecisionBinarySearch {
302 type Value = Precision;
303
304 fn current(&self) -> Precision {
305 let v = self.0.current();
306 Precision::from(v & Precision::MASK)
307 }
308
309 fn simplify(&mut self) -> bool {
310 self.0.simplify()
311 }
312
313 fn complicate(&mut self) -> bool {
314 self.0.complicate()
315 }
316 }
317
318 #[derive(Debug)]
319 pub struct AnyPrecision;
320
321 impl Strategy for AnyPrecision {
322 type Tree = PrecisionBinarySearch;
323 type Value = Precision;
324
325 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
326 Ok(PrecisionBinarySearch(num::u8::BinarySearch::new(
327 runner.rng().next_u32() as u8,
328 )))
329 }
330 }
331
332 impl Arbitrary for Precision {
333 type Parameters = ();
334 type Strategy = AnyPrecision;
335
336 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
337 AnyPrecision
338 }
339 }
340
341 pub fn gen_precision() -> impl Strategy<Value = Precision> {
342 any::<Precision>()
343 }
344
345 impl CoordinateSystem {
346 const MASK: u8 = CoordinateSystem::Enu.into_inner()
347 | CoordinateSystem::Ned.into_inner()
348 | CoordinateSystem::Nwu.into_inner();
349 }
350
351 pub struct CoordinateSystemBinarySearch(num::u8::BinarySearch);
352
353 impl ValueTree for CoordinateSystemBinarySearch {
354 type Value = CoordinateSystem;
355
356 fn current(&self) -> CoordinateSystem {
357 let v = self.0.current();
358 CoordinateSystem::from(v & CoordinateSystem::MASK)
359 }
360
361 fn simplify(&mut self) -> bool {
362 self.0.simplify()
363 }
364
365 fn complicate(&mut self) -> bool {
366 self.0.complicate()
367 }
368 }
369
370 #[derive(Debug)]
371 pub struct AnyCoordinateSystem;
372
373 impl Strategy for AnyCoordinateSystem {
374 type Tree = CoordinateSystemBinarySearch;
375 type Value = CoordinateSystem;
376
377 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
378 Ok(CoordinateSystemBinarySearch(num::u8::BinarySearch::new(
379 runner.rng().next_u32() as u8,
380 )))
381 }
382 }
383
384 impl Arbitrary for CoordinateSystem {
385 type Parameters = ();
386 type Strategy = AnyCoordinateSystem;
387
388 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
389 AnyCoordinateSystem
390 }
391 }
392
393 pub fn gen_coordinate_system() -> impl Strategy<Value = CoordinateSystem> {
394 any::<CoordinateSystem>()
395 }
396
397 pub struct DataTypeBinarySearch(num::u16::BinarySearch);
398
399 impl ValueTree for DataTypeBinarySearch {
400 type Value = DataType;
401
402 fn current(&self) -> DataType {
403 let v = self.0.current();
404 DataType::from(v & DataType::MASK)
405 }
406
407 fn simplify(&mut self) -> bool {
408 self.0.simplify()
409 }
410
411 fn complicate(&mut self) -> bool {
412 self.0.complicate()
413 }
414 }
415
416 #[derive(Debug)]
417 pub struct AnyDataType;
418
419 impl Strategy for AnyDataType {
420 type Tree = DataTypeBinarySearch;
421 type Value = DataType;
422
423 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
424 Ok(DataTypeBinarySearch(num::u16::BinarySearch::new(
425 runner.rng().next_u32() as u16,
426 )))
427 }
428 }
429
430 impl Arbitrary for DataType {
431 type Parameters = ();
432 type Strategy = AnyDataType;
433
434 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
435 AnyDataType
436 }
437 }
438
439 pub fn gen_data_type() -> impl Strategy<Value = DataType> {
440 any::<DataType>()
441 }
442
443 prop_compose! {
444 pub fn gen_data_id()(
445 data_type in gen_data_type(),
446 precision in gen_precision(),
447 coordinate_system in gen_coordinate_system(),
448 ) -> DataId {
449 DataId::new(data_type, precision, coordinate_system)
450 }
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457 use pretty_assertions::assert_eq;
458 use propt::*;
459 use proptest::prelude::*;
460
461 static WIRE_BYTES: [u8; 2] = [0x20, 0x16];
462
463 #[test]
464 fn buffer_len() {
465 assert_eq!(WireDataId::<&[u8]>::buffer_len(), mem::size_of::<u16>());
466 assert_eq!(WireDataId::<&[u8]>::WIRE_SIZE, mem::size_of::<u16>());
467 }
468
469 #[test]
470 fn construct() {
471 let mut bytes = [0xFF; 2];
472 let mut w = WireDataId::new_unchecked(&mut bytes[..]);
473 assert_eq!(w.check_len(), Ok(()));
474 w.set_data_id(DataId::new(
475 DataType::Quaternion,
476 Precision::Fp1632,
477 CoordinateSystem::Ned,
478 ));
479 assert_eq!(w.into_inner(), &WIRE_BYTES[..]);
480 }
481
482 #[test]
483 fn deconstruct() {
484 let w = WireDataId::new(&WIRE_BYTES[..]).unwrap();
485 assert_eq!(
486 w.data_id(),
487 DataId::new(
488 DataType::Quaternion,
489 Precision::Fp1632,
490 CoordinateSystem::Ned,
491 )
492 );
493 }
494
495 #[test]
496 fn missing_bytes() {
497 let bytes = [0xFF; 2 - 1];
498 assert_eq!(bytes.len(), WireDataId::<&[u8]>::buffer_len() - 1);
499 let w = WireDataId::new(&bytes[..]);
500 assert_eq!(w.unwrap_err(), WireError::MissingBytes);
501 }
502
503 proptest! {
504 #[test]
505 fn round_trip_precision(v_in in gen_precision()) {
506 let wire = u8::from(v_in);
507 let v_out = Precision::from(wire);
508 assert_eq!(v_in, v_out);
509 }
510
511 #[test]
512 fn round_trip_coordinate_system(v_in in gen_coordinate_system()) {
513 let wire = u8::from(v_in);
514 let v_out = CoordinateSystem::from(wire);
515 assert_eq!(v_in, v_out);
516 }
517
518 #[test]
519 fn round_trip_data_type(v_in in gen_data_type()) {
520 let wire = u16::from(v_in);
521 let v_out = DataType::from(wire);
522 assert_eq!(v_in, v_out);
523 }
524
525 #[test]
526 fn round_trip_data_id(v_in in gen_data_id()) {
527 let wire = u16::from(v_in).to_be_bytes();
528 let v_out = DataId::from(u16::from_be_bytes(wire));
529 assert_eq!(v_in, v_out);
530 }
531 }
532}