erfa_rust/
G32_safe.rs

1// G32
2//   taitt.c   → eraTaitt_safe
3//   taiut1.c  → eraTaiut1_safe
4//   taiutc.c  → eraTaiutc_safe
5//   tcbtdb.c  → eraTcbtdb_safe
6//   tcgtt.c   → eraTcgtt_safe
7//   tdbtcb.c  → eraTdbtcb_safe
8//   tdbtt.c   → eraTdbtt_safe
9//   tf2a.c    → eraTf2a_safe
10//   tf2d.c    → eraTf2d_safe
11//   tpors.c   → eraTpors_safe
12//   tporv.c   → eraTporv_safe
13//   tpsts.c   → eraTpsts_safe
14//   tpstv.c   → eraTpstv_safe
15//   tpxes.c   → eraTpxes_safe
16//   tpxev.c   → eraTpxev_safe
17
18use crate::G1_safe::eraAnp_safe;
19use crate::G33_safe::eraUtctai_safe;
20use crate::H1_safe::{
21    ERFA_DAYSEC, ERFA_DJM0, ERFA_DJM77, ERFA_DS2R, ERFA_ELB, ERFA_ELG, ERFA_TDB0, ERFA_TTMTAI,
22};
23
24pub type ErfaResult<T> = Result<T, ()>;
25
26// TAI → TT (two-part JD)
27pub fn eraTaitt_safe(tai1: f64, tai2: f64) -> ErfaResult<((f64, f64), i32)> {
28    const DTAT: f64 = ERFA_TTMTAI / ERFA_DAYSEC;
29    let (tt1, tt2) = if tai1.abs() > tai2.abs() {
30        (tai1, tai2 + DTAT)
31    } else {
32        (tai1 + DTAT, tai2)
33    };
34    Ok(((tt1, tt2), 0))
35}
36
37// TAI → UT1 given dTA = UT1−TAI (s)
38pub fn eraTaiut1_safe(tai1: f64, tai2: f64, dta: f64) -> ErfaResult<((f64, f64), i32)> {
39    let dtad = dta / ERFA_DAYSEC;
40    let (ut11, ut12) = if tai1.abs() > tai2.abs() {
41        (tai1, tai2 + dtad)
42    } else {
43        (tai1 + dtad, tai2)
44    };
45    Ok(((ut11, ut12), 0))
46}
47
48// TAI → UTC using iteration with UTC→TAI
49pub fn eraTaiutc_safe(tai1: f64, tai2: f64) -> ErfaResult<((f64, f64), i32)> {
50    let big1 = tai1.abs() >= tai2.abs();
51    let (a1, a2) = if big1 { (tai1, tai2) } else { (tai2, tai1) };
52
53    let (u1, mut u2) = (a1, a2);
54    let mut j: i32 = 0;
55
56    for _ in 0..3 {
57        let ((g1, g2), jj) = eraUtctai_safe(u1, u2)?;
58        j = jj;
59        if j < 0 {
60            let (utc1, utc2) = if big1 { (u1, u2) } else { (u2, u1) };
61            return Ok(((utc1, utc2), j));
62        }
63        u2 += a1 - g1;
64        u2 += a2 - g2;
65    }
66
67    let (utc1, utc2) = if big1 { (u1, u2) } else { (u2, u1) };
68    Ok(((utc1, utc2), j))
69}
70
71// TCB → TDB (two-part JD)
72pub fn eraTcbtdb_safe(tcb1: f64, tcb2: f64) -> ErfaResult<((f64, f64), i32)> {
73    const T77TD: f64 = ERFA_DJM0 + ERFA_DJM77;
74    const T77TF: f64 = ERFA_TTMTAI / ERFA_DAYSEC;
75    const TDB0: f64 = ERFA_TDB0 / ERFA_DAYSEC;
76
77    let (tdb1, tdb2) = if tcb1.abs() > tcb2.abs() {
78        let d = tcb1 - T77TD;
79        (tcb1, tcb2 + TDB0 - (d + (tcb2 - T77TF)) * ERFA_ELB)
80    } else {
81        let d = tcb2 - T77TD;
82        (tcb1 + TDB0 - (d + (tcb1 - T77TF)) * ERFA_ELB, tcb2)
83    };
84    Ok(((tdb1, tdb2), 0))
85}
86
87// TCG → TT (two-part JD)
88pub fn eraTcgtt_safe(tcg1: f64, tcg2: f64) -> ErfaResult<((f64, f64), i32)> {
89    const T77T: f64 = ERFA_DJM77 + ERFA_TTMTAI / ERFA_DAYSEC;
90    let (tt1, tt2) = if tcg1.abs() > tcg2.abs() {
91        (tcg1, tcg2 - ((tcg1 - ERFA_DJM0) + (tcg2 - T77T)) * ERFA_ELG)
92    } else {
93        (tcg1 - ((tcg2 - ERFA_DJM0) + (tcg1 - T77T)) * ERFA_ELG, tcg2)
94    };
95    Ok(((tt1, tt2), 0))
96}
97
98// TDB → TCB (two-part JD)
99pub fn eraTdbtcb_safe(tdb1: f64, tdb2: f64) -> ErfaResult<((f64, f64), i32)> {
100    const T77TD: f64 = ERFA_DJM0 + ERFA_DJM77;
101    const T77TF: f64 = ERFA_TTMTAI / ERFA_DAYSEC;
102    const TDB0: f64 = ERFA_TDB0 / ERFA_DAYSEC;
103    const ELBB: f64 = ERFA_ELB / (1.0 - ERFA_ELB);
104
105    let (tcb1, tcb2) = if tdb1.abs() > tdb2.abs() {
106        let d = T77TD - tdb1;
107        let f = tdb2 - TDB0;
108        (tdb1, f - (d - (f - T77TF)) * ELBB)
109    } else {
110        let d = T77TD - tdb2;
111        let f = tdb1 - TDB0;
112        (f - (d - (f - T77TF)) * ELBB, tdb2)
113    };
114    Ok(((tcb1, tcb2), 0))
115}
116
117// TDB → TT using supplied ΔT_R = TDB−TT (s)
118pub fn eraTdbtt_safe(tdb1: f64, tdb2: f64, dtr: f64) -> ErfaResult<((f64, f64), i32)> {
119    let dtrd = dtr / ERFA_DAYSEC;
120    let (tt1, tt2) = if tdb1.abs() > tdb2.abs() {
121        (tdb1, tdb2 - dtrd)
122    } else {
123        (tdb1 - dtrd, tdb2)
124    };
125    Ok(((tt1, tt2), 0))
126}
127
128// HMS → radians; j=0 OK, 1 bad hour, 2 bad minute, 3 bad second
129pub fn eraTf2a_safe(s: char, ihour: i32, imin: i32, sec: f64) -> ErfaResult<(f64, i32)> {
130    let sign = if s == '-' { -1.0 } else { 1.0 };
131    let rad =
132        sign * (60.0 * (60.0 * (ihour.abs() as f64) + (imin.abs() as f64)) + sec.abs()) * ERFA_DS2R;
133    if ihour < 0 || ihour > 23 {
134        return Ok((rad, 1));
135    }
136    if imin < 0 || imin > 59 {
137        return Ok((rad, 2));
138    }
139    if sec < 0.0 || sec >= 60.0 {
140        return Ok((rad, 3));
141    }
142    Ok((rad, 0))
143}
144
145// HMS → days; j=0 OK, 1 bad hour, 2 bad minute, 3 bad second
146pub fn eraTf2d_safe(s: char, ihour: i32, imin: i32, sec: f64) -> ErfaResult<(f64, i32)> {
147    let sign = if s == '-' { -1.0 } else { 1.0 };
148    let days = sign * (60.0 * (60.0 * (ihour.abs() as f64) + (imin.abs() as f64)) + sec.abs())
149        / ERFA_DAYSEC;
150    if ihour < 0 || ihour > 23 {
151        return Ok((days, 1));
152    }
153    if imin < 0 || imin > 59 {
154        return Ok((days, 2));
155    }
156    if sec < 0.0 || sec >= 60.0 {
157        return Ok((days, 3));
158    }
159    Ok((days, 0))
160}
161
162// Tangent-point from star (spherical); returns two solutions and status
163pub fn eraTpors_safe(
164    xi: f64,
165    eta: f64,
166    a: f64,
167    b: f64,
168) -> ErfaResult<((f64, f64), (f64, f64), i32)> {
169    let xi2 = xi * xi;
170    let r = (1.0 + xi2 + eta * eta).sqrt();
171    let sb = b.sin();
172    let cb = b.cos();
173    let rsb = r * sb;
174    let rcb = r * cb;
175    let w2 = rcb * rcb - xi2;
176
177    if w2 >= 0.0 {
178        let mut w = w2.sqrt();
179        let mut s = rsb - eta * w;
180        let mut c = rsb * eta + w;
181        if xi == 0.0 && w == 0.0 {
182            w = 1.0;
183        }
184        let a01 = eraAnp_safe(a - xi.atan2(w))?;
185        let b01 = s.atan2(c);
186
187        let w2n = -w;
188        s = rsb - eta * w2n;
189        c = rsb * eta + w2n;
190        let a02 = eraAnp_safe(a - xi.atan2(w2n))?;
191        let b02 = s.atan2(c);
192
193        let j = if rsb.abs() < 1.0 { 1 } else { 2 };
194        Ok(((a01, b01), (a02, b02), j))
195    } else {
196        Ok(((0.0, 0.0), (0.0, 0.0), 0))
197    }
198}
199
200// Tangent-point from star (vector); returns two unit vectors and status
201pub fn eraTporv_safe(xi: f64, eta: f64, v: &[f64; 3]) -> ErfaResult<([f64; 3], [f64; 3], i32)> {
202    let x = v[0];
203    let y = v[1];
204    let z = v[2];
205
206    let rxy2 = x * x + y * y;
207    let xi2 = xi * xi;
208    let eta2p1 = eta * eta + 1.0;
209    let r = (xi2 + eta2p1).sqrt();
210    let rsb = r * z;
211    let rcb = r * rxy2.sqrt();
212    let w2 = rcb * rcb - xi2;
213
214    if w2 > 0.0 {
215        let mut w = w2.sqrt();
216        let mut c = (rsb * eta + w) / (eta2p1 * ((rxy2 * (w2 + xi2)).sqrt()));
217        let v01 = [
218            c * (x * w + y * xi),
219            c * (y * w - x * xi),
220            (rsb - eta * w) / eta2p1,
221        ];
222
223        w = -w;
224        c = (rsb * eta + w) / (eta2p1 * ((rxy2 * (w2 + xi2)).sqrt()));
225        let v02 = [
226            c * (x * w + y * xi),
227            c * (y * w - x * xi),
228            (rsb - eta * w) / eta2p1,
229        ];
230
231        let j = if rsb.abs() < 1.0 { 1 } else { 2 };
232        Ok((v01, v02, j))
233    } else {
234        Ok(([0.0; 3], [0.0; 3], 0))
235    }
236}
237
238// Star coords from tangent-plane coords and tangent point (spherical)
239pub fn eraTpsts_safe(xi: f64, eta: f64, a0: f64, b0: f64) -> ErfaResult<(f64, f64)> {
240    let sb0 = b0.sin();
241    let cb0 = b0.cos();
242    let d = cb0 - eta * sb0;
243    let a = eraAnp_safe(xi.atan2(d) + a0)?;
244    let b = (sb0 + eta * cb0).atan2((xi * xi + d * d).sqrt());
245    Ok((a, b))
246}
247
248// Star vector from tangent-plane coords and tangent-point vector
249pub fn eraTpstv_safe(xi: f64, eta: f64, v0: &[f64; 3]) -> ErfaResult<[f64; 3]> {
250    let mut x = v0[0];
251    let y = v0[1];
252    let z = v0[2];
253
254    let mut r = (x * x + y * y).sqrt();
255    if r == 0.0 {
256        r = 1e-20;
257        x = r;
258    }
259    let f = (1.0 + xi * xi + eta * eta).sqrt();
260
261    let vx = (x - (xi * y + eta * x * z) / r) / f;
262    let vy = (y + (xi * x - eta * y * z) / r) / f;
263    let vz = (z + eta * r) / f;
264
265    Ok([vx, vy, vz])
266}
267
268// Solve for (xi,eta) given two sets of spherical coordinates
269pub fn eraTpxes_safe(a: f64, b: f64, a0: f64, b0: f64) -> ErfaResult<((f64, f64), i32)> {
270    const TINY: f64 = 1e-6;
271
272    let sb0 = b0.sin();
273    let sb = b.sin();
274    let cb0 = b0.cos();
275    let cb = b.cos();
276    let da = a - a0;
277    let sda = da.sin();
278    let cda = da.cos();
279
280    let mut d = sb * sb0 + cb * cb0 * cda;
281
282    let j = if d > TINY {
283        0
284    } else if d >= 0.0 {
285        d = TINY;
286        1
287    } else if d > -TINY {
288        d = -TINY;
289        2
290    } else {
291        3
292    };
293
294    let xi = cb * sda / d;
295    let eta = (sb * cb0 - cb * sb0 * cda) / d;
296    Ok(((xi, eta), j))
297}
298
299// Solve for (xi,eta) given two direction-cosine vectors
300pub fn eraTpxev_safe(v: &[f64; 3], v0: &[f64; 3]) -> ErfaResult<((f64, f64), i32)> {
301    const TINY: f64 = 1e-6;
302
303    let x = v[0];
304    let y = v[1];
305    let z = v[2];
306    let mut x0 = v0[0];
307    let y0 = v0[1];
308    let z0 = v0[2];
309
310    let r2 = x0 * x0 + y0 * y0;
311    let mut r = r2.sqrt();
312    if r == 0.0 {
313        r = 1e-20;
314        x0 = r;
315    }
316
317    let w = x * x0 + y * y0;
318    let mut d = w + z * z0;
319
320    let j = if d > TINY {
321        0
322    } else if d >= 0.0 {
323        d = TINY;
324        1
325    } else if d > -TINY {
326        d = -TINY;
327        2
328    } else {
329        3
330    };
331
332    d *= r;
333    let xi = (y * x0 - x * y0) / d;
334    let eta = (z * r2 - z0 * w) / d;
335    Ok(((xi, eta), j))
336}