1use crate::protocol::{
10 ads1299_to_microvolts, ADS1299_GAIN, ADS1299_VREF, ACC_SENSITIVITY, EEG_FRAME_BYTES,
11 EEG_NUM_CHANNELS, GYRO_SENSITIVITY, MAG_SENSITIVITY,
12};
13use crate::types::{EegSample, MotionData, XyzSample};
14
15pub fn decode_i24_be(bytes: &[u8]) -> i32 {
30 debug_assert!(bytes.len() >= 3);
31 let unsigned = ((bytes[0] as u32) << 16) | ((bytes[1] as u32) << 8) | (bytes[2] as u32);
32 if unsigned & 0x80_0000 != 0 {
34 (unsigned | 0xFF00_0000) as i32
35 } else {
36 unsigned as i32
37 }
38}
39
40pub fn parse_eeg_packet(data: &[u8], timestamp: f64) -> Vec<EegSample> {
59 if data.len() < 1 + EEG_FRAME_BYTES {
60 return vec![];
61 }
62
63 let packet_index = data[0];
64 let payload = &data[1..];
65 let n_complete = payload.len() / EEG_FRAME_BYTES;
66
67 if payload.len() % EEG_FRAME_BYTES != 0 {
68 log::warn!(
69 "EEG packet {packet_index}: {} bytes payload is not a multiple of {EEG_FRAME_BYTES}; \
70 dropping {} trailing byte(s)",
71 payload.len(),
72 payload.len() % EEG_FRAME_BYTES,
73 );
74 }
75
76 (0..n_complete)
77 .map(|sample_idx| {
78 let offset = sample_idx * EEG_FRAME_BYTES;
79 let mut channels = [0.0_f64; EEG_NUM_CHANNELS];
80 for ch in 0..EEG_NUM_CHANNELS {
81 let raw = decode_i24_be(&payload[offset + ch * 3..]);
82 channels[ch] = ads1299_to_microvolts(raw, ADS1299_GAIN, ADS1299_VREF);
83 }
84 EegSample {
85 packet_index,
86 sample_index: sample_idx,
87 timestamp,
88 channels,
89 }
90 })
91 .collect()
92}
93
94pub fn detect_missing_packets(last: Option<u8>, current: u8) -> Vec<u8> {
109 let Some(last) = last else {
110 return vec![];
111 };
112 let expected = (last.wrapping_add(1)) % 128;
113 if expected == current {
114 return vec![];
115 }
116 let mut missing = Vec::new();
117 let mut idx = expected;
118 while idx != current {
119 missing.push(idx);
120 idx = (idx.wrapping_add(1)) % 128;
121 }
122 missing
123}
124
125pub fn parse_motion(data: &[u8], timestamp: f64) -> Option<MotionData> {
134 if data.len() < 18 {
135 return None;
136 }
137 let i16_at = |off: usize| -> i16 { i16::from_le_bytes([data[off], data[off + 1]]) };
138
139 let ax = i16_at(0) as f32 * ACC_SENSITIVITY;
140 let ay = i16_at(2) as f32 * ACC_SENSITIVITY;
141 let az = i16_at(4) as f32 * ACC_SENSITIVITY;
142
143 let gx = i16_at(6) as f32 * GYRO_SENSITIVITY;
144 let gy = i16_at(8) as f32 * GYRO_SENSITIVITY;
145 let gz = i16_at(10) as f32 * GYRO_SENSITIVITY;
146
147 let mx = i16_at(12) as f32 * MAG_SENSITIVITY;
148 let my = i16_at(14) as f32 * MAG_SENSITIVITY;
149 let mz = i16_at(16) as f32 * MAG_SENSITIVITY;
150
151 Some(MotionData {
152 timestamp,
153 accel: XyzSample {
154 x: ax,
155 y: ay,
156 z: az,
157 },
158 gyro: XyzSample {
159 x: gx,
160 y: gy,
161 z: gz,
162 },
163 mag: XyzSample {
164 x: mx,
165 y: my,
166 z: mz,
167 },
168 })
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn decode_i24_be_positive() {
177 assert_eq!(decode_i24_be(&[0x00, 0x03, 0xE8]), 1000);
178 }
179
180 #[test]
181 fn decode_i24_be_negative() {
182 assert_eq!(decode_i24_be(&[0xFF, 0xFF, 0xFF]), -1);
183 assert_eq!(decode_i24_be(&[0xFF, 0xFC, 0x18]), -1000);
184 }
185
186 #[test]
187 fn detect_missing_none() {
188 assert!(detect_missing_packets(None, 0).is_empty());
189 }
190
191 #[test]
192 fn detect_missing_sequential() {
193 assert!(detect_missing_packets(Some(0), 1).is_empty());
194 assert!(detect_missing_packets(Some(127), 0).is_empty());
195 }
196
197 #[test]
198 fn detect_missing_gap() {
199 assert_eq!(detect_missing_packets(Some(5), 8), vec![6, 7]);
200 }
201
202 #[test]
203 fn detect_missing_wrap() {
204 assert_eq!(detect_missing_packets(Some(126), 1), vec![127, 0]);
205 }
206
207 #[test]
208 fn ads1299_scale() {
209 let uv = ads1299_to_microvolts(1, 12.0, 4.5);
210 assert!((uv - 0.04470).abs() < 0.001);
212 }
213}