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]; let mut y = data[idx+1]; 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]; let mut y: f64 = data[idx+1]; 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
145pub 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 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 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 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 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
231pub 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}