use crate::error::ControlError;
use crate::linear_algebra::{Matrix, Vector, Vector3D};
use crate::scalar::Numeric;
use crate::spatial::SO3;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ThrustCommand<T: Numeric = f64> {
attitude: SO3<T>,
thrust_acceleration: T,
}
impl<T: Numeric> ThrustCommand<T> {
#[inline]
#[must_use]
pub fn attitude(&self) -> SO3<T> {
self.attitude
}
#[inline]
#[must_use]
pub fn thrust_acceleration(&self) -> T {
self.thrust_acceleration
}
#[inline]
#[must_use]
pub fn thrust_force(&self, mass: T) -> T {
self.thrust_acceleration * mass
}
}
pub fn thrust_command_from_acceleration<T: Numeric>(
acceleration_command: Vector3D<T>,
desired_heading: T,
gravity: T,
) -> Result<ThrustCommand<T>, ControlError> {
if !acceleration_command.is_finite() || !desired_heading.is_finite() || !gravity.is_finite() {
return Err(ControlError::NonFinite);
}
let push = acceleration_command + Vector::new([T::ZERO, T::ZERO, gravity]);
let thrust_acceleration = push.norm();
let up_axis = push
.try_normalized()
.ok_or(ControlError::UndefinedThrustDirection)?;
let heading_direction = Vector::new([desired_heading.cos(), desired_heading.sin(), T::ZERO]);
let side_axis = up_axis
.cross(heading_direction)
.try_normalized()
.ok_or(ControlError::UndefinedHeadingDirection)?;
let forward_axis = side_axis.cross(up_axis);
let rotation = Matrix::new([
[forward_axis[0], side_axis[0], up_axis[0]],
[forward_axis[1], side_axis[1], up_axis[1]],
[forward_axis[2], side_axis[2], up_axis[2]],
]);
let attitude = SO3::try_from_matrix(rotation).ok_or(ControlError::NonFinite)?;
Ok(ThrustCommand {
attitude,
thrust_acceleration,
})
}