1use crate::{Error, Result};
2
3pub const COPC_INFO_BYTES: usize = 160;
4
5#[derive(Clone, Copy, Debug, PartialEq)]
7pub struct CopcInfo {
8 pub center: (f64, f64, f64),
9 pub halfsize: f64,
10 pub spacing: f64,
11 pub root_hier_offset: u64,
12 pub root_hier_size: u64,
13 pub gpstime_min: f64,
14 pub gpstime_max: f64,
15}
16
17impl CopcInfo {
18 pub fn from_le_bytes(bytes: &[u8]) -> Result<Self> {
19 if bytes.len() < COPC_INFO_BYTES {
20 return Err(Error::InvalidData(format!(
21 "COPC info payload is {} bytes, expected at least {}",
22 bytes.len(),
23 COPC_INFO_BYTES
24 )));
25 }
26 Ok(Self {
27 center: (read_f64(bytes, 0), read_f64(bytes, 8), read_f64(bytes, 16)),
28 halfsize: read_f64(bytes, 24),
29 spacing: read_f64(bytes, 32),
30 root_hier_offset: read_u64(bytes, 40),
31 root_hier_size: read_u64(bytes, 48),
32 gpstime_min: read_f64(bytes, 56),
33 gpstime_max: read_f64(bytes, 64),
34 })
35 }
36
37 pub fn write_le_bytes(self) -> [u8; COPC_INFO_BYTES] {
38 let mut out = [0u8; COPC_INFO_BYTES];
39 write_f64(&mut out, 0, self.center.0);
40 write_f64(&mut out, 8, self.center.1);
41 write_f64(&mut out, 16, self.center.2);
42 write_f64(&mut out, 24, self.halfsize);
43 write_f64(&mut out, 32, self.spacing);
44 write_u64(&mut out, 40, self.root_hier_offset);
45 write_u64(&mut out, 48, self.root_hier_size);
46 write_f64(&mut out, 56, self.gpstime_min);
47 write_f64(&mut out, 64, self.gpstime_max);
48 out
49 }
50}
51
52fn read_u64(bytes: &[u8], offset: usize) -> u64 {
53 u64::from_le_bytes(
54 bytes[offset..offset + 8]
55 .try_into()
56 .expect("u64 width checked by caller"),
57 )
58}
59
60fn read_f64(bytes: &[u8], offset: usize) -> f64 {
61 f64::from_le_bytes(
62 bytes[offset..offset + 8]
63 .try_into()
64 .expect("f64 width checked by caller"),
65 )
66}
67
68fn write_u64(bytes: &mut [u8], offset: usize, value: u64) {
69 bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
70}
71
72fn write_f64(bytes: &mut [u8], offset: usize, value: f64) {
73 bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn copc_info_round_trips() {
82 let info = CopcInfo {
83 center: (1.0, 2.0, 3.0),
84 halfsize: 4.0,
85 spacing: 0.25,
86 root_hier_offset: 100,
87 root_hier_size: 320,
88 gpstime_min: 10.0,
89 gpstime_max: 20.0,
90 };
91 assert_eq!(
92 CopcInfo::from_le_bytes(&info.write_le_bytes()).unwrap(),
93 info
94 );
95 }
96}