cu_sensor_payloads/
pointcloud.rs1use crate::{CuHandlePayload, CuHandlePayloadInit, CuHandlePayloadMeta};
2use alloc::alloc::{Layout, alloc_zeroed, handle_alloc_error};
3use alloc::boxed::Box;
4use bincode::de::Decoder;
5use bincode::error::DecodeError;
6use bincode::{Decode, Encode};
7use cu29::prelude::*;
8use cu29::units::si::f32::{Length, Ratio};
9use cu29::units::si::length::meter;
10use cu29::units::si::ratio::percent;
11use cu29_clock::CuTime;
12use cu29_soa_derive::Soa;
13use serde::{Deserialize, Serialize};
14
15pub type Distance = Length;
16pub type Reflectivity = Ratio;
17
18#[derive(
23 Default, Clone, PartialEq, Debug, Soa, Serialize, Deserialize, Encode, Decode, Reflect,
24)]
25#[reflect(from_reflect = false)]
26pub struct PointCloud {
27 pub tov: CuTime, pub x: Distance,
29 pub y: Distance,
30 pub z: Distance,
31 pub i: Reflectivity,
32 pub return_order: u8, }
34
35impl PointCloud {
36 pub fn new(tov: CuTime, x: f32, y: f32, z: f32, i: f32, return_order: Option<u8>) -> Self {
37 Self {
38 tov,
39 x: Distance::new::<meter>(x),
40 y: Distance::new::<meter>(y),
41 z: Distance::new::<meter>(z),
42 i: Reflectivity::new::<percent>(i),
43 return_order: return_order.unwrap_or(0),
44 }
45 }
46
47 pub fn new_units(
48 tov: CuTime,
49 x: Length,
50 y: Length,
51 z: Length,
52 i: Ratio,
53 return_order: Option<u8>,
54 ) -> Self {
55 Self {
56 tov,
57 x,
58 y,
59 z,
60 i,
61 return_order: return_order.unwrap_or(0),
62 }
63 }
64}
65
66pub struct PointCloudSoaHandleMeta;
67
68impl CuHandlePayloadMeta for PointCloudSoaHandleMeta {
69 const TYPE_PATH: &'static str = "cu_sensor_payloads::PointCloudSoaHandle";
70 const SHORT_TYPE_PATH: &'static str = "PointCloudSoaHandle";
71 const TYPE_IDENT: Option<&'static str> = Some("PointCloudSoaHandle");
72 const CRATE_NAME: Option<&'static str> = Some("cu_sensor_payloads");
73 const MODULE_PATH: Option<&'static str> = Some("cu_sensor_payloads");
74}
75
76pub type PointCloudSoaHandle<const N: usize> =
79 CuHandlePayload<PointCloudSoa<N>, PointCloudSoaHandleMeta>;
80
81impl<const N: usize> PointCloudSoa<N> {
82 pub fn boxed_zeroed() -> Box<Self> {
89 let layout = Layout::new::<Self>();
90 let ptr = unsafe { alloc_zeroed(layout) };
91 if ptr.is_null() {
92 handle_alloc_error(layout);
93 }
94 unsafe { Box::from_raw(ptr.cast()) }
95 }
96
97 pub fn sort(&mut self) {
99 self.quicksort(0, N - 1);
100 }
101
102 fn quicksort(&mut self, low: usize, high: usize) {
104 if low < high {
105 let pivot_index = self.partition(low, high);
106 if pivot_index > 0 {
107 self.quicksort(low, pivot_index - 1);
108 }
109 self.quicksort(pivot_index + 1, high);
110 }
111 }
112
113 fn partition(&mut self, low: usize, high: usize) -> usize {
115 let pivot = self.tov[high];
116 let mut i = low;
117 for j in low..high {
118 if self.tov[j] <= pivot {
119 self.swap(i, j);
120 i += 1;
121 }
122 }
123 self.swap(i, high);
124 i
125 }
126
127 pub fn swap(&mut self, a: usize, b: usize) {
129 self.tov.swap(a, b);
130 self.x.swap(a, b);
131 self.y.swap(a, b);
132 self.z.swap(a, b);
133 self.i.swap(a, b);
134 }
135}
136
137impl<const N: usize> CuHandlePayloadInit for PointCloudSoa<N> {
138 fn boxed_init() -> Box<Self> {
139 Self::boxed_zeroed()
140 }
141
142 fn decode_boxed<D>(decoder: &mut D) -> Result<Box<Self>, DecodeError>
143 where
144 D: Decoder<Context = ()>,
145 {
146 let mut result = Self::boxed_zeroed();
147 result.len = Decode::decode(decoder)?;
148 if result.len > N {
149 return Err(DecodeError::ArrayLengthMismatch {
150 required: N,
151 found: result.len,
152 });
153 }
154
155 for idx in 0..result.len {
156 result.tov[idx] = Decode::decode(decoder)?;
157 }
158 for idx in 0..result.len {
159 result.x[idx] = Decode::decode(decoder)?;
160 }
161 for idx in 0..result.len {
162 result.y[idx] = Decode::decode(decoder)?;
163 }
164 for idx in 0..result.len {
165 result.z[idx] = Decode::decode(decoder)?;
166 }
167 for idx in 0..result.len {
168 result.i[idx] = Decode::decode(decoder)?;
169 }
170 for idx in 0..result.len {
171 result.return_order[idx] = Decode::decode(decoder)?;
172 }
173
174 Ok(result)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use cu29_clock::CuTime;
182
183 #[test]
184 fn test_point_payload() {
185 let payload = PointCloud::new(CuTime::from_nanos(1), 1.0, 2.0, 3.0, 0.0, None);
186 assert_eq!(payload.x.value, 1.0);
187 assert_eq!(payload.y.value, 2.0);
188 assert_eq!(payload.z.value, 3.0);
189 }
190
191 #[test]
192 fn test_length_add_sub() {
193 let a = Distance::new::<meter>(1.0);
194 let b = Distance::new::<meter>(2.0);
195 let c = a + b;
196 assert_eq!(c.value, 3.0);
197 let d = c - a;
198 assert_eq!(d.value, 2.0);
199 }
200
201 #[test]
202 fn test_encoding_length() {
203 let a = Distance::new::<meter>(1.0);
204 let mut encoded = vec![0u8; 1024]; let length =
207 bincode::encode_into_slice(a, &mut encoded, bincode::config::standard()).unwrap();
208 assert_eq!(length, 4);
209 }
210}