1use super::{MountTypeFlags, Term};
2
3pub struct IH;
4pub struct ID;
5pub struct CH;
6pub struct NP;
7pub struct MA;
8pub struct ME;
9pub struct TF;
10pub struct TX;
11pub struct DAF;
12pub struct FO;
13pub struct HCES;
14pub struct HCEC;
15pub struct DCES;
16pub struct DCEC;
17
18impl Term for IH {
19 fn name(&self) -> &str {
20 "IH"
21 }
22 fn description(&self) -> &str {
23 "Hour angle index error"
24 }
25 fn jacobian_equatorial(&self, _h: f64, _dec: f64, _lat: f64, _pier: f64) -> (f64, f64) {
26 (-1.0, 0.0)
27 }
28 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
29 (0.0, 0.0)
30 }
31 fn applicable_mounts(&self) -> MountTypeFlags {
32 MountTypeFlags::EQUATORIAL
33 }
34}
35
36impl Term for ID {
37 fn name(&self) -> &str {
38 "ID"
39 }
40 fn description(&self) -> &str {
41 "Declination index error"
42 }
43 fn pier_sensitive(&self) -> bool {
44 true
45 }
46 fn jacobian_equatorial(&self, _h: f64, _dec: f64, _lat: f64, pier: f64) -> (f64, f64) {
47 (0.0, -pier)
48 }
49 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
50 (0.0, 0.0)
51 }
52 fn applicable_mounts(&self) -> MountTypeFlags {
53 MountTypeFlags::EQUATORIAL
54 }
55}
56
57impl Term for CH {
58 fn name(&self) -> &str {
59 "CH"
60 }
61 fn description(&self) -> &str {
62 "East-west collimation error"
63 }
64 fn pier_sensitive(&self) -> bool {
65 true
66 }
67 fn jacobian_equatorial(&self, _h: f64, dec: f64, _lat: f64, pier: f64) -> (f64, f64) {
68 (-pier / libm::cos(dec), 0.0)
69 }
70 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
71 (0.0, 0.0)
72 }
73 fn applicable_mounts(&self) -> MountTypeFlags {
74 MountTypeFlags::EQUATORIAL
75 }
76}
77
78impl Term for NP {
79 fn name(&self) -> &str {
80 "NP"
81 }
82 fn description(&self) -> &str {
83 "Non-perpendicularity of axes"
84 }
85 fn pier_sensitive(&self) -> bool {
86 true
87 }
88 fn jacobian_equatorial(&self, _h: f64, dec: f64, _lat: f64, pier: f64) -> (f64, f64) {
89 (-pier * libm::tan(dec), 0.0)
90 }
91 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
92 (0.0, 0.0)
93 }
94 fn applicable_mounts(&self) -> MountTypeFlags {
95 MountTypeFlags::EQUATORIAL
96 }
97}
98
99impl Term for MA {
100 fn name(&self) -> &str {
101 "MA"
102 }
103 fn description(&self) -> &str {
104 "Polar axis azimuth misalignment"
105 }
106 fn jacobian_equatorial(&self, h: f64, dec: f64, _lat: f64, _pier: f64) -> (f64, f64) {
107 (libm::cos(h) * libm::tan(dec), -libm::sin(h))
108 }
109 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
110 (0.0, 0.0)
111 }
112 fn applicable_mounts(&self) -> MountTypeFlags {
113 MountTypeFlags::EQUATORIAL
114 }
115}
116
117impl Term for ME {
118 fn name(&self) -> &str {
119 "ME"
120 }
121 fn description(&self) -> &str {
122 "Polar axis elevation misalignment"
123 }
124 fn jacobian_equatorial(&self, h: f64, dec: f64, _lat: f64, _pier: f64) -> (f64, f64) {
125 (-libm::sin(h) * libm::tan(dec), -libm::cos(h))
126 }
127 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
128 (0.0, 0.0)
129 }
130 fn applicable_mounts(&self) -> MountTypeFlags {
131 MountTypeFlags::EQUATORIAL
132 }
133}
134
135impl Term for TF {
136 fn name(&self) -> &str {
137 "TF"
138 }
139 fn description(&self) -> &str {
140 "Tube flexure (sin zeta)"
141 }
142 fn jacobian_equatorial(&self, h: f64, dec: f64, lat: f64, _pier: f64) -> (f64, f64) {
143 let dh = libm::cos(lat) * libm::sin(h) / libm::cos(dec);
144 let dd = libm::cos(lat) * libm::cos(h) * libm::sin(dec) - libm::sin(lat) * libm::cos(dec);
145 (dh, dd)
146 }
147 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
148 (0.0, 0.0)
149 }
150 fn applicable_mounts(&self) -> MountTypeFlags {
151 MountTypeFlags::EQUATORIAL
152 }
153}
154
155impl Term for TX {
156 fn name(&self) -> &str {
157 "TX"
158 }
159 fn description(&self) -> &str {
160 "Tube flexure (tan zeta)"
161 }
162 fn jacobian_equatorial(&self, h: f64, dec: f64, lat: f64, _pier: f64) -> (f64, f64) {
163 let sin_alt =
164 libm::sin(lat) * libm::sin(dec) + libm::cos(lat) * libm::cos(dec) * libm::cos(h);
165 let alt = libm::asin(sin_alt);
166 let sin_a = libm::sin(alt);
167 if sin_a.abs() < 1e-10 {
168 return (0.0, 0.0);
169 }
170 let dh_tf = libm::cos(lat) * libm::sin(h) / libm::cos(dec);
171 let dd_tf =
172 libm::cos(lat) * libm::cos(h) * libm::sin(dec) - libm::sin(lat) * libm::cos(dec);
173 (dh_tf / sin_a, dd_tf / sin_a)
174 }
175 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
176 (0.0, 0.0)
177 }
178 fn applicable_mounts(&self) -> MountTypeFlags {
179 MountTypeFlags::EQUATORIAL
180 }
181}
182
183impl Term for DAF {
184 fn name(&self) -> &str {
185 "DAF"
186 }
187 fn description(&self) -> &str {
188 "Declination axis flexure"
189 }
190 fn jacobian_equatorial(&self, h: f64, dec: f64, lat: f64, _pier: f64) -> (f64, f64) {
191 (
192 -(libm::sin(lat) * libm::tan(dec) + libm::cos(lat) * libm::cos(h)),
193 0.0,
194 )
195 }
196 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
197 (0.0, 0.0)
198 }
199 fn applicable_mounts(&self) -> MountTypeFlags {
200 MountTypeFlags::EQUATORIAL
201 }
202}
203
204impl Term for FO {
205 fn name(&self) -> &str {
206 "FO"
207 }
208 fn description(&self) -> &str {
209 "Fork flexure"
210 }
211 fn jacobian_equatorial(&self, h: f64, _dec: f64, _lat: f64, _pier: f64) -> (f64, f64) {
212 (0.0, libm::cos(h))
213 }
214 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
215 (0.0, 0.0)
216 }
217 fn applicable_mounts(&self) -> MountTypeFlags {
218 MountTypeFlags::EQUATORIAL
219 }
220}
221
222impl Term for HCES {
223 fn name(&self) -> &str {
224 "HCES"
225 }
226 fn description(&self) -> &str {
227 "HA centering error (sin)"
228 }
229 fn jacobian_equatorial(&self, h: f64, _dec: f64, _lat: f64, _pier: f64) -> (f64, f64) {
230 (libm::sin(h), 0.0)
231 }
232 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
233 (0.0, 0.0)
234 }
235 fn applicable_mounts(&self) -> MountTypeFlags {
236 MountTypeFlags::EQUATORIAL
237 }
238}
239
240impl Term for HCEC {
241 fn name(&self) -> &str {
242 "HCEC"
243 }
244 fn description(&self) -> &str {
245 "HA centering error (cos)"
246 }
247 fn jacobian_equatorial(&self, h: f64, _dec: f64, _lat: f64, _pier: f64) -> (f64, f64) {
248 (libm::cos(h), 0.0)
249 }
250 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
251 (0.0, 0.0)
252 }
253 fn applicable_mounts(&self) -> MountTypeFlags {
254 MountTypeFlags::EQUATORIAL
255 }
256}
257
258impl Term for DCES {
259 fn name(&self) -> &str {
260 "DCES"
261 }
262 fn description(&self) -> &str {
263 "Dec centering error (sin)"
264 }
265 fn jacobian_equatorial(&self, _h: f64, dec: f64, _lat: f64, _pier: f64) -> (f64, f64) {
266 (0.0, libm::sin(dec))
267 }
268 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
269 (0.0, 0.0)
270 }
271 fn applicable_mounts(&self) -> MountTypeFlags {
272 MountTypeFlags::EQUATORIAL
273 }
274}
275
276impl Term for DCEC {
277 fn name(&self) -> &str {
278 "DCEC"
279 }
280 fn description(&self) -> &str {
281 "Dec centering error (cos)"
282 }
283 fn jacobian_equatorial(&self, _h: f64, dec: f64, _lat: f64, _pier: f64) -> (f64, f64) {
284 (0.0, libm::cos(dec))
285 }
286 fn jacobian_altaz(&self, _az: f64, _el: f64, _lat: f64) -> (f64, f64) {
287 (0.0, 0.0)
288 }
289 fn applicable_mounts(&self) -> MountTypeFlags {
290 MountTypeFlags::EQUATORIAL
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297 use std::f64::consts::{FRAC_PI_2, FRAC_PI_4, PI};
298
299 #[test]
300 fn ih_jacobian() {
301 let (dh, dd) = IH.jacobian_equatorial(0.0, 0.0, 0.0, 1.0);
302 assert_eq!(dh, -1.0);
303 assert_eq!(dd, 0.0);
304 }
305
306 #[test]
307 fn id_pier_east() {
308 let (dh, dd) = ID.jacobian_equatorial(0.0, 0.0, 0.0, 1.0);
309 assert_eq!(dh, 0.0);
310 assert_eq!(dd, -1.0);
311 }
312
313 #[test]
314 fn id_pier_west() {
315 let (dh, dd) = ID.jacobian_equatorial(0.0, 0.0, 0.0, -1.0);
316 assert_eq!(dh, 0.0);
317 assert_eq!(dd, 1.0);
318 }
319
320 #[test]
321 fn ch_at_zero_dec_east() {
322 let (dh, dd) = CH.jacobian_equatorial(0.0, 0.0, 0.0, 1.0);
323 assert_eq!(dh, -1.0);
324 assert_eq!(dd, 0.0);
325 }
326
327 #[test]
328 fn ch_at_zero_dec_west() {
329 let (dh, dd) = CH.jacobian_equatorial(0.0, 0.0, 0.0, -1.0);
330 assert_eq!(dh, 1.0);
331 assert_eq!(dd, 0.0);
332 }
333
334 #[test]
335 fn np_at_zero_dec() {
336 let (dh, dd) = NP.jacobian_equatorial(0.0, 0.0, 0.0, 1.0);
337 assert_eq!(dh, 0.0);
338 assert_eq!(dd, 0.0);
339 }
340
341 #[test]
342 fn np_at_45_dec_east() {
343 let dec = FRAC_PI_4;
344 let (dh, dd) = NP.jacobian_equatorial(0.0, dec, 0.0, 1.0);
345 assert_eq!(dh, -dec.tan());
346 assert_eq!(dd, 0.0);
347 }
348
349 #[test]
350 fn ma_at_zero_ha() {
351 let dec = FRAC_PI_4;
352 let (dh, dd) = MA.jacobian_equatorial(0.0, dec, 0.0, 1.0);
353 assert_eq!(dh, dec.tan());
354 assert_eq!(dd, 0.0);
355 }
356
357 #[test]
358 fn ma_at_6h_ha() {
359 let h = FRAC_PI_2;
360 let dec = FRAC_PI_4;
361 let (dh, dd) = MA.jacobian_equatorial(h, dec, 0.0, 1.0);
362 assert_eq!(dh, libm::cos(h) * dec.tan());
363 assert_eq!(dd, -libm::sin(h));
364 }
365
366 #[test]
367 fn me_at_zero_ha() {
368 let dec = FRAC_PI_4;
369 let (dh, dd) = ME.jacobian_equatorial(0.0, dec, 0.0, 1.0);
370 assert_eq!(dh, 0.0);
371 assert_eq!(dd, -1.0);
372 }
373
374 #[test]
375 fn hces_at_pi_over_2() {
376 let (dh, dd) = HCES.jacobian_equatorial(FRAC_PI_2, 0.0, 0.0, 1.0);
377 assert_eq!(dh, 1.0);
378 assert_eq!(dd, 0.0);
379 }
380
381 #[test]
382 fn hcec_at_zero() {
383 let (dh, dd) = HCEC.jacobian_equatorial(0.0, 0.0, 0.0, 1.0);
384 assert_eq!(dh, 1.0);
385 assert_eq!(dd, 0.0);
386 }
387
388 #[test]
389 fn dces_at_pi_over_2() {
390 let (dh, dd) = DCES.jacobian_equatorial(0.0, FRAC_PI_2, 0.0, 1.0);
391 assert_eq!(dh, 0.0);
392 assert_eq!(dd, 1.0);
393 }
394
395 #[test]
396 fn dcec_at_zero() {
397 let (dh, dd) = DCEC.jacobian_equatorial(0.0, 0.0, 0.0, 1.0);
398 assert_eq!(dh, 0.0);
399 assert_eq!(dd, 1.0);
400 }
401
402 #[test]
403 fn fo_at_zero_ha() {
404 let (dh, dd) = FO.jacobian_equatorial(0.0, 0.0, 0.0, 1.0);
405 assert_eq!(dh, 0.0);
406 assert_eq!(dd, 1.0);
407 }
408
409 #[test]
410 fn fo_at_pi() {
411 let (dh, dd) = FO.jacobian_equatorial(PI, 0.0, 0.0, 1.0);
412 assert_eq!(dh, 0.0);
413 assert_eq!(dd, -1.0);
414 }
415
416 #[test]
417 fn equatorial_terms_return_zero_for_altaz() {
418 let terms: Vec<Box<dyn Term>> = vec![
419 Box::new(IH),
420 Box::new(ID),
421 Box::new(CH),
422 Box::new(NP),
423 Box::new(MA),
424 Box::new(ME),
425 Box::new(TF),
426 Box::new(TX),
427 Box::new(DAF),
428 Box::new(FO),
429 Box::new(HCES),
430 Box::new(HCEC),
431 Box::new(DCES),
432 Box::new(DCEC),
433 ];
434 for term in &terms {
435 let (da, de) = term.jacobian_altaz(1.0, 0.5, 0.7);
436 assert_eq!(
437 (da, de),
438 (0.0, 0.0),
439 "term {} should return (0,0) for altaz",
440 term.name()
441 );
442 }
443 }
444
445 #[test]
446 fn pier_sensitivity_flags() {
447 assert!(!IH.pier_sensitive());
448 assert!(ID.pier_sensitive());
449 assert!(CH.pier_sensitive());
450 assert!(NP.pier_sensitive());
451 assert!(!MA.pier_sensitive());
452 assert!(!ME.pier_sensitive());
453 assert!(!TF.pier_sensitive());
454 }
455}