gmt_dos_clients_lom/
lib.rs1use std::{io::Read, path::Path, sync::Arc};
8
9use flate2::bufread::GzDecoder;
10use gmt_dos_clients_io::{
11 gmt_m1::M1RigidBodyMotions,
12 gmt_m2::{asm::M2ASMReferenceBodyNodes, M2RigidBodyMotions},
13 optics::{
14 MaskedWavefront, SegmentD21PistonRSS, SegmentPiston, SegmentTipTilt, SegmentWfeRms,
15 TipTilt, Wavefront, WfeRms,
16 },
17};
18use gmt_lom::{LinearOpticalModelError, Loader, LOM};
19use interface::{self, Data, Size, Units, Update, Write};
20
21mod optical_sensitivity;
22pub use optical_sensitivity::OpticalSensitivities;
23
24#[derive(Debug, Clone)]
26pub struct LinearOpticalModel {
27 lom: LOM,
28 m1_rbm: Arc<Vec<f64>>,
29 m2_rbm: Arc<Vec<f64>>,
30}
31impl LinearOpticalModel {
32 pub fn new() -> std::result::Result<Self, LinearOpticalModelError> {
33 let sens = include_bytes!("optical_sensitivities.rs.bin.gz");
34 let mut gz = GzDecoder::new(sens.as_slice());
35 let mut bytes = vec![];
36 gz.read_to_end(&mut bytes)?;
37 Ok(Self {
38 lom: LOM::try_from(bytes.as_slice())?,
39 m1_rbm: Arc::new(vec![0f64; 42]),
40 m2_rbm: Arc::new(vec![0f64; 42]),
41 })
42 }
43 pub fn new_with_path(
44 path: impl AsRef<Path>,
45 ) -> std::result::Result<Self, LinearOpticalModelError> {
46 let path = path.as_ref();
47 let filename = path.file_name().unwrap();
48 let sens_loader = Loader::<gmt_lom::OpticalSensitivities>::default()
49 .path(path.parent().unwrap())
50 .filename(filename.to_str().unwrap());
51 Ok(Self {
52 lom: LOM::builder()
53 .load_optical_sensitivities(sens_loader)?
54 .build()?,
55 m1_rbm: Arc::new(vec![0f64; 42]),
56 m2_rbm: Arc::new(vec![0f64; 42]),
57 })
58 }
59}
60
61impl Units for LinearOpticalModel {}
62
63impl Update for LinearOpticalModel {
64 fn update(&mut self) {
65 self.lom.rbm = vec![(self.m1_rbm.as_slice(), self.m2_rbm.as_slice())]
66 .into_iter()
67 .collect();
68 }
69}
70impl interface::Read<M1RigidBodyMotions> for LinearOpticalModel {
71 fn read(&mut self, data: Data<M1RigidBodyMotions>) {
72 self.m1_rbm = data.into_arc();
73 }
74}
75impl interface::Read<M2RigidBodyMotions> for LinearOpticalModel {
76 fn read(&mut self, data: Data<M2RigidBodyMotions>) {
77 self.m2_rbm = data.into_arc();
78 }
79}
80impl interface::Read<M2ASMReferenceBodyNodes> for LinearOpticalModel {
81 fn read(&mut self, data: Data<M2ASMReferenceBodyNodes>) {
82 self.m2_rbm = data.into_arc();
83 }
84}
85
86impl Write<TipTilt> for LinearOpticalModel {
87 fn write(&mut self) -> Option<Data<TipTilt>> {
88 Some(Data::new(self.lom.tiptilt().into()))
89 }
90}
91impl Write<SegmentTipTilt> for LinearOpticalModel {
92 fn write(&mut self) -> Option<Data<SegmentTipTilt>> {
93 Some(Data::new(self.lom.segment_tiptilt().into()))
94 }
95}
96impl<const E: i32> Write<SegmentPiston<E>> for LinearOpticalModel {
97 fn write(&mut self) -> Option<Data<SegmentPiston<E>>> {
98 let mut piston: Vec<f64> = self.lom.segment_piston().into();
99 if E != 0 {
100 piston.iter_mut().for_each(|p| {
101 *p *= 10f64.powi(-E);
102 });
103 }
104 Some(piston.into())
105 }
106}
107impl<const E: i32> Write<SegmentWfeRms<E>> for LinearOpticalModel {
108 fn write(&mut self) -> Option<Data<SegmentWfeRms<E>>> {
109 Some(self.lom.segment_wfe_rms::<E>().into())
110 }
111}
112impl<const E: i32> Write<SegmentD21PistonRSS<E>> for LinearOpticalModel {
113 fn write(&mut self) -> Option<Data<SegmentD21PistonRSS<E>>> {
114 let piston: Vec<f64> = self.lom.segment_piston().into();
115 let mut sum_squared = 0f64;
116 for p_i in piston.iter().take(6) {
117 for p_j in piston.iter().skip(1) {
118 sum_squared += (p_i - p_j).powi(2);
119 }
120 }
121 let mut rss = sum_squared.sqrt();
122 if E != 0 {
123 rss *= 10f64.powi(-E);
124 }
125 Some(vec![rss].into())
126 }
127}
128
129impl Write<Wavefront> for LinearOpticalModel {
130 fn write(&mut self) -> Option<Data<Wavefront>> {
131 Some(Data::new(self.lom.wavefront().into()))
132 }
133}
134
135impl Write<MaskedWavefront> for LinearOpticalModel {
136 fn write(&mut self) -> Option<Data<MaskedWavefront>> {
137 Some(Data::new(self.lom.masked_wavefront().into()))
138 }
139}
140
141impl<const E: i32> Write<WfeRms<E>> for LinearOpticalModel {
142 fn write(&mut self) -> Option<Data<WfeRms<E>>> {
143 let wavefront = self.lom.masked_wavefront();
144 let n = wavefront.len() as f64;
145 let (s, m) = wavefront
146 .into_iter()
147 .fold((0f64, 0f64), |(mut s, mut m), w| {
148 s += w * w;
149 m += w;
150 (s, m)
151 });
152 let mut wfe_rms = ((s - m * m / n) / n).sqrt();
153 if E != 0 {
154 wfe_rms *= 10f64.powi(-E);
155 }
156 Some(Data::new(vec![wfe_rms]))
157 }
158}
159
160impl Size<TipTilt> for LinearOpticalModel {
161 fn len(&self) -> usize {
162 2
163 }
164}
165impl Size<SegmentTipTilt> for LinearOpticalModel {
166 fn len(&self) -> usize {
167 14
168 }
169}
170impl Size<SegmentPiston> for LinearOpticalModel {
171 fn len(&self) -> usize {
172 7
173 }
174}
175impl Size<Wavefront> for LinearOpticalModel {
176 fn len(&self) -> usize {
177 512 * 512
178 }
179}
180impl Size<WfeRms> for LinearOpticalModel {
181 fn len(&self) -> usize {
182 1
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn m1_segment_rxy() {
192 let mut rbm2lom = LinearOpticalModel::new().unwrap();
193 let s = 1e-6;
194 let m1_rbm: Vec<_> = (0..7)
195 .flat_map(|_| {
196 let mut v = vec![0f64; 6];
197 v[3] = s;
198 v
199 })
200 .collect();
201 <LinearOpticalModel as interface::Read<M1RigidBodyMotions>>::read(
202 &mut rbm2lom,
203 m1_rbm.into(),
204 );
205 rbm2lom.update();
206 let mut stt: Vec<f64> = <LinearOpticalModel as Write<SegmentTipTilt>>::write(&mut rbm2lom)
207 .unwrap()
208 .into();
209 stt.iter_mut().for_each(|x| *x /= s);
210 let stt_mag: Vec<_> = stt[..7]
211 .iter()
212 .zip(&stt[7..])
213 .map(|(x, y)| x.hypot(*y))
214 .collect();
215 println!("STT: {:.2?}", stt_mag);
216 }
217
218 #[test]
219 fn m2_segment_rxy() {
220 let mut rbm2lom = LinearOpticalModel::new().unwrap();
221 let s = 1e-6;
222 let m1_rbm: Vec<_> = (0..7)
223 .flat_map(|_| {
224 let mut v = vec![0f64; 6];
225 v[4] = s;
226 v
227 })
228 .collect();
229 <LinearOpticalModel as interface::Read<M2RigidBodyMotions>>::read(
230 &mut rbm2lom,
231 m1_rbm.into(),
232 );
233 rbm2lom.update();
234 let mut stt: Vec<f64> = <LinearOpticalModel as Write<SegmentTipTilt>>::write(&mut rbm2lom)
235 .unwrap()
236 .into();
237 stt.iter_mut().for_each(|x| *x /= s);
238 let stt_mag: Vec<_> = stt[..7]
239 .iter()
240 .zip(&stt[7..])
241 .map(|(x, y)| x.hypot(*y))
242 .collect();
243 println!("STT: {:.2?}", stt_mag);
244 }
245
246 #[test]
247 fn m1_segment_txy() {
248 let mut rbm2lom = LinearOpticalModel::new().unwrap();
249 let s = 1e-6;
250 let m1_rbm: Vec<_> = (0..7)
251 .flat_map(|_| {
252 let mut v = vec![0f64; 6];
253 v[0] = s;
254 v
255 })
256 .collect();
257 <LinearOpticalModel as interface::Read<M1RigidBodyMotions>>::read(
258 &mut rbm2lom,
259 m1_rbm.into(),
260 );
261 rbm2lom.update();
262 let mut stt: Vec<f64> = <LinearOpticalModel as Write<SegmentTipTilt>>::write(&mut rbm2lom)
263 .unwrap()
264 .into();
265 stt.iter_mut().for_each(|x| *x /= s);
266 let stt_mag: Vec<_> = stt[..7]
267 .iter()
268 .zip(&stt[7..])
269 .map(|(x, y)| x.hypot(*y))
270 .collect();
271 println!("STT: {:.3?}", stt_mag);
272 }
273}