1use std::f64::consts::TAU;
2use std::time::SystemTime;
3
4mod consts;
5pub use consts::*;
6fn julian_date(time: SystemTime) -> f64 {
8 let unix_time: f64 = match time.duration_since(SystemTime::UNIX_EPOCH) {
9 Ok(duration) => duration.as_secs_f64(),
10 Err(earlier) => -1. * earlier.duration().as_secs_f64(),
11 };
12 unix_time / 86400. + 2440587.5
13}
14fn illumination(phase: f64) -> f64 {
16 0.5 * (1.0 - (TAU * phase).cos())
17}
18
19fn lunation(julian_date: f64) -> u16 {
21 (1. + (julian_date - LUNATION_BASE) / ORBIT_PERIOD).floor() as u16
22}
23
24fn phase(julian_date: f64) -> f64 {
26 ((julian_date - ORBIT_OFFSET) / ORBIT_PERIOD).fract()
27}
28fn distance_phase(julian_date: f64) -> f64 {
30 ((julian_date - DISTANCE_OFFSET) / DISTANCE_PERIOD).fract()
31}
32fn distance(phase: f64, julian_date: f64) -> f64 {
33 let distance_p = distance_phase(julian_date);
34 let distance_p_tau: f64 = TAU * distance_p;
35 let p_tau: f64 = 2.0 * TAU * phase;
36 let p_distance_tau_diff = p_tau - distance_p_tau;
37
38 60.4 - 3.3 * distance_p_tau.cos() - 0.6 * (p_distance_tau_diff).cos() - 0.5 * (p_tau).cos()
39}
40impl Moon {
41 pub fn new(time: SystemTime) -> Moon {
42 let julian_date: f64 = julian_date(time);
43 let phase: f64 = phase(julian_date);
44 let age: f64 = phase * ORBIT_PERIOD;
45 let illumination: f64 = illumination(phase);
46 let lunation: u16 = lunation(julian_date);
47 let distance: f64 = distance(phase, julian_date);
48
49 Moon {
50 julian_date,
51 age,
52 phase,
53 illumination,
54 distance,
55 lunation,
56 }
57 }
58
59 pub fn distance_km(&self) -> f64 {
61 self.distance * EARTH_RADIUS_KM
62 }
63
64 pub fn is_waning(&self) -> bool {
66 self.age < 0.5
67 }
68 pub fn is_waxing(&self) -> bool {
70 self.age > 0.5
71 }
72 pub fn phase_name(&self) -> &'static str {
74 for phase in PHASES.iter() {
75 if self.phase >= phase.start && self.phase < phase.end {
76 return phase.name;
77 }
78 }
79 "Unknown"
80 }
81
82 pub fn phase_emoji(&self) -> &'static str {
84 for phase in PHASES.iter() {
85 if self.phase >= phase.start && self.phase < phase.end {
86 return phase.emoji;
87 }
88 }
89 "Unknown"
90 }
91}