rust_zmanim/util/zenith_adjustments.rs
1//! Basic calculations used for all sun time calculation algorithms (though
2//! currently only [the NOAA algorithm](super::noaa_calculator) is used)
3
4/// 90° below the vertical. Used as a basis for most calculations since the
5/// location of the sun is 90° below the vertical at sunrise and sunset.
6pub const GEOMETRIC_ZENITH: f64 = 90.0;
7
8/// 34 arcminutes of refraction
9pub const REFRACTION: f64 = 34.0 / 60.0;
10
11/// 16 arcminutes for the sun's radius in the sky
12pub const SOLAR_RADIUS: f64 = 16.0 / 60.0;
13
14/// The commonly used average earth radius in KM
15pub const EARTH_RADIUS: f64 = 6_356.9;
16
17/// Function to return the adjustment to the zenith required to account for the
18/// elevation.
19///
20/// Since a person at a higher elevation can see farther below the horizon, the
21/// calculation for sunrise / sunset is calculated below the horizon used at sea
22/// level. The algorithm used is
23///
24/// <pre>
25/// (EARTH_RADIUS / (EARTH_RADIUS + (elevation / 1_000.0))).acos().to_degrees()
26/// </pre>
27///
28/// The source of this algorithm is *Calendrical Calculations* by Edward M.
29/// Reingold and Nachum Dershowitz. An alternate algorithm that produces similar
30/// (but not completely accurate) result found in *Ma'aglay Tzedek* by Moishe
31/// Kosower and other sources is:
32///
33/// <pre>
34/// 0.0347 * elevation.sqrt();
35/// </pre>
36pub fn elevation_adjustment(elevation: f64) -> f64 {
37 (EARTH_RADIUS / (EARTH_RADIUS + (elevation / 1_000.0)))
38 .acos()
39 .to_degrees()
40}
41
42/// Adjusts the zenith of astronomical sunrise and sunset to account for solar
43/// refraction, solar radius and elevation.
44///
45/// The value for Sun's zenith and true rise/set Zenith is the angle that the
46/// center of the Sun makes to a line perpendicular to the Earth's surface. If
47/// the Sun were a point and the Earth were without an atmosphere, true sunset
48/// and sunrise would correspond to a 90° zenith. Because the Sun is not a
49/// point, and because the atmosphere refracts light, this 90° zenith does
50/// not, in fact, correspond to true sunset or sunrise, instead the center of
51/// the Sun's disk must lie just below the horizon for the upper edge to be
52/// obscured. This means that a zenith of just above 90° must be used. The
53/// Sun subtends an angle of [16 minutes of arc](SOLAR_RADIUS) and atmospheric
54/// refraction accounts for [34 minutes](REFRACTION) or so, giving a total of 50
55/// arcminutes. The total value is therefore 90+(5/6) or 90.8333333° for
56/// true sunrise/sunset. Since a person at an elevation can see below the
57/// horizon of a person at sea level, this will also adjust the zenith to
58/// account for elevation if available. Note that this will only adjust the
59/// value if the zenith is exactly 90 degrees. For values below and above this
60/// no correction is done, as calculations above or below 90° are usually
61/// calculated as an offset to 90°
62pub fn adjusted_zenith(zenith: f64, elevation: f64) -> f64 {
63 if zenith != GEOMETRIC_ZENITH {
64 return zenith;
65 }
66 zenith + SOLAR_RADIUS + REFRACTION + elevation_adjustment(elevation)
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_adjusted_zenith() {
75 assert_eq!(adjusted_zenith(90.0, 123.4), 91.1903339839496);
76 assert_eq!(adjusted_zenith(96.0, 234.5), 96.0);
77 assert_eq!(adjusted_zenith(75.4, 543.2), 75.4);
78 }
79
80 #[test]
81 fn test_elevation_adjustment() {
82 assert_eq!(elevation_adjustment(526.0), 0.7370430237803639);
83 assert_eq!(elevation_adjustment(100.0), 0.3213750031005735);
84 assert_eq!(elevation_adjustment(0.0), 0.0);
85 assert_eq!(elevation_adjustment(3.456789), 0.059751839197933074);
86 }
87}