astronoby 0.3.0 → 0.5.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -0
  3. data/Gemfile.lock +19 -17
  4. data/README.md +195 -25
  5. data/UPGRADING.md +72 -0
  6. data/lib/astronoby/aberration.rb +7 -5
  7. data/lib/astronoby/angle.rb +26 -34
  8. data/lib/astronoby/astronomical_models/ephemeride_lunaire_parisienne.rb +143 -0
  9. data/lib/astronoby/astronomical_models/moon_phases_periodic_terms.rb +249 -0
  10. data/lib/astronoby/bodies/moon.rb +335 -0
  11. data/lib/astronoby/bodies/sun.rb +129 -132
  12. data/lib/astronoby/constants.rb +31 -0
  13. data/lib/astronoby/coordinates/ecliptic.rb +4 -4
  14. data/lib/astronoby/coordinates/equatorial.rb +7 -5
  15. data/lib/astronoby/coordinates/horizontal.rb +24 -12
  16. data/lib/astronoby/distance.rb +83 -0
  17. data/lib/astronoby/epoch.rb +0 -2
  18. data/lib/astronoby/equinox_solstice.rb +3 -2
  19. data/lib/astronoby/events/moon_phases.rb +143 -0
  20. data/lib/astronoby/events/observation_events.rb +259 -0
  21. data/lib/astronoby/events/rise_transit_set_iteration.rb +215 -0
  22. data/lib/astronoby/events/twilight_events.rb +121 -0
  23. data/lib/astronoby/geocentric_parallax.rb +36 -56
  24. data/lib/astronoby/mean_obliquity.rb +2 -2
  25. data/lib/astronoby/moon_phase.rb +43 -0
  26. data/lib/astronoby/nutation.rb +5 -3
  27. data/lib/astronoby/observer.rb +39 -18
  28. data/lib/astronoby/precession.rb +1 -1
  29. data/lib/astronoby/refraction.rb +8 -10
  30. data/lib/astronoby/time/greenwich_sidereal_time.rb +18 -29
  31. data/lib/astronoby/time/local_sidereal_time.rb +4 -4
  32. data/lib/astronoby/util/maths.rb +72 -0
  33. data/lib/astronoby/util/time.rb +88 -0
  34. data/lib/astronoby/util/trigonometry.rb +4 -4
  35. data/lib/astronoby/version.rb +1 -1
  36. data/lib/astronoby.rb +12 -1
  37. metadata +15 -4
  38. data/lib/astronoby/body.rb +0 -155
@@ -0,0 +1,335 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Moon
5
+ SEMIDIAMETER_VARIATION = 0.7275
6
+ MEAN_GEOCENTRIC_DISTANCE = Astronoby::Distance.from_meters(385_000_560)
7
+
8
+ # Source:
9
+ # Title: Astronomical Algorithms
10
+ # Author: Jean Meeus
11
+ # Edition: 2nd edition
12
+ # Chapter: 49 - Phases of the Moon
13
+
14
+ # @param year [Integer] Requested year
15
+ # @param month [Integer] Requested month
16
+ # @return [Array<Astronoby::MoonPhase>] Moon phases for the requested year
17
+ def self.monthly_phase_events(year:, month:)
18
+ Events::MoonPhases.phases_for(year: year, month: month)
19
+ end
20
+
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
+ )
68
+
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
+ )
93
+ end
94
+
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
+ )
106
+ end
107
+
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
+ )
119
+ end
120
+
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
132
+
133
+ # Source:
134
+ # Title: Astronomical Algorithms
135
+ # Author: Jean Meeus
136
+ # Edition: 2nd edition
137
+ # Chapter: 48 - Illuminated Fraction of the Moon's Disk
138
+
139
+ # @return [Angle] Moon's phase angle
140
+ def phase_angle
141
+ @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
146
+ 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
155
+ )
156
+
157
+ term1 = sun.earth_distance.km * geocentric_elongation.sin
158
+ term2 = distance.km - sun.earth_distance.km * geocentric_elongation.cos
159
+ angle = Angle.atan(term1 / term2)
160
+ Astronoby::Util::Trigonometry
161
+ .adjustement_for_arctangent(term1, term2, angle)
162
+ end
163
+ end
164
+
165
+ # @return [Float] Moon's illuminated fraction
166
+ def illuminated_fraction
167
+ @illuminated_fraction ||= (1 + phase_angle.cos) / 2
168
+ end
169
+
170
+ # @param observer [Astronoby::Observer] Observer of the event
171
+ # @return [Astronoby::Events::ObservationEvents] Moon's observation events
172
+ def observation_events(observer:)
173
+ today = @time.to_date
174
+ leap_seconds = Util::Time.terrestrial_universal_time_delta(today)
175
+ yesterday = today.prev_day
176
+ yesterday_midnight_terrestrial_time =
177
+ Time.utc(yesterday.year, yesterday.month, yesterday.day) - leap_seconds
178
+ today_midnight_terrestrial_time =
179
+ Time.utc(today.year, today.month, today.day) - leap_seconds
180
+ tomorrow = today.next_day
181
+ tomorrow_midnight_terrestrial_time =
182
+ Time.utc(tomorrow.year, tomorrow.month, tomorrow.day) - leap_seconds
183
+
184
+ coordinates_of_the_previous_day = self.class
185
+ .new(time: yesterday_midnight_terrestrial_time)
186
+ .apparent_equatorial_coordinates
187
+ coordinates_of_the_day = self.class
188
+ .new(time: today_midnight_terrestrial_time)
189
+ .apparent_equatorial_coordinates
190
+ coordinates_of_the_next_day = self.class
191
+ .new(time: tomorrow_midnight_terrestrial_time)
192
+ .apparent_equatorial_coordinates
193
+ additional_altitude = -Angle.from_degrees(
194
+ SEMIDIAMETER_VARIATION *
195
+ GeocentricParallax.angle(distance: distance).degrees
196
+ )
197
+
198
+ Events::ObservationEvents.new(
199
+ observer: observer,
200
+ date: today,
201
+ coordinates_of_the_previous_day: coordinates_of_the_previous_day,
202
+ coordinates_of_the_day: coordinates_of_the_day,
203
+ coordinates_of_the_next_day: coordinates_of_the_next_day,
204
+ additional_altitude: additional_altitude
205
+ )
206
+ end
207
+
208
+ private
209
+
210
+ def terrestrial_time
211
+ @terrestrial_time ||= begin
212
+ delta = Util::Time.terrestrial_universal_time_delta(@time)
213
+ @time + delta
214
+ end
215
+ end
216
+
217
+ def julian_date
218
+ Epoch.from_time(terrestrial_time)
219
+ end
220
+
221
+ def elapsed_centuries
222
+ (julian_date - Epoch::DEFAULT_EPOCH) / Constants::DAYS_PER_JULIAN_CENTURY
223
+ end
224
+
225
+ def sun
226
+ @sun ||= Sun.new(time: @time)
227
+ end
228
+
229
+ def sun_mean_anomaly
230
+ @sun_mean_anomaly ||= sun.mean_anomaly
231
+ end
232
+
233
+ def a1
234
+ @a1 ||= Angle.from_degrees(
235
+ (119.75 + 131.849 * elapsed_centuries) % 360
236
+ )
237
+ end
238
+
239
+ def a2
240
+ @a2 ||= Angle.from_degrees(
241
+ (53.09 + 479264.290 * elapsed_centuries) % 360
242
+ )
243
+ end
244
+
245
+ def a3
246
+ @a3 ||= Angle.from_degrees(
247
+ (313.45 + 481266.484 * elapsed_centuries) % 360
248
+ )
249
+ end
250
+
251
+ def eccentricity_correction
252
+ @eccentricity_correction ||=
253
+ 1 - 0.002516 * elapsed_centuries - 0.0000074 * elapsed_centuries**2
254
+ end
255
+
256
+ def latitude_terms
257
+ @latitude_terms ||=
258
+ Astronoby::EphemerideLunaireParisienne
259
+ .periodic_terms_for_moon_latitude
260
+ .inject(0) do |sum, terms|
261
+ value = terms[4] * Math.sin(
262
+ terms[0] * mean_elongation.radians +
263
+ terms[1] * sun_mean_anomaly.radians +
264
+ terms[2] * mean_anomaly.radians +
265
+ terms[3] * argument_of_latitude.radians
266
+ )
267
+
268
+ value *= eccentricity_correction if terms[1].abs == 1
269
+ value *= eccentricity_correction**2 if terms[1].abs == 2
270
+
271
+ sum + value
272
+ end
273
+ end
274
+
275
+ def additive_latitude_terms
276
+ @additive_latitude_terms ||=
277
+ -2235 * mean_longitude.sin +
278
+ 382 * a3.sin +
279
+ 175 * (a1 - argument_of_latitude).sin +
280
+ 175 * (a1 + argument_of_latitude).sin +
281
+ 127 * (mean_longitude - mean_anomaly).sin -
282
+ 115 * (mean_longitude + mean_anomaly).sin
283
+ end
284
+
285
+ def longitude_terms
286
+ @longitude_terms ||=
287
+ Astronoby::EphemerideLunaireParisienne
288
+ .periodic_terms_for_moon_longitude_and_distance
289
+ .inject(0) do |sum, terms|
290
+ value = terms[4] * Math.sin(
291
+ terms[0] * mean_elongation.radians +
292
+ terms[1] * sun_mean_anomaly.radians +
293
+ terms[2] * mean_anomaly.radians +
294
+ terms[3] * argument_of_latitude.radians
295
+ )
296
+
297
+ value *= eccentricity_correction if terms[1].abs == 1
298
+ value *= eccentricity_correction**2 if terms[1].abs == 2
299
+
300
+ sum + value
301
+ end
302
+ end
303
+
304
+ def additive_longitude_terms
305
+ @additive_longitude_terms ||=
306
+ 3958 * a1.sin +
307
+ 1962 * (mean_longitude - argument_of_latitude).sin +
308
+ 318 * a2.sin
309
+ end
310
+
311
+ def distance_terms
312
+ @distance_terms ||=
313
+ EphemerideLunaireParisienne
314
+ .periodic_terms_for_moon_longitude_and_distance
315
+ .inject(0) do |sum, terms|
316
+ value = terms[5] * Math.cos(
317
+ terms[0] * mean_elongation.radians +
318
+ terms[1] * sun_mean_anomaly.radians +
319
+ terms[2] * mean_anomaly.radians +
320
+ terms[3] * argument_of_latitude.radians
321
+ )
322
+
323
+ value *= eccentricity_correction if terms[1].abs == 1
324
+ value *= eccentricity_correction**2 if terms[1].abs == 2
325
+
326
+ sum + value
327
+ end
328
+ end
329
+
330
+ def nutation
331
+ @nutation ||=
332
+ Astronoby::Nutation.for_ecliptic_longitude(epoch: julian_date)
333
+ end
334
+ end
335
+ end
@@ -4,30 +4,63 @@ module Astronoby
4
4
  class Sun
5
5
  SEMI_MAJOR_AXIS_IN_METERS = 149_598_500_000
6
6
  ANGULAR_DIAMETER = Angle.from_degrees(0.533128)
7
- INTERPOLATION_FACTOR = BigDecimal("24.07")
7
+ INTERPOLATION_FACTOR = 24.07
8
+
9
+ TWILIGHTS = [
10
+ CIVIL = :civil,
11
+ NAUTICAL = :nautical,
12
+ ASTRONOMICAL = :astronomical
13
+ ].freeze
14
+
15
+ TWILIGHT_ANGLES = {
16
+ CIVIL => Angle.from_degrees(96),
17
+ NAUTICAL => Angle.from_degrees(102),
18
+ ASTRONOMICAL => Angle.from_degrees(108)
19
+ }.freeze
20
+
21
+ PERIODS_OF_THE_DAY = [
22
+ MORNING = :morning,
23
+ EVENING = :evening
24
+ ].freeze
25
+
26
+ attr_reader :time
8
27
 
9
28
  # Source:
10
- # Title: Practical Astronomy with your Calculator or Spreadsheet
11
- # Authors: Peter Duffett-Smith and Jonathan Zwart
12
- # Edition: Cambridge University Press
13
- # Chapter: 51 - The equation of time
29
+ # Title: Astronomical Algorithms
30
+ # Author: Jean Meeus
31
+ # Edition: 2nd edition
32
+ # Chapter: 28 - Equation of Time
14
33
 
15
- # @param date [Date] Requested date
34
+ # @param date_or_time [Date, Time] Requested date
16
35
  # @return [Integer] Equation of time in seconds
17
- def self.equation_of_time(date:)
18
- noon = Time.utc(date.year, date.month, date.day, 12)
19
- epoch_at_noon = Epoch.from_time(noon)
20
- sun_at_noon = new(epoch: epoch_at_noon)
21
- equatorial_hours = sun_at_noon
36
+ def self.equation_of_time(date_or_time:)
37
+ noon = Time.utc(date_or_time.year, date_or_time.month, date_or_time.day, 12)
38
+ time = date_or_time.is_a?(Time) ? date_or_time : noon
39
+ epoch = Epoch.from_time(time)
40
+ sun = new(time: time)
41
+ right_ascension = sun
22
42
  .apparent_ecliptic_coordinates
23
- .to_apparent_equatorial(epoch: epoch_at_noon)
43
+ .to_apparent_equatorial(epoch: epoch)
24
44
  .right_ascension
25
- .hours
26
- gst = GreenwichSiderealTime
27
- .new(date: date, time: equatorial_hours)
28
- .to_utc
29
-
30
- (noon - gst).to_i
45
+ t = (epoch - Epoch::J2000) / Constants::DAYS_PER_JULIAN_MILLENIA
46
+ l0 = (280.4664567 +
47
+ 360_007.6982779 * t +
48
+ 0.03032028 * t**2 +
49
+ t**3 / 49_931 -
50
+ t**4 / 15_300 -
51
+ t**5 / 2_000_000) % Constants::DEGREES_PER_CIRCLE
52
+ nutation = Nutation.for_ecliptic_longitude(epoch: epoch)
53
+ obliquity = TrueObliquity.for_epoch(epoch)
54
+
55
+ (
56
+ Angle
57
+ .from_degrees(
58
+ l0 -
59
+ Constants::EQUATION_OF_TIME_CONSTANT -
60
+ right_ascension.degrees +
61
+ nutation.degrees * obliquity.cos
62
+ ).hours * Constants::SECONDS_PER_HOUR
63
+ ).round
31
64
  end
32
65
 
33
66
  # Source:
@@ -36,9 +69,13 @@ module Astronoby
36
69
  # Edition: MIT Press
37
70
  # Chapter: 6 - The Sun
38
71
 
39
- # @param epoch [Numeric] Considered epoch, in Julian days
40
- def initialize(epoch:)
41
- @epoch = epoch
72
+ # @param time [Time] Considered time
73
+ def initialize(time:)
74
+ @time = time
75
+ end
76
+
77
+ def epoch
78
+ @epoch ||= Epoch.from_time(@time)
42
79
  end
43
80
 
44
81
  def true_ecliptic_coordinates
@@ -49,10 +86,10 @@ module Astronoby
49
86
  end
50
87
 
51
88
  def apparent_ecliptic_coordinates
52
- nutation = Nutation.for_ecliptic_longitude(epoch: @epoch)
89
+ nutation = Nutation.for_ecliptic_longitude(epoch: epoch)
53
90
  longitude_with_aberration = Aberration.for_ecliptic_coordinates(
54
91
  coordinates: true_ecliptic_coordinates,
55
- epoch: @epoch
92
+ epoch: epoch
56
93
  ).longitude
57
94
  apparent_longitude = nutation + longitude_with_aberration
58
95
 
@@ -64,74 +101,65 @@ module Astronoby
64
101
 
65
102
  # Computes the Sun's horizontal coordinates
66
103
  #
67
- # @param latitude [Astronoby::Angle] Latitude of the observer
68
- # @param longitude [Astronoby::Angle] Longitude of the observer
104
+ # @param observer [Astronoby::Observer] Observer of the event
69
105
  # @return [Astronoby::Coordinates::Horizontal] Sun's horizontal coordinates
70
- def horizontal_coordinates(latitude:, longitude:)
71
- time = Epoch.to_utc(@epoch)
72
-
106
+ def horizontal_coordinates(observer:)
73
107
  apparent_ecliptic_coordinates
74
- .to_apparent_equatorial(epoch: @epoch)
75
- .to_horizontal(time: time, latitude: latitude, longitude: longitude)
76
- end
77
-
78
- # @param observer [Astronoby::Observer] Observer of the event
79
- # @return [Time] Time of sunrise
80
- def rising_time(observer:)
81
- event_date = Epoch.to_utc(@epoch).to_date
82
- lst1 = event_local_sidereal_time_for_date(event_date, observer, :rising)
83
- next_day = event_date.next_day(1)
84
- lst2 = event_local_sidereal_time_for_date(next_day, observer, :rising)
85
- time = (INTERPOLATION_FACTOR * lst1) / (INTERPOLATION_FACTOR + lst1 - lst2)
86
-
87
- LocalSiderealTime.new(
88
- date: event_date,
89
- time: time,
90
- longitude: observer.longitude
91
- ).to_gst.to_utc
108
+ .to_apparent_equatorial(epoch: epoch)
109
+ .to_horizontal(time: @time, observer: observer)
92
110
  end
93
111
 
94
112
  # @param observer [Astronoby::Observer] Observer of the event
95
- # @return [Astronoby::Angle, nil] Azimuth of sunrise
96
- def rising_azimuth(observer:)
97
- equatorial_coordinates = apparent_ecliptic_coordinates
98
- .to_apparent_equatorial(epoch: @epoch)
99
- Body.new(equatorial_coordinates).rising_azimuth(
100
- latitude: observer.latitude,
101
- vertical_shift: vertical_shift
113
+ # @return [Astronoby::Events::ObservationEvents] Sun's observation events
114
+ def observation_events(observer:)
115
+ today = @time.to_date
116
+ leap_seconds = Util::Time.terrestrial_universal_time_delta(today)
117
+ yesterday = today.prev_day
118
+ yesterday_midnight_terrestrial_time =
119
+ Time.utc(yesterday.year, yesterday.month, yesterday.day) - leap_seconds
120
+ yesterday_epoch = Epoch.from_time(yesterday_midnight_terrestrial_time)
121
+ today_midnight_terrestrial_time =
122
+ Time.utc(today.year, today.month, today.day) - leap_seconds
123
+ today_epoch = Epoch.from_time(today_midnight_terrestrial_time)
124
+ tomorrow = today.next_day
125
+ tomorrow_midnight_terrestrial_time =
126
+ Time.utc(tomorrow.year, tomorrow.month, tomorrow.day) - leap_seconds
127
+ tomorrow_epoch = Epoch.from_time(tomorrow_midnight_terrestrial_time)
128
+
129
+ coordinates_of_the_previous_day = self.class
130
+ .new(time: yesterday_midnight_terrestrial_time)
131
+ .apparent_ecliptic_coordinates
132
+ .to_apparent_equatorial(epoch: yesterday_epoch)
133
+ coordinates_of_the_day = self.class
134
+ .new(time: today_midnight_terrestrial_time)
135
+ .apparent_ecliptic_coordinates
136
+ .to_apparent_equatorial(epoch: today_epoch)
137
+ coordinates_of_the_next_day = self.class
138
+ .new(time: tomorrow_midnight_terrestrial_time)
139
+ .apparent_ecliptic_coordinates
140
+ .to_apparent_equatorial(epoch: tomorrow_epoch)
141
+
142
+ Events::ObservationEvents.new(
143
+ observer: observer,
144
+ date: today,
145
+ coordinates_of_the_previous_day: coordinates_of_the_previous_day,
146
+ coordinates_of_the_day: coordinates_of_the_day,
147
+ coordinates_of_the_next_day: coordinates_of_the_next_day,
148
+ additional_altitude: Angle.from_degrees(angular_size.degrees / 2)
102
149
  )
103
150
  end
104
151
 
105
- # @param observer [Astronoby::Observer] Observer of the event
106
- # @return [Time] Time of sunset
107
- def setting_time(observer:)
108
- event_date = Epoch.to_utc(@epoch).to_date
109
- lst1 = event_local_sidereal_time_for_date(event_date, observer, :setting)
110
- next_day = event_date.next_day(1)
111
- lst2 = event_local_sidereal_time_for_date(next_day, observer, :setting)
112
- time = (INTERPOLATION_FACTOR * lst1) / (INTERPOLATION_FACTOR + lst1 - lst2)
113
-
114
- LocalSiderealTime.new(
115
- date: event_date,
116
- time: time,
117
- longitude: observer.longitude
118
- ).to_gst.to_utc
152
+ # @param observer [Astronoby::Observer] Observer of the events
153
+ # @return [Astronoby::Events::TwilightEvents] Sun's twilight events
154
+ def twilight_events(observer:)
155
+ Events::TwilightEvents.new(sun: self, observer: observer)
119
156
  end
120
157
 
121
- # @param observer [Astronoby::Observer] Observer of the event
122
- # @return [Astronoby::Angle, nil] Azimuth of sunset
123
- def setting_azimuth(observer:)
124
- equatorial_coordinates = apparent_ecliptic_coordinates
125
- .to_apparent_equatorial(epoch: @epoch)
126
- Body.new(equatorial_coordinates).setting_azimuth(
127
- latitude: observer.latitude,
128
- vertical_shift: vertical_shift
129
- )
130
- end
131
-
132
- # @return [Numeric] Earth-Sun distance in meters
158
+ # @return [Astronoby::Distance] Earth-Sun distance
133
159
  def earth_distance
134
- SEMI_MAJOR_AXIS_IN_METERS / distance_angular_size_factor
160
+ Distance.from_meters(
161
+ SEMI_MAJOR_AXIS_IN_METERS / distance_angular_size_factor
162
+ )
135
163
  end
136
164
 
137
165
  # @return [Astronoby::Angle] Apparent Sun's angular size
@@ -154,20 +182,32 @@ module Astronoby
154
182
  (1 + orbital_eccentricity.degrees) / (1 - orbital_eccentricity.degrees)
155
183
  ) * Math.tan(eccentric_anomaly.radians / 2)
156
184
 
157
- Angle.from_degrees((Angle.atan(tan).degrees * 2) % 360)
185
+ Angle.from_degrees(
186
+ (Angle.atan(tan).degrees * 2) % Constants::DEGREES_PER_CIRCLE
187
+ )
188
+ end
189
+
190
+ # @return [Astronoby::Angle] Sun's mean anomaly
191
+ def mean_anomaly
192
+ Angle.from_degrees(
193
+ (longitude_at_base_epoch - longitude_at_perigee).degrees %
194
+ Constants::DEGREES_PER_CIRCLE
195
+ )
158
196
  end
159
197
 
160
198
  # @return [Astronoby::Angle] Sun's longitude at perigee
161
199
  def longitude_at_perigee
162
200
  Angle.from_degrees(
163
- (281.2208444 + 1.719175 * centuries + 0.000452778 * centuries**2) % 360
201
+ (281.2208444 + 1.719175 * centuries + 0.000452778 * centuries**2) %
202
+ Constants::DEGREES_PER_CIRCLE
164
203
  )
165
204
  end
166
205
 
167
206
  # @return [Astronoby::Angle] Sun's orbital eccentricity
168
207
  def orbital_eccentricity
169
208
  Angle.from_degrees(
170
- (0.01675104 - 0.0000418 * centuries - 0.000000126 * centuries**2) % 360
209
+ (0.01675104 - 0.0000418 * centuries - 0.000000126 * centuries**2) %
210
+ Constants::DEGREES_PER_CIRCLE
171
211
  )
172
212
  end
173
213
 
@@ -175,27 +215,23 @@ module Astronoby
175
215
 
176
216
  def true_longitude
177
217
  Angle.from_degrees(
178
- (true_anomaly + longitude_at_perigee).degrees % 360
179
- )
180
- end
181
-
182
- def mean_anomaly
183
- Angle.from_degrees(
184
- (longitude_at_base_epoch - longitude_at_perigee).degrees % 360
218
+ (true_anomaly + longitude_at_perigee).degrees %
219
+ Constants::DEGREES_PER_CIRCLE
185
220
  )
186
221
  end
187
222
 
188
223
  def days_since_epoch
189
- Epoch::DEFAULT_EPOCH - @epoch
224
+ Epoch::DEFAULT_EPOCH - epoch
190
225
  end
191
226
 
192
227
  def centuries
193
- @centuries ||= (@epoch - Epoch::J1900) / Epoch::DAYS_PER_JULIAN_CENTURY
228
+ @centuries ||= (epoch - Epoch::J1900) / Constants::DAYS_PER_JULIAN_CENTURY
194
229
  end
195
230
 
196
231
  def longitude_at_base_epoch
197
232
  Angle.from_degrees(
198
- (279.6966778 + 36000.76892 * centuries + 0.0003025 * centuries**2) % 360
233
+ (279.6966778 + 36000.76892 * centuries + 0.0003025 * centuries**2) %
234
+ Constants::DEGREES_PER_CIRCLE
199
235
  )
200
236
  end
201
237
 
@@ -205,44 +241,5 @@ module Astronoby
205
241
 
206
242
  term1 / term2
207
243
  end
208
-
209
- def event_local_sidereal_time_for_date(date, observer, event)
210
- midnight_utc = Time.utc(date.year, date.month, date.day)
211
- epoch = Epoch.from_time(midnight_utc)
212
- sun_at_midnight = self.class.new(epoch: epoch)
213
- shift = Body::DEFAULT_REFRACTION_VERTICAL_SHIFT +
214
- GeocentricParallax.angle(distance: sun_at_midnight.earth_distance) +
215
- Angle.from_degrees(sun_at_midnight.angular_size.degrees / 2)
216
- ecliptic_coordinates = sun_at_midnight.apparent_ecliptic_coordinates
217
- equatorial_coordinates = ecliptic_coordinates
218
- .to_apparent_equatorial(epoch: epoch)
219
-
220
- event_time = if event == :rising
221
- Body.new(equatorial_coordinates).rising_time(
222
- latitude: observer.latitude,
223
- longitude: observer.longitude,
224
- date: midnight_utc.to_date,
225
- vertical_shift: shift
226
- )
227
- else
228
- Body.new(equatorial_coordinates).setting_time(
229
- latitude: observer.latitude,
230
- longitude: observer.longitude,
231
- date: midnight_utc.to_date,
232
- vertical_shift: shift
233
- )
234
- end
235
-
236
- GreenwichSiderealTime
237
- .from_utc(event_time.utc)
238
- .to_lst(longitude: observer.longitude)
239
- .time
240
- end
241
-
242
- def vertical_shift
243
- Astronoby::Body::DEFAULT_REFRACTION_VERTICAL_SHIFT +
244
- Astronoby::GeocentricParallax.angle(distance: earth_distance) +
245
- Astronoby::Angle.from_degrees(angular_size.degrees / 2)
246
- end
247
244
  end
248
245
  end