Skip to main content

rust_gnc/control/
axis.rs

1//! # Axis Processing
2//! 
3//! This module implements the `AxisProcessor`, which encapsulates the full 
4//! control pipeline for a single degree of freedom (e.g., Roll, Pitch, or Yaw).
5//!
6//! By combining a `Filter` and a `PidController`, the `AxisProcessor` transforms 
7//! raw inertial data into a normalized control signal.
8
9use crate::filters::{Filter, InertialInput};
10use crate::control::pid::PidController;
11use crate::units::Radians;
12
13/// A generic controller for a single physical axis.
14/// 
15/// The `AxisProcessor` is generic over any `Filter` that accepts `InertialInput` 
16/// and produces `Radians`. This allows the same control logic to be used with 
17/// Complementary filters, Kalman filters, or simple low-pass filters.
18pub struct AxisProcessor<F> 
19where 
20    F: Filter<Input = InertialInput, Output = Radians> 
21{
22    /// The PID algorithm responsible for generating the corrective signal.
23    pub pid: PidController,
24    /// The sensor fusion filter responsible for estimating the current orientation.
25    pub filter: F,
26}
27
28impl<F> AxisProcessor<F> 
29where 
30    F: Filter<Input = InertialInput, Output = Radians> 
31{
32    /// Creates a new `AxisProcessor` with the provided estimation and control components.
33    pub fn new(filter: F, pid: PidController) -> Self {
34        Self { pid, filter }
35    }
36
37    /// Executes one iteration of the control loop for this axis.
38    /// 
39    /// This method performs two primary steps:
40    /// 1. **State Estimation**: Updates the internal filter with new IMU data.
41    /// 2. **Control Generation**: Computes the PID output based on the error 
42    ///    between the `target_angle` and the estimated `current_angle`.
43    /// 
44    /// ### Parameters
45    /// * `input` - Raw rate and reference data from sensors.
46    /// * `target_angle` - The desired orientation for this axis.
47    /// * `dt` - Time delta since the last update in seconds.
48    /// 
49    /// ### Returns
50    /// A normalized control signal (f32), typically in the range [-1.0, 1.0].
51    pub fn process(&mut self, input: InertialInput, target_angle: Radians, dt: f32) -> f32 {
52        // Step 1: Perception (Where are we?)
53        let current_angle = self.filter.update(input, dt);
54        
55        // Step 2: Control (How do we get to where we want to be?)
56        self.pid.update(target_angle.0, current_angle.0, dt)
57    }
58}