Skip to main content

celestial_pointing/terms/
equatorial.rs

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}