use crate::linalg::allocator::Allocator;
use crate::linalg::DefaultAllocator;
use crate::md::prelude::Interpolatable;
use crate::od::{GroundStation, ODAlmanacSnafu, ODError, TrackingDevice};
use crate::{Spacecraft, State};
use anise::prelude::Almanac;
use indexmap::IndexSet;
use nalgebra::{DimName, OMatrix, U1};
use snafu::ResultExt;
use std::marker::PhantomData;
use std::sync::Arc;
use super::measurement::Measurement;
use super::MeasurementType;
trait ScalarSensitivityT<SolveState: State, Rx, Tx>
where
Self: Sized,
DefaultAllocator: Allocator<SolveState::Size>
+ Allocator<SolveState::VecLength>
+ Allocator<SolveState::Size, SolveState::Size>,
{
fn new(
msr_type: MeasurementType,
msr: &Measurement,
rx: &Rx,
tx: &Tx,
almanac: Arc<Almanac>,
) -> Result<Self, ODError>;
}
pub trait TrackerSensitivity<SolveState: Interpolatable, Rx>: TrackingDevice<SolveState>
where
Self: Sized,
DefaultAllocator: Allocator<SolveState::Size>
+ Allocator<SolveState::VecLength>
+ Allocator<SolveState::Size, SolveState::Size>,
{
fn h_tilde<M: DimName>(
&self,
msr: &Measurement,
msr_types: &IndexSet<MeasurementType>, rx: &Rx,
almanac: Arc<Almanac>,
) -> Result<OMatrix<f64, M, SolveState::Size>, ODError>
where
DefaultAllocator: Allocator<M> + Allocator<M, SolveState::Size>;
}
struct ScalarSensitivity<SolveState: State, Rx, Tx>
where
DefaultAllocator: Allocator<SolveState::Size>
+ Allocator<SolveState::VecLength>
+ Allocator<SolveState::Size, SolveState::Size>
+ Allocator<U1, SolveState::Size>,
{
sensitivity_row: OMatrix<f64, U1, SolveState::Size>,
_rx: PhantomData<Rx>,
_tx: PhantomData<Tx>,
}
impl TrackerSensitivity<Spacecraft, Spacecraft> for GroundStation
where
DefaultAllocator: Allocator<<Spacecraft as State>::Size>
+ Allocator<<Spacecraft as State>::VecLength>
+ Allocator<<Spacecraft as State>::Size, <Spacecraft as State>::Size>,
{
fn h_tilde<M: DimName>(
&self,
msr: &Measurement,
msr_types: &IndexSet<MeasurementType>,
rx: &Spacecraft,
almanac: Arc<Almanac>,
) -> Result<OMatrix<f64, M, <Spacecraft as State>::Size>, ODError>
where
DefaultAllocator: Allocator<M> + Allocator<M, <Spacecraft as State>::Size>,
{
let mut mat = OMatrix::<f64, M, <Spacecraft as State>::Size>::identity();
for (ith_row, msr_type) in msr_types.iter().enumerate() {
if !msr.data.contains_key(msr_type) {
continue;
}
let scalar_h =
<ScalarSensitivity<Spacecraft, Spacecraft, GroundStation> as ScalarSensitivityT<
Spacecraft,
Spacecraft,
GroundStation,
>>::new(*msr_type, msr, rx, self, almanac.clone())?;
mat.set_row(ith_row, &scalar_h.sensitivity_row);
}
Ok(mat)
}
}
impl ScalarSensitivityT<Spacecraft, Spacecraft, GroundStation>
for ScalarSensitivity<Spacecraft, Spacecraft, GroundStation>
{
fn new(
msr_type: MeasurementType,
msr: &Measurement,
rx: &Spacecraft,
tx: &GroundStation,
almanac: Arc<Almanac>,
) -> Result<Self, ODError> {
let receiver = rx.orbit;
let transmitter = tx
.location(rx.orbit.epoch, rx.orbit.frame, almanac.clone())
.context(ODAlmanacSnafu {
action: "computing transmitter location when computing sensitivity matrix",
})?;
let delta_r = receiver.radius_km - transmitter.radius_km;
let delta_v = receiver.velocity_km_s - transmitter.velocity_km_s;
match msr_type {
MeasurementType::Doppler => {
let ρ_km = match msr.data.get(&MeasurementType::Range) {
Some(range_km) => *range_km,
None => {
tx.azimuth_elevation_of(receiver, None, &almanac)
.context(ODAlmanacSnafu {
action: "computing range for Doppler measurement",
})?
.range_km
}
};
let ρ_dot_km_s = msr.data.get(&MeasurementType::Doppler).unwrap();
let m11 = delta_r.x / ρ_km;
let m12 = delta_r.y / ρ_km;
let m13 = delta_r.z / ρ_km;
let m21 = delta_v.x / ρ_km - ρ_dot_km_s * delta_r.x / ρ_km.powi(2);
let m22 = delta_v.y / ρ_km - ρ_dot_km_s * delta_r.y / ρ_km.powi(2);
let m23 = delta_v.z / ρ_km - ρ_dot_km_s * delta_r.z / ρ_km.powi(2);
let sensitivity_row =
OMatrix::<f64, U1, <Spacecraft as State>::Size>::from_row_slice(&[
m21, m22, m23, m11, m12, m13, 0.0, 0.0, 0.0,
]);
Ok(Self {
sensitivity_row,
_rx: PhantomData::<_>,
_tx: PhantomData::<_>,
})
}
MeasurementType::Range => {
let ρ_km = msr.data.get(&MeasurementType::Range).unwrap();
let m11 = delta_r.x / ρ_km;
let m12 = delta_r.y / ρ_km;
let m13 = delta_r.z / ρ_km;
let sensitivity_row =
OMatrix::<f64, U1, <Spacecraft as State>::Size>::from_row_slice(&[
m11, m12, m13, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
]);
Ok(Self {
sensitivity_row,
_rx: PhantomData::<_>,
_tx: PhantomData::<_>,
})
}
MeasurementType::Azimuth => {
let m11 = -delta_r.y / (delta_r.x.powi(2) + delta_r.y.powi(2));
let m12 = delta_r.x / (delta_r.x.powi(2) + delta_r.y.powi(2));
let m13 = 0.0;
let sensitivity_row =
OMatrix::<f64, U1, <Spacecraft as State>::Size>::from_row_slice(&[
m11, m12, m13, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
]);
Ok(Self {
sensitivity_row,
_rx: PhantomData::<_>,
_tx: PhantomData::<_>,
})
}
MeasurementType::Elevation => {
let r2 = delta_r.norm().powi(2);
let m11 = -(delta_r.x * delta_r.z) / (r2 * (r2 - delta_r.z.powi(2)).sqrt());
let m12 = -(delta_r.y * delta_r.z) / (r2 * (r2 - delta_r.z.powi(2)).sqrt());
let m13 = (delta_r.x.powi(2) + delta_r.y.powi(2)).sqrt() / r2;
let sensitivity_row =
OMatrix::<f64, U1, <Spacecraft as State>::Size>::from_row_slice(&[
m11, m12, m13, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
]);
Ok(Self {
sensitivity_row,
_rx: PhantomData::<_>,
_tx: PhantomData::<_>,
})
}
}
}
}