astronoby 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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