1use bincode::de::Decoder;
2use bincode::enc::Encoder;
3use bincode::error::{DecodeError, EncodeError};
4use bincode::{Decode, Encode};
5use serde::{Deserialize, Serialize};
6use uom::si::acceleration::meter_per_second_squared;
7use uom::si::angular_velocity::radian_per_second;
8use uom::si::f32::{Acceleration, AngularVelocity, MagneticFluxDensity, ThermodynamicTemperature};
9use uom::si::magnetic_flux_density::microtesla;
10use uom::si::thermodynamic_temperature::degree_celsius;
11
12#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
14pub struct ImuPayload {
15 pub accel_x: Acceleration,
16 pub accel_y: Acceleration,
17 pub accel_z: Acceleration,
18 pub gyro_x: AngularVelocity,
19 pub gyro_y: AngularVelocity,
20 pub gyro_z: AngularVelocity,
21 pub temperature: ThermodynamicTemperature,
22}
23
24impl Default for ImuPayload {
25 fn default() -> Self {
26 Self {
27 accel_x: Acceleration::new::<meter_per_second_squared>(0.0),
28 accel_y: Acceleration::new::<meter_per_second_squared>(0.0),
29 accel_z: Acceleration::new::<meter_per_second_squared>(0.0),
30 gyro_x: AngularVelocity::new::<radian_per_second>(0.0),
31 gyro_y: AngularVelocity::new::<radian_per_second>(0.0),
32 gyro_z: AngularVelocity::new::<radian_per_second>(0.0),
33 temperature: ThermodynamicTemperature::new::<degree_celsius>(0.0),
34 }
35 }
36}
37
38impl ImuPayload {
39 pub fn from_raw(accel_mps2: [f32; 3], gyro_rad: [f32; 3], temperature_c: f32) -> Self {
45 let [accel_x, accel_y, accel_z] =
46 accel_mps2.map(Acceleration::new::<meter_per_second_squared>);
47 let [gyro_x, gyro_y, gyro_z] = gyro_rad.map(AngularVelocity::new::<radian_per_second>);
48 let temperature = ThermodynamicTemperature::new::<degree_celsius>(temperature_c);
49
50 Self {
51 accel_x,
52 accel_y,
53 accel_z,
54 gyro_x,
55 gyro_y,
56 gyro_z,
57 temperature,
58 }
59 }
60
61 pub fn from_uom(
63 accel_x: Acceleration,
64 accel_y: Acceleration,
65 accel_z: Acceleration,
66 gyro_x: AngularVelocity,
67 gyro_y: AngularVelocity,
68 gyro_z: AngularVelocity,
69 temperature: ThermodynamicTemperature,
70 ) -> Self {
71 Self {
72 accel_x,
73 accel_y,
74 accel_z,
75 gyro_x,
76 gyro_y,
77 gyro_z,
78 temperature,
79 }
80 }
81}
82
83impl Encode for ImuPayload {
84 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
85 Encode::encode(&self.accel_x.value, encoder)?;
86 Encode::encode(&self.accel_y.value, encoder)?;
87 Encode::encode(&self.accel_z.value, encoder)?;
88 Encode::encode(&self.gyro_x.value, encoder)?;
89 Encode::encode(&self.gyro_y.value, encoder)?;
90 Encode::encode(&self.gyro_z.value, encoder)?;
91 Encode::encode(&self.temperature.get::<degree_celsius>(), encoder)?;
92 Ok(())
93 }
94}
95
96impl Decode<()> for ImuPayload {
97 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
98 let accel_x = Acceleration::new::<meter_per_second_squared>(Decode::decode(decoder)?);
99 let accel_y = Acceleration::new::<meter_per_second_squared>(Decode::decode(decoder)?);
100 let accel_z = Acceleration::new::<meter_per_second_squared>(Decode::decode(decoder)?);
101
102 let gyro_x = AngularVelocity::new::<radian_per_second>(Decode::decode(decoder)?);
103 let gyro_y = AngularVelocity::new::<radian_per_second>(Decode::decode(decoder)?);
104 let gyro_z = AngularVelocity::new::<radian_per_second>(Decode::decode(decoder)?);
105
106 let temperature = ThermodynamicTemperature::new::<degree_celsius>(Decode::decode(decoder)?);
107
108 Ok(Self {
109 accel_x,
110 accel_y,
111 accel_z,
112 gyro_x,
113 gyro_y,
114 gyro_z,
115 temperature,
116 })
117 }
118}
119
120#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
122pub struct MagnetometerPayload {
123 pub mag_x: MagneticFluxDensity,
124 pub mag_y: MagneticFluxDensity,
125 pub mag_z: MagneticFluxDensity,
126}
127
128impl Default for MagnetometerPayload {
129 fn default() -> Self {
130 Self {
131 mag_x: MagneticFluxDensity::new::<microtesla>(0.0),
132 mag_y: MagneticFluxDensity::new::<microtesla>(0.0),
133 mag_z: MagneticFluxDensity::new::<microtesla>(0.0),
134 }
135 }
136}
137
138impl MagnetometerPayload {
139 pub fn from_raw(mag_ut: [f32; 3]) -> Self {
141 let [mag_x, mag_y, mag_z] = mag_ut.map(MagneticFluxDensity::new::<microtesla>);
142 Self {
143 mag_x,
144 mag_y,
145 mag_z,
146 }
147 }
148
149 pub fn from_uom(
151 mag_x: MagneticFluxDensity,
152 mag_y: MagneticFluxDensity,
153 mag_z: MagneticFluxDensity,
154 ) -> Self {
155 Self {
156 mag_x,
157 mag_y,
158 mag_z,
159 }
160 }
161}
162
163impl Encode for MagnetometerPayload {
164 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
165 Encode::encode(&self.mag_x.get::<microtesla>(), encoder)?;
166 Encode::encode(&self.mag_y.get::<microtesla>(), encoder)?;
167 Encode::encode(&self.mag_z.get::<microtesla>(), encoder)?;
168 Ok(())
169 }
170}
171
172impl Decode<()> for MagnetometerPayload {
173 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
174 let mag_x = MagneticFluxDensity::new::<microtesla>(Decode::decode(decoder)?);
175 let mag_y = MagneticFluxDensity::new::<microtesla>(Decode::decode(decoder)?);
176 let mag_z = MagneticFluxDensity::new::<microtesla>(Decode::decode(decoder)?);
177
178 Ok(Self {
179 mag_x,
180 mag_y,
181 mag_z,
182 })
183 }
184}
185
186#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
188pub struct ImuWithMagPayload {
189 pub imu: ImuPayload,
190 pub mag: Option<MagnetometerPayload>,
191}
192
193impl ImuWithMagPayload {
194 pub fn new(imu: ImuPayload, mag: Option<MagnetometerPayload>) -> Self {
195 Self { imu, mag }
196 }
197}
198
199impl Encode for ImuWithMagPayload {
200 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
201 Encode::encode(&self.imu, encoder)?;
202 Encode::encode(&self.mag.is_some(), encoder)?;
203 if let Some(mag) = self.mag {
204 Encode::encode(&mag, encoder)?;
205 }
206 Ok(())
207 }
208}
209
210impl Decode<()> for ImuWithMagPayload {
211 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
212 let imu = ImuPayload::decode(decoder)?;
213 let has_mag: bool = Decode::decode(decoder)?;
214 let mag = if has_mag {
215 Some(MagnetometerPayload::decode(decoder)?)
216 } else {
217 None
218 };
219
220 Ok(Self { imu, mag })
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 use bincode::config;
228
229 #[test]
230 fn round_trip_encode_decode() {
231 let payload = ImuPayload::from_raw([9.8, -2.0, 0.5], [0.01, -0.02, 0.5], 36.5);
232
233 let cfg = config::standard();
234 let mut buffer = [0u8; 128];
235 let len = bincode::encode_into_slice(payload, &mut buffer, cfg).unwrap();
236 let (decoded, used) =
237 bincode::decode_from_slice::<ImuPayload, _>(&buffer[..len], cfg).unwrap();
238
239 assert_eq!(used, len);
240 assert_eq!(decoded.accel_x.value, payload.accel_x.value);
241 assert_eq!(decoded.gyro_y.value, payload.gyro_y.value);
242 assert_eq!(
243 decoded.temperature.get::<degree_celsius>(),
244 payload.temperature.get::<degree_celsius>()
245 );
246 }
247
248 #[test]
249 fn builds_from_units() {
250 let accel = Acceleration::new::<meter_per_second_squared>(9.81);
251 let gyro = AngularVelocity::new::<radian_per_second>(0.25);
252 let temp = ThermodynamicTemperature::new::<degree_celsius>(20.0);
253
254 let payload = ImuPayload::from_uom(accel, accel, accel, gyro, gyro, gyro, temp);
255
256 assert_eq!(payload.accel_x.value, accel.value);
257 assert_eq!(payload.gyro_z.value, gyro.value);
258 }
259
260 #[test]
261 fn magnetometer_round_trip() {
262 let mag_payload = MagnetometerPayload::from_raw([42.0, -13.0, 8.0]);
263 let cfg = config::standard();
264 let mut buffer = [0u8; 128];
265 let len = bincode::encode_into_slice(mag_payload, &mut buffer, cfg).unwrap();
266 let (decoded, used) =
267 bincode::decode_from_slice::<MagnetometerPayload, _>(&buffer[..len], cfg).unwrap();
268
269 assert_eq!(used, len);
270 assert_eq!(decoded.mag_x.value, mag_payload.mag_x.value);
271 assert_eq!(decoded.mag_z.value, mag_payload.mag_z.value);
272 }
273
274 #[test]
275 fn combined_payload_handles_optional_mag() {
276 let imu = ImuPayload::from_raw([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], 22.0);
277 let mag = MagnetometerPayload::from_raw([7.0, 8.0, 9.0]);
278 let combined = ImuWithMagPayload::new(imu, Some(mag));
279
280 let cfg = config::standard();
281 let mut buffer = [0u8; 256];
282 let len = bincode::encode_into_slice(combined, &mut buffer, cfg).unwrap();
283 let (decoded, used) =
284 bincode::decode_from_slice::<ImuWithMagPayload, _>(&buffer[..len], cfg).unwrap();
285
286 assert_eq!(used, len);
287 assert_eq!(decoded.imu.accel_y.value, imu.accel_y.value);
288 assert_eq!(decoded.mag.unwrap().mag_y.value, mag.mag_y.value);
289 }
290}