crazyflie_lib/subsystems/memory/
lighthouse.rs1use crate::{Error, Result, subsystems::memory::{MemoryBackend, memory_types}};
8use memory_types::{FromMemoryBackend, MemoryType};
9use std::collections::HashMap;
10
11const SIZE_FLOAT: usize = std::mem::size_of::<f32>();
13const SIZE_U32: usize = std::mem::size_of::<u32>();
14const SIZE_BOOL: usize = std::mem::size_of::<u8>();
15const SIZE_VECTOR: usize = 3 * SIZE_FLOAT;
16const NUM_SWEEP_PARAMS: usize = 7;
17const NUM_SWEEPS: usize = 2;
18const NUM_ROTATION_ROWS: usize = 3;
19
20fn read_f32(data: &[u8], offset: usize) -> Result<f32> {
22 data.get(offset..offset + SIZE_FLOAT)
23 .and_then(|slice| slice.try_into().ok())
24 .map(f32::from_le_bytes)
25 .ok_or_else(|| Error::MemoryError(format!(
26 "Failed to read f32 at offset {}", offset
27 )))
28}
29
30fn read_u32(data: &[u8], offset: usize) -> Result<u32> {
32 data.get(offset..offset + SIZE_U32)
33 .and_then(|slice| slice.try_into().ok())
34 .map(u32::from_le_bytes)
35 .ok_or_else(|| Error::MemoryError(format!(
36 "Failed to read u32 at offset {}", offset
37 )))
38}
39
40#[derive(Debug, Clone, Copy, Default, PartialEq)]
42pub struct LighthouseCalibrationSweep {
43 pub phase: f32,
45 pub tilt: f32,
47 pub curve: f32,
49 pub gibmag: f32,
51 pub gibphase: f32,
53 pub ogeemag: f32,
55 pub ogeephase: f32,
57}
58
59impl LighthouseCalibrationSweep {
60 pub const SIZE: usize = NUM_SWEEP_PARAMS * SIZE_FLOAT;
62
63 pub fn from_bytes(data: &[u8]) -> Result<Self> {
65 if data.len() < Self::SIZE {
66 return Err(Error::MemoryError(format!(
67 "Insufficient data for calibration sweep: expected {} bytes, got {}",
68 Self::SIZE, data.len()
69 )));
70 }
71
72 Ok(Self {
73 phase: read_f32(data, 0 * SIZE_FLOAT)?,
74 tilt: read_f32(data, 1 * SIZE_FLOAT)?,
75 curve: read_f32(data, 2 * SIZE_FLOAT)?,
76 gibmag: read_f32(data, 3 * SIZE_FLOAT)?,
77 gibphase: read_f32(data, 4 * SIZE_FLOAT)?,
78 ogeemag: read_f32(data, 5 * SIZE_FLOAT)?,
79 ogeephase: read_f32(data, 6 * SIZE_FLOAT)?,
80 })
81 }
82
83 #[must_use]
85 pub fn to_bytes(&self) -> Vec<u8> {
86 let mut data = Vec::with_capacity(Self::SIZE);
87 data.extend_from_slice(&self.phase.to_le_bytes());
88 data.extend_from_slice(&self.tilt.to_le_bytes());
89 data.extend_from_slice(&self.curve.to_le_bytes());
90 data.extend_from_slice(&self.gibmag.to_le_bytes());
91 data.extend_from_slice(&self.gibphase.to_le_bytes());
92 data.extend_from_slice(&self.ogeemag.to_le_bytes());
93 data.extend_from_slice(&self.ogeephase.to_le_bytes());
94 data
95 }
96}
97
98#[derive(Debug, Clone, Default, PartialEq)]
100pub struct LighthouseBsCalibration {
101 pub sweeps: [LighthouseCalibrationSweep; NUM_SWEEPS],
103 pub uid: u32,
105 pub valid: bool,
107}
108
109impl LighthouseBsCalibration {
110 pub const SIZE: usize = NUM_SWEEPS * LighthouseCalibrationSweep::SIZE + SIZE_U32 + SIZE_BOOL;
112
113 const SWEEP0_OFFSET: usize = 0;
115 const SWEEP1_OFFSET: usize = LighthouseCalibrationSweep::SIZE;
116 const UID_OFFSET: usize = NUM_SWEEPS * LighthouseCalibrationSweep::SIZE;
117 const VALID_OFFSET: usize = Self::UID_OFFSET + SIZE_U32;
118
119 pub fn from_bytes(data: &[u8]) -> Result<Self> {
121 if data.len() < Self::SIZE {
122 return Err(Error::MemoryError(format!(
123 "Insufficient data for calibration: expected {} bytes, got {}",
124 Self::SIZE, data.len()
125 )));
126 }
127
128 let sweep0 = LighthouseCalibrationSweep::from_bytes(&data[Self::SWEEP0_OFFSET..Self::SWEEP1_OFFSET])?;
129 let sweep1 = LighthouseCalibrationSweep::from_bytes(&data[Self::SWEEP1_OFFSET..Self::UID_OFFSET])?;
130 let uid = read_u32(data, Self::UID_OFFSET)?;
131 let valid = data.get(Self::VALID_OFFSET).map_or(false, |&b| b != 0);
132
133 Ok(Self {
134 sweeps: [sweep0, sweep1],
135 uid,
136 valid,
137 })
138 }
139
140 #[must_use]
142 pub fn to_bytes(&self) -> Vec<u8> {
143 let mut data = Vec::with_capacity(Self::SIZE);
144 data.extend(self.sweeps[0].to_bytes());
145 data.extend(self.sweeps[1].to_bytes());
146 data.extend_from_slice(&self.uid.to_le_bytes());
147 data.push(u8::from(self.valid));
148 data
149 }
150}
151
152#[derive(Debug, Clone, PartialEq)]
154pub struct LighthouseBsGeometry {
155 pub origin: [f32; 3],
157 pub rotation_matrix: [[f32; 3]; 3],
159 pub valid: bool,
161}
162
163impl Default for LighthouseBsGeometry {
164 fn default() -> Self {
165 Self {
166 origin: [0.0, 0.0, 0.0],
167 rotation_matrix: [[0.0; 3]; 3],
168 valid: false,
169 }
170 }
171}
172
173impl LighthouseBsGeometry {
174 pub const SIZE: usize = (1 + NUM_ROTATION_ROWS) * SIZE_VECTOR + SIZE_BOOL;
176
177 const ORIGIN_OFFSET: usize = 0;
179 const ROTATION_OFFSET: usize = SIZE_VECTOR;
180 const VALID_OFFSET: usize = (1 + NUM_ROTATION_ROWS) * SIZE_VECTOR;
181
182 pub fn from_bytes(data: &[u8]) -> Result<Self> {
184 if data.len() < Self::SIZE {
185 return Err(Error::MemoryError(format!(
186 "Insufficient data for geometry: expected {} bytes, got {}",
187 Self::SIZE, data.len()
188 )));
189 }
190
191 let read_vector = |offset: usize| -> Result<[f32; 3]> {
192 Ok([
193 read_f32(data, offset)?,
194 read_f32(data, offset + SIZE_FLOAT)?,
195 read_f32(data, offset + 2 * SIZE_FLOAT)?,
196 ])
197 };
198
199 let origin = read_vector(Self::ORIGIN_OFFSET)?;
200 let rotation_matrix = [
201 read_vector(Self::ROTATION_OFFSET)?,
202 read_vector(Self::ROTATION_OFFSET + SIZE_VECTOR)?,
203 read_vector(Self::ROTATION_OFFSET + 2 * SIZE_VECTOR)?,
204 ];
205 let valid = data.get(Self::VALID_OFFSET).map_or(false, |&b| b != 0);
206
207 Ok(Self {
208 origin,
209 rotation_matrix,
210 valid,
211 })
212 }
213
214 #[must_use]
216 pub fn to_bytes(&self) -> Vec<u8> {
217 let mut data = Vec::with_capacity(Self::SIZE);
218
219 for &v in &self.origin {
221 data.extend_from_slice(&v.to_le_bytes());
222 }
223
224 for row in &self.rotation_matrix {
226 for &v in row {
227 data.extend_from_slice(&v.to_le_bytes());
228 }
229 }
230
231 data.push(u8::from(self.valid));
233
234 data
235 }
236}
237
238#[derive(Debug)]
243pub struct LighthouseMemory {
244 memory: MemoryBackend,
245}
246
247impl LighthouseMemory {
248 pub const GEO_START_ADDR: usize = 0x00;
250 pub const CALIB_START_ADDR: usize = 0x1000;
252 pub const PAGE_SIZE: usize = 0x100;
254 pub const MAX_BASE_STATIONS: usize = 16;
256
257 fn validate_bs_id(bs_id: u8) -> Result<()> {
259 if bs_id as usize >= Self::MAX_BASE_STATIONS {
260 return Err(Error::InvalidArgument(format!(
261 "Base station ID {} out of range (0-{})",
262 bs_id, Self::MAX_BASE_STATIONS - 1
263 )));
264 }
265 Ok(())
266 }
267
268 fn from_backend(memory: MemoryBackend) -> Result<Self> {
270 if memory.memory_type == MemoryType::Lighthouse {
271 Ok(Self { memory })
272 } else {
273 Err(Error::MemoryError(format!(
274 "Expected Lighthouse memory type, got {:?}",
275 memory.memory_type
276 )))
277 }
278 }
279
280 pub async fn read_geometry(&self, bs_id: u8) -> Result<LighthouseBsGeometry> {
288 Self::validate_bs_id(bs_id)?;
289
290 let addr = Self::GEO_START_ADDR + (bs_id as usize) * Self::PAGE_SIZE;
291 let data = self.memory.read::<fn(usize, usize)>(addr, LighthouseBsGeometry::SIZE, None).await?;
292 LighthouseBsGeometry::from_bytes(&data)
293 }
294
295 pub async fn write_geometry(&self, bs_id: u8, geometry: &LighthouseBsGeometry) -> Result<()> {
301 Self::validate_bs_id(bs_id)?;
302
303 let addr = Self::GEO_START_ADDR + (bs_id as usize) * Self::PAGE_SIZE;
304 let data = geometry.to_bytes();
305 self.memory.write::<fn(usize, usize)>(addr, &data, None).await
306 }
307
308 pub async fn read_calibration(&self, bs_id: u8) -> Result<LighthouseBsCalibration> {
316 Self::validate_bs_id(bs_id)?;
317
318 let addr = Self::CALIB_START_ADDR + (bs_id as usize) * Self::PAGE_SIZE;
319 let data = self.memory.read::<fn(usize, usize)>(addr, LighthouseBsCalibration::SIZE, None).await?;
320 LighthouseBsCalibration::from_bytes(&data)
321 }
322
323 pub async fn write_calibration(&self, bs_id: u8, calibration: &LighthouseBsCalibration) -> Result<()> {
329 Self::validate_bs_id(bs_id)?;
330
331 let addr = Self::CALIB_START_ADDR + (bs_id as usize) * Self::PAGE_SIZE;
332 let data = calibration.to_bytes();
333 self.memory.write::<fn(usize, usize)>(addr, &data, None).await
334 }
335
336 pub async fn read_all_geometries(&self) -> Result<HashMap<u8, LighthouseBsGeometry>> {
344 self.read_all_geometries_with_progress(|_, _| {}).await
345 }
346
347 pub async fn read_all_geometries_with_progress<F>(&self, mut progress_callback: F) -> Result<HashMap<u8, LighthouseBsGeometry>>
352 where
353 F: FnMut(usize, usize),
354 {
355 let mut result = HashMap::new();
356
357 for bs_id in 0..Self::MAX_BASE_STATIONS as u8 {
358 match self.read_geometry(bs_id).await {
359 Ok(geo) => {
360 if geo.valid {
361 result.insert(bs_id, geo);
362 }
363 }
364 Err(Error::MemoryError(_)) => {
365 }
367 Err(e) => return Err(e),
368 }
369 progress_callback(bs_id as usize + 1, Self::MAX_BASE_STATIONS);
370 }
371
372 Ok(result)
373 }
374
375 pub async fn read_all_calibrations(&self) -> Result<HashMap<u8, LighthouseBsCalibration>> {
383 self.read_all_calibrations_with_progress(|_, _| {}).await
384 }
385
386 pub async fn read_all_calibrations_with_progress<F>(&self, mut progress_callback: F) -> Result<HashMap<u8, LighthouseBsCalibration>>
391 where
392 F: FnMut(usize, usize),
393 {
394 let mut result = HashMap::new();
395
396 for bs_id in 0..Self::MAX_BASE_STATIONS as u8 {
397 match self.read_calibration(bs_id).await {
398 Ok(calib) => {
399 if calib.valid {
400 result.insert(bs_id, calib);
401 }
402 }
403 Err(Error::MemoryError(_)) => {
404 }
406 Err(e) => return Err(e),
407 }
408 progress_callback(bs_id as usize + 1, Self::MAX_BASE_STATIONS);
409 }
410
411 Ok(result)
412 }
413
414 pub async fn write_geometries(&self, geometries: &HashMap<u8, LighthouseBsGeometry>) -> Result<()> {
419 self.write_geometries_with_progress(geometries, |_, _| {}).await
420 }
421
422 pub async fn write_geometries_with_progress<F>(
428 &self,
429 geometries: &HashMap<u8, LighthouseBsGeometry>,
430 mut progress_callback: F,
431 ) -> Result<()>
432 where
433 F: FnMut(usize, usize),
434 {
435 let total = geometries.len();
436 let mut completed = 0;
437
438 for (&bs_id, geometry) in geometries {
439 self.write_geometry(bs_id, geometry).await?;
440 completed += 1;
441 progress_callback(completed, total);
442 }
443
444 Ok(())
445 }
446
447 pub async fn write_calibrations(&self, calibrations: &HashMap<u8, LighthouseBsCalibration>) -> Result<()> {
452 self.write_calibrations_with_progress(calibrations, |_, _| {}).await
453 }
454
455 pub async fn write_calibrations_with_progress<F>(
461 &self,
462 calibrations: &HashMap<u8, LighthouseBsCalibration>,
463 mut progress_callback: F,
464 ) -> Result<()>
465 where
466 F: FnMut(usize, usize),
467 {
468 let total = calibrations.len();
469 let mut completed = 0;
470
471 for (&bs_id, calibration) in calibrations {
472 self.write_calibration(bs_id, calibration).await?;
473 completed += 1;
474 progress_callback(completed, total);
475 }
476
477 Ok(())
478 }
479}
480
481impl FromMemoryBackend for LighthouseMemory {
482 async fn from_memory_backend(memory: MemoryBackend) -> Result<Self> {
483 Self::from_backend(memory)
484 }
485
486 async fn initialize_memory_backend(memory: MemoryBackend) -> Result<Self> {
487 Self::from_backend(memory)
488 }
489
490 fn close_memory(self) -> MemoryBackend {
491 self.memory
492 }
493}