Skip to main content

copc_core/
info.rs

1use crate::{Error, Result};
2
3pub const COPC_INFO_BYTES: usize = 160;
4
5/// COPC info VLR payload.
6#[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}