simple_elpmpp02/
lib.rs

1#![feature(portable_simd)]
2
3use core::f64;
4use std::simd::{f64x4, num::SimdFloat};
5
6mod coeff;
7
8const RAD: f64 = 648000.0 / f64::consts::PI; 
9const J2000: f64 = 2451545.0;
10
11
12fn u64_as_f64_slice(data: &[u64]) -> &[f64] {
13    unsafe {
14        std::slice::from_raw_parts(
15            data.as_ptr() as *const f64,
16            data.len(),
17        )
18    }
19}
20
21fn accumulate_main(data_u64: &[u64], tj: f64, tol: f64) -> (f64, f64) {
22    let data = u64_as_f64_slice(data_u64);
23    let n_blocks = data.len() / 6;
24    let tj2 = tj * tj;
25    let tj3 = tj * tj2;
26    let tj4 = tj * tj3;
27
28    let tj_vec = f64x4::from_array([tj, tj2, tj3, tj4]);
29    let tj_1_vec = f64x4::from_array([1.0, tj, tj2, tj3]);
30    let k_vec = f64x4::from_array([1.0, 2.0, 3.0, 4.0]);
31
32    let mut res: f64 = 0.0;
33    let mut res_p: f64 = 0.0;
34
35    let mut blk_start_idx= 0;
36    let mut blk_end_idx = n_blocks;
37
38    loop {
39        let blk_mid_idx = (blk_start_idx + blk_end_idx) / 2;
40        if data[blk_mid_idx * 6].abs() > tol {
41            blk_end_idx = blk_mid_idx;
42        } else {
43            blk_start_idx = blk_mid_idx;
44        }
45        if blk_end_idx - blk_start_idx < 2 {
46            break
47        }
48    }
49
50    let mut idx = blk_start_idx * 6;
51
52    loop {
53        let x = data[idx]; // cmpb[n];
54
55        let mut y = data[idx+1]; // fmpb[0, n];
56        let mut yp = 0.0;
57
58        let fmpb_1_5_vec = f64x4::from_slice(&data[idx+2..idx+6]);
59        y += (fmpb_1_5_vec * tj_vec).reduce_sum();
60        yp += (k_vec * fmpb_1_5_vec * tj_1_vec).reduce_sum();
61
62        res += x * y.sin();
63        res_p += x * yp * y.cos();
64        
65        idx += 6;
66        if idx >= data.len() {
67            break
68        }
69    }
70
71    (res, res_p)
72}
73
74fn accumulate_perturb(data_u64: &[u64], tj: f64, it: i32, tol: f64) -> (f64, f64) {
75    let data = u64_as_f64_slice(data_u64);
76    let n_blocks = data.len() / 6;
77
78    let tj2 = tj * tj;
79    let tj3 = tj * tj2;
80    let tj4 = tj * tj3;
81
82    let tj_vec = f64x4::from_array([tj, tj2, tj3, tj4]);
83    let tj_1_vec = f64x4::from_array([1.0, tj, tj2, tj3]);
84    let k_vec = f64x4::from_array([1.0, 2.0, 3.0, 4.0]);
85
86    let tit = match it {
87        1 => tj,
88        2 => tj2,
89        3 => tj3,
90        _ => 1.0,
91    };
92
93    let tit_1 = match it {
94        1 => 0.0,
95        2 => tj,
96        3 => tj2,
97        _ => 1.0,
98    };
99
100
101    let mut res: f64 = 0.0;
102    let mut res_p: f64 = 0.0;
103
104
105    let mut blk_start_idx= 0;
106    let mut blk_end_idx = n_blocks;
107
108    loop {
109        let blk_mid_idx = (blk_start_idx + blk_end_idx) / 2;
110        if data[blk_mid_idx * 6].abs() > tol {
111            blk_end_idx = blk_mid_idx;
112        } else {
113            blk_start_idx = blk_mid_idx;
114        }
115        if blk_end_idx - blk_start_idx < 2 {
116            break
117        }
118    }
119
120    let mut idx = blk_start_idx * 6;
121
122    loop {
123        let x = data[idx]; // cper[n];
124
125        let mut y: f64 = data[idx+1]; // fper[0, n];
126        let xp: f64 = (it as f64) * x * tit_1;
127
128        let fper_1_5_vec = f64x4::from_slice(&data[idx+2..idx+6]);
129        y += (fper_1_5_vec * tj_vec).reduce_sum();
130        let yp = (k_vec * fper_1_5_vec * tj_1_vec).reduce_sum();
131
132        res += x * tit * y.sin();
133        res_p += xp * y.sin() + x * tit * yp * y.cos();
134        idx += 6;
135
136        if idx >= data.len() {
137            break
138        }
139    }
140
141    (res, res_p)
142}
143
144
145/// Calculate geocentric spherical coordinates of the Moon. The frame is dynamical
146/// equinox and ecliptic J2000. The time scale of `t` is TDB. To speed up, we ignore
147/// terms whose `cmpb` or `cper` are smaller than `tol`.
148///
149/// # Return 
150///  * out.0: longitude  (rad)
151///  * out.1: latitude   (rad)
152///  * out.2: distance   (km)
153///  * out.3: longitude' (rad/day)
154///  * out.4: latitude'  (rad/day)
155///  * out.5: distance'  (km/day)
156pub fn spherical(t:f64, tol:f64) -> (f64, f64, f64, f64, f64, f64) {
157    let tj = (t - J2000) / coeff::CONST_SC;
158    let mut lng: f64 = 0.0; 
159    let mut lat: f64 = 0.0; 
160    let mut dst: f64 = 0.0; 
161    let mut v_lng: f64 = 0.0; 
162    let mut v_lat: f64 = 0.0; 
163    let mut v_dst: f64 = 0.0; 
164
165    // Main problem
166    let src = &coeff::DATA_COEFF_MAIN_0;
167    let (d, dv) = accumulate_main(src, tj, tol);
168    lng += d; v_lng += dv;
169
170    let src = &coeff::DATA_COEFF_MAIN_1;
171    let (d, dv) = accumulate_main(src, tj, tol);
172    lat += d; v_lat += dv;
173
174    let src = &coeff::DATA_COEFF_MAIN_2;
175    let (d, dv) = accumulate_main(src, tj, tol);
176    dst += d; v_dst += dv;
177
178
179    // Perturbation for lng
180    let src = &coeff::DATA_COEFF_PERTURB_0_0;
181    let (d, dv) = accumulate_perturb(src, tj, 0, tol);
182    lng += d; v_lng += dv;
183
184    let src = &coeff::DATA_COEFF_PERTURB_0_1;
185    let (d, dv) = accumulate_perturb(src, tj, 1, tol);
186    lng += d; v_lng += dv;
187
188    let src = &coeff::DATA_COEFF_PERTURB_0_2;
189    let (d, dv) = accumulate_perturb(src, tj, 2, tol);
190    lng += d; v_lng += dv;
191
192    let src = &coeff::DATA_COEFF_PERTURB_0_3;
193    let (d, dv) = accumulate_perturb(src, tj, 3, tol);
194    lng += d; v_lng += dv;
195
196    // Perturbation for lat
197    let src = &coeff::DATA_COEFF_PERTURB_1_0;
198    let (d, dv) = accumulate_perturb(src, tj, 0, tol);
199    lat += d; v_lat += dv;
200
201    let src = &coeff::DATA_COEFF_PERTURB_1_1;
202    let (d, dv) = accumulate_perturb(src, tj, 1, tol);
203    lat += d; v_lat += dv;
204
205    let src = &coeff::DATA_COEFF_PERTURB_1_2;
206    let (d, dv) = accumulate_perturb(src, tj, 2, tol);
207    lat += d; v_lat += dv;
208
209
210    // Perturbation for dist
211    let src = &coeff::DATA_COEFF_PERTURB_2_0;
212    let (d, dv) = accumulate_perturb(src, tj, 0, tol);
213    dst += d; v_dst += dv;
214
215    let src = &coeff::DATA_COEFF_PERTURB_2_1;
216    let (d, dv) = accumulate_perturb(src, tj, 1, tol);
217    dst += d; v_dst += dv;
218
219    let src = &coeff::DATA_COEFF_PERTURB_2_2;
220    let (d, dv) = accumulate_perturb(src, tj, 2, tol);
221    dst += d; v_dst += dv;
222
223    let src = &coeff::DATA_COEFF_PERTURB_2_3;
224    let (d, dv) = accumulate_perturb(src, tj, 3, tol);
225    dst += d; v_dst += dv;
226
227    (lng, lat, dst, v_lng, v_lat, v_dst)
228}
229
230
231/// Calculate geocentric rectangular coordinates of the Moon. The frame is dynamical
232/// equinox and ecliptic J2000. The time scale of `t` is TDB. To speed up, we ignore
233/// terms whose `cmpb` or `cper` are smaller than `tol`.
234///
235/// # Return 
236///  * out.0: X  (km)
237///  * out.1: Y  (km)
238///  * out.2: Z  (km)
239///  * out.3: X' (km/day)
240///  * out.4: Y' (km/day)
241///  * out.5: Z' (km/day)
242pub fn cartesian(t: f64, tol: f64) -> (f64, f64, f64, f64, f64, f64) {
243    let mut v = spherical(t, tol);
244
245    let tj = (t - J2000) / coeff::CONST_SC;
246    let tj2 = tj * tj;
247    let tj3 = tj2 * tj;
248    let tj4 = tj3 * tj;
249
250    v.0 = v.0 / RAD  
251        + coeff::CONST_W[0] 
252        + coeff::CONST_W[1] * tj
253        + coeff::CONST_W[2] * tj2
254        + coeff::CONST_W[3] * tj3
255        + coeff::CONST_W[4] * tj4;
256    v.1 = v.1 / RAD;
257    v.2 = v.2 * coeff::CONST_A405 / coeff::CONST_AELP;
258    v.3 = v.3 / RAD
259        + coeff::CONST_W[1] 
260        + 2.0 * coeff::CONST_W[2] * tj
261        + 3.0 * coeff::CONST_W[3] * tj2
262        + 4.0 * coeff::CONST_W[4] * tj3;
263    v.4 = v.4 / RAD;
264
265    let mut xyz: (f64, f64, f64, f64, f64, f64) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
266
267    let clamb = v.0.cos();
268    let slamb = v.0.sin();
269    let cbeta = v.1.cos();
270    let sbeta = v.1.sin();
271    let cw = v.2 * cbeta;
272    let sw = v.2 * sbeta;
273
274    let x1 = cw * clamb;
275    let x2 = cw * slamb;
276    let x3 = sw;
277    let xp1    = (v.5*cbeta-v.4*sw)*clamb-v.3*x2;
278    let xp2    = (v.5*cbeta-v.4*sw)*slamb+v.3*x1;
279    let xp3    = v.5*sbeta+v.4*cw;
280    
281    let pw     = (
282        coeff::CONST_P_POLY[0]
283        +coeff::CONST_P_POLY[1]*tj
284        +coeff::CONST_P_POLY[2]*tj2
285        +coeff::CONST_P_POLY[3]*tj3
286        +coeff::CONST_P_POLY[4]*tj4)*tj;
287
288    let qw     = (
289        coeff::CONST_Q_POLY[0]
290        +coeff::CONST_Q_POLY[1]*tj
291        +coeff::CONST_Q_POLY[2]*tj2
292        +coeff::CONST_Q_POLY[3]*tj3
293        +coeff::CONST_Q_POLY[4]*tj4)*tj;
294
295    let ra     = 2.0*((1.0-pw*pw-qw*qw).sqrt());
296    let pwqw   = 2.0*pw*qw;
297    let pw2    = 1.0-2.0*pw*pw;
298    let qw2    = 1.0-2.0*qw*qw;
299    let pwra   = pw*ra;
300    let qwra   = qw*ra;
301    
302    xyz.0 = pw2*x1+pwqw*x2+pwra*x3;
303    xyz.1 = pwqw*x1+qw2*x2-qwra*x3;
304    xyz.2 = -pwra*x1+qwra*x2+(pw2+qw2-1.0)*x3;
305    
306    let ppw     = coeff::CONST_P_POLY[0] + (
307        2.0*coeff::CONST_P_POLY[1]
308        +3.0*coeff::CONST_P_POLY[2]*tj
309        +4.0*coeff::CONST_P_POLY[3]*tj2
310        +5.0*coeff::CONST_P_POLY[4]*tj3)*tj;
311    let qpw    = coeff::CONST_Q_POLY[0] + (
312        2.0*coeff::CONST_Q_POLY[1]
313        +3.0*coeff::CONST_Q_POLY[2]*tj
314        +4.0*coeff::CONST_Q_POLY[3]*tj2
315        +5.0*coeff::CONST_Q_POLY[4]*tj3)*tj;
316
317    let ppw2   = -4.0*pw*ppw;
318    let qpw2   = -4.0*qw*qpw;
319    let ppwqpw = 2.0*(ppw*qw+pw*qpw);
320    let rap    = (ppw2+qpw2)/ra;
321    let ppwra  = ppw*ra+pw*rap;
322    let qpwra  = qpw*ra+qw*rap;
323    
324    xyz.3 = (pw2*xp1+pwqw*xp2+pwra*xp3 + ppw2*x1+ppwqpw*x2+ppwra*x3)/coeff::CONST_SC;
325    xyz.4 = (pwqw*xp1+qw2*xp2-qwra*xp3 + ppwqpw*x1+qpw2*x2-qpwra*x3)/coeff::CONST_SC;
326    xyz.5 = (-pwra*xp1+qwra*xp2+(pw2+qw2-1.0)*xp3 - ppwra*x1+qpwra*x2+(ppw2+qpw2)*x3)/coeff::CONST_SC;
327
328    xyz
329}
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334
335    #[test]
336    fn test_cartesian() {
337        let tj0=2444239.5;
338        let dtj=2000.0;
339        for n in 1..6 {
340            let t = tj0 + ((n-1) as f64) * dtj;
341            println!("JD {t}: {:#?}", cartesian(t, 0.0));
342        }
343    }
344
345    fn is_matched(
346        a: &(f64, f64, f64, f64, f64, f64),
347        b: &(f64, f64, f64, f64, f64, f64),
348    ) -> bool {
349        let tol = 1e-4;
350        if (a.0 - b.0).abs() > tol { return false; }
351        if (a.1 - b.1).abs() > tol { return false; }
352        if (a.2 - b.2).abs() > tol { return false; }
353        if (a.3 - b.3).abs() > tol { return false; }
354        if (a.4 - b.4).abs() > tol { return false; }
355        if (a.5 - b.5).abs() > tol { return false; }
356
357        return true;
358    }
359    #[test]
360    fn check_values() {
361        let cases = vec![
362            (2444239.5, 
363                (43890.2824005, 381188.7274523, -31633.3816524, -87516.1974842, 13707.6644351, 2754.2212424),
364            ),
365            (2446239.5, 
366                (-313664.5964499, 212007.2667385, 33744.7512039, -47315.9128069, -75710.8750091, -1475.6286887),
367            ),
368            (2448239.5, 
369                (-273220.0606714, -296859.7682229, -34604.3569962, 60542.3275903, -58162.3167425, 2270.8869053),
370            ),
371            (2450239.5, 
372                (171613.1427993, -318097.3375025, 31293.5482404, 83266.7799036, 42585.8302849, -1695.8261107),
373            ),
374            (2452239.5, 
375                (396530.0063512, 47487.9224886, -36085.3090343, -12664.2869360, 83512.7571912, 1507.3675566),
376            ),
377        ];
378
379        let mut failcnt = 0;
380        for (t, expected) in cases.iter() {
381            let actual = cartesian(*t, 0.0);
382
383            let mut failed = false;
384            if !is_matched(expected, &actual) {
385                failed = true;
386            }
387            if !failed {
388                println!("[t={t}] PASSED")
389            } else {
390                failcnt += 1; 
391                println!("[t={t}] FAILED")
392            }
393        }
394
395        assert_eq!(failcnt, 0);
396    }
397
398}