feat(firmware): validate configuration and migrate sensor-driven flight APIs
Validate configuration through its owning schemas and keep board declarations separate from shared F722 resource ownership. Bound USB transitions, isolate transport failures and retain restart-only activation. Migrate navigation and flight consumers with the sensor and schema contracts. Require explicit installation data and synchronous output authority; board motor outputs remain inhibited. Validation: assembled-prefix host tests, strict Clippy, platform/USB tests and both ARM release builds. Final-source coverage and scoped Miri/Kani evidence are retained separately from hardware qualification.
This commit is contained in:
1 parent
fb1b8af2d1
commit
f9a61a086f
211 files changed
+45657
-12740
No files matched your search
@@ -2,28 +2,10 @@ use kaptan_config::{Config, ConfigViolation, ValidationRule};
|
||||
|
||||
use super::ids;
|
||||
|
||||
#[derive(Clone, Copy, Config)]
|
||||
pub(super) struct AidingCandidate {
|
||||
#[config(
|
||||
id = 6,
|
||||
key = "aiding.position_variance_m2",
|
||||
unit = "m2",
|
||||
finite,
|
||||
range(min = 0.000_001, max = 10_000.0)
|
||||
)]
|
||||
pub(super) position_variance_m2: f32,
|
||||
#[config(
|
||||
id = 7,
|
||||
key = "aiding.velocity_variance_m2ps2",
|
||||
unit = "m2/s2",
|
||||
finite,
|
||||
range(min = 0.000_001, max = 10_000.0)
|
||||
)]
|
||||
pub(super) velocity_variance_m2_per_second_squared: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Config)]
|
||||
pub(super) struct ControlCandidate {
|
||||
/// Outer-loop controller policy.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Config)]
|
||||
#[config(post = Self::validate_control)]
|
||||
pub struct ControlSettings {
|
||||
#[config(
|
||||
id = 8,
|
||||
key = "control.position_gain_per_s2",
|
||||
@@ -31,7 +13,7 @@ pub(super) struct ControlCandidate {
|
||||
finite,
|
||||
range(min = 0.0, max = 100.0)
|
||||
)]
|
||||
pub(super) position_gain_per_second_squared: f32,
|
||||
pub position_gain_per_second_squared: f32,
|
||||
#[config(
|
||||
id = 9,
|
||||
key = "control.velocity_gain_per_s",
|
||||
@@ -39,7 +21,7 @@ pub(super) struct ControlCandidate {
|
||||
finite,
|
||||
range(min = 0.0, max = 100.0)
|
||||
)]
|
||||
pub(super) velocity_gain_per_second: f32,
|
||||
pub velocity_gain_per_second: f32,
|
||||
#[config(
|
||||
id = 10,
|
||||
key = "control.maximum_horizontal_acceleration_mps2",
|
||||
@@ -47,7 +29,7 @@ pub(super) struct ControlCandidate {
|
||||
finite,
|
||||
range(min = 0.001, max = 100.0)
|
||||
)]
|
||||
pub(super) maximum_horizontal_acceleration_mps2: f32,
|
||||
pub maximum_horizontal_acceleration_mps2: f32,
|
||||
#[config(
|
||||
id = 11,
|
||||
key = "control.maximum_upward_acceleration_mps2",
|
||||
@@ -55,7 +37,7 @@ pub(super) struct ControlCandidate {
|
||||
finite,
|
||||
range(min = 0.001, max = 100.0)
|
||||
)]
|
||||
pub(super) maximum_upward_acceleration_mps2: f32,
|
||||
pub maximum_upward_acceleration_mps2: f32,
|
||||
#[config(
|
||||
id = 12,
|
||||
key = "control.maximum_downward_acceleration_mps2",
|
||||
@@ -63,11 +45,43 @@ pub(super) struct ControlCandidate {
|
||||
finite,
|
||||
range(min = 0.001, max = 100.0)
|
||||
)]
|
||||
pub(super) maximum_downward_acceleration_mps2: f32,
|
||||
pub maximum_downward_acceleration_mps2: f32,
|
||||
#[config(
|
||||
id = 41,
|
||||
key = "control.attitude_roll_pitch_gain_per_s",
|
||||
unit = "1/s",
|
||||
finite,
|
||||
range(min = 0.0, max = 10.0)
|
||||
)]
|
||||
pub attitude_roll_pitch_gain_per_second: f32,
|
||||
#[config(
|
||||
id = 42,
|
||||
key = "control.attitude_yaw_gain_per_s",
|
||||
unit = "1/s",
|
||||
finite,
|
||||
range(min = 0.0, max = 10.0)
|
||||
)]
|
||||
pub attitude_yaw_gain_per_second: f32,
|
||||
}
|
||||
#[derive(Clone, Copy, Config)]
|
||||
|
||||
impl ControlSettings {
|
||||
fn validate_control(&self) -> Result<(), ConfigViolation> {
|
||||
// Positive multicopter thrust requires downward acceleration strictly
|
||||
// below gravity. An inclusive scalar maximum cannot express this rule.
|
||||
if self.maximum_downward_acceleration_mps2 >= kaptan_navigation::GRAVITY_MPS2 {
|
||||
return Err(ConfigViolation::field(
|
||||
ids::MAXIMUM_DOWNWARD_ACCELERATION_MPS2,
|
||||
ValidationRule::Inconsistent,
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Physical thrust envelope.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Config)]
|
||||
#[config(post = Self::validate_thrust)]
|
||||
pub(super) struct ThrustCandidate {
|
||||
pub struct ThrustSettings {
|
||||
#[config(
|
||||
id = 13,
|
||||
key = "thrust.mass_kg",
|
||||
@@ -75,7 +89,7 @@ pub(super) struct ThrustCandidate {
|
||||
finite,
|
||||
range(min = 0.01, max = 100.0)
|
||||
)]
|
||||
pub(super) mass_kg: f32,
|
||||
pub mass_kg: f32,
|
||||
#[config(
|
||||
id = 14,
|
||||
key = "thrust.maximum_collective_thrust_n",
|
||||
@@ -83,7 +97,7 @@ pub(super) struct ThrustCandidate {
|
||||
finite,
|
||||
range(min = 0.1, max = 10_000.0)
|
||||
)]
|
||||
pub(super) maximum_collective_thrust_newtons: f32,
|
||||
pub maximum_collective_thrust_newtons: f32,
|
||||
#[config(
|
||||
id = 15,
|
||||
key = "thrust.maximum_tilt_tangent",
|
||||
@@ -91,11 +105,16 @@ pub(super) struct ThrustCandidate {
|
||||
finite,
|
||||
range(min = 0.001, max = 10.0)
|
||||
)]
|
||||
pub(super) maximum_tilt_tangent: f32,
|
||||
pub maximum_tilt_tangent: f32,
|
||||
}
|
||||
impl ThrustCandidate {
|
||||
impl ThrustSettings {
|
||||
fn validate_thrust(&self) -> Result<(), ConfigViolation> {
|
||||
if self.maximum_collective_thrust_newtons <= self.mass_kg * 9.806_65 {
|
||||
if self.maximum_collective_thrust_newtons <= self.mass_kg * kaptan_navigation::GRAVITY_MPS2
|
||||
|| f64::from(self.maximum_collective_thrust_newtons)
|
||||
> f64::from(self.mass_kg)
|
||||
* (f64::from(kaptan_navigation::MAXIMUM_FREE_ACCELERATION_MPS2)
|
||||
- f64::from(kaptan_navigation::GRAVITY_MPS2))
|
||||
{
|
||||
return Err(ConfigViolation::related(
|
||||
ids::MAXIMUM_COLLECTIVE_THRUST_NEWTONS,
|
||||
ids::MASS_KG,
|
||||
@@ -106,9 +125,10 @@ impl ThrustCandidate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Config)]
|
||||
/// Roll/pitch and yaw P/I gains with shared derivative and contribution limits.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Config)]
|
||||
#[config(post = Self::validate_pid)]
|
||||
pub(super) struct PidCandidate {
|
||||
pub struct PidSettings {
|
||||
#[config(
|
||||
id = 16,
|
||||
key = "control.rate_pid.proportional",
|
||||
@@ -116,7 +136,7 @@ pub(super) struct PidCandidate {
|
||||
finite,
|
||||
range(min = 0.0, max = 10.0)
|
||||
)]
|
||||
pub(super) proportional: f32,
|
||||
pub proportional: f32,
|
||||
#[config(
|
||||
id = 17,
|
||||
key = "control.rate_pid.integral",
|
||||
@@ -124,7 +144,7 @@ pub(super) struct PidCandidate {
|
||||
finite,
|
||||
range(min = 0.0, max = 10.0)
|
||||
)]
|
||||
pub(super) integral: f32,
|
||||
pub integral: f32,
|
||||
#[config(
|
||||
id = 18,
|
||||
key = "control.rate_pid.derivative",
|
||||
@@ -132,7 +152,7 @@ pub(super) struct PidCandidate {
|
||||
finite,
|
||||
range(min = 0.0, max = 10.0)
|
||||
)]
|
||||
pub(super) derivative: f32,
|
||||
pub derivative: f32,
|
||||
#[config(
|
||||
id = 19,
|
||||
key = "control.rate_pid.output_limit",
|
||||
@@ -140,7 +160,7 @@ pub(super) struct PidCandidate {
|
||||
finite,
|
||||
range(min = 0.001, max = 1.0)
|
||||
)]
|
||||
pub(super) output_limit: f32,
|
||||
pub output_limit: f32,
|
||||
#[config(
|
||||
id = 20,
|
||||
key = "control.rate_pid.integral_limit",
|
||||
@@ -148,9 +168,25 @@ pub(super) struct PidCandidate {
|
||||
finite,
|
||||
range(min = 0.0, max = 1.0)
|
||||
)]
|
||||
pub(super) integral_limit: f32,
|
||||
pub integral_limit: f32,
|
||||
#[config(
|
||||
id = 43,
|
||||
key = "control.yaw_rate_pid.proportional",
|
||||
unit = "",
|
||||
finite,
|
||||
range(min = 0.0, max = 10.0)
|
||||
)]
|
||||
pub yaw_proportional: f32,
|
||||
#[config(
|
||||
id = 44,
|
||||
key = "control.yaw_rate_pid.integral",
|
||||
unit = "",
|
||||
finite,
|
||||
range(min = 0.0, max = 10.0)
|
||||
)]
|
||||
pub yaw_integral: f32,
|
||||
}
|
||||
impl PidCandidate {
|
||||
impl PidSettings {
|
||||
fn validate_pid(&self) -> Result<(), ConfigViolation> {
|
||||
if self.integral_limit > self.output_limit {
|
||||
return Err(ConfigViolation::related(
|
||||
@@ -163,9 +199,10 @@ impl PidCandidate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Config)]
|
||||
/// Actuator command bounds.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Config)]
|
||||
#[config(post = Self::validate_actuator_limits)]
|
||||
pub(super) struct ActuatorLimitsCandidate {
|
||||
pub struct ActuatorLimits {
|
||||
#[config(
|
||||
id = 21,
|
||||
key = "actuator.minimum",
|
||||
@@ -173,7 +210,7 @@ pub(super) struct ActuatorLimitsCandidate {
|
||||
finite,
|
||||
range(min = 0.0, max = 1.0)
|
||||
)]
|
||||
pub(super) minimum: f32,
|
||||
pub minimum: f32,
|
||||
#[config(
|
||||
id = 22,
|
||||
key = "actuator.maximum",
|
||||
@@ -181,7 +218,7 @@ pub(super) struct ActuatorLimitsCandidate {
|
||||
finite,
|
||||
range(min = 0.0, max = 1.0)
|
||||
)]
|
||||
pub(super) maximum: f32,
|
||||
pub maximum: f32,
|
||||
#[config(
|
||||
id = 23,
|
||||
key = "actuator.slew_per_second",
|
||||
@@ -189,9 +226,9 @@ pub(super) struct ActuatorLimitsCandidate {
|
||||
finite,
|
||||
range(min = 0.001, max = 100.0)
|
||||
)]
|
||||
pub(super) slew_per_second: f32,
|
||||
pub slew_per_second: f32,
|
||||
}
|
||||
impl ActuatorLimitsCandidate {
|
||||
impl ActuatorLimits {
|
||||
fn validate_actuator_limits(&self) -> Result<(), ConfigViolation> {
|
||||
if self.minimum >= self.maximum {
|
||||
return Err(ConfigViolation::related(
|
||||
|
||||
Reference in new issue
Block a user