feat(drivers): validate bounded IMU FIFO acquisition

Drain bounded MPU6000 and ICM-42688 FIFO batches. Publish the latest
validated sample only after overflow checks. Reject invalid drains.

Validation: driver, sensor and platform tests plus both ARM release builds
from a fresh build directory.
This commit is contained in:
Onur Özkan committed 2026-11-07 18:12:11 +03:00
1 parent f9a61a086f
commit d32b03c87f
13 files changed
+1303 -833

No files matched your search

+30 -19
View File
@@ -2,6 +2,7 @@
use super::peripherals::{Spi1, Spi1Error, Spi3, Spi3Error, TimerClock};
use kaptan_drivers::{
ImuBatch,
icm42688::{Icm42688, Icm42688Service, Icm42688ServiceEvent},
mpu6000::{Mpu6000, Mpu6000Config, Mpu6000Service, Mpu6000ServiceEvent},
};
@@ -14,8 +15,7 @@ use crate::ImuMount;
use super::routes::{Icm42688Spi3Route, Mpu6000Spi1Route};
/// A profile-neutral IMU worker result. Device protocol errors are consumed
/// here because flight policy only needs the terminal healthy/fault outcome.
/// IMU worker result with device errors mapped to reportable IDs.
pub enum ImuEvent {
Idle,
Initialized,
@@ -23,8 +23,7 @@ pub enum ImuEvent {
Fault(ErrorId),
}
/// Operations used by the deferred IMU worker, statically dispatched to the
/// route's concrete service. No peripheral ownership crosses this interface.
/// Sensor operations invoked by the deferred IMU worker.
pub trait ImuService: Send + 'static {
fn begin(&mut self) -> ImuEvent;
fn on_periodic_tick(&mut self) -> ImuEvent;
@@ -82,7 +81,6 @@ impl ImuService for Mpu6000Imu {
}
}
/// Concrete ICM owner, including its board-declared mounting transform.
pub struct Icm42688Imu {
inner: Icm42688Service<TimerClock, Icm42688Spi3Route>,
mount: ImuMount,
@@ -136,7 +134,7 @@ fn mpu_event(event: Mpu6000ServiceEvent<Spi1Error>, mount: ImuMount) -> ImuEvent
match event {
Mpu6000ServiceEvent::Idle => ImuEvent::Idle,
Mpu6000ServiceEvent::Initialized => ImuEvent::Initialized,
Mpu6000ServiceEvent::Sample(sample) => ImuEvent::Sample(align_mount(sample, mount)),
Mpu6000ServiceEvent::Samples(samples) => latest_sample(samples, mount),
Mpu6000ServiceEvent::Fault(error) => ImuEvent::Fault(error.error_id()),
}
}
@@ -145,14 +143,21 @@ fn icm_event(event: Icm42688ServiceEvent<Spi3Error>, mount: ImuMount) -> ImuEven
match event {
Icm42688ServiceEvent::Idle => ImuEvent::Idle,
Icm42688ServiceEvent::Initialized => ImuEvent::Initialized,
Icm42688ServiceEvent::Sample(sample) => ImuEvent::Sample(align_mount(sample, mount)),
Icm42688ServiceEvent::Samples(samples) => latest_sample(samples, mount),
Icm42688ServiceEvent::Fault(error) => ImuEvent::Fault(error.error_id()),
}
}
/// Applies the board-declared fixed IMU-to-body transform.
///
/// This is immutable board metadata, not an airframe calibration setting.
fn latest_sample(samples: ImuBatch, mount: ImuMount) -> ImuEvent {
// Scheduled MAVLink publication retains only the newest validated record.
let sample = samples
.as_slice()
.last()
.expect("driver batches are nonempty");
ImuEvent::Sample(align_mount(*sample, mount))
}
/// Applies the fixed IMU-to-body transform from the board wiring.
fn align_mount(mut sample: ImuSnapshot, mount: ImuMount) -> ImuSnapshot {
match mount {
ImuMount::BodyAligned => sample,
@@ -183,6 +188,7 @@ mod tests {
use crate::ImuMount;
use crate::f722::peripherals::{Spi1Error, Spi3Error};
use kaptan_drivers::{
ImuBatch,
icm42688::{Error as Icm42688Error, Icm42688ServiceEvent},
mpu6000::{Error as Mpu6000Error, Mpu6000ServiceEvent},
};
@@ -208,7 +214,7 @@ mod tests {
}
#[test]
fn mpu_samples_apply_declared_mount_without_changing_timestamp_or_temperature() {
fn aligns_latest_fifo_sample() {
let sample = ImuSnapshot::new(
Timestamp::from_microseconds(123),
[10, 20, 30],
@@ -220,18 +226,23 @@ mod tests {
(ImuMount::Yaw180, [-10, -20, 30], [-40, -50, 60]),
(ImuMount::Yaw270, [-20, 10, 30], [-50, 40, 60]),
] {
let ImuEvent::Sample(aligned) = mpu_event(Mpu6000ServiceEvent::Sample(sample), mount)
let mut samples = ImuBatch::new(sample);
let later = ImuSnapshot {
timestamp: Timestamp::from_microseconds(456),
..sample
};
samples.push(later).unwrap();
let ImuEvent::Sample(aligned) = mpu_event(Mpu6000ServiceEvent::Samples(samples), mount)
else {
panic!("sample event must stay a sample");
panic!("validated batch must produce a sample");
};
assert_eq!(
aligned,
ImuSnapshot::new(
Timestamp::from_microseconds(123),
acceleration,
angular_rate,
25,
)
ImuSnapshot {
acceleration_milligravity: acceleration,
angular_rate_milliradians_per_second: angular_rate,
..later
}
);
}
}