erfa_rust/
G13_safe.rs

1// G13
2//   epv00.c → eraEpv00_safe
3
4use crate::H1_safe::{ERFA_DJ00, ERFA_DJY};
5
6#[path = "data/G13_safe/E0Z.rs"]
7mod e0z_mod;
8use e0z_mod::E0Z;
9#[path = "data/G13_safe/E0X.rs"]
10mod e0x_mod;
11use e0x_mod::E0X;
12#[path = "data/G13_safe/E0Y.rs"]
13mod e0y_mod;
14use e0y_mod::E0Y;
15#[path = "data/G13_safe/E1X.rs"]
16mod e1x_mod;
17use e1x_mod::E1X;
18#[path = "data/G13_safe/E1Y.rs"]
19mod e1y_mod;
20use e1y_mod::E1Y;
21#[path = "data/G13_safe/E1Z.rs"]
22mod e1z_mod;
23use e1z_mod::E1Z;
24#[path = "data/G13_safe/S0X.rs"]
25mod s0x_mod;
26use s0x_mod::S0X;
27#[path = "data/G13_safe/S0Y.rs"]
28mod s0y_mod;
29use s0y_mod::S0Y;
30#[path = "data/G13_safe/S0Z.rs"]
31mod s0z_mod;
32use s0z_mod::S0Z;
33#[path = "data/G13_safe/S1X.rs"]
34mod s1x_mod;
35use s1x_mod::S1X;
36#[path = "data/G13_safe/S1Y.rs"]
37mod s1y_mod;
38use s1y_mod::S1Y;
39#[path = "data/G13_safe/S2Y.rs"]
40mod s2y_mod;
41use s2y_mod::S2Y;
42#[path = "data/G13_safe/S2X.rs"]
43mod s2x_mod;
44use s2x_mod::S2X;
45#[path = "data/G13_safe/S1Z.rs"]
46mod s1z_mod;
47use s1z_mod::S1Z;
48
49pub type ErfaResult<T> = Result<T, ()>;
50
51// Orientation matrix aligning simplified VSOP2000 to DE405.
52const AM12: f64 = 0.000000211284;
53const AM13: f64 = -0.000000091603;
54const AM21: f64 = -0.000000230286;
55const AM22: f64 = 0.917482137087;
56const AM23: f64 = -0.397776982902;
57const AM32: f64 = 0.397776982902;
58const AM33: f64 = 0.917482137087;
59
60// Exact constants as in the original source (keep scientific notation).
61static E2X: &[f64] = &[
62    -0.4143818297913e-10,
63    0.0000000000000e+00,
64    0.0000000000000e+00,
65    0.2171497694435e-10,
66    0.4398225628264e+01,
67    0.1256615170089e+02,
68    0.9845398442516e-11,
69    0.2079720838384e+00,
70    0.6283075850446e+01,
71    0.9256833552682e-12,
72    0.4191264694361e+01,
73    0.1884922755134e+02,
74    0.1022049384115e-12,
75    0.5381133195658e+01,
76    0.8399684731857e+02,
77];
78static E2Y: &[f64] = &[
79    0.5063375872532e-10,
80    0.0000000000000e+00,
81    0.0000000000000e+00,
82    0.2173815785980e-10,
83    0.2827805833053e+01,
84    0.1256615170089e+02,
85    0.1010231999920e-10,
86    0.4634612377133e+01,
87    0.6283075850446e+01,
88    0.9259745317636e-12,
89    0.2620612076189e+01,
90    0.1884922755134e+02,
91    0.1022202095812e-12,
92    0.3809562326066e+01,
93    0.8399684731857e+02,
94];
95static E2Z: &[f64] = &[
96    0.9722666114891e-10,
97    0.5152219582658e+01,
98    0.6283075850446e+01,
99    -0.3494819171909e-11,
100    0.0000000000000e+00,
101    0.0000000000000e+00,
102    0.6713034376076e-12,
103    0.6440188750495e+00,
104    0.1256615170089e+02,
105];
106
107static S2Z: &[f64] = &[
108    0.3749920358054e-12,
109    0.3230285558668e+01,
110    0.2132990797783e+00,
111    0.2735037220939e-12,
112    0.6154322683046e+01,
113    0.5296909721118e+00,
114];
115
116const CE0: [&[f64]; 3] = [E0X, E0Y, E0Z];
117const CE1: [&[f64]; 3] = [E1X, E1Y, E1Z];
118const CE2: [&[f64]; 3] = [E2X, E2Y, E2Z];
119const CS0: [&[f64]; 3] = [S0X, S0Y, S0Z];
120const CS1: [&[f64]; 3] = [S1X, S1Y, S1Z];
121const CS2: [&[f64]; 3] = [S2X, S2Y, S2Z];
122
123// Earth heliocentric and barycentric position-velocity (au, au/day).
124// Returns (pvh, pvb, jstat) with jstat=0 OK, +1 if |t|>100y (warning).
125pub fn eraEpv00_safe(date1: f64, date2: f64) -> ErfaResult<([[f64; 3]; 2], [[f64; 3]; 2], i32)> {
126    // Time since J2000.0 in Julian years
127    let t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJY;
128
129    // Warn if |t| > 100 years
130    let jstat = if t.abs() <= 100.0 { 0 } else { 1 };
131
132    // Work vectors
133    let mut ph = [0.0_f64; 3];
134    let mut vh = [0.0_f64; 3];
135    let mut pb = [0.0_f64; 3];
136    let mut vb = [0.0_f64; 3];
137
138    // Loop over X,Y,Z
139    for i in 0..3 {
140        let (mut xyz, mut xyzd) = (0.0_f64, 0.0_f64);
141
142        // Sun-to-Earth
143        accumulate_component(&mut xyz, &mut xyzd, CE0[i], t, 0);
144        accumulate_component(&mut xyz, &mut xyzd, CE1[i], t, 1);
145        accumulate_component(&mut xyz, &mut xyzd, CE2[i], t, 2);
146
147        ph[i] = xyz;
148        vh[i] = xyzd / ERFA_DJY;
149
150        // SSB-to-Sun (accumulate without reset → SSB-to-Earth)
151        accumulate_component(&mut xyz, &mut xyzd, CS0[i], t, 0);
152        accumulate_component(&mut xyz, &mut xyzd, CS1[i], t, 1);
153        accumulate_component(&mut xyz, &mut xyzd, CS2[i], t, 2);
154
155        pb[i] = xyz;
156        vb[i] = xyzd / ERFA_DJY;
157    }
158
159    // Rotate ecliptic vectors to BCRS
160    let mut pvh = [[0.0_f64; 3]; 2];
161    let mut pvb = [[0.0_f64; 3]; 2];
162    rotate_xyz(&ph, &mut pvh[0]);
163    rotate_xyz(&vh, &mut pvh[1]);
164    rotate_xyz(&pb, &mut pvb[0]);
165    rotate_xyz(&vb, &mut pvb[1]);
166
167    Ok((pvh, pvb, jstat))
168}
169
170// Accumulate a single component for given power of t (0,1,2), summing in reverse order.
171#[inline]
172fn accumulate_component(xyz: &mut f64, xyzd: &mut f64, coeffs: &[f64], t: f64, power: u8) {
173    let nterms = coeffs.len() / 3;
174    for k in (0..nterms).rev() {
175        let a = coeffs[3 * k];
176        let b = coeffs[3 * k + 1];
177        let c = coeffs[3 * k + 2];
178        let ct = c * t;
179        let p = b + ct;
180        let (sp, cp) = p.sin_cos();
181        match power {
182            0 => {
183                *xyz += a * cp;
184                *xyzd -= a * c * sp;
185            }
186            1 => {
187                *xyz += a * t * cp;
188                *xyzd += a * (cp - ct * sp);
189            }
190            2 => {
191                *xyz += a * t * t * cp;
192                *xyzd += a * t * (2.0 * cp - ct * sp);
193            }
194            _ => unreachable!(),
195        }
196    }
197}
198
199// Apply the small rotation to align to the BCRS frame.
200#[inline]
201fn rotate_xyz(src: &[f64; 3], dst: &mut [f64; 3]) {
202    let (x, y, z) = (src[0], src[1], src[2]);
203    dst[0] = x + AM12 * y + AM13 * z;
204    dst[1] = AM21 * x + AM22 * y + AM23 * z;
205    dst[2] = AM32 * y + AM33 * z;
206}