use crate::linalg::DefaultAllocator;
use crate::linalg::allocator::Allocator;
use crate::od::groundpnt::GroundAsset;
use crate::od::interlink::InterlinkTxSpacecraft;
use crate::od::msr::{Measurement, MeasurementType, sensitivity::ScalarSensitivity};
use crate::od::prelude::sensitivity::{ScalarSensitivityT, TrackerSensitivity};
use crate::od::{ODAlmanacSnafu, ODError};
use crate::{Spacecraft, State};
use anise::errors::AlmanacError;
use anise::prelude::Almanac;
use indexmap::IndexSet;
use nalgebra::{DimName, OMatrix, U1};
use snafu::ResultExt;
use std::marker::PhantomData;
impl TrackerSensitivity<GroundAsset, GroundAsset> for InterlinkTxSpacecraft
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: &GroundAsset,
almanac: &Almanac,
) -> Result<OMatrix<f64, M, <GroundAsset as State>::Size>, ODError>
where
DefaultAllocator: Allocator<M> + Allocator<M, <GroundAsset as State>::Size>,
{
let mut mat = OMatrix::<f64, M, <GroundAsset 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<GroundAsset, GroundAsset, InterlinkTxSpacecraft> as ScalarSensitivityT<
GroundAsset,
GroundAsset,
InterlinkTxSpacecraft,
>>::new(*msr_type, msr, rx, self, almanac)?;
mat.set_row(ith_row, &scalar_h.sensitivity_row);
}
Ok(mat)
}
}
impl ScalarSensitivityT<GroundAsset, GroundAsset, InterlinkTxSpacecraft>
for ScalarSensitivity<GroundAsset, GroundAsset, InterlinkTxSpacecraft>
{
fn new(
msr_type: MeasurementType,
msr: &Measurement,
rx: &GroundAsset,
tx: &InterlinkTxSpacecraft,
almanac: &Almanac,
) -> Result<Self, ODError> {
let receiver = rx.orbit();
let loc = rx.to_location();
let transmitter = tx
.traj
.at(receiver.epoch)
.map_err(|source| ODError::ODTrajError {
source,
details: "computing sensitivity ground asset / interlink".into(),
})?
.orbit;
let jac = rx
.geodetic_to_cartesian_jacobian()
.map_err(|e| ODError::ODAlmanac {
source: Box::new(AlmanacError::AlmanacPhysics {
action: "computing Jacobian for geodetics",
source: Box::new(e),
}),
action: "computing Jacobian for geodetics",
})?;
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 => {
almanac
.azimuth_elevation_range_sez_from_location(transmitter, loc, None, None)
.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, <GroundAsset as State>::Size>::from_row_slice(&[
m21, m22, m23, m11, m12, m13,
]) * jac;
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, <GroundAsset as State>::Size>::from_row_slice(&[
m11, m12, m13, 0.0, 0.0, 0.0,
]) * jac;
Ok(Self {
sensitivity_row,
_rx: PhantomData::<_>,
_tx: PhantomData::<_>,
})
}
_ => Err(ODError::MeasurementSimError {
details: format!("{msr_type:?} is only supported in CCSDS TDM parsing"),
}),
}
}
}