1#![forbid(unsafe_code)]
13
14mod delta_t;
15mod julian;
16mod lunisolar;
17mod new_moon;
18mod nutation_data;
19mod solar_terms;
20mod vsop87d_earth;
21
22pub use delta_t::delta_t_for_year;
23pub use julian::{jd_from_ymd, ymd_from_jd};
24pub use lunisolar::{
25 gregorian_to_lunisolar, lunar_months_for_year, lunar_new_year, CivilDate, LunarMonth,
26 LunisolarDate,
27};
28pub use new_moon::{find_new_moons_in_range, new_moon_jde};
29pub use solar_terms::{find_solar_term_moment, SOLAR_TERM_LONGITUDES};
30
31use core::f64::consts::{PI, TAU};
32use nutation_data::NUT_COEFFS;
33use vsop87d_earth::{EARTH_L, EARTH_R};
34
35const ARCSEC_TO_RAD: f64 = PI / 180.0 / 3600.0;
37const RAD_TO_DEG: f64 = 180.0 / PI;
39
40#[derive(Debug, Clone, Copy, PartialEq)]
42pub struct SolarState {
43 pub true_longitude_degrees: f64,
46 pub apparent_longitude_degrees: f64,
49 pub radius_au: f64,
51}
52
53pub fn solar_ecliptic_state(jde_tt: f64) -> SolarState {
56 let tau = (jde_tt - 2451545.0) / 365250.0; let t = (jde_tt - 2451545.0) / 36525.0; let mut lon = eval_vsop_series(EARTH_L, tau);
61 let r = eval_vsop_series(EARTH_R, tau);
62
63 let tau2 = tau * tau;
66 lon += (-0.106674 - 0.616597 * tau2 + 0.315446 * tau2 * tau2 - 0.050315 * tau2 * tau2 * tau2)
67 / 206264.806;
68
69 let geo_true = lon + PI;
72
73 let (dl, dlp, df, dd, dom) = delaunay_args(t);
75 let dpsi = nutation_dpsi(dl, dlp, df, dd, dom, t);
76 let apparent = geo_true + dpsi * ARCSEC_TO_RAD + (-20.4898 / r) * ARCSEC_TO_RAD;
77
78 SolarState {
79 true_longitude_degrees: normalize_radians(geo_true) * RAD_TO_DEG,
80 apparent_longitude_degrees: normalize_radians(apparent) * RAD_TO_DEG,
81 radius_au: r,
82 }
83}
84
85fn eval_vsop_series(series: &[&[[f64; 3]]], tau: f64) -> f64 {
88 let mut result = 0.0;
89 let mut tau_pow = 1.0;
90 for terms in series {
91 let mut sum = 0.0;
92 for term in *terms {
93 sum += term[0] * (term[1] + term[2] * tau).cos();
94 }
95 result += sum * tau_pow;
96 tau_pow *= tau;
97 }
98 result
99}
100
101fn delaunay_args(t: f64) -> (f64, f64, f64, f64, f64) {
104 let t2 = t * t;
105 let t3 = t2 * t;
106 let t4 = t3 * t;
107 let l = ((485868.249036 + 1717915923.2178 * t + 31.8792 * t2 + 0.051635 * t3
110 - 0.00024470 * t4)
111 % 1296000.0)
112 * ARCSEC_TO_RAD;
113 let lp = ((1287104.79305 + 129596581.0481 * t - 0.5532 * t2 + 0.000136 * t3 - 0.00001149 * t4)
114 % 1296000.0)
115 * ARCSEC_TO_RAD;
116 let f = ((335779.526232 + 1739527262.8478 * t - 12.7512 * t2 - 0.001037 * t3
117 + 0.00000417 * t4)
118 % 1296000.0)
119 * ARCSEC_TO_RAD;
120 let d = ((1072260.70369 + 1602961601.2090 * t - 6.3706 * t2 + 0.006593 * t3 - 0.00003169 * t4)
121 % 1296000.0)
122 * ARCSEC_TO_RAD;
123 let om = ((450160.398036 - 6962890.5431 * t + 7.4722 * t2 + 0.007702 * t3 - 0.00005939 * t4)
124 % 1296000.0)
125 * ARCSEC_TO_RAD;
126 (l, lp, f, d, om)
127}
128
129fn nutation_dpsi(l: f64, lp: f64, f: f64, d: f64, om: f64, t: f64) -> f64 {
131 let mut dpsi = 0.0;
132 for row in NUT_COEFFS {
133 let arg = row[0] * l + row[1] * lp + row[2] * f + row[3] * d + row[4] * om;
134 dpsi += (row[5] + row[6] * t) * arg.sin();
135 }
136 dpsi / 1e7
138}
139
140fn normalize_radians(rad: f64) -> f64 {
142 ((rad % TAU) + TAU) % TAU
143}