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}