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.
243 lines
6.4 KiB
Rust
243 lines
6.4 KiB
Rust
use kaptan_config::{Config, ConfigViolation, ValidationRule};
|
|
|
|
use super::ids;
|
|
|
|
/// 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",
|
|
unit = "1/s2",
|
|
finite,
|
|
range(min = 0.0, max = 100.0)
|
|
)]
|
|
pub position_gain_per_second_squared: f32,
|
|
#[config(
|
|
id = 9,
|
|
key = "control.velocity_gain_per_s",
|
|
unit = "1/s",
|
|
finite,
|
|
range(min = 0.0, max = 100.0)
|
|
)]
|
|
pub velocity_gain_per_second: f32,
|
|
#[config(
|
|
id = 10,
|
|
key = "control.maximum_horizontal_acceleration_mps2",
|
|
unit = "m/s2",
|
|
finite,
|
|
range(min = 0.001, max = 100.0)
|
|
)]
|
|
pub maximum_horizontal_acceleration_mps2: f32,
|
|
#[config(
|
|
id = 11,
|
|
key = "control.maximum_upward_acceleration_mps2",
|
|
unit = "m/s2",
|
|
finite,
|
|
range(min = 0.001, max = 100.0)
|
|
)]
|
|
pub maximum_upward_acceleration_mps2: f32,
|
|
#[config(
|
|
id = 12,
|
|
key = "control.maximum_downward_acceleration_mps2",
|
|
unit = "m/s2",
|
|
finite,
|
|
range(min = 0.001, max = 100.0)
|
|
)]
|
|
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,
|
|
}
|
|
|
|
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 struct ThrustSettings {
|
|
#[config(
|
|
id = 13,
|
|
key = "thrust.mass_kg",
|
|
unit = "kg",
|
|
finite,
|
|
range(min = 0.01, max = 100.0)
|
|
)]
|
|
pub mass_kg: f32,
|
|
#[config(
|
|
id = 14,
|
|
key = "thrust.maximum_collective_thrust_n",
|
|
unit = "N",
|
|
finite,
|
|
range(min = 0.1, max = 10_000.0)
|
|
)]
|
|
pub maximum_collective_thrust_newtons: f32,
|
|
#[config(
|
|
id = 15,
|
|
key = "thrust.maximum_tilt_tangent",
|
|
unit = "",
|
|
finite,
|
|
range(min = 0.001, max = 10.0)
|
|
)]
|
|
pub maximum_tilt_tangent: f32,
|
|
}
|
|
impl ThrustSettings {
|
|
fn validate_thrust(&self) -> Result<(), ConfigViolation> {
|
|
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,
|
|
ValidationRule::Inconsistent,
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// 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 struct PidSettings {
|
|
#[config(
|
|
id = 16,
|
|
key = "control.rate_pid.proportional",
|
|
unit = "",
|
|
finite,
|
|
range(min = 0.0, max = 10.0)
|
|
)]
|
|
pub proportional: f32,
|
|
#[config(
|
|
id = 17,
|
|
key = "control.rate_pid.integral",
|
|
unit = "",
|
|
finite,
|
|
range(min = 0.0, max = 10.0)
|
|
)]
|
|
pub integral: f32,
|
|
#[config(
|
|
id = 18,
|
|
key = "control.rate_pid.derivative",
|
|
unit = "",
|
|
finite,
|
|
range(min = 0.0, max = 10.0)
|
|
)]
|
|
pub derivative: f32,
|
|
#[config(
|
|
id = 19,
|
|
key = "control.rate_pid.output_limit",
|
|
unit = "",
|
|
finite,
|
|
range(min = 0.001, max = 1.0)
|
|
)]
|
|
pub output_limit: f32,
|
|
#[config(
|
|
id = 20,
|
|
key = "control.rate_pid.integral_limit",
|
|
unit = "",
|
|
finite,
|
|
range(min = 0.0, max = 1.0)
|
|
)]
|
|
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 PidSettings {
|
|
fn validate_pid(&self) -> Result<(), ConfigViolation> {
|
|
if self.integral_limit > self.output_limit {
|
|
return Err(ConfigViolation::related(
|
|
ids::RATE_PID_INTEGRAL_LIMIT,
|
|
ids::RATE_PID_OUTPUT_LIMIT,
|
|
ValidationRule::Inconsistent,
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Actuator command bounds.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Config)]
|
|
#[config(post = Self::validate_actuator_limits)]
|
|
pub struct ActuatorLimits {
|
|
#[config(
|
|
id = 21,
|
|
key = "actuator.minimum",
|
|
unit = "",
|
|
finite,
|
|
range(min = 0.0, max = 1.0)
|
|
)]
|
|
pub minimum: f32,
|
|
#[config(
|
|
id = 22,
|
|
key = "actuator.maximum",
|
|
unit = "",
|
|
finite,
|
|
range(min = 0.0, max = 1.0)
|
|
)]
|
|
pub maximum: f32,
|
|
#[config(
|
|
id = 23,
|
|
key = "actuator.slew_per_second",
|
|
unit = "1/s",
|
|
finite,
|
|
range(min = 0.001, max = 100.0)
|
|
)]
|
|
pub slew_per_second: f32,
|
|
}
|
|
impl ActuatorLimits {
|
|
fn validate_actuator_limits(&self) -> Result<(), ConfigViolation> {
|
|
if self.minimum >= self.maximum {
|
|
return Err(ConfigViolation::related(
|
|
ids::ACTUATOR_MINIMUM,
|
|
ids::ACTUATOR_MAXIMUM,
|
|
ValidationRule::Inconsistent,
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|