astronoby 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/.standard.yml +1 -0
  4. data/CHANGELOG.md +116 -0
  5. data/Gemfile.lock +45 -23
  6. data/README.md +42 -285
  7. data/UPGRADING.md +238 -0
  8. data/lib/astronoby/aberration.rb +56 -31
  9. data/lib/astronoby/angle.rb +20 -16
  10. data/lib/astronoby/angles/dms.rb +2 -2
  11. data/lib/astronoby/angles/hms.rb +2 -2
  12. data/lib/astronoby/bodies/earth.rb +56 -0
  13. data/lib/astronoby/bodies/jupiter.rb +11 -0
  14. data/lib/astronoby/bodies/mars.rb +11 -0
  15. data/lib/astronoby/bodies/mercury.rb +11 -0
  16. data/lib/astronoby/bodies/moon.rb +50 -290
  17. data/lib/astronoby/bodies/neptune.rb +11 -0
  18. data/lib/astronoby/bodies/saturn.rb +11 -0
  19. data/lib/astronoby/bodies/solar_system_body.rb +122 -0
  20. data/lib/astronoby/bodies/sun.rb +16 -220
  21. data/lib/astronoby/bodies/uranus.rb +11 -0
  22. data/lib/astronoby/bodies/venus.rb +11 -0
  23. data/lib/astronoby/constants.rb +13 -1
  24. data/lib/astronoby/coordinates/ecliptic.rb +2 -37
  25. data/lib/astronoby/coordinates/equatorial.rb +25 -7
  26. data/lib/astronoby/coordinates/horizontal.rb +0 -46
  27. data/lib/astronoby/corrections/light_time_delay.rb +90 -0
  28. data/lib/astronoby/deflection.rb +187 -0
  29. data/lib/astronoby/distance.rb +9 -0
  30. data/lib/astronoby/ephem.rb +39 -0
  31. data/lib/astronoby/equinox_solstice.rb +21 -18
  32. data/lib/astronoby/errors.rb +4 -0
  33. data/lib/astronoby/events/moon_phases.rb +2 -1
  34. data/lib/astronoby/events/rise_transit_set_calculator.rb +352 -0
  35. data/lib/astronoby/events/rise_transit_set_event.rb +13 -0
  36. data/lib/astronoby/events/rise_transit_set_events.rb +13 -0
  37. data/lib/astronoby/events/twilight_calculator.rb +166 -0
  38. data/lib/astronoby/events/twilight_event.rb +28 -0
  39. data/lib/astronoby/instant.rb +171 -0
  40. data/lib/astronoby/mean_obliquity.rb +23 -10
  41. data/lib/astronoby/nutation.rb +227 -42
  42. data/lib/astronoby/observer.rb +55 -0
  43. data/lib/astronoby/precession.rb +91 -17
  44. data/lib/astronoby/reference_frame.rb +49 -0
  45. data/lib/astronoby/reference_frames/apparent.rb +60 -0
  46. data/lib/astronoby/reference_frames/astrometric.rb +21 -0
  47. data/lib/astronoby/reference_frames/geometric.rb +20 -0
  48. data/lib/astronoby/reference_frames/mean_of_date.rb +38 -0
  49. data/lib/astronoby/reference_frames/topocentric.rb +82 -0
  50. data/lib/astronoby/true_obliquity.rb +2 -1
  51. data/lib/astronoby/util/maths.rb +70 -73
  52. data/lib/astronoby/util/time.rb +454 -31
  53. data/lib/astronoby/vector.rb +36 -0
  54. data/lib/astronoby/velocity.rb +116 -0
  55. data/lib/astronoby/version.rb +1 -1
  56. data/lib/astronoby.rb +26 -5
  57. metadata +61 -16
  58. data/.tool-versions +0 -1
  59. data/lib/astronoby/astronomical_models/ephemeride_lunaire_parisienne.rb +0 -143
  60. data/lib/astronoby/events/observation_events.rb +0 -285
  61. data/lib/astronoby/events/rise_transit_set_iteration.rb +0 -218
  62. data/lib/astronoby/events/twilight_events.rb +0 -121
  63. data/lib/astronoby/util/astrodynamics.rb +0 -60
@@ -1,9 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
- class Moon
4
+ class Moon < SolarSystemBody
5
5
  SEMIDIAMETER_VARIATION = 0.7275
6
- MEAN_GEOCENTRIC_DISTANCE = Astronoby::Distance.from_meters(385_000_560)
6
+ EQUATORIAL_RADIUS = Distance.from_meters(1_737_400)
7
+
8
+ def self.ephemeris_segments(ephem_source)
9
+ if ephem_source == ::Ephem::SPK::JPL_DE
10
+ [
11
+ [SOLAR_SYSTEM_BARYCENTER, EARTH_MOON_BARYCENTER],
12
+ [EARTH_MOON_BARYCENTER, MOON]
13
+ ]
14
+ elsif ephem_source == ::Ephem::SPK::INPOP
15
+ [
16
+ [SOLAR_SYSTEM_BARYCENTER, EARTH],
17
+ [EARTH, MOON]
18
+ ]
19
+ end
20
+ end
7
21
 
8
22
  # Source:
9
23
  # Title: Astronomical Algorithms
@@ -18,117 +32,24 @@ module Astronoby
18
32
  Events::MoonPhases.phases_for(year: year, month: month)
19
33
  end
20
34
 
21
- def initialize(time:)
22
- @time = time
23
- end
24
-
25
- # @return [Astronoby::Coordinates::Ecliptic] Apparent ecliptic coordinates
26
- # of the Moon
27
- def apparent_ecliptic_coordinates
28
- @ecliptic_coordinates ||= begin
29
- latitude = Astronoby::Angle.from_degrees(
30
- (latitude_terms + additive_latitude_terms) *
31
- EphemerideLunaireParisienne::DEGREES_UNIT
32
- )
33
-
34
- longitude = mean_longitude + Astronoby::Angle.from_degrees(
35
- (longitude_terms + additive_longitude_terms) *
36
- EphemerideLunaireParisienne::DEGREES_UNIT
37
- ) + nutation
38
-
39
- Coordinates::Ecliptic.new(
40
- latitude: latitude,
41
- longitude: longitude
42
- )
43
- end
44
- end
45
-
46
- # Source:
47
- # Title: Astronomical Algorithms
48
- # Author: Jean Meeus
49
- # Edition: 2nd edition
50
- # Chapter: 47 - Position of the Moon
51
-
52
- # @return [Astronoby::Coordinates::Equatorial] Apparent geocentric
53
- # equatorial coordinates of the Moon
54
- def apparent_equatorial_coordinates
55
- @apparent_equatorial_coordinates ||=
56
- apparent_ecliptic_coordinates
57
- .to_apparent_equatorial(epoch: Epoch.from_time(@time))
58
- end
59
-
60
- def horizontal_coordinates(observer:)
61
- apparent_topocentric_equatorial_coordinates =
62
- Astronoby::GeocentricParallax.for_equatorial_coordinates(
63
- observer: observer,
64
- time: @time,
65
- coordinates: apparent_equatorial_coordinates,
66
- distance: distance
67
- )
35
+ attr_reader :phase_angle
68
36
 
69
- apparent_topocentric_equatorial_coordinates.to_horizontal(
70
- observer: observer,
71
- time: @time
72
- )
73
- end
74
-
75
- # @return [Astronoby::Distance] Distance between the Earth and the Moon centers
76
- def distance
77
- @distance ||= Astronoby::Distance.from_meters(
78
- (MEAN_GEOCENTRIC_DISTANCE.meters + distance_terms).round
79
- )
80
- end
81
-
82
- # @return [Angle] Moon's mean longitude
83
- def mean_longitude
84
- @mean_longitude ||= Angle.from_degrees(
85
- (
86
- 218.3164477 +
87
- 481267.88123421 * elapsed_centuries -
88
- 0.0015786 * elapsed_centuries**2 +
89
- elapsed_centuries**3 / 538841 -
90
- elapsed_centuries**4 / 65194000
91
- ) % 360
92
- )
37
+ def initialize(instant:, ephem:)
38
+ super
39
+ @phase_angle = compute_phase_angle(ephem)
93
40
  end
94
41
 
95
- # @return [Angle] Moon's mean elongation
96
- def mean_elongation
97
- @mean_elongation ||= Angle.from_degrees(
98
- (
99
- 297.8501921 +
100
- 445267.1114034 * elapsed_centuries -
101
- 0.0018819 * elapsed_centuries**2 +
102
- elapsed_centuries**3 / 545868 -
103
- elapsed_centuries**4 / 113065000
104
- ) % 360
105
- )
42
+ # @return [Float] Moon's illuminated fraction
43
+ def illuminated_fraction
44
+ @illuminated_fraction ||= (1 + phase_angle.cos) / 2.0
106
45
  end
107
46
 
108
- # @return [Angle] Moon's mean anomaly
109
- def mean_anomaly
110
- @mean_anomaly ||= Angle.from_degrees(
111
- (
112
- 134.9633964 +
113
- 477198.8675055 * elapsed_centuries +
114
- 0.0087414 * elapsed_centuries**2 +
115
- elapsed_centuries**3 / 69699 -
116
- elapsed_centuries**4 / 14712000
117
- ) % 360
118
- )
47
+ # @return [Float] Phase fraction, from 0 to 1
48
+ def current_phase_fraction
49
+ mean_elongation.degrees / Constants::DEGREES_PER_CIRCLE
119
50
  end
120
51
 
121
- # @return [Angle] Moon's argument of latitude
122
- def argument_of_latitude
123
- @argument_of_latitude ||= Angle.from_degrees(
124
- (
125
- 93.2720950 +
126
- 483202.0175233 * elapsed_centuries -
127
- 0.0036539 * elapsed_centuries**2 -
128
- elapsed_centuries**3 / 3526000
129
- ) % 360
130
- )
131
- end
52
+ private
132
53
 
133
54
  # Source:
134
55
  # Title: Astronomical Algorithms
@@ -137,204 +58,43 @@ module Astronoby
137
58
  # Chapter: 48 - Illuminated Fraction of the Moon's Disk
138
59
 
139
60
  # @return [Angle] Moon's phase angle
140
- def phase_angle
61
+ def compute_phase_angle(ephem)
141
62
  @phase_angle ||= begin
142
- sun_apparent_equatorial_coordinates = sun
143
- .apparent_ecliptic_coordinates
144
- .to_apparent_equatorial(epoch: Epoch.from_time(@time))
145
- moon_apparent_equatorial_coordinates = apparent_equatorial_coordinates
63
+ sun = Sun.new(instant: @instant, ephem: ephem)
146
64
  geocentric_elongation = Angle.acos(
147
- sun_apparent_equatorial_coordinates.declination.sin *
148
- moon_apparent_equatorial_coordinates.declination.sin +
149
- sun_apparent_equatorial_coordinates.declination.cos *
150
- moon_apparent_equatorial_coordinates.declination.cos *
151
- (
152
- sun_apparent_equatorial_coordinates.right_ascension -
153
- moon_apparent_equatorial_coordinates.right_ascension
154
- ).cos
65
+ sun.apparent.equatorial.declination.sin *
66
+ apparent.equatorial.declination.sin +
67
+ sun.apparent.equatorial.declination.cos *
68
+ apparent.equatorial.declination.cos *
69
+ (
70
+ sun.apparent.equatorial.right_ascension -
71
+ apparent.equatorial.right_ascension
72
+ ).cos
155
73
  )
156
74
 
157
- term1 = sun.earth_distance.km * geocentric_elongation.sin
158
- term2 = distance.km - sun.earth_distance.km * geocentric_elongation.cos
75
+ term1 = sun.astrometric.distance.km * geocentric_elongation.sin
76
+ term2 = astrometric.distance.km -
77
+ sun.astrometric.distance.km * geocentric_elongation.cos
159
78
  angle = Angle.atan(term1 / term2)
160
79
  Astronoby::Util::Trigonometry
161
80
  .adjustement_for_arctangent(term1, term2, angle)
162
81
  end
163
82
  end
164
83
 
165
- # @return [Float] Moon's illuminated fraction
166
- def illuminated_fraction
167
- @illuminated_fraction ||= (1 + phase_angle.cos) / 2
168
- end
169
-
170
- # @return [Float] Phase fraction, from 0 to 1
171
- def current_phase_fraction
172
- mean_elongation.degrees / Constants::DEGREES_PER_CIRCLE
173
- end
174
-
175
- # @param observer [Astronoby::Observer] Observer of the event
176
- # @return [Astronoby::Events::ObservationEvents] Moon's observation events
177
- def observation_events(observer:)
178
- today = @time.to_date
179
- leap_seconds = Util::Time.terrestrial_universal_time_delta(today)
180
- yesterday = today.prev_day
181
- yesterday_midnight_terrestrial_time =
182
- Time.utc(yesterday.year, yesterday.month, yesterday.day) - leap_seconds
183
- today_midnight_terrestrial_time =
184
- Time.utc(today.year, today.month, today.day) - leap_seconds
185
- tomorrow = today.next_day
186
- tomorrow_midnight_terrestrial_time =
187
- Time.utc(tomorrow.year, tomorrow.month, tomorrow.day) - leap_seconds
188
-
189
- coordinates_of_the_previous_day = self.class
190
- .new(time: yesterday_midnight_terrestrial_time)
191
- .apparent_equatorial_coordinates
192
- coordinates_of_the_day = self.class
193
- .new(time: today_midnight_terrestrial_time)
194
- .apparent_equatorial_coordinates
195
- coordinates_of_the_next_day = self.class
196
- .new(time: tomorrow_midnight_terrestrial_time)
197
- .apparent_equatorial_coordinates
198
- additional_altitude = -Angle.from_degrees(
199
- SEMIDIAMETER_VARIATION *
200
- GeocentricParallax.angle(distance: distance).degrees
201
- )
202
-
203
- Events::ObservationEvents.new(
204
- observer: observer,
205
- date: today,
206
- coordinates_of_the_previous_day: coordinates_of_the_previous_day,
207
- coordinates_of_the_day: coordinates_of_the_day,
208
- coordinates_of_the_next_day: coordinates_of_the_next_day,
209
- additional_altitude: additional_altitude
84
+ def mean_elongation
85
+ @mean_elongation ||= Angle.from_degrees(
86
+ (
87
+ 297.8501921 +
88
+ 445267.1114034 * elapsed_centuries -
89
+ 0.0018819 * elapsed_centuries**2 +
90
+ elapsed_centuries**3 / 545868 -
91
+ elapsed_centuries**4 / 113065000
92
+ ) % 360
210
93
  )
211
94
  end
212
95
 
213
- private
214
-
215
- def terrestrial_time
216
- @terrestrial_time ||= begin
217
- delta = Util::Time.terrestrial_universal_time_delta(@time)
218
- @time + delta
219
- end
220
- end
221
-
222
- def julian_date
223
- Epoch.from_time(terrestrial_time)
224
- end
225
-
226
96
  def elapsed_centuries
227
- (julian_date - Epoch::DEFAULT_EPOCH) / Constants::DAYS_PER_JULIAN_CENTURY
228
- end
229
-
230
- def sun
231
- @sun ||= Sun.new(time: @time)
232
- end
233
-
234
- def sun_mean_anomaly
235
- @sun_mean_anomaly ||= sun.mean_anomaly
236
- end
237
-
238
- def a1
239
- @a1 ||= Angle.from_degrees(
240
- (119.75 + 131.849 * elapsed_centuries) % 360
241
- )
242
- end
243
-
244
- def a2
245
- @a2 ||= Angle.from_degrees(
246
- (53.09 + 479264.290 * elapsed_centuries) % 360
247
- )
248
- end
249
-
250
- def a3
251
- @a3 ||= Angle.from_degrees(
252
- (313.45 + 481266.484 * elapsed_centuries) % 360
253
- )
254
- end
255
-
256
- def eccentricity_correction
257
- @eccentricity_correction ||=
258
- 1 - 0.002516 * elapsed_centuries - 0.0000074 * elapsed_centuries**2
259
- end
260
-
261
- def latitude_terms
262
- @latitude_terms ||=
263
- Astronoby::EphemerideLunaireParisienne
264
- .periodic_terms_for_moon_latitude
265
- .inject(0) do |sum, terms|
266
- value = terms[4] * Math.sin(
267
- terms[0] * mean_elongation.radians +
268
- terms[1] * sun_mean_anomaly.radians +
269
- terms[2] * mean_anomaly.radians +
270
- terms[3] * argument_of_latitude.radians
271
- )
272
-
273
- value *= eccentricity_correction if terms[1].abs == 1
274
- value *= eccentricity_correction**2 if terms[1].abs == 2
275
-
276
- sum + value
277
- end
278
- end
279
-
280
- def additive_latitude_terms
281
- @additive_latitude_terms ||=
282
- -2235 * mean_longitude.sin +
283
- 382 * a3.sin +
284
- 175 * (a1 - argument_of_latitude).sin +
285
- 175 * (a1 + argument_of_latitude).sin +
286
- 127 * (mean_longitude - mean_anomaly).sin -
287
- 115 * (mean_longitude + mean_anomaly).sin
288
- end
289
-
290
- def longitude_terms
291
- @longitude_terms ||=
292
- Astronoby::EphemerideLunaireParisienne
293
- .periodic_terms_for_moon_longitude_and_distance
294
- .inject(0) do |sum, terms|
295
- value = terms[4] * Math.sin(
296
- terms[0] * mean_elongation.radians +
297
- terms[1] * sun_mean_anomaly.radians +
298
- terms[2] * mean_anomaly.radians +
299
- terms[3] * argument_of_latitude.radians
300
- )
301
-
302
- value *= eccentricity_correction if terms[1].abs == 1
303
- value *= eccentricity_correction**2 if terms[1].abs == 2
304
-
305
- sum + value
306
- end
307
- end
308
-
309
- def additive_longitude_terms
310
- @additive_longitude_terms ||=
311
- 3958 * a1.sin +
312
- 1962 * (mean_longitude - argument_of_latitude).sin +
313
- 318 * a2.sin
314
- end
315
-
316
- def distance_terms
317
- @distance_terms ||=
318
- EphemerideLunaireParisienne
319
- .periodic_terms_for_moon_longitude_and_distance
320
- .inject(0) do |sum, terms|
321
- value = terms[5] * Math.cos(
322
- terms[0] * mean_elongation.radians +
323
- terms[1] * sun_mean_anomaly.radians +
324
- terms[2] * mean_anomaly.radians +
325
- terms[3] * argument_of_latitude.radians
326
- )
327
-
328
- value *= eccentricity_correction if terms[1].abs == 1
329
- value *= eccentricity_correction**2 if terms[1].abs == 2
330
-
331
- sum + value
332
- end
333
- end
334
-
335
- def nutation
336
- @nutation ||=
337
- Astronoby::Nutation.for_ecliptic_longitude(epoch: julian_date)
97
+ (@instant.tt - Epoch::DEFAULT_EPOCH) / Constants::DAYS_PER_JULIAN_CENTURY
338
98
  end
339
99
  end
340
100
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Neptune < SolarSystemBody
5
+ EQUATORIAL_RADIUS = Distance.from_meters(24_764_000)
6
+
7
+ def self.ephemeris_segments(_ephem_source)
8
+ [[SOLAR_SYSTEM_BARYCENTER, NEPTUNE_BARYCENTER]]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Saturn < SolarSystemBody
5
+ EQUATORIAL_RADIUS = Distance.from_meters(60_268_000)
6
+
7
+ def self.ephemeris_segments(_ephem_source)
8
+ [[SOLAR_SYSTEM_BARYCENTER, SATURN_BARYCENTER]]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class SolarSystemBody
5
+ SOLAR_SYSTEM_BARYCENTER = 0
6
+ SUN = 10
7
+ MERCURY_BARYCENTER = 1
8
+ MERCURY = 199
9
+ VENUS_BARYCENTER = 2
10
+ VENUS = 299
11
+ EARTH_MOON_BARYCENTER = 3
12
+ EARTH = 399
13
+ MOON = 301
14
+ MARS_BARYCENTER = 4
15
+ JUPITER_BARYCENTER = 5
16
+ SATURN_BARYCENTER = 6
17
+ URANUS_BARYCENTER = 7
18
+ NEPTUNE_BARYCENTER = 8
19
+
20
+ attr_reader :geometric, :instant
21
+
22
+ def self.geometric(ephem:, instant:)
23
+ compute_geometric(ephem: ephem, instant: instant)
24
+ end
25
+
26
+ def self.compute_geometric(ephem:, instant:)
27
+ segments = ephemeris_segments(ephem.type)
28
+ segment1 = segments[0]
29
+ segment2 = segments[1] if segments.size == 2
30
+
31
+ state1 = ephem[*segment1].state_at(instant.terrestrial_time)
32
+
33
+ if segment2
34
+ state2 = ephem[*segment2].state_at(instant.terrestrial_time)
35
+ position = state1.position + state2.position
36
+ velocity = state1.velocity + state2.velocity
37
+ else
38
+ position = state1.position
39
+ velocity = state1.velocity
40
+ end
41
+
42
+ position_vector = Vector[
43
+ Distance.from_kilometers(position.x),
44
+ Distance.from_kilometers(position.y),
45
+ Distance.from_kilometers(position.z)
46
+ ]
47
+
48
+ velocity_vector = Vector[
49
+ Velocity.from_kilometers_per_day(velocity.x),
50
+ Velocity.from_kilometers_per_day(velocity.y),
51
+ Velocity.from_kilometers_per_day(velocity.z)
52
+ ]
53
+
54
+ Geometric.new(
55
+ position: position_vector,
56
+ velocity: velocity_vector,
57
+ instant: instant,
58
+ target_body: self
59
+ )
60
+ end
61
+
62
+ def self.ephemeris_segments(_ephem_source)
63
+ raise NotImplementedError
64
+ end
65
+
66
+ def initialize(ephem:, instant:)
67
+ @instant = instant
68
+ @geometric = compute_geometric(ephem)
69
+ @earth_geometric = Earth.geometric(ephem: ephem, instant: instant)
70
+ @light_time_corrected_position,
71
+ @light_time_corrected_velocity =
72
+ Correction::LightTimeDelay.compute(
73
+ center: @earth_geometric,
74
+ target: @geometric,
75
+ ephem: ephem
76
+ )
77
+ end
78
+
79
+ def astrometric
80
+ @astrometric ||= Astrometric.build_from_geometric(
81
+ instant: @instant,
82
+ earth_geometric: @earth_geometric,
83
+ light_time_corrected_position: @light_time_corrected_position,
84
+ light_time_corrected_velocity: @light_time_corrected_velocity,
85
+ target_body: self
86
+ )
87
+ end
88
+
89
+ def mean_of_date
90
+ @mean_of_date ||= MeanOfDate.build_from_geometric(
91
+ instant: @instant,
92
+ target_geometric: @geometric,
93
+ earth_geometric: @earth_geometric,
94
+ target_body: self
95
+ )
96
+ end
97
+
98
+ def apparent
99
+ @apparent ||= Apparent.build_from_astrometric(
100
+ instant: @instant,
101
+ target_astrometric: astrometric,
102
+ earth_geometric: @earth_geometric,
103
+ target_body: self
104
+ )
105
+ end
106
+
107
+ def observed_by(observer)
108
+ Topocentric.build_from_apparent(
109
+ apparent: apparent,
110
+ observer: observer,
111
+ instant: @instant,
112
+ target_body: self
113
+ )
114
+ end
115
+
116
+ private
117
+
118
+ def compute_geometric(ephem)
119
+ self.class.compute_geometric(ephem: ephem, instant: @instant)
120
+ end
121
+ end
122
+ end