1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
use crate::{anomaly::Anomaly, conversions::radians_in_circle};
use strum::AsRefStr;
#[derive(Debug, Default, Clone, Copy)]
/// This is the collection of orbital types a body would follow
pub enum Type {
/// The orbit path is round, like a donut.
Circular,
/// The orbit path is oval, like an egg not a treadmill.
Elliptical,
/// The orbit path is boomerang shaped.
Parabolic,
/// The orbit path is like the sides of a guitar.
Hyperbolic,
/// The orbit path is like a pencil (how?)
Straight,
/// The orbit path is unknown, you probably entered incorrect data.
#[default]
Unknown,
}
impl Type {
/// Gives the shape of the keplerian body based of orbital shpae deviation
pub fn shape(&self, obe: f64) -> Self {
match obe {
e if e == 0.0 => Self::Circular,
e if e > 0.0 && e < 1.0 => Self::Elliptical,
e if e == 1.0 => Self::Parabolic,
e if e > 1.0 => Self::Hyperbolic,
e if e == f64::INFINITY => Self::Straight,
_ => Self::Unknown,
}
}
}
#[derive(Debug, Clone, Copy)]
/// This data structure contains perihelion data.
pub struct Perihelion {
/// ### (Start, End)
pub month: (f64, f64),
/// ### (Start, End)
pub ls: (f64, f64),
/// ### The solar longitude of the perihelion
pub perihelion: f64,
}
impl Perihelion {
/// The days since the the perihelion by the orbital_period and day in planet
/// orbital_period is the body's orbital period, not the earth.
///
pub fn elapse(&mut self, day: f64, orbital_period: f64) -> f64 {
(day - self.date()) / orbital_period
}
/// The date of the perihelion by the orbital period
pub fn date(&mut self) -> f64 {
let avg_days = self.month.1 - self.month.0;
let avg_ls = self.ls.1 - self.ls.0;
let until_peri = self.perihelion - self.ls.0;
let peri_day = avg_days / avg_ls;
(peri_day * until_peri) + self.month.0
}
/// The time of the perihelion within the orbit
pub fn time(&mut self) -> f64 {
radians_in_circle() * (1.0 - self.perihelion / 360.0)
}
/// The average solar longitude between the start and end of the perihelion
pub fn avg_ls(&mut self) -> f64 {
self.ls.1 - self.ls.0
}
}
#[derive(Debug, Copy, Clone)]
/// This is the data for calculating solar longitude among orbiting bodies.
pub struct SolarLongitude;
impl SolarLongitude {
/// This method computes the ls which should be given by the body.
/// * The final computation is in *degrees*
///
pub fn compute(
&self,
shape: Type,
day: f64,
orbital_eccentricity: f64,
mut peri: Perihelion,
orbital_period: f64,
major_axis: f64,
) -> f64 {
let theta = Anomaly.truly(
shape,
day,
orbital_eccentricity,
peri,
orbital_period,
major_axis,
);
let mut ls = theta - peri.time();
if ls < 0.0 {
ls += radians_in_circle();
}
if ls > radians_in_circle() {
ls -= radians_in_circle();
}
ls.to_degrees()
}
}
#[derive(Debug, Copy, Clone)]
/// This structure is for the semi axises of an ellipse
pub struct SemiAxis(pub f64);
impl SemiAxis {
/// This is just a wrapper to return the major axis.
///
pub fn major(self) -> f64 {
self.0
}
/// Calculates the shortest distance between the center of the body to the edge of the body.
///
/// ```rust
///
/// use crate::rust_solar::planets::mars::Mars;
/// use crate::rust_solar::kepler::Body;
/// use crate::rust_solar::orbit::SemiAxis;
///
/// let martian_semi_minor_axis = SemiAxis(Mars.semimajor()).minor(Mars.orbital_eccentricity());
///
/// assert_eq!(1.5067401888, martian_semi_minor_axis)
///
/// ```
pub fn minor(self, orbital_eccentricity: f64) -> f64 {
self.major() * (1.0 - orbital_eccentricity.powf(2.0))
}
}
/// The collection of seasons in which all keplerian bodies follow
#[derive(AsRefStr, Debug, Default, Copy, Clone)]
pub enum Season {
/// March 19th
#[strum(serialize = "Vernal Equinox")]
VernalEquinox,
/// July 6
#[strum(serialize = "Aphelion")]
Aphelion,
/// June 20th -> June 21st
#[strum(serialize = "Summer Solstice")]
SummerSolstice,
/// September 22
#[strum(serialize = "Autumn Equinox")]
AutumnEquinox,
/// January 3rd
#[strum(serialize = "Perihelion")]
Perihelion,
/// December 21st -> December 22nd
#[strum(serialize = "Winter Solstice")]
WinterSolstice,
/// This structure is not the problem, it must be the solar longitude,
/// or maybe the planet you chose doesn't have seasons?
#[strum(serialize = "N/A")]
#[default]
Unknown,
}
impl Season {
/// This method creates a season given a solar longitude.
pub fn from(&self, ls: u32) -> String {
match ls {
71 => Self::Aphelion,
251 => Self::Perihelion,
0..=90 => Self::VernalEquinox,
91..=180 => Self::SummerSolstice,
181..=270 => Self::AutumnEquinox,
271..=360 => Self::WinterSolstice,
_ => Self::Unknown,
}
.as_ref()
.to_string()
}
}
/// The mean motion where all bodies share
#[derive(Debug, Default, Copy, Clone)]
pub struct MeanMotion;
impl MeanMotion {
/// This method abstracts the ability to calculate the mean motion
///
/// 
///
/// - `n` is the mean motion
/// - `P` is the orbital period
pub fn by(&mut self, day: f64, mut peri: Perihelion, orbital_period: f64) -> f64 {
let elapse = Perihelion::elapse(&mut peri, day, orbital_period);
radians_in_circle() * (elapse - elapse.round())
}
}