bno08x_rs/
driver.rs

1// Copyright 2025 Au-Zone Technologies Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! BNO08x IMU driver implementation.
5//!
6//! This module contains the main [`BNO08x`] driver for the BNO08x family of IMU
7//! sensors. It provides a high-level API for initializing the sensor, enabling
8//! reports, and reading sensor data.
9//!
10//! # Supported Sensors
11//!
12//! This driver supports the following sensor reports:
13//!
14//! | Report | Constant | Data Type | Units |
15//! |--------|----------|-----------|-------|
16//! | Accelerometer | [`SENSOR_REPORTID_ACCELEROMETER`] | `[f32; 3]` | m/s² |
17//! | Gyroscope | [`SENSOR_REPORTID_GYROSCOPE`] | `[f32; 3]` | rad/s |
18//! | Magnetometer | [`SENSOR_REPORTID_MAGNETIC_FIELD`] | `[f32; 3]` | µT |
19//! | Rotation Vector | [`SENSOR_REPORTID_ROTATION_VECTOR`] | `[f32; 4]` | quaternion |
20//! | Game Rotation Vector | [`SENSOR_REPORTID_ROTATION_VECTOR_GAME`] | `[f32; 4]` | quaternion |
21//! | Geomagnetic Rotation | [`SENSOR_REPORTID_ROTATION_VECTOR_GEOMAGNETIC`] | `[f32; 4]` | quaternion |
22//! | Linear Acceleration | [`SENSOR_REPORTID_LINEAR_ACCEL`] | `[f32; 3]` | m/s² |
23//! | Gravity | [`SENSOR_REPORTID_GRAVITY`] | `[f32; 3]` | m/s² |
24//! | Uncalibrated Gyroscope | [`SENSOR_REPORTID_GYROSCOPE_UNCALIB`] | `[f32; 3]` | rad/s |
25//!
26//! # Example
27//!
28//! ```no_run
29//! use bno08x_rs::{BNO08x, SENSOR_REPORTID_ACCELEROMETER};
30//!
31//! fn main() -> std::io::Result<()> {
32//!     let mut imu = BNO08x::new_spi_from_symbol("/dev/spidev1.0", "IMU_INT", "IMU_RST")?;
33//!     imu.init()?;
34//!     imu.enable_report(SENSOR_REPORTID_ACCELEROMETER, 100)?; // 10 Hz
35//!
36//!     loop {
37//!         imu.handle_all_messages(100);
38//!         let accel = imu.accelerometer()?;
39//!         println!("Accel: {:?}", accel);
40//!     }
41//! }
42//! ```
43//!
44//! [`SENSOR_REPORTID_ACCELEROMETER`]: crate::SENSOR_REPORTID_ACCELEROMETER
45//! [`SENSOR_REPORTID_GYROSCOPE`]: crate::SENSOR_REPORTID_GYROSCOPE
46//! [`SENSOR_REPORTID_MAGNETIC_FIELD`]: crate::SENSOR_REPORTID_MAGNETIC_FIELD
47//! [`SENSOR_REPORTID_ROTATION_VECTOR`]: crate::SENSOR_REPORTID_ROTATION_VECTOR
48//! [`SENSOR_REPORTID_ROTATION_VECTOR_GAME`]: crate::SENSOR_REPORTID_ROTATION_VECTOR_GAME
49//! [`SENSOR_REPORTID_ROTATION_VECTOR_GEOMAGNETIC`]: crate::SENSOR_REPORTID_ROTATION_VECTOR_GEOMAGNETIC
50//! [`SENSOR_REPORTID_LINEAR_ACCEL`]: crate::SENSOR_REPORTID_LINEAR_ACCEL
51//! [`SENSOR_REPORTID_GRAVITY`]: crate::SENSOR_REPORTID_GRAVITY
52//! [`SENSOR_REPORTID_GYROSCOPE_UNCALIB`]: crate::SENSOR_REPORTID_GYROSCOPE_UNCALIB
53
54use crate::{
55    constants::{
56        frs_status_to_str, q_to_f32, CHANNEL_COMMAND, CHANNEL_EXECUTABLE, CHANNEL_HUB_CONTROL,
57        CHANNEL_SENSOR_REPORTS, CMD_RESP_ADVERTISEMENT, CMD_RESP_ERROR_LIST,
58        EXECUTABLE_DEVICE_CMD_RESET, EXECUTABLE_DEVICE_RESP_RESET_COMPLETE, FRS_STATUS_NO_DATA,
59        FRS_STATUS_WRITE_COMPLETE, FRS_STATUS_WRITE_FAILED, FRS_STATUS_WRITE_READY, NUM_CHANNELS,
60        PACKET_RECV_BUF_LEN, PACKET_SEND_BUF_LEN, Q_POINTS, Q_POINTS2,
61        SENSOR_REPORTID_ACCELEROMETER, SENSOR_REPORTID_GRAVITY, SENSOR_REPORTID_GYROSCOPE,
62        SENSOR_REPORTID_GYROSCOPE_UNCALIB, SENSOR_REPORTID_LINEAR_ACCEL,
63        SENSOR_REPORTID_MAGNETIC_FIELD, SENSOR_REPORTID_ROTATION_VECTOR,
64        SENSOR_REPORTID_ROTATION_VECTOR_GAME, SENSOR_REPORTID_ROTATION_VECTOR_GEOMAGNETIC,
65        SH2_INIT_SYSTEM, SH2_STARTUP_INIT_UNSOLICITED, SHUB_COMMAND_RESP, SHUB_FRS_WRITE_RESP,
66        SHUB_GET_FEATURE_RESP, SHUB_PROD_ID_REQ, SHUB_PROD_ID_RESP, SHUB_REPORT_SET_FEATURE_CMD,
67    },
68    frs::{
69        build_frs_write_data, build_frs_write_request, quaternion_to_frs_words,
70        FRS_TYPE_SENSOR_ORIENTATION,
71    },
72    interface::{
73        delay::delay_ms,
74        gpio::{GpiodIn, GpiodOut},
75        spi::SpiControlLines,
76        spidev::SpiDevice,
77        SensorInterface, SpiInterface, PACKET_HEADER_LENGTH,
78    },
79};
80use log::{debug, trace, warn};
81
82use core::ops::Shr;
83use std::{
84    collections::HashMap,
85    fmt::Debug,
86    io::{self, Error, ErrorKind},
87    time::{Instant, SystemTime},
88};
89
90/// Type alias for sensor update callback functions
91type ReportCallbackMap<'a, SI> = HashMap<String, Box<dyn Fn(&BNO08x<'a, SI>) + 'a>>;
92
93/// Driver-level errors that can occur during BNO08x operations.
94///
95/// This enum wraps communication errors from the underlying interface
96/// and adds driver-specific error conditions.
97#[derive(Debug)]
98pub enum DriverError<E> {
99    /// Communications error from the underlying SPI/I2C interface
100    CommError(E),
101    /// Invalid chip ID was read during initialization
102    InvalidChipId(u8),
103    /// Unsupported sensor firmware version detected
104    InvalidFWVersion(u8),
105    /// Expected sensor data but none was available
106    NoDataAvailable,
107}
108
109impl<E: std::fmt::Debug> From<DriverError<E>> for io::Error {
110    fn from(err: DriverError<E>) -> Self {
111        match err {
112            DriverError::CommError(e) => io::Error::other(format!("Communication error: {:?}", e)),
113            DriverError::InvalidChipId(id) => {
114                io::Error::new(ErrorKind::InvalidData, format!("Invalid chip ID: {}", id))
115            }
116            DriverError::InvalidFWVersion(ver) => io::Error::new(
117                ErrorKind::InvalidData,
118                format!("Invalid firmware version: {}", ver),
119            ),
120            DriverError::NoDataAvailable => {
121                io::Error::new(ErrorKind::TimedOut, "No sensor data available")
122            }
123        }
124    }
125}
126
127/// BNO08x IMU driver.
128///
129/// This struct provides the main interface for communicating with BNO08x
130/// family IMU sensors (BNO080, BNO085, BNO086) over SPI.
131///
132/// # Usage
133///
134/// 1. Create the driver using [`new_spi`] or [`new_spi_from_symbol`]
135/// 2. Initialize the sensor with [`init`]
136/// 3. Enable desired sensor reports with [`enable_report`]
137/// 4. Call [`handle_messages`] or [`handle_all_messages`] to process incoming
138///    data
139/// 5. Read sensor values with accessor methods like [`accelerometer`],
140///    [`rotation_quaternion`], etc.
141///
142/// # Example
143///
144/// ```no_run
145/// use bno08x_rs::{BNO08x, SENSOR_REPORTID_ROTATION_VECTOR};
146///
147/// fn main() -> std::io::Result<()> {
148///     let mut imu = BNO08x::new_spi_from_symbol("/dev/spidev1.0", "IMU_INT", "IMU_RST")?;
149///     imu.init()?;
150///     imu.enable_report(SENSOR_REPORTID_ROTATION_VECTOR, 100)?;
151///
152///     loop {
153///         imu.handle_all_messages(100);
154///         let quat = imu.rotation_quaternion()?;
155///         println!("Quaternion: {:?}", quat);
156///     }
157/// }
158/// ```
159///
160/// [`new_spi`]: BNO08x::new_spi
161/// [`new_spi_from_symbol`]: BNO08x::new_spi_from_symbol
162/// [`init`]: BNO08x::init
163/// [`enable_report`]: BNO08x::enable_report
164/// [`handle_messages`]: BNO08x::handle_messages
165/// [`handle_all_messages`]: BNO08x::handle_all_messages
166/// [`accelerometer`]: BNO08x::accelerometer
167/// [`rotation_quaternion`]: BNO08x::rotation_quaternion
168pub struct BNO08x<'a, SI> {
169    pub(crate) sensor_interface: SI,
170    /// Each communication channel with the device has its own sequence number
171    sequence_numbers: [u8; NUM_CHANNELS],
172    /// Buffer for building and sending packets to the sensor hub
173    packet_send_buf: [u8; PACKET_SEND_BUF_LEN],
174    /// Buffer for building packets received from the sensor hub
175    packet_recv_buf: [u8; PACKET_RECV_BUF_LEN],
176
177    last_packet_len_received: usize,
178    /// Has the device been successfully reset
179    device_reset: bool,
180    /// Has the product ID been verified
181    prod_id_verified: bool,
182
183    init_received: bool,
184
185    /// Have we received the full advertisement
186    advert_received: bool,
187
188    /// Is the device ready to do an FRS write
189    frs_write_status: u8,
190
191    /// Have we received an error list
192    error_list_received: bool,
193    last_error_received: u8,
194
195    last_chan_received: u8,
196    last_exec_chan_rid: u8,
197    last_command_chan_rid: u8,
198
199    /// Accelerometer data [x, y, z] in m/s^2
200    accelerometer: [f32; 3],
201
202    /// Rotation vector as unit quaternion [i, j, k, real]
203    rotation_quaternion: [f32; 4],
204    /// Rotation vector accuracy estimate (radians)
205    rotation_acc: f32,
206
207    /// Geomagnetic rotation vector as unit quaternion [i, j, k, real]
208    geomag_rotation_quaternion: [f32; 4],
209    /// Geomagnetic rotation accuracy estimate (radians)
210    geomag_rotation_acc: f32,
211
212    /// Game rotation vector as unit quaternion [i, j, k, real]
213    game_rotation_quaternion: [f32; 4],
214
215    /// Linear acceleration [x, y, z] in m/s^2 (gravity removed)
216    linear_accel: [f32; 3],
217
218    /// Gravity vector [x, y, z] in m/s^2
219    gravity: [f32; 3],
220
221    /// Calibrated gyroscope data [x, y, z] in rad/s
222    gyro: [f32; 3],
223
224    /// Uncalibrated gyroscope data [x, y, z] in rad/s
225    uncalib_gyro: [f32; 3],
226
227    /// Calibrated magnetic field [x, y, z] in uTesla
228    mag_field: [f32; 3],
229
230    /// Which reports are enabled
231    report_enabled: [bool; 16],
232
233    /// Timestamp of last update for each report
234    report_update_time: [u128; 16],
235
236    /// Callbacks for report updates
237    report_update_callbacks: [ReportCallbackMap<'a, SI>; 16],
238}
239
240impl<SI> BNO08x<'_, SI> {
241    /// Create a new BNO08x driver with the given sensor interface
242    pub fn new_with_interface(sensor_interface: SI) -> Self {
243        Self {
244            sensor_interface,
245            sequence_numbers: [0; NUM_CHANNELS],
246            packet_send_buf: [0; PACKET_SEND_BUF_LEN],
247            packet_recv_buf: [0; PACKET_RECV_BUF_LEN],
248            last_packet_len_received: 0,
249            device_reset: false,
250            prod_id_verified: false,
251            frs_write_status: FRS_STATUS_NO_DATA,
252            init_received: false,
253            advert_received: false,
254            error_list_received: false,
255            last_error_received: 0,
256            last_chan_received: 0,
257            last_exec_chan_rid: 0,
258            last_command_chan_rid: 0,
259            accelerometer: [0.0; 3],
260            rotation_quaternion: [0.0; 4],
261            rotation_acc: 0.0,
262            game_rotation_quaternion: [0.0; 4],
263            geomag_rotation_quaternion: [0.0; 4],
264            geomag_rotation_acc: 0.0,
265            linear_accel: [0.0; 3],
266            gravity: [0.0; 3],
267            gyro: [0.0; 3],
268            uncalib_gyro: [0.0; 3],
269            mag_field: [0.0; 3],
270            report_enabled: [false; 16],
271            report_update_time: [0; 16],
272            report_update_callbacks: std::array::from_fn(|_| HashMap::new()),
273        }
274    }
275
276    /// Returns previously consumed sensor interface instance.
277    pub fn free(self) -> SI {
278        self.sensor_interface
279    }
280}
281
282/// Find a GPIO pin by its symbolic name across all GPIO chips.
283///
284/// This function searches through all available GPIO chips on the system
285/// and returns the chip path and line number for the first pin matching
286/// the given symbolic name.
287///
288/// # Arguments
289/// * `symbol` - The symbolic name to search for (e.g., "IMU_INT")
290///
291/// # Returns
292/// * `Ok(Some((chip_path, line_number)))` - If the pin was found
293/// * `Ok(None)` - If no pin with the given name was found
294/// * `Err(_)` - If there was an error accessing GPIO chips
295fn find_gpio_by_symbol(symbol: &str) -> io::Result<Option<(String, u32)>> {
296    let gpio_chips = gpiod::Chip::list_devices()?;
297
298    for entry in gpio_chips {
299        let chip = gpiod::Chip::new(&entry)?;
300        for i in 0..chip.num_lines() {
301            let line_info = chip.line_info(i)?;
302            trace!("--- {} ---", line_info.name);
303            if line_info.name == symbol {
304                return Ok(Some((entry.display().to_string(), i)));
305            }
306        }
307    }
308    Ok(None)
309}
310
311impl<'a> BNO08x<'a, SpiInterface<SpiDevice, GpiodIn, GpiodOut>> {
312    /// Create a new BNO08x driver using SPI with explicit GPIO chip and pin
313    /// numbers
314    ///
315    /// # Arguments
316    /// * `spidevice` - Path to the SPI device (e.g., "/dev/spidev1.0")
317    /// * `hintn_gpiochip` - GPIO chip for the interrupt pin
318    /// * `hintn_pin` - GPIO pin number for the interrupt
319    /// * `reset_gpiochip` - GPIO chip for the reset pin
320    /// * `reset_pin` - GPIO pin number for reset
321    pub fn new_spi(
322        spidevice: &str,
323        hintn_gpiochip: &str,
324        hintn_pin: u32,
325        reset_gpiochip: &str,
326        reset_pin: u32,
327    ) -> io::Result<BNO08x<'a, SpiInterface<SpiDevice, GpiodIn, GpiodOut>>> {
328        let hintn: GpiodIn;
329        let reset: GpiodOut;
330        if hintn_gpiochip == reset_gpiochip {
331            let chip = gpiod::Chip::new(hintn_gpiochip)?;
332            hintn = GpiodIn::new(&chip, hintn_pin)?;
333            reset = GpiodOut::new(&chip, reset_pin)?;
334        } else {
335            let chip0 = gpiod::Chip::new(hintn_gpiochip)?;
336            hintn = GpiodIn::new(&chip0, hintn_pin)?;
337            let chip1 = gpiod::Chip::new(reset_gpiochip)?;
338            reset = GpiodOut::new(&chip1, reset_pin)?;
339        }
340
341        let spidev = SpiDevice::new(spidevice)?;
342        let ctrl_lines: SpiControlLines<SpiDevice, GpiodIn, GpiodOut> =
343            SpiControlLines::<SpiDevice, GpiodIn, GpiodOut> {
344                spi: spidev,
345                hintn,
346                reset,
347            };
348
349        let spi_int: SpiInterface<SpiDevice, GpiodIn, GpiodOut> = SpiInterface::new(ctrl_lines);
350        let imu_driver: BNO08x<SpiInterface<SpiDevice, GpiodIn, GpiodOut>> =
351            BNO08x::new_with_interface(spi_int);
352
353        Ok(imu_driver)
354    }
355
356    /// Create a new BNO08x driver using SPI with GPIO pin names (symbol lookup)
357    ///
358    /// This method searches for GPIO pins by their symbolic names across all
359    /// GPIO chips on the system.
360    ///
361    /// # Arguments
362    /// * `spidevice` - Path to the SPI device (e.g., "/dev/spidev1.0")
363    /// * `hintn_pin` - Symbolic name of the interrupt pin (e.g., "IMU_INT")
364    /// * `reset_pin` - Symbolic name of the reset pin (e.g., "IMU_RST")
365    pub fn new_spi_from_symbol(
366        spidevice: &str,
367        hintn_pin: &str,
368        reset_pin: &str,
369    ) -> io::Result<BNO08x<'a, SpiInterface<SpiDevice, GpiodIn, GpiodOut>>> {
370        let (hintn_gpio_chip, hintn_num) = find_gpio_by_symbol(hintn_pin)?.ok_or_else(|| {
371            Error::new(
372                ErrorKind::AddrNotAvailable,
373                format!("Did not find hintn pin \"{}\"", hintn_pin),
374            )
375        })?;
376
377        let (reset_gpio_chip, reset_num) = find_gpio_by_symbol(reset_pin)?.ok_or_else(|| {
378            Error::new(
379                ErrorKind::AddrNotAvailable,
380                format!("Did not find reset pin \"{}\"", reset_pin),
381            )
382        })?;
383
384        Self::new_spi(
385            spidevice,
386            hintn_gpio_chip.as_str(),
387            hintn_num,
388            reset_gpio_chip.as_str(),
389            reset_num,
390        )
391    }
392}
393
394impl<'a, SI, SE> BNO08x<'a, SI>
395where
396    SI: SensorInterface<SensorError = SE>,
397    SE: core::fmt::Debug,
398{
399    /// Consume all available messages on the port without processing them.
400    ///
401    /// This is useful for clearing the message queue before starting
402    /// a new measurement sequence.
403    pub fn eat_all_messages(&mut self) {
404        loop {
405            let msg_count = self.eat_one_message();
406            if msg_count == 0 {
407                break;
408            }
409            delay_ms(1);
410        }
411    }
412
413    /// Handle up to `max_count` messages with the given timeout.
414    ///
415    /// This method processes incoming sensor messages and updates internal
416    /// sensor data. It returns when either `max_count` messages have been
417    /// processed or no more messages are available within the timeout.
418    ///
419    /// # Arguments
420    ///
421    /// * `timeout_ms` - Maximum time to wait for each message (milliseconds)
422    /// * `max_count` - Maximum number of messages to process
423    ///
424    /// # Returns
425    ///
426    /// The total number of messages processed.
427    ///
428    /// # Example
429    ///
430    /// ```no_run
431    /// use bno08x_rs::BNO08x;
432    ///
433    /// fn main() -> std::io::Result<()> {
434    ///     let mut imu = BNO08x::new_spi_from_symbol("/dev/spidev1.0", "IMU_INT", "IMU_RST")?;
435    ///     // Process up to 20 messages, waiting up to 10ms for each
436    ///     let processed = imu.handle_messages(10, 20);
437    ///     Ok(())
438    /// }
439    /// ```
440    pub fn handle_messages(&mut self, timeout_ms: usize, max_count: u32) -> u32 {
441        let mut total_handled: u32 = 0;
442        let mut i: u32 = 0;
443        while i < max_count {
444            let handled_count = self.handle_one_message(timeout_ms);
445            if handled_count == 0 || total_handled > max_count {
446                break;
447            } else {
448                total_handled += handled_count;
449                delay_ms(1);
450            }
451            i += 1
452        }
453        total_handled
454    }
455
456    /// Handle all available messages with a timeout.
457    ///
458    /// This method continuously processes incoming sensor messages until
459    /// no more messages are available within the timeout period. Use this
460    /// in your main loop to keep sensor data up to date.
461    ///
462    /// # Arguments
463    ///
464    /// * `timeout_ms` - Maximum time to wait for each message (milliseconds)
465    ///
466    /// # Returns
467    ///
468    /// The total number of messages processed.
469    ///
470    /// # Example
471    ///
472    /// ```no_run
473    /// use bno08x_rs::BNO08x;
474    ///
475    /// fn main() -> std::io::Result<()> {
476    ///     let mut imu = BNO08x::new_spi_from_symbol("/dev/spidev1.0", "IMU_INT", "IMU_RST")?;
477    ///     loop {
478    ///         // Process all available messages, waiting up to 100ms
479    ///         imu.handle_all_messages(100);
480    ///
481    ///         // Read updated sensor data
482    ///         let accel = imu.accelerometer()?;
483    ///         println!("Accel: {:?}", accel);
484    ///     }
485    /// }
486    /// ```
487    pub fn handle_all_messages(&mut self, timeout_ms: usize) -> u32 {
488        let mut total_handled: u32 = 0;
489        loop {
490            let handled_count = self.handle_one_message(timeout_ms);
491            if handled_count == 0 {
492                break;
493            } else {
494                total_handled += handled_count;
495                delay_ms(1);
496            }
497        }
498        total_handled
499    }
500
501    /// Handle one message and return the count of messages handled (0 or 1)
502    pub fn handle_one_message(&mut self, max_ms: usize) -> u32 {
503        let mut msg_count = 0;
504
505        let res = self.receive_packet_with_timeout(max_ms);
506        if let Ok(received_len) = res {
507            if received_len > 0 {
508                msg_count += 1;
509                if let Err(e) = self.handle_received_packet(received_len) {
510                    warn!("{:?}", e)
511                }
512            }
513        } else {
514            trace!("handle1 err {:?}", res);
515        }
516
517        msg_count
518    }
519
520    /// Receive and ignore one message, returning the packet size or zero
521    pub fn eat_one_message(&mut self) -> usize {
522        let res = self.receive_packet_with_timeout(150);
523        if let Ok(received_len) = res {
524            received_len
525        } else {
526            trace!("e1 err {:?}", res);
527            0
528        }
529    }
530
531    fn handle_advertise_response(&mut self, received_len: usize) {
532        let payload_len = received_len - PACKET_HEADER_LENGTH;
533        let payload = &self.packet_recv_buf[PACKET_HEADER_LENGTH..received_len];
534        let mut cursor: usize = 1; // skip response type
535
536        while cursor < payload_len {
537            let _tag: u8 = payload[cursor];
538            cursor += 1;
539            let len: u8 = payload[cursor];
540            cursor += 1;
541            cursor += len as usize;
542        }
543
544        self.advert_received = true;
545    }
546
547    fn read_u8_at_cursor(msg: &[u8], cursor: &mut usize) -> u8 {
548        let val = msg[*cursor];
549        *cursor += 1;
550        val
551    }
552
553    fn read_i16_at_cursor(msg: &[u8], cursor: &mut usize) -> i16 {
554        let val = (msg[*cursor] as i16) | ((msg[*cursor + 1] as i16) << 8);
555        *cursor += 2;
556        val
557    }
558
559    fn try_read_i16_at_cursor(msg: &[u8], cursor: &mut usize) -> Option<i16> {
560        let remaining = msg.len() - *cursor;
561        if remaining >= 2 {
562            let val = (msg[*cursor] as i16) | ((msg[*cursor + 1] as i16) << 8);
563            *cursor += 2;
564            Some(val)
565        } else {
566            None
567        }
568    }
569
570    /// Read data values from a single input report
571    fn handle_one_input_report(
572        outer_cursor: usize,
573        msg: &[u8],
574    ) -> (usize, u8, i16, i16, i16, i16, i16) {
575        let mut cursor = outer_cursor;
576
577        let feature_report_id = Self::read_u8_at_cursor(msg, &mut cursor);
578        let _rep_seq_num = Self::read_u8_at_cursor(msg, &mut cursor);
579        let _rep_status = Self::read_u8_at_cursor(msg, &mut cursor);
580        let _delay = Self::read_u8_at_cursor(msg, &mut cursor);
581
582        let data1: i16 = Self::read_i16_at_cursor(msg, &mut cursor);
583        let data2: i16 = Self::read_i16_at_cursor(msg, &mut cursor);
584        let data3: i16 = Self::read_i16_at_cursor(msg, &mut cursor);
585        let data4: i16 = Self::try_read_i16_at_cursor(msg, &mut cursor).unwrap_or(0);
586        let data5: i16 = Self::try_read_i16_at_cursor(msg, &mut cursor).unwrap_or(0);
587
588        (cursor, feature_report_id, data1, data2, data3, data4, data5)
589    }
590
591    fn handle_sensor_report_update(&mut self, report_id: u8, timestamp: u128) {
592        self.report_update_time[report_id as usize] = timestamp;
593        for (_, val) in self.report_update_callbacks[report_id as usize].iter() {
594            val(self);
595        }
596    }
597
598    /// Handle parsing of an input report packet (may contain multiple reports)
599    fn handle_sensor_reports(&mut self, received_len: usize) {
600        let mut outer_cursor: usize = PACKET_HEADER_LENGTH + 5; // skip header, timestamp
601        if received_len < outer_cursor {
602            return;
603        }
604
605        let payload_len = received_len - outer_cursor;
606        if payload_len < 10 {
607            trace!(
608                "bad report: {:?}",
609                &self.packet_recv_buf[..PACKET_HEADER_LENGTH]
610            );
611            return;
612        }
613
614        while outer_cursor < payload_len {
615            let (inner_cursor, report_id, data1, data2, data3, data4, data5) =
616                Self::handle_one_input_report(outer_cursor, &self.packet_recv_buf[..received_len]);
617            outer_cursor = inner_cursor;
618
619            let timestamp = SystemTime::now()
620                .duration_since(SystemTime::UNIX_EPOCH)
621                .map(|d| d.as_nanos())
622                .unwrap_or(0);
623
624            match report_id {
625                SENSOR_REPORTID_ACCELEROMETER => {
626                    self.update_accelerometer(data1, data2, data3);
627                    self.handle_sensor_report_update(report_id, timestamp)
628                }
629                SENSOR_REPORTID_ROTATION_VECTOR => {
630                    self.update_rotation_quaternion(data1, data2, data3, data4, data5);
631                    self.handle_sensor_report_update(report_id, timestamp)
632                }
633                SENSOR_REPORTID_ROTATION_VECTOR_GAME => {
634                    self.update_rotation_quaternion_game(data1, data2, data3, data4);
635                    self.handle_sensor_report_update(report_id, timestamp)
636                }
637                SENSOR_REPORTID_ROTATION_VECTOR_GEOMAGNETIC => {
638                    self.update_rotation_quaternion_geomag(data1, data2, data3, data4, data5);
639                    self.handle_sensor_report_update(report_id, timestamp)
640                }
641                SENSOR_REPORTID_LINEAR_ACCEL => {
642                    self.update_linear_accel(data1, data2, data3);
643                    self.handle_sensor_report_update(report_id, timestamp)
644                }
645                SENSOR_REPORTID_GRAVITY => {
646                    self.update_gravity(data1, data2, data3);
647                    self.handle_sensor_report_update(report_id, timestamp)
648                }
649                SENSOR_REPORTID_GYROSCOPE => {
650                    self.update_gyro_calib(data1, data2, data3);
651                    self.handle_sensor_report_update(report_id, timestamp)
652                }
653                SENSOR_REPORTID_GYROSCOPE_UNCALIB => {
654                    self.update_gyro_uncalib(data1, data2, data3);
655                    self.handle_sensor_report_update(report_id, timestamp)
656                }
657                SENSOR_REPORTID_MAGNETIC_FIELD => {
658                    self.update_magnetic_field_calib(data1, data2, data3);
659                    self.handle_sensor_report_update(report_id, timestamp)
660                }
661                _ => {}
662            }
663        }
664    }
665
666    fn update_accelerometer(&mut self, x: i16, y: i16, z: i16) {
667        let q = Q_POINTS[SENSOR_REPORTID_ACCELEROMETER as usize];
668        self.accelerometer = [q_to_f32(x, q), q_to_f32(y, q), q_to_f32(z, q)];
669    }
670
671    fn update_rotation_quaternion(&mut self, q_i: i16, q_j: i16, q_k: i16, q_r: i16, q_a: i16) {
672        let q = Q_POINTS[SENSOR_REPORTID_ROTATION_VECTOR as usize];
673        let q2 = Q_POINTS2[SENSOR_REPORTID_ROTATION_VECTOR as usize];
674        self.rotation_quaternion = [
675            q_to_f32(q_i, q),
676            q_to_f32(q_j, q),
677            q_to_f32(q_k, q),
678            q_to_f32(q_r, q),
679        ];
680        self.rotation_acc = q_to_f32(q_a, q2);
681    }
682
683    fn update_rotation_quaternion_geomag(
684        &mut self,
685        q_i: i16,
686        q_j: i16,
687        q_k: i16,
688        q_r: i16,
689        q_a: i16,
690    ) {
691        let q = Q_POINTS[SENSOR_REPORTID_ROTATION_VECTOR_GEOMAGNETIC as usize];
692        let q2 = Q_POINTS2[SENSOR_REPORTID_ROTATION_VECTOR_GEOMAGNETIC as usize];
693        self.geomag_rotation_quaternion = [
694            q_to_f32(q_i, q),
695            q_to_f32(q_j, q),
696            q_to_f32(q_k, q),
697            q_to_f32(q_r, q),
698        ];
699        self.geomag_rotation_acc = q_to_f32(q_a, q2);
700    }
701
702    fn update_rotation_quaternion_game(&mut self, q_i: i16, q_j: i16, q_k: i16, q_r: i16) {
703        let q = Q_POINTS[SENSOR_REPORTID_ROTATION_VECTOR_GAME as usize];
704        self.game_rotation_quaternion = [
705            q_to_f32(q_i, q),
706            q_to_f32(q_j, q),
707            q_to_f32(q_k, q),
708            q_to_f32(q_r, q),
709        ];
710    }
711
712    fn update_linear_accel(&mut self, x: i16, y: i16, z: i16) {
713        let q = Q_POINTS[SENSOR_REPORTID_LINEAR_ACCEL as usize];
714        self.linear_accel = [q_to_f32(x, q), q_to_f32(y, q), q_to_f32(z, q)];
715    }
716
717    fn update_gravity(&mut self, x: i16, y: i16, z: i16) {
718        let q = Q_POINTS[SENSOR_REPORTID_GRAVITY as usize];
719        self.gravity = [q_to_f32(x, q), q_to_f32(y, q), q_to_f32(z, q)];
720    }
721
722    fn update_gyro_calib(&mut self, x: i16, y: i16, z: i16) {
723        let q = Q_POINTS[SENSOR_REPORTID_GYROSCOPE as usize];
724        self.gyro = [q_to_f32(x, q), q_to_f32(y, q), q_to_f32(z, q)];
725    }
726
727    fn update_gyro_uncalib(&mut self, x: i16, y: i16, z: i16) {
728        let q = Q_POINTS[SENSOR_REPORTID_GYROSCOPE_UNCALIB as usize];
729        self.uncalib_gyro = [q_to_f32(x, q), q_to_f32(y, q), q_to_f32(z, q)];
730    }
731
732    fn update_magnetic_field_calib(&mut self, x: i16, y: i16, z: i16) {
733        let q = Q_POINTS[SENSOR_REPORTID_MAGNETIC_FIELD as usize];
734        self.mag_field = [q_to_f32(x, q), q_to_f32(y, q), q_to_f32(z, q)];
735    }
736
737    /// Handle one or more errors sent in response to a command
738    fn handle_cmd_resp_error_list(&mut self, received_len: usize) {
739        let payload_len = received_len - PACKET_HEADER_LENGTH;
740        let payload = &self.packet_recv_buf[PACKET_HEADER_LENGTH..received_len];
741
742        self.error_list_received = true;
743        for err in payload.iter().take(payload_len).skip(1) {
744            let err: u8 = *err;
745            self.last_error_received = err;
746            match err {
747                0 => {}
748                1 => {
749                    warn!("Hub application attempted to exceed maximum read cargo length: Error code {}", err);
750                }
751                2 => {
752                    warn!(
753                        "Host write was too short (need at least a 4-byte header): Error code {}",
754                        err
755                    );
756                }
757                3 => {
758                    warn!("Host wrote a header with length greater than maximum write cargo length: Error code {}", err);
759                }
760                4 => {
761                    warn!("Host wrote a header with length less than or equal to header length: Error code {}", err);
762                }
763                5 => {
764                    warn!("Host wrote beginning of fragmented cargo, fragmentation not supported: Error code {}", err);
765                }
766                6 => {
767                    warn!("Host wrote continuation of fragmented cargo, fragmentation not supported: Error code {}", err);
768                }
769                7 => {
770                    warn!(
771                        "Unrecognized command on control channel: Error code {}",
772                        err
773                    );
774                }
775                8 => {
776                    warn!(
777                        "Unrecognized parameter to get-advertisement command: Error code {}",
778                        err
779                    );
780                }
781                9 => {
782                    warn!("Host wrote to unrecognized channel: Error code {}", err);
783                }
784                10 => {
785                    warn!("Advertisement request received while Advertisement Response was pending: Error code {}", err);
786                }
787                11 => {
788                    warn!("Host performed a write operation before the hub had finished sending its advertisement response: Error code {}", err);
789                }
790                12 => {
791                    warn!("Error list too long to send, truncated: Error code {}", err);
792                }
793                _ => {
794                    debug!("Unknown error code {}", err);
795                }
796            }
797        }
798    }
799
800    /// Handle a received packet and dispatch to appropriate handler
801    pub fn handle_received_packet(&mut self, received_len: usize) -> Result<(), Box<dyn Debug>> {
802        let mut rec_len = received_len;
803        if rec_len > PACKET_RECV_BUF_LEN {
804            warn!(
805                "Packet length of {} exceeded the buffer length of {}",
806                received_len, PACKET_RECV_BUF_LEN
807            );
808            rec_len = PACKET_RECV_BUF_LEN;
809        } else if rec_len < PACKET_HEADER_LENGTH {
810            return Err(Box::new(format!(
811                "Packet length of {} was ignored. Shorter than header length of {}",
812                received_len, PACKET_HEADER_LENGTH
813            )));
814        }
815        let msg = &self.packet_recv_buf[..rec_len];
816        let chan_num = msg[2];
817        let report_id: u8 = if rec_len > PACKET_HEADER_LENGTH {
818            msg[4]
819        } else {
820            0
821        };
822        self.last_chan_received = chan_num;
823        match chan_num {
824            CHANNEL_COMMAND => match report_id {
825                CMD_RESP_ADVERTISEMENT => {
826                    self.handle_advertise_response(rec_len);
827                }
828                CMD_RESP_ERROR_LIST => {
829                    self.handle_cmd_resp_error_list(rec_len);
830                }
831                _ => {
832                    self.last_command_chan_rid = report_id;
833                    return Err(Box::new(format!("unknown cmd: {}", report_id)));
834                }
835            },
836            CHANNEL_EXECUTABLE => match report_id {
837                EXECUTABLE_DEVICE_RESP_RESET_COMPLETE => {
838                    self.device_reset = true;
839                    trace!("resp_reset {}", 1);
840                }
841                _ => {
842                    self.last_exec_chan_rid = report_id;
843                    return Err(Box::new(format!("unknown exe: {}", report_id)));
844                }
845            },
846            CHANNEL_HUB_CONTROL => match report_id {
847                SHUB_COMMAND_RESP => {
848                    let cmd_resp = msg[6];
849                    if cmd_resp == SH2_STARTUP_INIT_UNSOLICITED || cmd_resp == SH2_INIT_SYSTEM {
850                        self.init_received = true;
851                    }
852                    trace!("CMD_RESP: 0x{:X}", cmd_resp);
853                }
854                SHUB_PROD_ID_RESP => {
855                    {
856                        let _sw_vers_major = msg[4 + 2];
857                        let _sw_vers_minor = msg[4 + 3];
858                        trace!("PID_RESP {}.{}", _sw_vers_major, _sw_vers_major);
859                    }
860                    self.prod_id_verified = true;
861                }
862                SHUB_GET_FEATURE_RESP => {
863                    trace!("feat resp: {}", msg[5]);
864                    self.report_enabled[msg[5] as usize] = true;
865                }
866                SHUB_FRS_WRITE_RESP => {
867                    trace!("write resp: {}", frs_status_to_str(msg[5]));
868                    self.frs_write_status = msg[5];
869                }
870                _ => {
871                    trace!(
872                        "unh hbc: 0x{:X} {:x?}",
873                        report_id,
874                        &msg[..PACKET_HEADER_LENGTH]
875                    );
876                    return Err(Box::new(format!(
877                        "unknown hbc: 0x{:X} {:x?}",
878                        report_id,
879                        &msg[..PACKET_HEADER_LENGTH]
880                    )));
881                }
882            },
883            CHANNEL_SENSOR_REPORTS => {
884                self.handle_sensor_reports(rec_len);
885            }
886            _ => {
887                self.last_chan_received = chan_num;
888                trace!("unh chan 0x{:X}", chan_num);
889                return Err(Box::new(format!("unknown chan 0x{:X}", chan_num)));
890            }
891        }
892        Ok(())
893    }
894
895    /// Initialize the BNO08x sensor.
896    ///
897    /// This method must be called after creating the driver and before
898    /// enabling any sensor reports. It performs the following:
899    ///
900    /// 1. Sets up the communication interface (SPI/GPIO)
901    /// 2. Performs a soft reset if required by the interface
902    /// 3. Processes initial advertisement and reset responses
903    /// 4. Verifies the sensor product ID
904    ///
905    /// # Errors
906    ///
907    /// Returns [`DriverError::CommError`] if communication fails, or
908    /// [`DriverError::InvalidChipId`] if the sensor doesn't respond correctly.
909    ///
910    /// # Example
911    ///
912    /// ```no_run
913    /// # use bno08x_rs::BNO08x;
914    /// let mut imu = BNO08x::new_spi_from_symbol("/dev/spidev1.0", "IMU_INT", "IMU_RST")?;
915    /// imu.init().expect("Failed to initialize IMU");
916    /// # Ok::<(), std::io::Error>(())
917    /// ```
918    pub fn init(&mut self) -> Result<(), DriverError<SE>> {
919        trace!("driver init");
920
921        // Section 5.1.1.1: On system startup, the SHTP control application will send
922        // its full advertisement response, unsolicited, to the host.
923        delay_ms(1);
924        self.sensor_interface
925            .setup()
926            .map_err(DriverError::CommError)?;
927
928        if self.sensor_interface.requires_soft_reset() {
929            delay_ms(1);
930            self.soft_reset()?;
931            delay_ms(250);
932            self.eat_all_messages();
933            delay_ms(250);
934            self.eat_all_messages();
935        } else {
936            // we only expect two messages after reset:
937            // eat the advertisement response
938            delay_ms(250);
939            trace!("Eating advertisement response");
940            self.handle_one_message(20);
941            trace!("Eating reset response");
942            delay_ms(250);
943            self.handle_one_message(20);
944        }
945        self.verify_product_id()?;
946        delay_ms(100);
947        Ok(())
948    }
949
950    /// Enable reporting of rotation vector (fused quaternion).
951    ///
952    /// Note that the maximum valid update rate is 1 kHz, based on the max
953    /// update rate of the sensor's gyros.
954    ///
955    /// Returns true if the report was successfully enabled.
956    pub fn enable_rotation_vector(
957        &mut self,
958        millis_between_reports: u16,
959    ) -> Result<bool, DriverError<SE>> {
960        self.enable_report(SENSOR_REPORTID_ROTATION_VECTOR, millis_between_reports)
961    }
962
963    /// Enable reporting of linear acceleration vector.
964    ///
965    /// Returns true if the report was successfully enabled.
966    pub fn enable_linear_accel(
967        &mut self,
968        millis_between_reports: u16,
969    ) -> Result<bool, DriverError<SE>> {
970        self.enable_report(SENSOR_REPORTID_LINEAR_ACCEL, millis_between_reports)
971    }
972
973    /// Enable reporting of calibrated gyroscope data.
974    ///
975    /// Returns true if the report was successfully enabled.
976    pub fn enable_gyro(&mut self, millis_between_reports: u16) -> Result<bool, DriverError<SE>> {
977        self.enable_report(SENSOR_REPORTID_GYROSCOPE, millis_between_reports)
978    }
979
980    /// Enable reporting of gravity vector.
981    ///
982    /// Returns true if the report was successfully enabled.
983    pub fn enable_gravity(&mut self, millis_between_reports: u16) -> Result<bool, DriverError<SE>> {
984        self.enable_report(SENSOR_REPORTID_GRAVITY, millis_between_reports)
985    }
986
987    /// Get the timestamp of the last update for a report
988    pub fn report_update_time(&self, report_id: u8) -> u128 {
989        if report_id as usize <= self.report_enabled.len() {
990            return self.report_update_time[report_id as usize];
991        }
992        0
993    }
994
995    /// Check if a report is enabled
996    pub fn is_report_enabled(&self, report_id: u8) -> bool {
997        if report_id as usize <= self.report_enabled.len() {
998            return self.report_enabled[report_id as usize];
999        }
1000        false
1001    }
1002
1003    /// Add a callback to be invoked when a sensor report is updated
1004    pub fn add_sensor_report_callback(
1005        &mut self,
1006        report_id: u8,
1007        key: String,
1008        func: impl Fn(&Self) + 'a,
1009    ) {
1010        self.report_update_callbacks[report_id as usize]
1011            .entry(key)
1012            .or_insert_with(|| Box::new(func));
1013    }
1014
1015    /// Remove a sensor report callback by key
1016    pub fn remove_sensor_report_callback(&mut self, report_id: u8, key: String) {
1017        self.report_update_callbacks[report_id as usize].remove(&key);
1018    }
1019
1020    /// Enable a sensor report with the specified update interval.
1021    ///
1022    /// Returns true if the report was successfully enabled.
1023    pub fn enable_report(
1024        &mut self,
1025        report_id: u8,
1026        millis_between_reports: u16,
1027    ) -> Result<bool, DriverError<SE>> {
1028        trace!("enable_report 0x{:X}", report_id);
1029
1030        let micros_between_reports: u32 = (millis_between_reports as u32) * 1000;
1031        let cmd_body: [u8; 17] = [
1032            SHUB_REPORT_SET_FEATURE_CMD,
1033            report_id,
1034            0,                                        // feature flags
1035            0,                                        // LSB change sensitivity
1036            0,                                        // MSB change sensitivity
1037            (micros_between_reports & 0xFFu32) as u8, // LSB report interval, microseconds
1038            (micros_between_reports.shr(8) & 0xFFu32) as u8,
1039            (micros_between_reports.shr(16) & 0xFFu32) as u8,
1040            (micros_between_reports.shr(24) & 0xFFu32) as u8, // MSB report interval
1041            0,                                                // LSB Batch Interval
1042            0,
1043            0,
1044            0, // MSB Batch interval
1045            0, // LSB sensor-specific config
1046            0,
1047            0,
1048            0, // MSB sensor-specific config
1049        ];
1050        self.send_packet(CHANNEL_HUB_CONTROL, &cmd_body)?;
1051
1052        let start = Instant::now();
1053        while !self.report_enabled[report_id as usize] && start.elapsed().as_millis() < 2000 {
1054            if let Ok(received_len) = self.receive_packet_with_timeout(250) {
1055                if received_len > 0 {
1056                    if let Err(e) = self.handle_received_packet(received_len) {
1057                        warn!("{:?}", e)
1058                    }
1059                }
1060            }
1061        }
1062        delay_ms(200);
1063        trace!(
1064            "Report {:x} is enabled: {}",
1065            report_id,
1066            self.report_enabled[report_id as usize]
1067        );
1068        if !self.report_enabled[report_id as usize] {
1069            return Ok(false);
1070        }
1071        Ok(true)
1072    }
1073
1074    /// Wait for FRS write status to change from NO_DATA.
1075    ///
1076    /// Polls the sensor for incoming packets until the FRS write status
1077    /// changes from `NO_DATA` or the timeout expires.
1078    ///
1079    /// Returns `true` if status changed before timeout.
1080    fn wait_for_frs_response(&mut self, timeout_ms: u128) -> bool {
1081        let start = Instant::now();
1082        while self.frs_write_status == FRS_STATUS_NO_DATA
1083            && start.elapsed().as_millis() < timeout_ms
1084        {
1085            if let Ok(received_len) = self.receive_packet_with_timeout(250) {
1086                if received_len > 0 {
1087                    if let Err(e) = self.handle_received_packet(received_len) {
1088                        warn!("{:?}", e)
1089                    }
1090                }
1091            }
1092        }
1093        self.frs_write_status != FRS_STATUS_NO_DATA
1094    }
1095
1096    /// Wait for FRS write to complete or fail.
1097    ///
1098    /// Polls the sensor for incoming packets until the FRS write status
1099    /// indicates completion or failure, or the timeout expires.
1100    ///
1101    /// Returns `true` if write completed successfully.
1102    fn wait_for_frs_completion(&mut self, timeout_ms: u128) -> bool {
1103        let start = Instant::now();
1104        while self.frs_write_status != FRS_STATUS_WRITE_FAILED
1105            && self.frs_write_status != FRS_STATUS_WRITE_COMPLETE
1106            && start.elapsed().as_millis() < timeout_ms
1107        {
1108            if let Ok(received_len) = self.receive_packet_with_timeout(250) {
1109                if received_len > 0 {
1110                    if let Err(e) = self.handle_received_packet(received_len) {
1111                        warn!("{:?}", e)
1112                    }
1113                }
1114            }
1115        }
1116        self.frs_write_status == FRS_STATUS_WRITE_COMPLETE
1117    }
1118
1119    /// Send FRS data chunk and wait for acknowledgment.
1120    ///
1121    /// Sends a pair of 32-bit words to the FRS at the specified offset
1122    /// and waits for the sensor to acknowledge receipt.
1123    fn send_frs_data_chunk(
1124        &mut self,
1125        offset: u16,
1126        word1: [u8; 4],
1127        word2: [u8; 4],
1128        timeout_ms: u128,
1129    ) -> Result<(), DriverError<SE>> {
1130        let cmd_body_data = build_frs_write_data(offset, word1, word2);
1131        let _ = self.send_packet(CHANNEL_HUB_CONTROL, cmd_body_data.as_ref())?;
1132
1133        self.frs_write_status = FRS_STATUS_NO_DATA;
1134        self.wait_for_frs_response(timeout_ms);
1135        delay_ms(150);
1136        Ok(())
1137    }
1138
1139    /// Set the sensor orientation using a quaternion.
1140    ///
1141    /// This configures the reference frame transformation applied to all
1142    /// sensor outputs.
1143    pub fn set_sensor_orientation(
1144        &mut self,
1145        qi: f32,
1146        qj: f32,
1147        qk: f32,
1148        qr: f32,
1149        timeout: u128,
1150    ) -> Result<bool, DriverError<SE>> {
1151        // Step 1: Request FRS write
1152        let length: u16 = 4;
1153        let cmd_body_req = build_frs_write_request(length, FRS_TYPE_SENSOR_ORIENTATION);
1154        let _ = self.send_packet(CHANNEL_HUB_CONTROL, cmd_body_req.as_ref())?;
1155
1156        // Step 2: Wait for write ready
1157        self.frs_write_status = FRS_STATUS_NO_DATA;
1158        self.wait_for_frs_response(timeout);
1159
1160        if self.frs_write_status != FRS_STATUS_WRITE_READY {
1161            trace!("FRS Write not ready");
1162            return Ok(false);
1163        }
1164        trace!("FRS Write ready");
1165        delay_ms(150);
1166
1167        // Step 3: Convert quaternion and send data chunks
1168        let (q30_qi, q30_qj, q30_qk, q30_qr) = quaternion_to_frs_words(qi, qj, qk, qr);
1169
1170        self.send_frs_data_chunk(0, q30_qi, q30_qj, 800)?;
1171        self.send_frs_data_chunk(2, q30_qk, q30_qr, 800)?;
1172
1173        // Step 4: Wait for completion
1174        self.frs_write_status = FRS_STATUS_NO_DATA;
1175        let success = self.wait_for_frs_completion(800);
1176        delay_ms(100);
1177
1178        Ok(success)
1179    }
1180
1181    /// Prepare a packet for sending, in our send buffer
1182    fn prep_send_packet(&mut self, channel: u8, body_data: &[u8]) -> usize {
1183        let body_len = body_data.len();
1184
1185        let packet_length = body_len + PACKET_HEADER_LENGTH;
1186        let packet_header = [
1187            (packet_length & 0xFF) as u8, // LSB
1188            packet_length.shr(8) as u8,   // MSB
1189            channel,
1190            self.sequence_numbers[channel as usize],
1191        ];
1192        self.sequence_numbers[channel as usize] += 1;
1193
1194        self.packet_send_buf[..PACKET_HEADER_LENGTH].copy_from_slice(packet_header.as_ref());
1195        self.packet_send_buf[PACKET_HEADER_LENGTH..packet_length].copy_from_slice(body_data);
1196
1197        packet_length
1198    }
1199
1200    /// Send packet from our packet send buf
1201    fn send_packet(&mut self, channel: u8, body_data: &[u8]) -> Result<usize, DriverError<SE>> {
1202        let packet_length = self.prep_send_packet(channel, body_data);
1203
1204        let rc = self
1205            .sensor_interface
1206            .send_and_receive_packet(
1207                &self.packet_send_buf[..packet_length],
1208                &mut self.packet_recv_buf,
1209            )
1210            .map_err(DriverError::CommError)?;
1211        if rc > 0 {
1212            if let Err(e) = self.handle_received_packet(rc) {
1213                warn!("{:?}", e)
1214            }
1215        }
1216        Ok(packet_length)
1217    }
1218
1219    /// Read one packet into the receive buffer
1220    pub(crate) fn receive_packet_with_timeout(
1221        &mut self,
1222        max_ms: usize,
1223    ) -> Result<usize, DriverError<SE>> {
1224        self.packet_recv_buf[0] = 0;
1225        self.packet_recv_buf[1] = 0;
1226        let packet_len = self
1227            .sensor_interface
1228            .read_with_timeout(&mut self.packet_recv_buf, max_ms)
1229            .map_err(DriverError::CommError)?;
1230
1231        self.last_packet_len_received = packet_len;
1232
1233        Ok(packet_len)
1234    }
1235
1236    /// Verify that the sensor returns an expected chip ID
1237    fn verify_product_id(&mut self) -> Result<(), DriverError<SE>> {
1238        trace!("request PID...");
1239        let cmd_body: [u8; 2] = [
1240            SHUB_PROD_ID_REQ, // request product ID
1241            0,                // reserved
1242        ];
1243
1244        // for some reason, reading PID right after sending request does not work with
1245        // i2c
1246        if self.sensor_interface.requires_soft_reset() {
1247            self.send_packet(CHANNEL_HUB_CONTROL, cmd_body.as_ref())?;
1248        } else {
1249            let response_size =
1250                self.send_and_receive_packet(CHANNEL_HUB_CONTROL, cmd_body.as_ref())?;
1251            if response_size > 0 {
1252                if let Err(e) = self.handle_received_packet(response_size) {
1253                    warn!("{:?}", e)
1254                }
1255            }
1256        }
1257
1258        // process all incoming messages until we get a product id (or no more data)
1259        while !self.prod_id_verified {
1260            trace!("read PID");
1261            let msg_count = self.handle_one_message(150);
1262            if msg_count < 1 {
1263                break;
1264            }
1265        }
1266
1267        if !self.prod_id_verified {
1268            return Err(DriverError::InvalidChipId(0));
1269        }
1270        Ok(())
1271    }
1272
1273    /// Get accelerometer data [x, y, z] in m/s^2
1274    pub fn accelerometer(&self) -> Result<[f32; 3], DriverError<SE>> {
1275        Ok(self.accelerometer)
1276    }
1277
1278    /// Get rotation quaternion [i, j, k, real] (unit quaternion)
1279    pub fn rotation_quaternion(&self) -> Result<[f32; 4], DriverError<SE>> {
1280        Ok(self.rotation_quaternion)
1281    }
1282
1283    /// Get rotation accuracy estimate in radians
1284    pub fn rotation_acc(&self) -> f32 {
1285        self.rotation_acc
1286    }
1287
1288    /// Get game rotation quaternion [i, j, k, real] (unit quaternion)
1289    pub fn game_rotation_quaternion(&self) -> Result<[f32; 4], DriverError<SE>> {
1290        Ok(self.game_rotation_quaternion)
1291    }
1292
1293    /// Get geomagnetic rotation quaternion [i, j, k, real] (unit quaternion)
1294    pub fn geomag_rotation_quaternion(&self) -> Result<[f32; 4], DriverError<SE>> {
1295        Ok(self.geomag_rotation_quaternion)
1296    }
1297
1298    /// Get geomagnetic rotation accuracy estimate in radians
1299    pub fn geomag_rotation_acc(&self) -> f32 {
1300        self.geomag_rotation_acc
1301    }
1302
1303    /// Get linear acceleration [x, y, z] in m/s^2 (gravity removed)
1304    pub fn linear_accel(&self) -> Result<[f32; 3], DriverError<SE>> {
1305        Ok(self.linear_accel)
1306    }
1307
1308    /// Get gravity vector [x, y, z] in m/s^2
1309    pub fn gravity(&self) -> Result<[f32; 3], DriverError<SE>> {
1310        Ok(self.gravity)
1311    }
1312
1313    /// Get calibrated gyroscope data [x, y, z] in rad/s
1314    pub fn gyro(&self) -> Result<[f32; 3], DriverError<SE>> {
1315        Ok(self.gyro)
1316    }
1317
1318    /// Get uncalibrated gyroscope data [x, y, z] in rad/s
1319    pub fn gyro_uncalib(&self) -> Result<[f32; 3], DriverError<SE>> {
1320        Ok(self.uncalib_gyro)
1321    }
1322
1323    /// Get calibrated magnetic field [x, y, z] in uT (micro-Tesla)
1324    pub fn mag_field(&self) -> Result<[f32; 3], DriverError<SE>> {
1325        Ok(self.mag_field)
1326    }
1327
1328    /// Tell the sensor to reset.
1329    ///
1330    /// Normally applications should not need to call this directly,
1331    /// as it is called during `init`.
1332    pub fn soft_reset(&mut self) -> Result<(), DriverError<SE>> {
1333        trace!("soft_reset");
1334        let data: [u8; 1] = [EXECUTABLE_DEVICE_CMD_RESET];
1335        let received_len = self.send_and_receive_packet(CHANNEL_EXECUTABLE, data.as_ref())?;
1336        if received_len > 0 {
1337            if let Err(e) = self.handle_received_packet(received_len) {
1338                warn!("{:?}", e)
1339            }
1340        }
1341        Ok(())
1342    }
1343
1344    /// Send a packet and receive the response
1345    fn send_and_receive_packet(
1346        &mut self,
1347        channel: u8,
1348        body_data: &[u8],
1349    ) -> Result<usize, DriverError<SE>> {
1350        let send_packet_length = self.prep_send_packet(channel, body_data);
1351
1352        let recv_packet_length = self
1353            .sensor_interface
1354            .send_and_receive_packet(
1355                self.packet_send_buf[..send_packet_length].as_ref(),
1356                &mut self.packet_recv_buf,
1357            )
1358            .map_err(DriverError::CommError)?;
1359
1360        Ok(recv_packet_length)
1361    }
1362}
1363
1364#[cfg(test)]
1365mod tests {
1366    use super::*;
1367    use crate::interface::SensorInterface;
1368
1369    // Mock sensor interface for testing without hardware
1370    struct MockSensorInterface {
1371        setup_called: bool,
1372        soft_reset_required: bool,
1373    }
1374
1375    impl MockSensorInterface {
1376        fn new() -> Self {
1377            Self {
1378                setup_called: false,
1379                soft_reset_required: false,
1380            }
1381        }
1382    }
1383
1384    #[derive(Debug)]
1385    struct MockError;
1386
1387    impl SensorInterface for MockSensorInterface {
1388        type SensorError = MockError;
1389
1390        fn setup(&mut self) -> Result<(), Self::SensorError> {
1391            self.setup_called = true;
1392            Ok(())
1393        }
1394
1395        fn write_packet(&mut self, _packet: &[u8]) -> Result<(), Self::SensorError> {
1396            Ok(())
1397        }
1398
1399        fn read_packet(&mut self, _recv_buf: &mut [u8]) -> Result<usize, Self::SensorError> {
1400            Ok(0)
1401        }
1402
1403        fn read_with_timeout(
1404            &mut self,
1405            _recv_buf: &mut [u8],
1406            _max_ms: usize,
1407        ) -> Result<usize, Self::SensorError> {
1408            Ok(0)
1409        }
1410
1411        fn send_and_receive_packet(
1412            &mut self,
1413            _send_buf: &[u8],
1414            _recv_buf: &mut [u8],
1415        ) -> Result<usize, Self::SensorError> {
1416            Ok(0)
1417        }
1418
1419        fn requires_soft_reset(&self) -> bool {
1420            self.soft_reset_required
1421        }
1422    }
1423
1424    // ==========================================================================
1425    // DriverError Tests
1426    // ==========================================================================
1427
1428    #[test]
1429    fn test_driver_error_comm_error_to_io_error() {
1430        let err: DriverError<&str> = DriverError::CommError("test error");
1431        let io_err: io::Error = err.into();
1432        assert!(io_err.to_string().contains("Communication error"));
1433        assert!(io_err.to_string().contains("test error"));
1434    }
1435
1436    #[test]
1437    fn test_driver_error_invalid_chip_id_to_io_error() {
1438        let err: DriverError<&str> = DriverError::InvalidChipId(0x42);
1439        let io_err: io::Error = err.into();
1440        assert_eq!(io_err.kind(), ErrorKind::InvalidData);
1441        assert!(io_err.to_string().contains("Invalid chip ID"));
1442        assert!(io_err.to_string().contains("66")); // 0x42 = 66
1443    }
1444
1445    #[test]
1446    fn test_driver_error_invalid_fw_version_to_io_error() {
1447        let err: DriverError<&str> = DriverError::InvalidFWVersion(0x10);
1448        let io_err: io::Error = err.into();
1449        assert_eq!(io_err.kind(), ErrorKind::InvalidData);
1450        assert!(io_err.to_string().contains("Invalid firmware version"));
1451    }
1452
1453    #[test]
1454    fn test_driver_error_no_data_available_to_io_error() {
1455        let err: DriverError<&str> = DriverError::NoDataAvailable;
1456        let io_err: io::Error = err.into();
1457        assert_eq!(io_err.kind(), ErrorKind::TimedOut);
1458        assert!(io_err.to_string().contains("No sensor data available"));
1459    }
1460
1461    // ==========================================================================
1462    // Cursor Reading Helper Tests
1463    // ==========================================================================
1464
1465    #[test]
1466    fn test_read_u8_at_cursor() {
1467        let data = [0x12, 0x34, 0x56, 0x78];
1468        let mut cursor = 0;
1469
1470        assert_eq!(
1471            BNO08x::<MockSensorInterface>::read_u8_at_cursor(&data, &mut cursor),
1472            0x12
1473        );
1474        assert_eq!(cursor, 1);
1475
1476        assert_eq!(
1477            BNO08x::<MockSensorInterface>::read_u8_at_cursor(&data, &mut cursor),
1478            0x34
1479        );
1480        assert_eq!(cursor, 2);
1481
1482        assert_eq!(
1483            BNO08x::<MockSensorInterface>::read_u8_at_cursor(&data, &mut cursor),
1484            0x56
1485        );
1486        assert_eq!(cursor, 3);
1487
1488        assert_eq!(
1489            BNO08x::<MockSensorInterface>::read_u8_at_cursor(&data, &mut cursor),
1490            0x78
1491        );
1492        assert_eq!(cursor, 4);
1493    }
1494
1495    #[test]
1496    fn test_read_i16_at_cursor_positive() {
1497        // Little-endian: 0x0102 stored as [0x02, 0x01]
1498        let data = [0x02, 0x01, 0x00, 0x00];
1499        let mut cursor = 0;
1500
1501        let value = BNO08x::<MockSensorInterface>::read_i16_at_cursor(&data, &mut cursor);
1502        assert_eq!(value, 0x0102);
1503        assert_eq!(cursor, 2);
1504    }
1505
1506    #[test]
1507    fn test_read_i16_at_cursor_negative() {
1508        // -1 in little-endian: [0xFF, 0xFF]
1509        let data = [0xFF, 0xFF, 0x00, 0x00];
1510        let mut cursor = 0;
1511
1512        let value = BNO08x::<MockSensorInterface>::read_i16_at_cursor(&data, &mut cursor);
1513        assert_eq!(value, -1);
1514        assert_eq!(cursor, 2);
1515    }
1516
1517    #[test]
1518    fn test_read_i16_at_cursor_max_positive() {
1519        // 32767 (0x7FFF) in little-endian: [0xFF, 0x7F]
1520        let data = [0xFF, 0x7F, 0x00, 0x00];
1521        let mut cursor = 0;
1522
1523        let value = BNO08x::<MockSensorInterface>::read_i16_at_cursor(&data, &mut cursor);
1524        assert_eq!(value, i16::MAX);
1525    }
1526
1527    #[test]
1528    fn test_read_i16_at_cursor_min_negative() {
1529        // -32768 (0x8000) in little-endian: [0x00, 0x80]
1530        let data = [0x00, 0x80, 0x00, 0x00];
1531        let mut cursor = 0;
1532
1533        let value = BNO08x::<MockSensorInterface>::read_i16_at_cursor(&data, &mut cursor);
1534        assert_eq!(value, i16::MIN);
1535    }
1536
1537    #[test]
1538    fn test_try_read_i16_at_cursor_success() {
1539        let data = [0x34, 0x12, 0x78, 0x56];
1540        let mut cursor = 0;
1541
1542        let result = BNO08x::<MockSensorInterface>::try_read_i16_at_cursor(&data, &mut cursor);
1543        assert_eq!(result, Some(0x1234));
1544        assert_eq!(cursor, 2);
1545
1546        let result = BNO08x::<MockSensorInterface>::try_read_i16_at_cursor(&data, &mut cursor);
1547        assert_eq!(result, Some(0x5678));
1548        assert_eq!(cursor, 4);
1549    }
1550
1551    #[test]
1552    fn test_try_read_i16_at_cursor_insufficient_data() {
1553        let data = [0x12];
1554        let mut cursor = 0;
1555
1556        let result = BNO08x::<MockSensorInterface>::try_read_i16_at_cursor(&data, &mut cursor);
1557        assert_eq!(result, None);
1558        assert_eq!(cursor, 0); // cursor should not advance on failure
1559    }
1560
1561    #[test]
1562    fn test_try_read_i16_at_cursor_exactly_at_end() {
1563        let data = [0x12, 0x34];
1564        let mut cursor = 2;
1565
1566        let result = BNO08x::<MockSensorInterface>::try_read_i16_at_cursor(&data, &mut cursor);
1567        assert_eq!(result, None);
1568    }
1569
1570    #[test]
1571    fn test_try_read_i16_at_cursor_one_byte_remaining() {
1572        let data = [0x12, 0x34, 0x56];
1573        let mut cursor = 2;
1574
1575        let result = BNO08x::<MockSensorInterface>::try_read_i16_at_cursor(&data, &mut cursor);
1576        assert_eq!(result, None);
1577    }
1578
1579    // ==========================================================================
1580    // Input Report Parsing Tests
1581    // ==========================================================================
1582
1583    #[test]
1584    fn test_handle_one_input_report_full_data() {
1585        // Simulated input report with all 5 data values
1586        // Format: [report_id, seq_num, status, delay, data1_lo, data1_hi, ...]
1587        #[rustfmt::skip]
1588        let msg: [u8; 14] = [
1589            0x01,       // report_id (accelerometer)
1590            0x00,       // sequence number
1591            0x00,       // status
1592            0x00,       // delay
1593            0x00, 0x01, // data1 = 256 (little-endian)
1594            0x00, 0x02, // data2 = 512
1595            0x00, 0x04, // data3 = 1024
1596            0x00, 0x08, // data4 = 2048
1597            0x00, 0x10, // data5 = 4096
1598        ];
1599
1600        let (cursor, report_id, d1, d2, d3, d4, d5) =
1601            BNO08x::<MockSensorInterface>::handle_one_input_report(0, &msg);
1602
1603        assert_eq!(report_id, SENSOR_REPORTID_ACCELEROMETER);
1604        assert_eq!(d1, 256);
1605        assert_eq!(d2, 512);
1606        assert_eq!(d3, 1024);
1607        assert_eq!(d4, 2048);
1608        assert_eq!(d5, 4096);
1609        assert_eq!(cursor, 14);
1610    }
1611
1612    #[test]
1613    fn test_handle_one_input_report_partial_data() {
1614        // Report with only 3 data values (like accelerometer)
1615        let msg: [u8; 10] = [
1616            0x01, // report_id
1617            0x01, // sequence number
1618            0x02, // status
1619            0x03, // delay
1620            0x10, 0x00, // data1 = 16
1621            0x20, 0x00, // data2 = 32
1622            0x30, 0x00, // data3 = 48
1623        ];
1624
1625        let (cursor, report_id, d1, d2, d3, d4, d5) =
1626            BNO08x::<MockSensorInterface>::handle_one_input_report(0, &msg);
1627
1628        assert_eq!(report_id, SENSOR_REPORTID_ACCELEROMETER);
1629        assert_eq!(d1, 16);
1630        assert_eq!(d2, 32);
1631        assert_eq!(d3, 48);
1632        assert_eq!(d4, 0); // default when not enough data
1633        assert_eq!(d5, 0); // default when not enough data
1634        assert_eq!(cursor, 10);
1635    }
1636
1637    #[test]
1638    fn test_handle_one_input_report_with_offset() {
1639        // Test that cursor offset works correctly
1640        #[rustfmt::skip]
1641        let msg: [u8; 16] = [
1642            0xFF, 0xFF, // padding (2 bytes)
1643            0x05,       // report_id (rotation vector)
1644            0x00,       // sequence number
1645            0x00,       // status
1646            0x00,       // delay
1647            0x01, 0x00, // data1
1648            0x02, 0x00, // data2
1649            0x03, 0x00, // data3
1650            0x04, 0x00, // data4
1651            0x05, 0x00, // data5
1652        ];
1653
1654        let (cursor, report_id, d1, d2, d3, d4, d5) =
1655            BNO08x::<MockSensorInterface>::handle_one_input_report(2, &msg);
1656
1657        assert_eq!(report_id, SENSOR_REPORTID_ROTATION_VECTOR);
1658        assert_eq!(d1, 1);
1659        assert_eq!(d2, 2);
1660        assert_eq!(d3, 3);
1661        assert_eq!(d4, 4);
1662        assert_eq!(d5, 5);
1663        assert_eq!(cursor, 16); // 2 + 14 bytes
1664    }
1665
1666    // ==========================================================================
1667    // BNO08x Constructor and State Tests
1668    // ==========================================================================
1669
1670    #[test]
1671    fn test_new_with_interface() {
1672        let mock = MockSensorInterface::new();
1673        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1674
1675        // Verify initial state
1676        assert!(!driver.device_reset);
1677        assert!(!driver.prod_id_verified);
1678        assert!(!driver.init_received);
1679        assert!(!driver.advert_received);
1680        assert!(!driver.error_list_received);
1681        assert_eq!(driver.last_packet_len_received, 0);
1682        assert_eq!(driver.sequence_numbers, [0; NUM_CHANNELS]);
1683        assert_eq!(driver.accelerometer, [0.0; 3]);
1684        assert_eq!(driver.rotation_quaternion, [0.0; 4]);
1685        assert_eq!(driver.gyro, [0.0; 3]);
1686    }
1687
1688    #[test]
1689    fn test_free_returns_interface() {
1690        let mock = MockSensorInterface::new();
1691        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1692        let _interface = driver.free();
1693        // If this compiles and runs, the interface was returned successfully
1694    }
1695
1696    // ==========================================================================
1697    // Sensor Data Accessor Tests
1698    // ==========================================================================
1699
1700    #[test]
1701    fn test_accelerometer_accessor() {
1702        let mock = MockSensorInterface::new();
1703        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1704
1705        let accel = driver.accelerometer().unwrap();
1706        assert_eq!(accel, [0.0, 0.0, 0.0]);
1707    }
1708
1709    #[test]
1710    fn test_rotation_quaternion_accessor() {
1711        let mock = MockSensorInterface::new();
1712        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1713
1714        let quat = driver.rotation_quaternion().unwrap();
1715        assert_eq!(quat, [0.0, 0.0, 0.0, 0.0]);
1716    }
1717
1718    #[test]
1719    fn test_rotation_acc_accessor() {
1720        let mock = MockSensorInterface::new();
1721        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1722
1723        assert_eq!(driver.rotation_acc(), 0.0);
1724    }
1725
1726    #[test]
1727    fn test_game_rotation_quaternion_accessor() {
1728        let mock = MockSensorInterface::new();
1729        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1730
1731        let quat = driver.game_rotation_quaternion().unwrap();
1732        assert_eq!(quat, [0.0, 0.0, 0.0, 0.0]);
1733    }
1734
1735    #[test]
1736    fn test_geomag_rotation_quaternion_accessor() {
1737        let mock = MockSensorInterface::new();
1738        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1739
1740        let quat = driver.geomag_rotation_quaternion().unwrap();
1741        assert_eq!(quat, [0.0, 0.0, 0.0, 0.0]);
1742    }
1743
1744    #[test]
1745    fn test_geomag_rotation_acc_accessor() {
1746        let mock = MockSensorInterface::new();
1747        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1748
1749        assert_eq!(driver.geomag_rotation_acc(), 0.0);
1750    }
1751
1752    #[test]
1753    fn test_linear_accel_accessor() {
1754        let mock = MockSensorInterface::new();
1755        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1756
1757        let accel = driver.linear_accel().unwrap();
1758        assert_eq!(accel, [0.0, 0.0, 0.0]);
1759    }
1760
1761    #[test]
1762    fn test_gravity_accessor() {
1763        let mock = MockSensorInterface::new();
1764        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1765
1766        let gravity = driver.gravity().unwrap();
1767        assert_eq!(gravity, [0.0, 0.0, 0.0]);
1768    }
1769
1770    #[test]
1771    fn test_gyro_accessor() {
1772        let mock = MockSensorInterface::new();
1773        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1774
1775        let gyro = driver.gyro().unwrap();
1776        assert_eq!(gyro, [0.0, 0.0, 0.0]);
1777    }
1778
1779    #[test]
1780    fn test_gyro_uncalib_accessor() {
1781        let mock = MockSensorInterface::new();
1782        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1783
1784        let gyro = driver.gyro_uncalib().unwrap();
1785        assert_eq!(gyro, [0.0, 0.0, 0.0]);
1786    }
1787
1788    #[test]
1789    fn test_mag_field_accessor() {
1790        let mock = MockSensorInterface::new();
1791        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1792
1793        let mag = driver.mag_field().unwrap();
1794        assert_eq!(mag, [0.0, 0.0, 0.0]);
1795    }
1796
1797    // ==========================================================================
1798    // Report State Tests
1799    // ==========================================================================
1800
1801    #[test]
1802    fn test_is_report_enabled_default() {
1803        let mock = MockSensorInterface::new();
1804        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1805
1806        // All reports should be disabled by default
1807        for i in 0..16 {
1808            assert!(!driver.is_report_enabled(i));
1809        }
1810    }
1811
1812    #[test]
1813    fn test_is_report_enabled_out_of_bounds() {
1814        let mock = MockSensorInterface::new();
1815        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1816
1817        // Out of bounds should return false
1818        assert!(!driver.is_report_enabled(255));
1819    }
1820
1821    #[test]
1822    fn test_report_update_time_default() {
1823        let mock = MockSensorInterface::new();
1824        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1825
1826        // All update times should be 0 by default
1827        for i in 0..16 {
1828            assert_eq!(driver.report_update_time(i), 0);
1829        }
1830    }
1831
1832    #[test]
1833    fn test_report_update_time_out_of_bounds() {
1834        let mock = MockSensorInterface::new();
1835        let driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1836
1837        // Out of bounds should return 0
1838        assert_eq!(driver.report_update_time(255), 0);
1839    }
1840
1841    // ==========================================================================
1842    // Packet Preparation Tests
1843    // ==========================================================================
1844
1845    #[test]
1846    fn test_prep_send_packet_basic() {
1847        let mock = MockSensorInterface::new();
1848        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1849
1850        let body = [0x01, 0x02, 0x03];
1851        let packet_len = driver.prep_send_packet(CHANNEL_HUB_CONTROL, &body);
1852
1853        // Packet length = header (4) + body (3) = 7
1854        assert_eq!(packet_len, 7);
1855
1856        // Check header
1857        assert_eq!(driver.packet_send_buf[0], 7); // LSB of length
1858        assert_eq!(driver.packet_send_buf[1], 0); // MSB of length
1859        assert_eq!(driver.packet_send_buf[2], CHANNEL_HUB_CONTROL);
1860        assert_eq!(driver.packet_send_buf[3], 0); // sequence number (first packet)
1861
1862        // Check body
1863        assert_eq!(driver.packet_send_buf[4], 0x01);
1864        assert_eq!(driver.packet_send_buf[5], 0x02);
1865        assert_eq!(driver.packet_send_buf[6], 0x03);
1866    }
1867
1868    #[test]
1869    fn test_prep_send_packet_sequence_increments() {
1870        let mock = MockSensorInterface::new();
1871        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1872
1873        let body = [0x01];
1874
1875        // First packet
1876        driver.prep_send_packet(CHANNEL_HUB_CONTROL, &body);
1877        assert_eq!(driver.sequence_numbers[CHANNEL_HUB_CONTROL as usize], 1);
1878
1879        // Second packet
1880        driver.prep_send_packet(CHANNEL_HUB_CONTROL, &body);
1881        assert_eq!(driver.sequence_numbers[CHANNEL_HUB_CONTROL as usize], 2);
1882
1883        // Third packet
1884        driver.prep_send_packet(CHANNEL_HUB_CONTROL, &body);
1885        assert_eq!(driver.sequence_numbers[CHANNEL_HUB_CONTROL as usize], 3);
1886    }
1887
1888    #[test]
1889    fn test_prep_send_packet_different_channels() {
1890        let mock = MockSensorInterface::new();
1891        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1892
1893        let body = [0x01];
1894
1895        // Send on different channels
1896        driver.prep_send_packet(CHANNEL_COMMAND, &body);
1897        driver.prep_send_packet(CHANNEL_HUB_CONTROL, &body);
1898        driver.prep_send_packet(CHANNEL_EXECUTABLE, &body);
1899
1900        // Each channel should have its own sequence
1901        assert_eq!(driver.sequence_numbers[CHANNEL_COMMAND as usize], 1);
1902        assert_eq!(driver.sequence_numbers[CHANNEL_HUB_CONTROL as usize], 1);
1903        assert_eq!(driver.sequence_numbers[CHANNEL_EXECUTABLE as usize], 1);
1904    }
1905
1906    #[test]
1907    fn test_prep_send_packet_large_body() {
1908        let mock = MockSensorInterface::new();
1909        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1910
1911        // Create a 100-byte body
1912        let body = [0xAB; 100];
1913        let packet_len = driver.prep_send_packet(CHANNEL_HUB_CONTROL, &body);
1914
1915        assert_eq!(packet_len, 104); // 4 header + 100 body
1916        assert_eq!(driver.packet_send_buf[0], 104); // LSB
1917        assert_eq!(driver.packet_send_buf[1], 0); // MSB
1918
1919        // Verify body content
1920        for i in 0..100 {
1921            assert_eq!(driver.packet_send_buf[4 + i], 0xAB);
1922        }
1923    }
1924
1925    // ==========================================================================
1926    // Sensor Update Method Tests
1927    // ==========================================================================
1928
1929    #[test]
1930    fn test_update_accelerometer() {
1931        let mock = MockSensorInterface::new();
1932        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1933
1934        // Q8 format: 256 = 1.0 m/s^2
1935        driver.update_accelerometer(256, 512, -256);
1936
1937        let accel = driver.accelerometer().unwrap();
1938        assert!((accel[0] - 1.0).abs() < 0.01);
1939        assert!((accel[1] - 2.0).abs() < 0.01);
1940        assert!((accel[2] + 1.0).abs() < 0.01);
1941    }
1942
1943    #[test]
1944    fn test_update_rotation_quaternion() {
1945        let mock = MockSensorInterface::new();
1946        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1947
1948        // Q14 format: 16384 = 1.0
1949        // Identity quaternion: [0, 0, 0, 1]
1950        driver.update_rotation_quaternion(0, 0, 0, 16384, 0);
1951
1952        let quat = driver.rotation_quaternion().unwrap();
1953        assert!(quat[0].abs() < 0.001);
1954        assert!(quat[1].abs() < 0.001);
1955        assert!(quat[2].abs() < 0.001);
1956        assert!((quat[3] - 1.0).abs() < 0.001);
1957    }
1958
1959    #[test]
1960    fn test_update_rotation_quaternion_with_accuracy() {
1961        let mock = MockSensorInterface::new();
1962        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1963
1964        // Q12 for accuracy: 4096 = 1.0 radian
1965        driver.update_rotation_quaternion(0, 0, 0, 16384, 2048);
1966
1967        assert!((driver.rotation_acc() - 0.5).abs() < 0.01);
1968    }
1969
1970    #[test]
1971    fn test_update_rotation_quaternion_game() {
1972        let mock = MockSensorInterface::new();
1973        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1974
1975        // Q14 format
1976        driver.update_rotation_quaternion_game(8192, 0, 0, 8192);
1977
1978        let quat = driver.game_rotation_quaternion().unwrap();
1979        assert!((quat[0] - 0.5).abs() < 0.001);
1980        assert!(quat[1].abs() < 0.001);
1981        assert!(quat[2].abs() < 0.001);
1982        assert!((quat[3] - 0.5).abs() < 0.001);
1983    }
1984
1985    #[test]
1986    fn test_update_rotation_quaternion_geomag() {
1987        let mock = MockSensorInterface::new();
1988        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
1989
1990        driver.update_rotation_quaternion_geomag(0, 0, 0, 16384, 4096);
1991
1992        let quat = driver.geomag_rotation_quaternion().unwrap();
1993        assert!((quat[3] - 1.0).abs() < 0.001);
1994        assert!((driver.geomag_rotation_acc() - 1.0).abs() < 0.01);
1995    }
1996
1997    #[test]
1998    fn test_update_linear_accel() {
1999        let mock = MockSensorInterface::new();
2000        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2001
2002        // Q8 format
2003        driver.update_linear_accel(128, 256, 512);
2004
2005        let accel = driver.linear_accel().unwrap();
2006        assert!((accel[0] - 0.5).abs() < 0.01);
2007        assert!((accel[1] - 1.0).abs() < 0.01);
2008        assert!((accel[2] - 2.0).abs() < 0.01);
2009    }
2010
2011    #[test]
2012    fn test_update_gravity() {
2013        let mock = MockSensorInterface::new();
2014        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2015
2016        // Q8 format: ~9.8 m/s^2 = ~2509 in Q8
2017        driver.update_gravity(0, 0, 2509);
2018
2019        let gravity = driver.gravity().unwrap();
2020        assert!(gravity[0].abs() < 0.01);
2021        assert!(gravity[1].abs() < 0.01);
2022        assert!((gravity[2] - 9.8).abs() < 0.1);
2023    }
2024
2025    #[test]
2026    fn test_update_gyro_calib() {
2027        let mock = MockSensorInterface::new();
2028        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2029
2030        // Q9 format: 512 = 1.0 rad/s
2031        driver.update_gyro_calib(512, -512, 256);
2032
2033        let gyro = driver.gyro().unwrap();
2034        assert!((gyro[0] - 1.0).abs() < 0.01);
2035        assert!((gyro[1] + 1.0).abs() < 0.01);
2036        assert!((gyro[2] - 0.5).abs() < 0.01);
2037    }
2038
2039    #[test]
2040    fn test_update_gyro_uncalib() {
2041        let mock = MockSensorInterface::new();
2042        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2043
2044        // Q9 format
2045        driver.update_gyro_uncalib(256, 512, 768);
2046
2047        let gyro = driver.gyro_uncalib().unwrap();
2048        assert!((gyro[0] - 0.5).abs() < 0.01);
2049        assert!((gyro[1] - 1.0).abs() < 0.01);
2050        assert!((gyro[2] - 1.5).abs() < 0.01);
2051    }
2052
2053    #[test]
2054    fn test_update_magnetic_field_calib() {
2055        let mock = MockSensorInterface::new();
2056        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2057
2058        // Q4 format: 16 = 1.0 µT
2059        driver.update_magnetic_field_calib(160, 320, 480);
2060
2061        let mag = driver.mag_field().unwrap();
2062        assert!((mag[0] - 10.0).abs() < 0.1);
2063        assert!((mag[1] - 20.0).abs() < 0.1);
2064        assert!((mag[2] - 30.0).abs() < 0.1);
2065    }
2066
2067    // ==========================================================================
2068    // Packet Handling Tests
2069    // ==========================================================================
2070
2071    #[test]
2072    fn test_handle_received_packet_too_short() {
2073        let mock = MockSensorInterface::new();
2074        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2075
2076        // Packet shorter than header length should return error
2077        let result = driver.handle_received_packet(2);
2078        assert!(result.is_err());
2079    }
2080
2081    #[test]
2082    fn test_handle_received_packet_clamped_to_buffer_size() {
2083        let mock = MockSensorInterface::new();
2084        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2085
2086        // Initialize buffer with valid packet data that will be processed
2087        // when clamped to buffer size
2088        driver.packet_recv_buf[0] = 8;
2089        driver.packet_recv_buf[1] = 0;
2090        driver.packet_recv_buf[2] = CHANNEL_HUB_CONTROL;
2091        driver.packet_recv_buf[3] = 0;
2092        driver.packet_recv_buf[4] = SHUB_PROD_ID_RESP;
2093        driver.packet_recv_buf[5] = 0;
2094        driver.packet_recv_buf[6] = 1;
2095        driver.packet_recv_buf[7] = 0;
2096
2097        // Packet larger than buffer - should be clamped
2098        // The function should clamp to PACKET_RECV_BUF_LEN and process
2099        let result = driver.handle_received_packet(PACKET_RECV_BUF_LEN + 100);
2100        // Should succeed since we have valid data in the buffer
2101        assert!(result.is_ok());
2102    }
2103
2104    #[test]
2105    fn test_handle_received_packet_executable_channel_reset() {
2106        let mock = MockSensorInterface::new();
2107        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2108
2109        // Simulate a reset complete response on executable channel
2110        // Packet format: [len_lo, len_hi, channel, seq, report_id, ...]
2111        driver.packet_recv_buf[0] = 5; // length low
2112        driver.packet_recv_buf[1] = 0; // length high
2113        driver.packet_recv_buf[2] = CHANNEL_EXECUTABLE; // channel
2114        driver.packet_recv_buf[3] = 0; // sequence
2115        driver.packet_recv_buf[4] = EXECUTABLE_DEVICE_RESP_RESET_COMPLETE;
2116
2117        assert!(!driver.device_reset);
2118        let result = driver.handle_received_packet(5);
2119        assert!(result.is_ok());
2120        assert!(driver.device_reset);
2121    }
2122
2123    #[test]
2124    fn test_handle_received_packet_unknown_executable_report() {
2125        let mock = MockSensorInterface::new();
2126        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2127
2128        driver.packet_recv_buf[0] = 5;
2129        driver.packet_recv_buf[1] = 0;
2130        driver.packet_recv_buf[2] = CHANNEL_EXECUTABLE;
2131        driver.packet_recv_buf[3] = 0;
2132        driver.packet_recv_buf[4] = 0xFF; // Unknown report ID
2133
2134        let result = driver.handle_received_packet(5);
2135        assert!(result.is_err());
2136        assert_eq!(driver.last_exec_chan_rid, 0xFF);
2137    }
2138
2139    #[test]
2140    fn test_handle_received_packet_unknown_channel() {
2141        let mock = MockSensorInterface::new();
2142        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2143
2144        driver.packet_recv_buf[0] = 5;
2145        driver.packet_recv_buf[1] = 0;
2146        driver.packet_recv_buf[2] = 0xFE; // Unknown channel
2147        driver.packet_recv_buf[3] = 0;
2148        driver.packet_recv_buf[4] = 0x01;
2149
2150        let result = driver.handle_received_packet(5);
2151        assert!(result.is_err());
2152        assert_eq!(driver.last_chan_received, 0xFE);
2153    }
2154
2155    #[test]
2156    fn test_handle_received_packet_hub_control_init() {
2157        let mock = MockSensorInterface::new();
2158        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2159
2160        // Simulate a command response with init
2161        driver.packet_recv_buf[0] = 8;
2162        driver.packet_recv_buf[1] = 0;
2163        driver.packet_recv_buf[2] = CHANNEL_HUB_CONTROL;
2164        driver.packet_recv_buf[3] = 0;
2165        driver.packet_recv_buf[4] = SHUB_COMMAND_RESP;
2166        driver.packet_recv_buf[5] = 0;
2167        driver.packet_recv_buf[6] = SH2_INIT_SYSTEM;
2168
2169        assert!(!driver.init_received);
2170        let result = driver.handle_received_packet(8);
2171        assert!(result.is_ok());
2172        assert!(driver.init_received);
2173    }
2174
2175    #[test]
2176    fn test_handle_received_packet_hub_control_prod_id() {
2177        let mock = MockSensorInterface::new();
2178        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2179
2180        // Simulate product ID response
2181        driver.packet_recv_buf[0] = 10;
2182        driver.packet_recv_buf[1] = 0;
2183        driver.packet_recv_buf[2] = CHANNEL_HUB_CONTROL;
2184        driver.packet_recv_buf[3] = 0;
2185        driver.packet_recv_buf[4] = SHUB_PROD_ID_RESP;
2186        driver.packet_recv_buf[5] = 0; // reset cause
2187        driver.packet_recv_buf[6] = 3; // sw version major
2188        driver.packet_recv_buf[7] = 5; // sw version minor
2189
2190        assert!(!driver.prod_id_verified);
2191        let result = driver.handle_received_packet(10);
2192        assert!(result.is_ok());
2193        assert!(driver.prod_id_verified);
2194    }
2195
2196    #[test]
2197    fn test_handle_received_packet_hub_control_feature_resp() {
2198        let mock = MockSensorInterface::new();
2199        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2200
2201        // Simulate feature response for accelerometer
2202        driver.packet_recv_buf[0] = 8;
2203        driver.packet_recv_buf[1] = 0;
2204        driver.packet_recv_buf[2] = CHANNEL_HUB_CONTROL;
2205        driver.packet_recv_buf[3] = 0;
2206        driver.packet_recv_buf[4] = SHUB_GET_FEATURE_RESP;
2207        driver.packet_recv_buf[5] = SENSOR_REPORTID_ACCELEROMETER;
2208
2209        assert!(!driver.report_enabled[SENSOR_REPORTID_ACCELEROMETER as usize]);
2210        let result = driver.handle_received_packet(8);
2211        assert!(result.is_ok());
2212        assert!(driver.report_enabled[SENSOR_REPORTID_ACCELEROMETER as usize]);
2213    }
2214
2215    #[test]
2216    fn test_handle_received_packet_hub_control_frs_write_resp() {
2217        let mock = MockSensorInterface::new();
2218        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2219
2220        driver.packet_recv_buf[0] = 8;
2221        driver.packet_recv_buf[1] = 0;
2222        driver.packet_recv_buf[2] = CHANNEL_HUB_CONTROL;
2223        driver.packet_recv_buf[3] = 0;
2224        driver.packet_recv_buf[4] = SHUB_FRS_WRITE_RESP;
2225        driver.packet_recv_buf[5] = FRS_STATUS_WRITE_READY;
2226
2227        let result = driver.handle_received_packet(8);
2228        assert!(result.is_ok());
2229        assert_eq!(driver.frs_write_status, FRS_STATUS_WRITE_READY);
2230    }
2231
2232    #[test]
2233    fn test_handle_received_packet_hub_control_unknown() {
2234        let mock = MockSensorInterface::new();
2235        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2236
2237        driver.packet_recv_buf[0] = 8;
2238        driver.packet_recv_buf[1] = 0;
2239        driver.packet_recv_buf[2] = CHANNEL_HUB_CONTROL;
2240        driver.packet_recv_buf[3] = 0;
2241        driver.packet_recv_buf[4] = 0xFE; // Unknown report
2242
2243        let result = driver.handle_received_packet(8);
2244        assert!(result.is_err());
2245    }
2246
2247    #[test]
2248    fn test_handle_received_packet_command_channel_unknown() {
2249        let mock = MockSensorInterface::new();
2250        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2251
2252        driver.packet_recv_buf[0] = 8;
2253        driver.packet_recv_buf[1] = 0;
2254        driver.packet_recv_buf[2] = CHANNEL_COMMAND;
2255        driver.packet_recv_buf[3] = 0;
2256        driver.packet_recv_buf[4] = 0xFE; // Unknown report
2257
2258        let result = driver.handle_received_packet(8);
2259        assert!(result.is_err());
2260        assert_eq!(driver.last_command_chan_rid, 0xFE);
2261    }
2262
2263    // ==========================================================================
2264    // Error List Handling Tests
2265    // ==========================================================================
2266
2267    #[test]
2268    fn test_handle_cmd_resp_error_list_empty() {
2269        let mock = MockSensorInterface::new();
2270        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2271
2272        driver.packet_recv_buf[0] = 6;
2273        driver.packet_recv_buf[1] = 0;
2274        driver.packet_recv_buf[2] = CHANNEL_COMMAND;
2275        driver.packet_recv_buf[3] = 0;
2276        driver.packet_recv_buf[4] = CMD_RESP_ERROR_LIST;
2277        driver.packet_recv_buf[5] = 0; // No errors
2278
2279        let result = driver.handle_received_packet(6);
2280        assert!(result.is_ok());
2281        assert!(driver.error_list_received);
2282    }
2283
2284    #[test]
2285    fn test_handle_cmd_resp_error_list_various_errors() {
2286        let mock = MockSensorInterface::new();
2287        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2288
2289        // Test with multiple error codes
2290        driver.packet_recv_buf[0] = 18;
2291        driver.packet_recv_buf[1] = 0;
2292        driver.packet_recv_buf[2] = CHANNEL_COMMAND;
2293        driver.packet_recv_buf[3] = 0;
2294        driver.packet_recv_buf[4] = CMD_RESP_ERROR_LIST;
2295        // Error codes 0-12 and one unknown
2296        for i in 0..13 {
2297            driver.packet_recv_buf[5 + i] = i as u8;
2298        }
2299        driver.packet_recv_buf[18] = 0xFF; // Unknown error
2300
2301        let result = driver.handle_received_packet(18);
2302        assert!(result.is_ok());
2303        assert!(driver.error_list_received);
2304    }
2305
2306    // ==========================================================================
2307    // Sensor Reports Handling Tests
2308    // ==========================================================================
2309
2310    #[test]
2311    fn test_handle_sensor_reports_accelerometer() {
2312        let mock = MockSensorInterface::new();
2313        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2314
2315        // Construct a sensor report packet for accelerometer
2316        // Format: header (4 bytes) + timestamp (5 bytes) + report (10 bytes)
2317        driver.packet_recv_buf[0] = 19; // length
2318        driver.packet_recv_buf[1] = 0;
2319        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2320        driver.packet_recv_buf[3] = 0;
2321        // Timestamp base (5 bytes)
2322        driver.packet_recv_buf[4] = 0xFB; // time base report
2323        driver.packet_recv_buf[5] = 0;
2324        driver.packet_recv_buf[6] = 0;
2325        driver.packet_recv_buf[7] = 0;
2326        driver.packet_recv_buf[8] = 0;
2327        // Accelerometer report
2328        driver.packet_recv_buf[9] = SENSOR_REPORTID_ACCELEROMETER;
2329        driver.packet_recv_buf[10] = 0; // seq
2330        driver.packet_recv_buf[11] = 0; // status
2331        driver.packet_recv_buf[12] = 0; // delay
2332                                        // X = 256 (1.0 m/s² in Q8)
2333        driver.packet_recv_buf[13] = 0x00;
2334        driver.packet_recv_buf[14] = 0x01;
2335        // Y = 512 (2.0 m/s² in Q8)
2336        driver.packet_recv_buf[15] = 0x00;
2337        driver.packet_recv_buf[16] = 0x02;
2338        // Z = -256 (-1.0 m/s² in Q8)
2339        driver.packet_recv_buf[17] = 0x00;
2340        driver.packet_recv_buf[18] = 0xFF;
2341
2342        let result = driver.handle_received_packet(19);
2343        assert!(result.is_ok());
2344
2345        let accel = driver.accelerometer().unwrap();
2346        assert!((accel[0] - 1.0).abs() < 0.1);
2347        assert!((accel[1] - 2.0).abs() < 0.1);
2348    }
2349
2350    #[test]
2351    fn test_handle_sensor_reports_rotation_vector() {
2352        let mock = MockSensorInterface::new();
2353        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2354
2355        // Construct rotation vector report
2356        driver.packet_recv_buf[0] = 23; // length
2357        driver.packet_recv_buf[1] = 0;
2358        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2359        driver.packet_recv_buf[3] = 0;
2360        // Timestamp base
2361        driver.packet_recv_buf[4] = 0xFB;
2362        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2363        // Rotation vector report (14 bytes: 4 header + 10 data)
2364        driver.packet_recv_buf[9] = SENSOR_REPORTID_ROTATION_VECTOR;
2365        driver.packet_recv_buf[10] = 0;
2366        driver.packet_recv_buf[11] = 0;
2367        driver.packet_recv_buf[12] = 0;
2368        // i, j, k, real (Q14: 16384 = 1.0), accuracy
2369        // Identity quaternion: [0, 0, 0, 1]
2370        driver.packet_recv_buf[13..15].copy_from_slice(&0i16.to_le_bytes()); // i
2371        driver.packet_recv_buf[15..17].copy_from_slice(&0i16.to_le_bytes()); // j
2372        driver.packet_recv_buf[17..19].copy_from_slice(&0i16.to_le_bytes()); // k
2373        driver.packet_recv_buf[19..21].copy_from_slice(&16384i16.to_le_bytes()); // real
2374        driver.packet_recv_buf[21..23].copy_from_slice(&0i16.to_le_bytes()); // accuracy
2375
2376        let result = driver.handle_received_packet(23);
2377        assert!(result.is_ok());
2378
2379        let quat = driver.rotation_quaternion().unwrap();
2380        assert!((quat[3] - 1.0).abs() < 0.01);
2381    }
2382
2383    #[test]
2384    fn test_handle_sensor_reports_game_rotation() {
2385        let mock = MockSensorInterface::new();
2386        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2387
2388        driver.packet_recv_buf[0] = 21;
2389        driver.packet_recv_buf[1] = 0;
2390        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2391        driver.packet_recv_buf[3] = 0;
2392        driver.packet_recv_buf[4] = 0xFB;
2393        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2394        driver.packet_recv_buf[9] = SENSOR_REPORTID_ROTATION_VECTOR_GAME;
2395        driver.packet_recv_buf[10..13].copy_from_slice(&[0, 0, 0]);
2396        // 45 degree rotation about Z: [0, 0, sin(22.5°), cos(22.5°)]
2397        driver.packet_recv_buf[13..15].copy_from_slice(&0i16.to_le_bytes());
2398        driver.packet_recv_buf[15..17].copy_from_slice(&0i16.to_le_bytes());
2399        driver.packet_recv_buf[17..19].copy_from_slice(&6270i16.to_le_bytes()); // ~0.383 in Q14
2400        driver.packet_recv_buf[19..21].copy_from_slice(&15137i16.to_le_bytes()); // ~0.924 in Q14
2401
2402        let result = driver.handle_received_packet(21);
2403        assert!(result.is_ok());
2404    }
2405
2406    #[test]
2407    fn test_handle_sensor_reports_geomag_rotation() {
2408        let mock = MockSensorInterface::new();
2409        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2410
2411        driver.packet_recv_buf[0] = 23;
2412        driver.packet_recv_buf[1] = 0;
2413        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2414        driver.packet_recv_buf[3] = 0;
2415        driver.packet_recv_buf[4] = 0xFB;
2416        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2417        driver.packet_recv_buf[9] = SENSOR_REPORTID_ROTATION_VECTOR_GEOMAGNETIC;
2418        driver.packet_recv_buf[10..13].copy_from_slice(&[0, 0, 0]);
2419        driver.packet_recv_buf[13..15].copy_from_slice(&0i16.to_le_bytes());
2420        driver.packet_recv_buf[15..17].copy_from_slice(&0i16.to_le_bytes());
2421        driver.packet_recv_buf[17..19].copy_from_slice(&0i16.to_le_bytes());
2422        driver.packet_recv_buf[19..21].copy_from_slice(&16384i16.to_le_bytes());
2423        driver.packet_recv_buf[21..23].copy_from_slice(&4096i16.to_le_bytes()); // accuracy
2424
2425        let result = driver.handle_received_packet(23);
2426        assert!(result.is_ok());
2427    }
2428
2429    #[test]
2430    fn test_handle_sensor_reports_linear_accel() {
2431        let mock = MockSensorInterface::new();
2432        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2433
2434        driver.packet_recv_buf[0] = 19;
2435        driver.packet_recv_buf[1] = 0;
2436        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2437        driver.packet_recv_buf[3] = 0;
2438        driver.packet_recv_buf[4] = 0xFB;
2439        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2440        driver.packet_recv_buf[9] = SENSOR_REPORTID_LINEAR_ACCEL;
2441        driver.packet_recv_buf[10..13].copy_from_slice(&[0, 0, 0]);
2442        driver.packet_recv_buf[13..15].copy_from_slice(&256i16.to_le_bytes());
2443        driver.packet_recv_buf[15..17].copy_from_slice(&512i16.to_le_bytes());
2444        driver.packet_recv_buf[17..19].copy_from_slice(&768i16.to_le_bytes());
2445
2446        let result = driver.handle_received_packet(19);
2447        assert!(result.is_ok());
2448
2449        let accel = driver.linear_accel().unwrap();
2450        assert!((accel[0] - 1.0).abs() < 0.1);
2451        assert!((accel[1] - 2.0).abs() < 0.1);
2452        assert!((accel[2] - 3.0).abs() < 0.1);
2453    }
2454
2455    #[test]
2456    fn test_handle_sensor_reports_gravity() {
2457        let mock = MockSensorInterface::new();
2458        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2459
2460        driver.packet_recv_buf[0] = 19;
2461        driver.packet_recv_buf[1] = 0;
2462        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2463        driver.packet_recv_buf[3] = 0;
2464        driver.packet_recv_buf[4] = 0xFB;
2465        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2466        driver.packet_recv_buf[9] = SENSOR_REPORTID_GRAVITY;
2467        driver.packet_recv_buf[10..13].copy_from_slice(&[0, 0, 0]);
2468        driver.packet_recv_buf[13..15].copy_from_slice(&0i16.to_le_bytes());
2469        driver.packet_recv_buf[15..17].copy_from_slice(&0i16.to_le_bytes());
2470        driver.packet_recv_buf[17..19].copy_from_slice(&2509i16.to_le_bytes()); // ~9.8 m/s²
2471
2472        let result = driver.handle_received_packet(19);
2473        assert!(result.is_ok());
2474
2475        let gravity = driver.gravity().unwrap();
2476        assert!((gravity[2] - 9.8).abs() < 0.1);
2477    }
2478
2479    #[test]
2480    fn test_handle_sensor_reports_gyro() {
2481        let mock = MockSensorInterface::new();
2482        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2483
2484        driver.packet_recv_buf[0] = 19;
2485        driver.packet_recv_buf[1] = 0;
2486        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2487        driver.packet_recv_buf[3] = 0;
2488        driver.packet_recv_buf[4] = 0xFB;
2489        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2490        driver.packet_recv_buf[9] = SENSOR_REPORTID_GYROSCOPE;
2491        driver.packet_recv_buf[10..13].copy_from_slice(&[0, 0, 0]);
2492        // Q9: 512 = 1.0 rad/s
2493        driver.packet_recv_buf[13..15].copy_from_slice(&512i16.to_le_bytes());
2494        driver.packet_recv_buf[15..17].copy_from_slice(&(-512i16).to_le_bytes());
2495        driver.packet_recv_buf[17..19].copy_from_slice(&256i16.to_le_bytes());
2496
2497        let result = driver.handle_received_packet(19);
2498        assert!(result.is_ok());
2499
2500        let gyro = driver.gyro().unwrap();
2501        assert!((gyro[0] - 1.0).abs() < 0.1);
2502        assert!((gyro[1] + 1.0).abs() < 0.1);
2503        assert!((gyro[2] - 0.5).abs() < 0.1);
2504    }
2505
2506    #[test]
2507    fn test_handle_sensor_reports_gyro_uncalib() {
2508        let mock = MockSensorInterface::new();
2509        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2510
2511        driver.packet_recv_buf[0] = 19;
2512        driver.packet_recv_buf[1] = 0;
2513        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2514        driver.packet_recv_buf[3] = 0;
2515        driver.packet_recv_buf[4] = 0xFB;
2516        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2517        driver.packet_recv_buf[9] = SENSOR_REPORTID_GYROSCOPE_UNCALIB;
2518        driver.packet_recv_buf[10..13].copy_from_slice(&[0, 0, 0]);
2519        driver.packet_recv_buf[13..15].copy_from_slice(&256i16.to_le_bytes());
2520        driver.packet_recv_buf[15..17].copy_from_slice(&512i16.to_le_bytes());
2521        driver.packet_recv_buf[17..19].copy_from_slice(&768i16.to_le_bytes());
2522
2523        let result = driver.handle_received_packet(19);
2524        assert!(result.is_ok());
2525    }
2526
2527    #[test]
2528    fn test_handle_sensor_reports_magnetometer() {
2529        let mock = MockSensorInterface::new();
2530        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2531
2532        driver.packet_recv_buf[0] = 19;
2533        driver.packet_recv_buf[1] = 0;
2534        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2535        driver.packet_recv_buf[3] = 0;
2536        driver.packet_recv_buf[4] = 0xFB;
2537        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2538        driver.packet_recv_buf[9] = SENSOR_REPORTID_MAGNETIC_FIELD;
2539        driver.packet_recv_buf[10..13].copy_from_slice(&[0, 0, 0]);
2540        // Q4: 16 = 1.0 µT
2541        driver.packet_recv_buf[13..15].copy_from_slice(&160i16.to_le_bytes()); // 10 µT
2542        driver.packet_recv_buf[15..17].copy_from_slice(&320i16.to_le_bytes()); // 20 µT
2543        driver.packet_recv_buf[17..19].copy_from_slice(&480i16.to_le_bytes()); // 30 µT
2544
2545        let result = driver.handle_received_packet(19);
2546        assert!(result.is_ok());
2547
2548        let mag = driver.mag_field().unwrap();
2549        assert!((mag[0] - 10.0).abs() < 0.5);
2550        assert!((mag[1] - 20.0).abs() < 0.5);
2551        assert!((mag[2] - 30.0).abs() < 0.5);
2552    }
2553
2554    #[test]
2555    fn test_handle_sensor_reports_unknown_report_id() {
2556        let mock = MockSensorInterface::new();
2557        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2558
2559        driver.packet_recv_buf[0] = 19;
2560        driver.packet_recv_buf[1] = 0;
2561        driver.packet_recv_buf[2] = CHANNEL_SENSOR_REPORTS;
2562        driver.packet_recv_buf[3] = 0;
2563        driver.packet_recv_buf[4] = 0xFB;
2564        driver.packet_recv_buf[5..9].copy_from_slice(&[0, 0, 0, 0]);
2565        driver.packet_recv_buf[9] = 0xFE; // Unknown report ID
2566        driver.packet_recv_buf[10..19].copy_from_slice(&[0; 9]);
2567
2568        // Should not crash, just ignore the unknown report
2569        let result = driver.handle_received_packet(19);
2570        assert!(result.is_ok());
2571    }
2572
2573    // ==========================================================================
2574    // Advertisement Response Tests
2575    // ==========================================================================
2576
2577    #[test]
2578    fn test_handle_advertise_response() {
2579        let mock = MockSensorInterface::new();
2580        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2581
2582        // Simulate advertisement response with 0xFF terminator
2583        driver.packet_recv_buf[0] = 10;
2584        driver.packet_recv_buf[1] = 0;
2585        driver.packet_recv_buf[2] = CHANNEL_COMMAND;
2586        driver.packet_recv_buf[3] = 0;
2587        driver.packet_recv_buf[4] = CMD_RESP_ADVERTISEMENT;
2588        // Advertisement entries (normally contain channel info)
2589        driver.packet_recv_buf[5] = 0x00;
2590        driver.packet_recv_buf[6] = 0xFF; // Terminator
2591
2592        assert!(!driver.advert_received);
2593        let result = driver.handle_received_packet(10);
2594        assert!(result.is_ok());
2595        assert!(driver.advert_received);
2596    }
2597
2598    // ==========================================================================
2599    // Callback Tests
2600    // ==========================================================================
2601
2602    #[test]
2603    fn test_add_sensor_report_callback() {
2604        let mock = MockSensorInterface::new();
2605        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2606
2607        let called = std::rc::Rc::new(std::cell::Cell::new(false));
2608        let called_clone = called.clone();
2609
2610        driver.add_sensor_report_callback(
2611            SENSOR_REPORTID_ACCELEROMETER,
2612            "test".to_string(),
2613            move |_driver| {
2614                called_clone.set(true);
2615            },
2616        );
2617
2618        // Verify callback was added
2619        assert!(!driver.report_update_callbacks[SENSOR_REPORTID_ACCELEROMETER as usize].is_empty());
2620    }
2621
2622    #[test]
2623    fn test_remove_sensor_report_callback() {
2624        let mock = MockSensorInterface::new();
2625        let mut driver: BNO08x<MockSensorInterface> = BNO08x::new_with_interface(mock);
2626
2627        driver.add_sensor_report_callback(
2628            SENSOR_REPORTID_ACCELEROMETER,
2629            "test".to_string(),
2630            |_| {},
2631        );
2632
2633        assert!(!driver.report_update_callbacks[SENSOR_REPORTID_ACCELEROMETER as usize].is_empty());
2634
2635        driver.remove_sensor_report_callback(SENSOR_REPORTID_ACCELEROMETER, "test".to_string());
2636
2637        assert!(driver.report_update_callbacks[SENSOR_REPORTID_ACCELEROMETER as usize].is_empty());
2638    }
2639}