1use crate::constants::{
8 BODY_ISB_ANGLE_COUNT, BODY_JOINT_COUNT, BODY_LANDMARK_COUNT, F64_BYTES, HAND_JOINT_COUNT,
9 HAND_LANDMARK_COUNT, MSG_BODY_ISB_ANGLES, MSG_BODY_LANDMARK, MSG_BODY_QUATERNION,
10 MSG_HAND_LANDMARK, MSG_HAND_QUATERNION, MSG_HELLO, TUPLE_BYTES,
11};
12use crate::frames::{
13 BodyIsbAnglesFrame, BodyLandmarkFrame, BodyQuaternionFrame, HandLandmarkFrame,
14 HandQuaternionFrame,
15};
16use crate::types::{HandSide, Landmark, Quaternion};
17
18pub const ENCODED_BODY_QUATERNION_LEN: usize = 1 + BODY_JOINT_COUNT * TUPLE_BYTES;
20
21pub const ENCODED_BODY_LANDMARK_LEN: usize = 1 + BODY_LANDMARK_COUNT * TUPLE_BYTES;
23
24pub const ENCODED_HAND_QUATERNION_LEN: usize = 2 + HAND_JOINT_COUNT * TUPLE_BYTES;
26
27pub const ENCODED_HAND_LANDMARK_LEN: usize = 2 + HAND_LANDMARK_COUNT * TUPLE_BYTES;
29
30pub const ENCODED_BODY_ISB_ANGLES_LEN: usize = 1 + BODY_ISB_ANGLE_COUNT * F64_BYTES;
32
33pub const ENCODED_HELLO_LEN: usize = 1 + 2;
35
36pub fn encode_body_quaternions(frame: &BodyQuaternionFrame) -> [u8; ENCODED_BODY_QUATERNION_LEN] {
38 let mut out = [0u8; ENCODED_BODY_QUATERNION_LEN];
39 out[0] = MSG_BODY_QUATERNION;
40 write_quaternions(&mut out[1..], &frame.to_array());
41 out
42}
43
44pub fn encode_body_landmarks(frame: &BodyLandmarkFrame) -> [u8; ENCODED_BODY_LANDMARK_LEN] {
46 let mut out = [0u8; ENCODED_BODY_LANDMARK_LEN];
47 out[0] = MSG_BODY_LANDMARK;
48 write_landmarks(&mut out[1..], &frame.to_array());
49 out
50}
51
52pub fn encode_hand_quaternions(
54 side: HandSide,
55 frame: &HandQuaternionFrame,
56) -> [u8; ENCODED_HAND_QUATERNION_LEN] {
57 let mut out = [0u8; ENCODED_HAND_QUATERNION_LEN];
58 out[0] = MSG_HAND_QUATERNION;
59 out[1] = side.as_byte();
60 write_quaternions(&mut out[2..], &frame.to_array());
61 out
62}
63
64pub fn encode_hand_landmarks(
66 side: HandSide,
67 frame: &HandLandmarkFrame,
68) -> [u8; ENCODED_HAND_LANDMARK_LEN] {
69 let mut out = [0u8; ENCODED_HAND_LANDMARK_LEN];
70 out[0] = MSG_HAND_LANDMARK;
71 out[1] = side.as_byte();
72 write_landmarks(&mut out[2..], &frame.to_array());
73 out
74}
75
76pub fn encode_body_isb_angles(frame: &BodyIsbAnglesFrame) -> [u8; ENCODED_BODY_ISB_ANGLES_LEN] {
78 let mut out = [0u8; ENCODED_BODY_ISB_ANGLES_LEN];
79 out[0] = MSG_BODY_ISB_ANGLES;
80 for (i, &val) in frame.to_array().iter().enumerate() {
81 write_f64(&mut out[1..], i * F64_BYTES, val);
82 }
83 out
84}
85
86pub fn encode_hello(protocol_version: u16) -> [u8; ENCODED_HELLO_LEN] {
88 let mut out = [0u8; ENCODED_HELLO_LEN];
89 out[0] = MSG_HELLO;
90 out[1..].copy_from_slice(&protocol_version.to_le_bytes());
91 out
92}
93
94fn write_quaternions(dst: &mut [u8], quats: &[Quaternion]) {
95 for (i, q) in quats.iter().enumerate() {
96 let base = i * TUPLE_BYTES;
97 write_f64(dst, base, q.w);
98 write_f64(dst, base + F64_BYTES, q.x);
99 write_f64(dst, base + 2 * F64_BYTES, q.y);
100 write_f64(dst, base + 3 * F64_BYTES, q.z);
101 }
102}
103
104fn write_landmarks(dst: &mut [u8], landmarks: &[Landmark]) {
105 for (i, lm) in landmarks.iter().enumerate() {
106 let base = i * TUPLE_BYTES;
107 write_f64(dst, base, lm.x);
108 write_f64(dst, base + F64_BYTES, lm.y);
109 write_f64(dst, base + 2 * F64_BYTES, lm.z);
110 write_f64(dst, base + 3 * F64_BYTES, lm.confidence);
111 }
112}
113
114fn write_f64(dst: &mut [u8], offset: usize, value: f64) {
115 dst[offset..offset + F64_BYTES].copy_from_slice(&value.to_le_bytes());
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::constants::BODY_ISB_ANGLE_COUNT;
122 use crate::decode::{decode, decode_hello, Message};
123
124 fn sample_quaternion_frame() -> BodyQuaternionFrame {
125 let mut quats = [Quaternion::default(); BODY_JOINT_COUNT];
126 for (i, q) in quats.iter_mut().enumerate() {
127 let f = i as f64;
128 *q = Quaternion {
129 w: f,
130 x: f + 0.1,
131 y: f + 0.2,
132 z: f + 0.3,
133 };
134 }
135 BodyQuaternionFrame::from_array(quats)
136 }
137
138 fn sample_landmark_frame() -> HandLandmarkFrame {
139 let mut landmarks = [Landmark::default(); HAND_LANDMARK_COUNT];
140 for (i, lm) in landmarks.iter_mut().enumerate() {
141 let f = i as f64;
142 *lm = Landmark {
143 x: f,
144 y: -f,
145 z: f * 2.0,
146 confidence: 0.5,
147 };
148 }
149 HandLandmarkFrame::from_array(landmarks)
150 }
151
152 #[test]
153 fn body_quaternions_round_trip() {
154 let frame = sample_quaternion_frame();
156
157 let encoded = encode_body_quaternions(&frame);
159 let decoded = decode(&encoded).expect("round-trips");
160
161 assert_eq!(decoded, Message::BodyQuaternions(frame));
163 }
164
165 #[test]
166 fn hand_landmarks_round_trip() {
167 let frame = sample_landmark_frame();
169
170 let encoded = encode_hand_landmarks(HandSide::Left, &frame);
172 let decoded = decode(&encoded).expect("round-trips");
173
174 assert_eq!(
176 decoded,
177 Message::HandLandmarks {
178 side: HandSide::Left,
179 frame
180 }
181 );
182 }
183
184 #[test]
185 fn hello_round_trips() {
186 let encoded = encode_hello(7);
188 let version = decode_hello(&encoded).expect("round-trips");
189
190 assert_eq!(version, 7);
192 }
193
194 #[test]
195 fn body_isb_angles_round_trip() {
196 let mut values = [0.0f64; BODY_ISB_ANGLE_COUNT];
198 for (i, v) in values.iter_mut().enumerate() {
199 *v = i as f64 * 0.1;
200 }
201 let frame = BodyIsbAnglesFrame::from_array(values);
202
203 let encoded = encode_body_isb_angles(&frame);
205 let decoded = decode(&encoded).expect("round-trips");
206
207 assert_eq!(decoded, Message::BodyIsbAngles(frame));
209 }
210
211 #[test]
212 fn body_isb_angles_preserves_nan() {
213 let mut values = [0.0f64; BODY_ISB_ANGLE_COUNT];
215 values[5] = f64::NAN; values[7] = f64::NAN; let frame = BodyIsbAnglesFrame::from_array(values);
218
219 let encoded = encode_body_isb_angles(&frame);
221 let decoded = decode(&encoded).expect("round-trips even with NaN");
222
223 match decoded {
225 Message::BodyIsbAngles(f) => {
226 assert!(f.right_shoulder_plane_of_elevation.is_nan());
227 assert!(f.left_shoulder_plane_of_elevation.is_nan());
228 assert_eq!(f.thorax_lateral_bend, 0.0);
229 }
230 other => panic!("expected BodyIsbAngles, got {other:?}"),
231 }
232 }
233
234 #[test]
235 fn encoded_lengths_are_correct() {
236 assert_eq!(ENCODED_BODY_QUATERNION_LEN, 417);
238 assert_eq!(ENCODED_BODY_LANDMARK_LEN, 609);
239 assert_eq!(ENCODED_HAND_QUATERNION_LEN, 514);
240 assert_eq!(ENCODED_HAND_LANDMARK_LEN, 674);
241 assert_eq!(ENCODED_HELLO_LEN, 3);
242 assert_eq!(ENCODED_BODY_ISB_ANGLES_LEN, 169);
243 }
244}