1use num::{Float, NumCast};
2
3pub fn fast_atan2<F: Float>(y: F, x: F) -> F {
5 let zero: F = NumCast::from(0).unwrap();
6 let pi: F = NumCast::from(std::f64::consts::PI).unwrap();
7
8 if x > zero {
9 return (y / x).atan();
10 } else if x == zero {
11 if y < zero {
12 return NumCast::from(-std::f64::consts::PI / 2.).unwrap();
13 } else if y == zero {
14 return zero;
15 } else if y > zero {
16 return NumCast::from(std::f64::consts::PI / 2.).unwrap();
17 }
18 } else if x < zero {
19 if y < zero {
20 return (y / x).atan() - pi;
21 } else if y >= zero {
22 return (y / x).atan() + pi;
23 }
24 }
25
26 return zero;
27}
28
29type JD2000 = f64;
32type JC2000 = f64;
34
35pub struct SunPosition {
36 azimuth: f64,
38 altitude: f64,
40}
41
42pub fn solaris_base_impl(
44 t_Jd: JD2000,
45 t_Jc: JC2000,
46 use_approx_R: bool,
47 obs_latitude: f64,
48 obs_longitude: f64,
49) -> SunPosition {
50 let λ0 = 4.895063168 + 628.331966786 * t_Jc + 5.291838e-6 * (t_Jc * t_Jc);
53
54 let M = 6.240060141 + 628.301955152 * t_Jc + -2.682571e-6 * (t_Jc * t_Jc);
57
58 let C = (3.34161088e-2 - 8.40725e-5 * t_Jc - 2.443e-7 * (t_Jc * t_Jc)) * M.sin()
61 + (3.489437e-4 - 1.76278e-6 * t_Jc) * (2.0 * M).sin();
62
63 let λ = λ0 + C;
65
66 let Ω = 2.1824390725 - 33.7570464271 * t_Jc + 3.622256e-5 * (t_Jc * t_Jc);
69
70 let delta_ψ = -8.338601e-5 * Ω.sin();
73
74 let e = 0.016708634 - 4.2037e-5 * t_Jc - 1.267e-7 * (t_Jc * t_Jc);
76 let v = M + C;
77 let R = if use_approx_R {
78 1.0000010178 } else {
80 1.0000010178 * (1. - e * e) / (1. + e * v.cos())
81 };
82
83 let delta_λ = (-9.93087e-5) / R;
86
87 let λ = λ + delta_λ + delta_ψ;
90 let λ = λ % (2. * std::f64::consts::PI);
92
93 const β: f64 = 0.0;
97
98 let ε0 = 0.409092804222 - 2.26965525e-4 * t_Jc - 2.86e-9 * (t_Jc * t_Jc);
101
102 let delta_ε = 4.4615e-5 * Ω.cos();
105
106 let ε = ε0 + delta_ε;
109
110 let cos_ε = ε.cos();
112 let sin_ε = ε.sin();
113 let tan_β = β.tan();
114 let cos_β = β.cos();
115 let sin_β = β.sin();
116 let sin_λ = λ.sin();
117 let cos_λ = λ.cos();
118
119 let α = fast_atan2(sin_λ * cos_ε - tan_β * sin_ε, cos_λ);
122 let α = if α < 0. {
124 α + (2. * std::f64::consts::PI)
125 } else {
126 α
127 };
128 let δ = (sin_β * cos_ε + cos_β * sin_ε * sin_λ).asin();
130
131 let delta_θ = delta_ψ * cos_ε;
134 let θ0 = 4.89496121 + 6.300388098985 * t_Jd + 6.77e-6 * (t_Jc * t_Jc);
135 let θ = θ0 + delta_θ + obs_longitude;
136
137 let H = θ - α;
140
141 let cos_H = H.cos();
142 let cos_δ = δ.cos();
143 let sin_δ = δ.sin();
144 let cos_lat = obs_latitude.cos();
145 let sin_lat = obs_latitude.sin();
146 let h = (sin_δ * sin_lat + cos_H * cos_δ * cos_lat).asin();
149
150 let sin_H = H.sin();
151 let A = fast_atan2(sin_H, cos_H * sin_lat - (sin_δ / cos_δ) * cos_lat);
154
155 let ζ = (std::f64::consts::PI / 2.) - h;
157
158 const T: f64 = 283.;
162 const P: f64 = 101.;
164 let delta_h = 0.0002967 * (1.0 / (h + (0.0031376 / (h + 0.0892)))).tan() * (283. / T) * (P / 101.);
165
166 let h = h + delta_h;
168
169 return SunPosition {
170 azimuth: A,
171 altitude: h,
172 };
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn initial_check() {
181 const jd2000_epoch: f64 = 2451545.00;
183 const now: f64 = 2460387.166666667;
185
186 const obs_lat: f64 = 40.884305 * (std::f64::consts::PI / 180.);
188 const obs_lon: f64 = -77.779766 * (std::f64::consts::PI / 180.);
189
190 const t_Jd: f64 = now - jd2000_epoch;
191 const t_Jc: f64 = t_Jd / (365.25 * 100.);
192 println!("jc = {}, jd = {}", t_Jc, t_Jd);
193
194 let sun_pos = solaris_base_impl(t_Jd, t_Jc, false, obs_lat, obs_lon);
195 println!(
196 "altitude = {}, azimuth = {}",
197 sun_pos.altitude * (180. / std::f64::consts::PI),
198 sun_pos.azimuth * (180. / std::f64::consts::PI),
199 );
200
201 let approx_eq = |x: f64, y: f64| -> bool {
202 let diff = (y - x).abs();
203 diff < 1e-6
204 };
205
206 assert!(approx_eq(sun_pos.azimuth * (180. / std::f64::consts::PI), -28.33772429847502));
207 assert!(approx_eq(sun_pos.altitude * (180. / std::f64::consts::PI), 44.4880091652758));
208 }
209
210 #[test]
211 fn fast_atan2_test() {
212 let approx_eq = |x: f64, y: f64| -> bool {
213 let diff = (y - x).abs();
214 diff < 1e-6
215 };
216
217 assert!(approx_eq(fast_atan2::<f64>(-3., -4.), -2.4980915448));
219 assert!(approx_eq(fast_atan2::<f64>(0., -4.), 3.14159265359));
220 assert!(approx_eq(fast_atan2::<f64>(3., -4.), 2.4980915448));
221
222 assert!(approx_eq(fast_atan2::<f64>(-3., 0.), -1.57079632679));
224 assert!(approx_eq(fast_atan2::<f64>(0., 0.), 0.));
225 assert!(approx_eq(fast_atan2::<f64>(3., 0.), 1.57079632679));
226
227 assert!(approx_eq(fast_atan2::<f64>(-3., 4.), -0.643501108793));
229 assert!(approx_eq(fast_atan2::<f64>(0., 4.), 0.));
230 assert!(approx_eq(fast_atan2::<f64>(3., 4.), 0.643501108793));
231 }
232}