1use crate::cosmic::Frame;
20use crate::linalg::allocator::Allocator;
21use crate::linalg::{DefaultAllocator, DimName, OMatrix, OVector, U3, U6};
22use crate::time::{Duration, Epoch};
23
24use std::fmt;
25
26#[allow(clippy::upper_case_acronyms)]
27pub type SNC3 = SNC<U3>;
28#[allow(clippy::upper_case_acronyms)]
29pub type SNC6 = SNC<U6>;
30
31#[derive(Clone)]
32#[allow(clippy::upper_case_acronyms)]
33pub struct SNC<A: DimName>
34where
35 DefaultAllocator: Allocator<A> + Allocator<A, A>,
36{
37 pub start_time: Option<Epoch>,
39 pub frame: Option<Frame>,
41 pub disable_time: Duration,
43 pub init_epoch: Option<Epoch>,
45 diag: OVector<f64, A>,
46 decay_diag: Option<Vec<f64>>,
47 pub prev_epoch: Option<Epoch>,
49}
50
51impl<A> fmt::Debug for SNC<A>
52where
53 A: DimName,
54 DefaultAllocator: Allocator<A> + Allocator<A, A>,
55{
56 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57 let mut fmt_cov = Vec::with_capacity(A::dim());
58 if let Some(decay) = &self.decay_diag {
59 for (i, dv) in decay.iter().enumerate() {
60 fmt_cov.push(format!("{:.1e} × exp(- {:.1e} × t)", self.diag[i], dv));
61 }
62 } else {
63 for i in 0..A::dim() {
64 fmt_cov.push(format!("{:.1e}", self.diag[i]));
65 }
66 }
67 write!(
68 f,
69 "SNC: diag({}) {}",
70 fmt_cov.join(", "),
71 if let Some(start) = self.start_time {
72 format!("starting at {start}")
73 } else {
74 "".to_string()
75 }
76 )
77 }
78}
79
80impl<A> fmt::Display for SNC<A>
81where
82 A: DimName,
83 DefaultAllocator: Allocator<A> + Allocator<A, A>,
84{
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 write!(f, "{self:?}")
87 }
88}
89
90impl<A: DimName> SNC<A>
91where
92 DefaultAllocator: Allocator<A> + Allocator<A, A>,
93{
94 pub fn from_diagonal(disable_time: Duration, values: &[f64]) -> Self {
96 assert_eq!(
97 values.len(),
98 A::dim(),
99 "Not enough values for the size of the SNC matrix"
100 );
101
102 let mut diag = OVector::zeros();
103 for (i, v) in values.iter().enumerate() {
104 diag[i] = *v;
105 }
106
107 Self {
108 diag,
109 disable_time,
110 start_time: None,
111 frame: None,
112 decay_diag: None,
113 init_epoch: None,
114 prev_epoch: None,
115 }
116 }
117
118 pub fn with_start_time(disable_time: Duration, values: &[f64], start_time: Epoch) -> Self {
120 let mut me = Self::from_diagonal(disable_time, values);
121 me.start_time = Some(start_time);
122 me
123 }
124
125 pub fn with_decay(
128 disable_time: Duration,
129 initial_snc: &[f64],
130 decay_constants_s: &[f64],
131 ) -> Self {
132 assert_eq!(
133 decay_constants_s.len(),
134 A::dim(),
135 "Not enough decay constants for the size of the SNC matrix"
136 );
137
138 let mut me = Self::from_diagonal(disable_time, initial_snc);
139 me.decay_diag = Some(decay_constants_s.to_vec());
140 me
141 }
142
143 pub fn to_matrix(&self, epoch: Epoch) -> Option<OMatrix<f64, A, A>> {
148 if let Some(start_time) = self.start_time {
149 if start_time > epoch {
150 debug!("@{} SNC starts at {}", epoch, start_time);
152 return None;
153 }
154 }
155
156 if let Some(prev_epoch) = self.prev_epoch {
158 if epoch - prev_epoch > self.disable_time {
159 debug!(
160 "@{} SNC disabled: prior use greater than {}",
161 epoch, self.disable_time
162 );
163 return None;
164 }
165 }
166 let mut snc = OMatrix::<f64, A, A>::zeros();
168 for i in 0..self.diag.nrows() {
169 snc[(i, i)] = self.diag[i];
170 }
171
172 if let Some(decay) = &self.decay_diag {
173 let total_delta_t = (epoch - self.init_epoch.unwrap()).to_seconds();
175 for i in 0..self.diag.nrows() {
176 snc[(i, i)] *= (-decay[i] * total_delta_t).exp();
177 }
178 }
179
180 debug!(
181 "@{} SNC diag {:?}",
182 epoch,
183 snc.diagonal().iter().copied().collect::<Vec<f64>>()
184 );
185
186 Some(snc)
187 }
188}
189
190#[test]
191fn test_snc_init() {
192 use crate::time::Unit;
193 let snc_expo = SNC3::with_decay(
194 2 * Unit::Minute,
195 &[1e-6, 1e-6, 1e-6],
196 &[3600.0, 3600.0, 3600.0],
197 );
198 println!("{}", snc_expo);
199
200 let snc_std = SNC3::with_start_time(
201 2 * Unit::Minute,
202 &[1e-6, 1e-6, 1e-6],
203 Epoch::from_et_seconds(3600.0),
204 );
205 println!("{}", snc_std);
206}