-
-
Notifications
You must be signed in to change notification settings - Fork 36
Open
Description
High level description
I've tried this in the past, but failed. Gemini proposes a solution which I might as well try and debug.
Requirements
Implement Q-Law \o/
Test plans
The Q Law paper test cases are already in the code, so I'll just need to clone these for the new Q-Law implementation
Design
Document, discuss, and optionally upload design diagram into this section.
use nalgebra::Vector3;
use std::sync::Arc;
pub struct QLaw {
pub objectives: Vec<Option<GuidanceObjective>>,
pub weights: Vec<f64>,
/// Scaling factors (tolerances) for each element, often x_i,tol in literature
pub tolerances: Vec<f64>,
pub max_eclipse_prct: Option<f64>,
}
impl GuidanceLaw for QLaw {
fn achieved(&self, state: &Spacecraft) -> Result<bool, GuidanceError> {
for obj in self.objectives.iter().flatten() {
if !obj.assess_value(state.value(obj.parameter).context(GuidStateSnafu)?).0 {
return Ok(false);
}
}
Ok(true)
}
fn direction(&self, sc: &Spacecraft) -> Result<Vector3<f64>, GuidanceError> {
if sc.mode() != GuidanceMode::Thrust {
return Ok(Vector3::zeros());
}
let osc = sc.orbit;
// Retrieve MEE elements: p, f, g, h, k, L
// We'll use these to build the GVE matrix B(x)
let p = osc.semi_parameter_km().context(GuidancePhysicsSnafu { action: "Q-Law" })?;
let f = osc.mee_f().context(GuidancePhysicsSnafu { action: "Q-Law" })?;
let g = osc.mee_g().context(GuidancePhysicsSnafu { action: "Q-Law" })?;
let h = osc.mee_h().context(GuidancePhysicsSnafu { action: "Q-Law" })?;
let k = osc.mee_k().context(GuidancePhysicsSnafu { action: "Q-Law" })?;
let l = osc.true_longitude_rad().context(GuidancePhysicsSnafu { action: "Q-Law" })?;
let (sin_l, cos_l) = l.sin_cos();
let w = 1.0 + f * cos_l + g * sin_l;
let s2 = 1.0 + h.powi(2) + k.powi(2);
let sqrt_p_mu = (p / osc.gm_km_s2()).sqrt();
// D is the direction vector: D = sum( dQ/dx_i * G_i )
// where G_i is the row of the GVE matrix corresponding to element i
let mut d_vector = Vector3::zeros();
for (i, obj_opt) in self.objectives.iter().enumerate() {
if let Some(obj) = obj_opt {
let weight = self.weights[i];
let tol = self.tolerances[i];
let current_val = sc.value(obj.parameter).context(GuidStateSnafu)?;
let target_val = obj.target; // Assuming GuidanceObjective stores a target
// Partial of Q with respect to element x_i: 2 * W_i * (x_i - x_target) / tol^2
let dq_dx = 2.0 * weight * (current_val - target_val) / tol.powi(2);
// GVE components for the RTN frame (Radial, Transverse, Normal)
let g_i = match obj.parameter {
StateParameter::Element(OrbitalElement::SemiParameter) => {
Vector3::new(0.0, 2.0 * p / w * sqrt_p_mu, 0.0)
}
StateParameter::Element(OrbitalElement::EquinoctialF) => {
Vector3::new(
sqrt_p_mu * sin_l,
sqrt_p_mu / w * ((w + 1.0) * cos_l + f),
-sqrt_p_mu * g / w * (h * sin_l - k * cos_l)
)
}
StateParameter::Element(OrbitalElement::EquinoctialG) => {
Vector3::new(
-sqrt_p_mu * cos_l,
sqrt_p_mu / w * ((w + 1.0) * sin_l + g),
sqrt_p_mu * f / w * (h * sin_l - k * cos_l)
)
}
StateParameter::Element(OrbitalElement::EquinoctialH) => {
Vector3::new(0.0, 0.0, sqrt_p_mu * s2 * cos_l / (2.0 * w))
}
StateParameter::Element(OrbitalElement::EquinoctialK) => {
Vector3::new(0.0, 0.0, sqrt_p_mu * s2 * sin_l / (2.0 * w))
}
_ => Vector3::zeros(),
};
d_vector += dq_dx * g_i;
}
}
// To minimize Q, we thrust in the direction opposite to the gradient
// Steering direction u = -D / |D|
if d_vector.norm() > 0.0 {
let steering_rtn = -d_vector.normalize();
// Convert RTN to Inertial
Ok(osc.dcm_from_rcn_to_inertial().context(GuidancePhysicsSnafu { action: "RTN to Inertial" })? * steering_rtn)
} else {
Ok(Vector3::zeros())
}
}
fn throttle(&self, sc: &Spacecraft) -> Result<f64, GuidanceError> {
// Q-law often includes an "effectivity" check.
// If the best dQ/dt is very small, it's better to coast.
// For simplicity, we trigger full thrust if a direction is found.
if sc.mode() == GuidanceMode::Thrust { Ok(1.0) } else { Ok(0.0) }
}
fn next(&self, sc: &mut Spacecraft, almanac: Arc<Almanac>) {
// Reuse logic from previous implementations for eclipse and mode switching
}
}Reactions are currently unavailable