use crate::core::matrix::{matmul, matvec, Matrix};
use crate::core::scalar::ControlScalar;
pub fn ackermann<S: ControlScalar, const N: usize>(
a: &Matrix<S, N, N>,
b: &Matrix<S, N, 1>,
desired_poles: &[S; N],
) -> Option<[S; N]> {
let mut w_c = Matrix::<S, N, N>::zeros();
let mut a_pow_b: [S; N] = core::array::from_fn(|i| b.data[i][0]);
for col in 0..N {
for (row, &val) in a_pow_b.iter().enumerate().take(N) {
w_c.data[row][col] = val;
}
if col < N - 1 {
a_pow_b = matvec(a, &a_pow_b);
}
}
let w_c_inv = w_c.inv()?;
let phi_a = char_poly_at_matrix(a, desired_poles);
let last_row = w_c_inv.data[N - 1]; let k = matvec(&phi_a.transpose(), &last_row);
Some(k)
}
fn char_poly_at_matrix<S: ControlScalar, const N: usize>(
a: &Matrix<S, N, N>,
roots: &[S; N],
) -> Matrix<S, N, N> {
let mut result = Matrix::<S, N, N>::identity();
for &r in roots {
let mut a_shifted = *a;
for i in 0..N {
a_shifted.data[i][i] -= r;
}
result = matmul(&result, &a_shifted);
}
result
}
pub struct StateFeedback<S: ControlScalar, const N: usize> {
pub k: [S; N],
}
impl<S: ControlScalar, const N: usize> StateFeedback<S, N> {
pub fn new(k: [S; N]) -> Self {
Self { k }
}
pub fn control(&self, x: &[S; N], x_ref: &[S; N]) -> S {
let mut u = S::ZERO;
for i in 0..N {
u -= self.k[i] * (x[i] - x_ref[i]);
}
u
}
pub fn regulate(&self, x: &[S; N]) -> S {
self.k
.iter()
.zip(x.iter())
.fold(S::ZERO, |acc, (&ki, &xi)| acc - ki * xi)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn double_integrator() -> (Matrix<f64, 2, 2>, Matrix<f64, 2, 1>) {
let a = Matrix::<f64, 2, 2> {
data: [[0.0, 1.0], [0.0, 0.0]],
};
let b = Matrix::<f64, 2, 1> {
data: [[0.0], [1.0]],
};
(a, b)
}
#[test]
fn ackermann_double_integrator() {
let (a, b) = double_integrator();
let poles = [-1.0_f64, -2.0];
let k = ackermann(&a, &b, &poles).expect("Should be controllable");
assert!(k[0].abs() > 0.0 || k[1].abs() > 0.0);
}
#[test]
fn ackermann_poles_stabilize_system() {
let (a, b) = double_integrator();
let poles = [0.5_f64, 0.5];
let k = ackermann(&a, &b, &poles).unwrap();
let mut x = [1.0_f64, 0.0]; for _ in 0..100 {
let u = -(k[0] * x[0] + k[1] * x[1]);
let x_new = [
a.data[0][0] * x[0] + a.data[0][1] * x[1] + b.data[0][0] * u,
a.data[1][0] * x[0] + a.data[1][1] * x[1] + b.data[1][0] * u,
];
x = x_new;
}
assert!(x[0].abs() < 0.01, "Should converge: x={:.4}", x[0]);
}
#[test]
fn uncontrollable_returns_none() {
let a = Matrix::<f64, 2, 2> {
data: [[1.0, 0.0], [0.0, 1.0]],
};
let b = Matrix::<f64, 2, 1> {
data: [[0.0], [0.0]],
};
let poles = [0.5_f64, 0.5];
assert!(ackermann(&a, &b, &poles).is_none());
}
#[test]
fn state_feedback_control_law() {
let sf = StateFeedback::new([2.0_f64, 3.0]);
let u = sf.control(&[1.0, 2.0], &[0.0, 0.0]);
assert!((u + 8.0).abs() < 1e-10); }
}