1use crate::linalg::DefaultAllocator;
20use crate::linalg::allocator::Allocator;
21use crate::od::interlink::InterlinkTxSpacecraft;
22use crate::od::msr::{Measurement, MeasurementType};
23use crate::od::prelude::sensitivity::{ScalarSensitivityT, TrackerSensitivity};
24use crate::od::{ODAlmanacSnafu, ODError, TrackingDevice};
25use crate::{Spacecraft, State};
26use anise::prelude::Almanac;
27use indexmap::IndexSet;
28use nalgebra::{DimName, OMatrix, U1};
29use snafu::ResultExt;
30use std::marker::PhantomData;
31
32struct ScalarSensitivity<SolveState: State, Rx, Tx>
33where
34 DefaultAllocator: Allocator<SolveState::Size>
35 + Allocator<SolveState::VecLength>
36 + Allocator<SolveState::Size, SolveState::Size>
37 + Allocator<U1, SolveState::Size>,
38{
39 sensitivity_row: OMatrix<f64, U1, SolveState::Size>,
40 _rx: PhantomData<Rx>,
41 _tx: PhantomData<Tx>,
42}
43
44impl TrackerSensitivity<Spacecraft, Spacecraft> for InterlinkTxSpacecraft
45where
46 DefaultAllocator: Allocator<<Spacecraft as State>::Size>
47 + Allocator<<Spacecraft as State>::VecLength>
48 + Allocator<<Spacecraft as State>::Size, <Spacecraft as State>::Size>,
49{
50 fn h_tilde<M: DimName>(
51 &self,
52 msr: &Measurement,
53 msr_types: &IndexSet<MeasurementType>,
54 rx: &Spacecraft,
55 almanac: &Almanac,
56 ) -> Result<OMatrix<f64, M, <Spacecraft as State>::Size>, ODError>
57 where
58 DefaultAllocator: Allocator<M> + Allocator<M, <Spacecraft as State>::Size>,
59 {
60 let mut mat = OMatrix::<f64, M, <Spacecraft as State>::Size>::identity();
62 for (ith_row, msr_type) in msr_types.iter().enumerate() {
63 if !msr.data.contains_key(msr_type) {
64 continue;
66 }
67 let scalar_h =
68 <ScalarSensitivity<Spacecraft, Spacecraft, InterlinkTxSpacecraft> as ScalarSensitivityT<
69 Spacecraft,
70 Spacecraft,
71 InterlinkTxSpacecraft,
72 >>::new(*msr_type, msr, rx, self, almanac)?;
73
74 mat.set_row(ith_row, &scalar_h.sensitivity_row);
75 }
76 Ok(mat)
77 }
78}
79
80impl ScalarSensitivityT<Spacecraft, Spacecraft, InterlinkTxSpacecraft>
81 for ScalarSensitivity<Spacecraft, Spacecraft, InterlinkTxSpacecraft>
82{
83 fn new(
84 msr_type: MeasurementType,
85 msr: &Measurement,
86 rx: &Spacecraft,
87 tx: &InterlinkTxSpacecraft,
88 almanac: &Almanac,
89 ) -> Result<Self, ODError> {
90 let receiver = rx.orbit;
91
92 let transmitter = <InterlinkTxSpacecraft as TrackingDevice<Spacecraft>>::location(
96 tx,
97 rx.orbit.epoch,
98 rx.orbit.frame,
99 almanac,
100 )
101 .context(ODAlmanacSnafu {
102 action: "computing transmitter location when computing sensitivity matrix",
103 })?;
104
105 let delta_r = receiver.radius_km - transmitter.radius_km;
106 let delta_v = receiver.velocity_km_s - transmitter.velocity_km_s;
107
108 match msr_type {
109 MeasurementType::Doppler => {
110 let ρ_km = msr.data.get(&MeasurementType::Range).ok_or_else(|| {
112 ODError::MeasurementSimError {
113 details: "Range measurement data is missing".to_string(),
114 }
115 })?;
116 let ρ_dot_km_s = msr.data.get(&MeasurementType::Doppler).ok_or_else(|| {
117 ODError::MeasurementSimError {
118 details: "Doppler measurement data is missing".to_string(),
119 }
120 })?;
121 let m11 = delta_r.x / ρ_km;
122 let m12 = delta_r.y / ρ_km;
123 let m13 = delta_r.z / ρ_km;
124 let m21 = delta_v.x / ρ_km - ρ_dot_km_s * delta_r.x / ρ_km.powi(2);
125 let m22 = delta_v.y / ρ_km - ρ_dot_km_s * delta_r.y / ρ_km.powi(2);
126 let m23 = delta_v.z / ρ_km - ρ_dot_km_s * delta_r.z / ρ_km.powi(2);
127
128 let sensitivity_row =
129 OMatrix::<f64, U1, <Spacecraft as State>::Size>::from_row_slice(&[
130 m21, m22, m23, m11, m12, m13, 0.0, 0.0, 0.0,
131 ]);
132
133 Ok(Self {
134 sensitivity_row,
135 _rx: PhantomData::<_>,
136 _tx: PhantomData::<_>,
137 })
138 }
139 MeasurementType::Range => {
140 let ρ_km = msr.data.get(&MeasurementType::Range).ok_or_else(|| {
141 ODError::MeasurementSimError {
142 details: "Range measurement data is missing".to_string(),
143 }
144 })?;
145 let m11 = delta_r.x / ρ_km;
146 let m12 = delta_r.y / ρ_km;
147 let m13 = delta_r.z / ρ_km;
148
149 let sensitivity_row =
150 OMatrix::<f64, U1, <Spacecraft as State>::Size>::from_row_slice(&[
151 m11, m12, m13, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
152 ]);
153
154 Ok(Self {
155 sensitivity_row,
156 _rx: PhantomData::<_>,
157 _tx: PhantomData::<_>,
158 })
159 }
160 MeasurementType::Azimuth
161 | MeasurementType::Elevation
162 | MeasurementType::ReceiveFrequency
163 | MeasurementType::TransmitFrequency
164 | MeasurementType::TransmitFrequencyRate
165 | MeasurementType::X
166 | MeasurementType::Y
167 | MeasurementType::Z => Err(ODError::MeasurementSimError {
168 details: format!("{msr_type:?} is not supported for interlink"),
169 }),
170 }
171 }
172}