erfa_rust/
G1_safe.rs

1// G1
2//   a2af.c       → eraA2af_safe
3//   a2tf.c       → eraA2tf_safe
4//   ab.c         → eraAb_safe
5//   ae2hd.c      → eraAe2hd_safe
6//   af2a.c       → eraAf2a_safe
7//   anp.c        → eraAnp_safe
8//   anpm.c       → eraAnpm_safe
9//   apcg.c       → eraApcg_safe
10//   apcg13.c     → eraApcg13_safe
11//   apci.c       → eraApci_safe
12//   apci13.c     → eraApci13_safe
13
14
15use crate::G11_safe::eraEors_safe;
16use crate::G13_safe::eraEpv00_safe;
17use crate::G24_safe::eraPdp_safe;
18use crate::G26_safe::eraPnm06a_safe;
19use crate::G2_safe::eraApcs_safe;
20use crate::G30_safe::eraS06_safe;
21use crate::G6_safe::eraBpn2xy_safe;
22use crate::G7_safe::eraC2ixys_safe;
23use crate::G9_safe::eraD2tf_safe;
24use crate::H1_safe::{eraASTROM, ERFA_D2PI, ERFA_DAS2R, ERFA_DPI, ERFA_SRS};
25
26pub type ErfaResult<T> = Result<T, ()>;
27
28//----------------------------------------------------------------------
29//  G1/a2af.c
30//----------------------------------------------------------------------
31
32// Convert radians to sign and DMS fields with C-compatible rounding.
33pub fn eraA2af_safe(ndp: i32, angle: f64) -> ErfaResult<(char, [i32; 4])> {
34    // Hours to degrees × radians to turns factor as in ERFA.
35    const F: f64 = 15.0 / ERFA_D2PI;
36    let (sign, idmsf) = eraD2tf_safe(ndp, angle * F)?;
37    Ok((sign, idmsf))
38}
39
40//----------------------------------------------------------------------
41//  G1/a2tf.c
42//----------------------------------------------------------------------
43
44// Convert radians to sign and HMS fields with C-compatible rounding.
45pub fn eraA2tf_safe(ndp: i32, angle: f64) -> ErfaResult<(char, [i32; 4])> {
46    let (sign, ihmsf) = eraD2tf_safe(ndp, angle / ERFA_D2PI)?;
47    Ok((sign, ihmsf))
48}
49
50//----------------------------------------------------------------------
51//  G1/ab.c
52//----------------------------------------------------------------------
53
54// Stellar aberration: return unit vector after aberration correction.
55pub fn eraAb_safe(pnat: &[f64; 3], v: &[f64; 3], s: f64, bm1: f64) -> ErfaResult<[f64; 3]> {
56    // Dot product of pnat and v.
57    let pdv = eraPdp_safe(pnat, v)?;
58
59    // Terms per ERFA ab.c
60    let w1 = 1.0 + pdv / (1.0 + bm1);
61    let w2 = ERFA_SRS / s;
62
63    let mut r2 = 0.0;
64    let mut p = [0.0_f64; 3];
65    for i in 0..3 {
66        let pnat_i = pnat[i];
67        let v_i = v[i];
68        let w = pnat_i * bm1 + w1 * v_i + w2 * (v_i - pdv * pnat_i);
69        p[i] = w;
70        r2 += w * w;
71    }
72
73    let r = r2.sqrt();
74    let mut ppr = [0.0_f64; 3];
75    for i in 0..3 {
76        ppr[i] = p[i] / r;
77    }
78    Ok(ppr)
79}
80
81//----------------------------------------------------------------------
82//  G1/ae2hd.c
83//----------------------------------------------------------------------
84
85// Convert azimuth/elevation to hour angle/declination (radians).
86pub fn eraAe2hd_safe(az: f64, el: f64, phi: f64) -> ErfaResult<(f64, f64)> {
87    let (sa, ca) = az.sin_cos();
88    let (se, ce) = el.sin_cos();
89    let (sp, cp) = phi.sin_cos();
90
91    // HA,Dec unit vector.
92    let x = -ca * ce * sp + se * cp;
93    let y = -sa * ce;
94    let z = ca * ce * cp + se * sp;
95
96    // To spherical.
97    let r = (x * x + y * y).sqrt();
98    let ha = if r != 0.0 { y.atan2(x) } else { 0.0 };
99    let dec = z.atan2(r);
100
101    Ok((ha, dec))
102}
103
104//----------------------------------------------------------------------
105//  G1/af2a.c
106//----------------------------------------------------------------------
107
108// Convert sign and DMS fields to radians; return status code per ERFA.
109pub fn eraAf2a_safe(s: char, ideg: i32, iamin: i32, asec: f64) -> ErfaResult<(f64, i32)> {
110    // Magnitude.
111    let mag =
112        (60.0 * (60.0 * (ideg.abs() as f64) + (iamin.abs() as f64)) + asec.abs()) * ERFA_DAS2R;
113    let rad = if s == '-' { -mag } else { mag };
114
115    // Range checks.
116    let status = if ideg < 0 || ideg > 359 {
117        1
118    } else if iamin < 0 || iamin > 59 {
119        2
120    } else if asec < 0.0 || asec >= 60.0 {
121        3
122    } else {
123        0
124    };
125
126    Ok((rad, status))
127}
128
129//----------------------------------------------------------------------
130//  G1/anp.c
131//----------------------------------------------------------------------
132
133// Normalize angle into [0, 2π).
134pub fn eraAnp_safe(a: f64) -> ErfaResult<f64> {
135    let mut w = a % ERFA_D2PI;
136    if w < 0.0 {
137        w += ERFA_D2PI;
138    }
139    Ok(w)
140}
141
142//----------------------------------------------------------------------
143//  G1/anpm.c
144//----------------------------------------------------------------------
145
146// Normalize angle into (-π, +π].
147pub fn eraAnpm_safe(a: f64) -> ErfaResult<f64> {
148    let mut w = a % ERFA_D2PI;
149    if w.abs() >= ERFA_DPI {
150        // Subtract 2π with the sign of a (DSIGN equivalent).
151        w -= ERFA_D2PI.copysign(a);
152    }
153    Ok(w)
154}
155
156//----------------------------------------------------------------------
157//  G1/apcg.c
158//----------------------------------------------------------------------
159
160// Fill astrom for a geocentric observer (PV at origin).
161pub fn eraApcg_safe(
162    date1: f64,
163    date2: f64,
164    ebpv: &[[f64; 3]; 2],
165    ehp: &[f64; 3],
166    astrom: &mut eraASTROM,
167) -> ErfaResult<()> {
168    // Geocenter PV = 0.
169    let pv = [[0.0_f64; 3]; 2];
170    eraApcs_safe(date1, date2, &pv, ebpv, ehp, astrom)?;
171    Ok(())
172}
173
174//----------------------------------------------------------------------
175//  G1/apcg13.c
176//----------------------------------------------------------------------
177
178// Fill astrom using IAU 2006/2000A star-independent parameters.
179pub fn eraApcg13_safe(date1: f64, date2: f64, astrom: &mut eraASTROM) -> ErfaResult<()> {
180    // Earth barycentric/heliocentric PV (au, au/day).
181    let (ehpv, ebpv, _jstat) = eraEpv00_safe(date1, date2)?;
182    // Use heliocentric position as 'ehp' input.
183    eraApcg_safe(date1, date2, &ebpv, &ehpv[0], astrom)
184}
185
186//----------------------------------------------------------------------
187//  G1/apci.c
188//----------------------------------------------------------------------
189
190// CIO-based astrometry parameters with supplied X,Y,s; updates astrom.bpn.
191pub fn eraApci_safe(
192    date1: f64,
193    date2: f64,
194    ebpv: &[[f64; 3]; 2],
195    ehp: &[f64; 3],
196    x: f64,
197    y: f64,
198    s: f64,
199    astrom: &mut eraASTROM,
200) -> ErfaResult<()> {
201    // Geocenter parameters.
202    eraApcg_safe(date1, date2, ebpv, ehp, astrom)?;
203
204    // Build CIO-based BPN matrix and assign.
205    let rc2i = eraC2ixys_safe(x, y, s)?;
206    astrom.bpn = rc2i;
207    Ok(())
208}
209
210//----------------------------------------------------------------------
211//  G1/apci13.c
212//----------------------------------------------------------------------
213
214// Compute astrometry parameters and return equation of origins (eo).
215pub fn eraApci13_safe(date1: f64, date2: f64, astrom: &mut eraASTROM) -> ErfaResult<f64> {
216    // Earth PV (au, au/day).
217    let (ehpv, ebpv, _jstat) = eraEpv00_safe(date1, date2)?;
218
219    // NPB matrix, IAU 2006/2000A.
220    let r = eraPnm06a_safe(date1, date2)?;
221
222    // CIP X,Y from NPB.
223    let (x, y) = eraBpn2xy_safe(&r)?;
224
225    // CIO locator s.
226    let s = eraS06_safe(date1, date2, x, y)?;
227
228    // Astrometry parameters.
229    eraApci_safe(date1, date2, &ebpv, &ehpv[0], x, y, s, astrom)?;
230
231    // Equation of origins.
232    let eo = eraEors_safe(&r, s)?;
233    Ok(eo)
234}