astronoby 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -0
- data/Gemfile.lock +3 -3
- data/README.md +52 -17
- data/UPGRADING.md +24 -0
- data/lib/astronoby/aberration.rb +7 -5
- data/lib/astronoby/angle.rb +26 -34
- data/lib/astronoby/bodies/sun.rb +117 -122
- data/lib/astronoby/constants.rb +25 -0
- data/lib/astronoby/coordinates/ecliptic.rb +4 -4
- data/lib/astronoby/coordinates/equatorial.rb +3 -2
- data/lib/astronoby/coordinates/horizontal.rb +6 -2
- data/lib/astronoby/epoch.rb +0 -2
- data/lib/astronoby/equinox_solstice.rb +3 -2
- data/lib/astronoby/events/observation_events.rb +319 -0
- data/lib/astronoby/events/twilight_events.rb +121 -0
- data/lib/astronoby/geocentric_parallax.rb +2 -2
- data/lib/astronoby/mean_obliquity.rb +2 -2
- data/lib/astronoby/nutation.rb +5 -3
- data/lib/astronoby/observer.rb +33 -14
- data/lib/astronoby/precession.rb +1 -1
- data/lib/astronoby/time/greenwich_sidereal_time.rb +18 -29
- data/lib/astronoby/time/local_sidereal_time.rb +4 -4
- data/lib/astronoby/util/maths.rb +72 -0
- data/lib/astronoby/util/time.rb +88 -0
- data/lib/astronoby/util/trigonometry.rb +4 -4
- data/lib/astronoby/version.rb +1 -1
- data/lib/astronoby.rb +5 -1
- metadata +8 -4
- data/lib/astronoby/body.rb +0 -155
@@ -0,0 +1,319 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
module Events
|
5
|
+
class ObservationEvents
|
6
|
+
STANDARD_ALTITUDE = Angle.from_dms(0, -34, 0)
|
7
|
+
RISING_SETTING_HOUR_ANGLE_RATIO_RANGE = (-1..1)
|
8
|
+
EARTH_SIDEREAL_ROTATION_RATE = 360.98564736629
|
9
|
+
|
10
|
+
attr_reader :rising_time,
|
11
|
+
:rising_azimuth,
|
12
|
+
:transit_time,
|
13
|
+
:transit_altitude,
|
14
|
+
:setting_time,
|
15
|
+
:setting_azimuth
|
16
|
+
|
17
|
+
# Source:
|
18
|
+
# Title: Astronomical Algorithms
|
19
|
+
# Author: Jean Meeus
|
20
|
+
# Edition: 2nd edition
|
21
|
+
# Chapter: 15 - Rising, Transit, and Setting
|
22
|
+
|
23
|
+
# @param observer [Astronoby::Observer] Observer
|
24
|
+
# @param date [Date] Date of the event
|
25
|
+
# @param coordinates_of_the_previous_day [Astronoby::Coordinates::Equatorial]
|
26
|
+
# Coordinates of the body of the previous day
|
27
|
+
# @param coordinates_of_the_day [Astronoby::Coordinates::Equatorial]
|
28
|
+
# Coordinates of the body of the day
|
29
|
+
# @param coordinates_of_the_next_day [Astronoby::Coordinates::Equatorial]
|
30
|
+
# Coordinates of the body of the next day
|
31
|
+
# @param additional_altitude [Astronoby::Angle] Additional altitude to the
|
32
|
+
# standard altitude adjustment
|
33
|
+
def initialize(
|
34
|
+
observer:,
|
35
|
+
date:,
|
36
|
+
coordinates_of_the_previous_day:,
|
37
|
+
coordinates_of_the_day:,
|
38
|
+
coordinates_of_the_next_day:,
|
39
|
+
additional_altitude: Angle.zero
|
40
|
+
)
|
41
|
+
@observer = observer
|
42
|
+
@date = date
|
43
|
+
@coordinates_of_the_previous_day = coordinates_of_the_previous_day
|
44
|
+
@coordinates_of_the_day = coordinates_of_the_day
|
45
|
+
@coordinates_of_the_next_day = coordinates_of_the_next_day
|
46
|
+
@standard_altitude = STANDARD_ALTITUDE
|
47
|
+
@additional_altitude = additional_altitude
|
48
|
+
compute
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def compute
|
54
|
+
@transit_time = Util::Time.decimal_hour_to_time(@date, initial_transit)
|
55
|
+
@transit_altitude = local_horizontal_altitude_transit
|
56
|
+
|
57
|
+
return if h0.nil?
|
58
|
+
|
59
|
+
delta_m_rising = (local_horizontal_altitude_rising - shift).degrees./(
|
60
|
+
Constants::DEGREES_PER_CIRCLE *
|
61
|
+
declination_rising.cos *
|
62
|
+
@observer.latitude.cos *
|
63
|
+
local_hour_angle_rising.sin
|
64
|
+
)
|
65
|
+
delta_m_transit = -local_hour_angle_transit.degrees / Constants::DEGREES_PER_CIRCLE
|
66
|
+
delta_m_setting = (local_horizontal_altitude_setting - shift).degrees./(
|
67
|
+
Constants::DEGREES_PER_CIRCLE *
|
68
|
+
declination_setting.cos *
|
69
|
+
@observer.latitude.cos *
|
70
|
+
local_hour_angle_setting.sin
|
71
|
+
)
|
72
|
+
|
73
|
+
corrected_rising = rationalize_decimal_hours(
|
74
|
+
Constants::HOURS_PER_DAY * (initial_rising + delta_m_rising)
|
75
|
+
)
|
76
|
+
corrected_transit = rationalize_decimal_hours(
|
77
|
+
Constants::HOURS_PER_DAY * (initial_transit + delta_m_transit)
|
78
|
+
)
|
79
|
+
corrected_setting = rationalize_decimal_hours(
|
80
|
+
Constants::HOURS_PER_DAY * (initial_setting + delta_m_setting)
|
81
|
+
)
|
82
|
+
|
83
|
+
@rising_time = Util::Time.decimal_hour_to_time(@date, corrected_rising)
|
84
|
+
@rising_azimuth = local_horizontal_azimuth_rising
|
85
|
+
@transit_time = Util::Time.decimal_hour_to_time(@date, corrected_transit)
|
86
|
+
@setting_time = Util::Time.decimal_hour_to_time(@date, corrected_setting)
|
87
|
+
@setting_azimuth = local_horizontal_azimuth_setting
|
88
|
+
end
|
89
|
+
|
90
|
+
def observer_longitude
|
91
|
+
# Longitude must be treated positively westwards from the meridian of
|
92
|
+
# Greenwich, and negatively to the east
|
93
|
+
@observer_longitude ||= -@observer.longitude
|
94
|
+
end
|
95
|
+
|
96
|
+
def h0
|
97
|
+
@h0 ||= begin
|
98
|
+
term1 = shift.sin -
|
99
|
+
@observer.latitude.sin * @coordinates_of_the_day.declination.sin
|
100
|
+
term2 = @observer.latitude.cos * @coordinates_of_the_day.declination.cos
|
101
|
+
ratio = term1 / term2
|
102
|
+
return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(ratio)
|
103
|
+
|
104
|
+
Angle.acos(ratio)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def apparent_gst_at_midnight
|
109
|
+
@apparent_gst_at_midnight ||= Angle.from_hours(
|
110
|
+
GreenwichSiderealTime.from_utc(
|
111
|
+
Time.utc(@date.year, @date.month, @date.day)
|
112
|
+
).time
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
def initial_transit
|
117
|
+
@initial_transit ||= rationalize_decimal_time(
|
118
|
+
(
|
119
|
+
@coordinates_of_the_day.right_ascension.degrees +
|
120
|
+
observer_longitude.degrees -
|
121
|
+
apparent_gst_at_midnight.degrees
|
122
|
+
) / Constants::DEGREES_PER_CIRCLE
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def initial_rising
|
127
|
+
@initial_rising ||=
|
128
|
+
rationalize_decimal_time(
|
129
|
+
initial_transit - h0.degrees / Constants::DEGREES_PER_CIRCLE
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
def initial_setting
|
134
|
+
@initial_setting ||=
|
135
|
+
rationalize_decimal_time(
|
136
|
+
initial_transit + h0.degrees / Constants::DEGREES_PER_CIRCLE
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
def gst_rising
|
141
|
+
@gst_rising ||= Angle.from_degrees(
|
142
|
+
apparent_gst_at_midnight.degrees +
|
143
|
+
EARTH_SIDEREAL_ROTATION_RATE * initial_rising
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
def gst_transit
|
148
|
+
@gst_transit ||= Angle.from_degrees(
|
149
|
+
apparent_gst_at_midnight.degrees +
|
150
|
+
EARTH_SIDEREAL_ROTATION_RATE * initial_transit
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
def gst_setting
|
155
|
+
@gst_setting ||= Angle.from_degrees(
|
156
|
+
apparent_gst_at_midnight.degrees +
|
157
|
+
EARTH_SIDEREAL_ROTATION_RATE * initial_setting
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
def leap_day_portion
|
162
|
+
@leap_day_portion ||= begin
|
163
|
+
leap_seconds = Util::Time.terrestrial_universal_time_delta(@date)
|
164
|
+
leap_seconds / Constants::SECONDS_PER_DAY
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def local_hour_angle_rising
|
169
|
+
@local_hour_angle_rising ||=
|
170
|
+
gst_rising - observer_longitude - right_ascension_rising
|
171
|
+
end
|
172
|
+
|
173
|
+
def local_hour_angle_transit
|
174
|
+
@local_hour_angle_transit ||=
|
175
|
+
gst_transit - observer_longitude - right_ascension_transit
|
176
|
+
end
|
177
|
+
|
178
|
+
def local_hour_angle_setting
|
179
|
+
@local_hour_angle_setting ||=
|
180
|
+
gst_setting - observer_longitude - right_ascension_setting
|
181
|
+
end
|
182
|
+
|
183
|
+
def local_horizontal_altitude_rising
|
184
|
+
@local_horizontal_altitude_rising ||= Angle.asin(
|
185
|
+
@observer.latitude.sin * declination_rising.sin +
|
186
|
+
@observer.latitude.cos * declination_rising.cos * local_hour_angle_rising.cos
|
187
|
+
)
|
188
|
+
end
|
189
|
+
|
190
|
+
def local_horizontal_azimuth_rising
|
191
|
+
@local_horizontal_azimuth_rising ||= begin
|
192
|
+
shift = -@standard_altitude
|
193
|
+
term1 = declination_rising.sin + shift.sin * @observer.latitude.cos
|
194
|
+
term2 = shift.cos * @observer.latitude.cos
|
195
|
+
angle = term1 / term2
|
196
|
+
Angle.acos(angle)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def local_horizontal_altitude_transit
|
201
|
+
@local_horizontal_altitude_transit ||= Angle.asin(
|
202
|
+
@observer.latitude.sin * declination_transit.sin +
|
203
|
+
@observer.latitude.cos * declination_transit.cos * local_hour_angle_transit.cos
|
204
|
+
)
|
205
|
+
end
|
206
|
+
|
207
|
+
def local_horizontal_altitude_setting
|
208
|
+
@local_horizontal_altitude_setting ||= Angle.asin(
|
209
|
+
@observer.latitude.sin * declination_setting.sin +
|
210
|
+
@observer.latitude.cos * declination_setting.cos * local_hour_angle_setting.cos
|
211
|
+
)
|
212
|
+
end
|
213
|
+
|
214
|
+
def local_horizontal_azimuth_setting
|
215
|
+
shift = -@standard_altitude
|
216
|
+
term1 = declination_setting.sin + shift.sin * @observer.latitude.cos
|
217
|
+
term2 = shift.cos * @observer.latitude.cos
|
218
|
+
angle = term1 / term2
|
219
|
+
Angle.from_degrees(
|
220
|
+
Constants::DEGREES_PER_CIRCLE - Angle.acos(angle).degrees
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
224
|
+
def rationalize_decimal_time(decimal_time)
|
225
|
+
decimal_time += 1 if decimal_time.negative?
|
226
|
+
decimal_time -= 1 if decimal_time > 1
|
227
|
+
decimal_time
|
228
|
+
end
|
229
|
+
|
230
|
+
def rationalize_decimal_hours(decimal_hours)
|
231
|
+
decimal_hours += Constants::HOURS_PER_DAY if decimal_hours.negative?
|
232
|
+
decimal_hours -= Constants::HOURS_PER_DAY if decimal_hours > Constants::HOURS_PER_DAY
|
233
|
+
decimal_hours
|
234
|
+
end
|
235
|
+
|
236
|
+
def right_ascension_rising
|
237
|
+
Angle.from_degrees(
|
238
|
+
Util::Maths.interpolate(
|
239
|
+
[
|
240
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
241
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
242
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
243
|
+
],
|
244
|
+
initial_rising + leap_day_portion
|
245
|
+
)
|
246
|
+
)
|
247
|
+
end
|
248
|
+
|
249
|
+
def right_ascension_transit
|
250
|
+
Angle.from_degrees(
|
251
|
+
Util::Maths.interpolate(
|
252
|
+
[
|
253
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
254
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
255
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
256
|
+
],
|
257
|
+
initial_transit + leap_day_portion
|
258
|
+
)
|
259
|
+
)
|
260
|
+
end
|
261
|
+
|
262
|
+
def right_ascension_setting
|
263
|
+
Angle.from_degrees(
|
264
|
+
Util::Maths.interpolate(
|
265
|
+
[
|
266
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
267
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
268
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
269
|
+
],
|
270
|
+
initial_setting + leap_day_portion
|
271
|
+
)
|
272
|
+
)
|
273
|
+
end
|
274
|
+
|
275
|
+
def declination_rising
|
276
|
+
Angle.from_degrees(
|
277
|
+
Util::Maths.interpolate(
|
278
|
+
[
|
279
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
280
|
+
@coordinates_of_the_day.declination.degrees,
|
281
|
+
@coordinates_of_the_next_day.declination.degrees
|
282
|
+
],
|
283
|
+
initial_rising + leap_day_portion
|
284
|
+
)
|
285
|
+
)
|
286
|
+
end
|
287
|
+
|
288
|
+
def declination_transit
|
289
|
+
Angle.from_degrees(
|
290
|
+
Util::Maths.interpolate(
|
291
|
+
[
|
292
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
293
|
+
@coordinates_of_the_day.declination.degrees,
|
294
|
+
@coordinates_of_the_next_day.declination.degrees
|
295
|
+
],
|
296
|
+
initial_transit + leap_day_portion
|
297
|
+
)
|
298
|
+
)
|
299
|
+
end
|
300
|
+
|
301
|
+
def declination_setting
|
302
|
+
Angle.from_degrees(
|
303
|
+
Util::Maths.interpolate(
|
304
|
+
[
|
305
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
306
|
+
@coordinates_of_the_day.declination.degrees,
|
307
|
+
@coordinates_of_the_next_day.declination.degrees
|
308
|
+
],
|
309
|
+
initial_setting + leap_day_portion
|
310
|
+
)
|
311
|
+
)
|
312
|
+
end
|
313
|
+
|
314
|
+
def shift
|
315
|
+
@standard_altitude - @additional_altitude
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
module Events
|
5
|
+
class TwilightEvents
|
6
|
+
TWILIGHTS = [
|
7
|
+
CIVIL = :civil,
|
8
|
+
NAUTICAL = :nautical,
|
9
|
+
ASTRONOMICAL = :astronomical
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
TWILIGHT_ANGLES = {
|
13
|
+
CIVIL => Angle.from_degrees(96),
|
14
|
+
NAUTICAL => Angle.from_degrees(102),
|
15
|
+
ASTRONOMICAL => Angle.from_degrees(108)
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
PERIODS_OF_THE_DAY = [
|
19
|
+
MORNING = :morning,
|
20
|
+
EVENING = :evening
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
attr_reader :morning_civil_twilight_time,
|
24
|
+
:evening_civil_twilight_time,
|
25
|
+
:morning_nautical_twilight_time,
|
26
|
+
:evening_nautical_twilight_time,
|
27
|
+
:morning_astronomical_twilight_time,
|
28
|
+
:evening_astronomical_twilight_time
|
29
|
+
|
30
|
+
def initialize(observer:, sun:)
|
31
|
+
@observer = observer
|
32
|
+
@sun = sun
|
33
|
+
PERIODS_OF_THE_DAY.each do |period_of_the_day|
|
34
|
+
TWILIGHT_ANGLES.each do |twilight, _|
|
35
|
+
zenith_angle = TWILIGHT_ANGLES[twilight]
|
36
|
+
instance_variable_set(
|
37
|
+
:"@#{period_of_the_day}_#{twilight}_twilight_time",
|
38
|
+
compute(period_of_the_day, zenith_angle)
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param period_of_the_day [Symbol] :morning or :evening
|
45
|
+
# @param zenith_angle [Angle] The zenith angle of the twilight
|
46
|
+
def time_for_zenith_angle(period_of_the_day:, zenith_angle:)
|
47
|
+
unless PERIODS_OF_THE_DAY.include?(period_of_the_day)
|
48
|
+
raise IncompatibleArgumentsError,
|
49
|
+
"Only #{PERIODS_OF_THE_DAY.join(" or ")} are allowed as period_of_the_day, got #{period_of_the_day}"
|
50
|
+
end
|
51
|
+
|
52
|
+
compute(period_of_the_day, zenith_angle)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Source:
|
58
|
+
# Title: Practical Astronomy with your Calculator or Spreadsheet
|
59
|
+
# Authors: Peter Duffett-Smith and Jonathan Zwart
|
60
|
+
# Edition: Cambridge University Press
|
61
|
+
# Chapter: 50 - Twilight
|
62
|
+
def compute(period_of_the_day, zenith_angle)
|
63
|
+
period_time = if period_of_the_day == MORNING
|
64
|
+
observation_events.rising_time
|
65
|
+
else
|
66
|
+
observation_events.setting_time
|
67
|
+
end
|
68
|
+
|
69
|
+
hour_angle_at_period = equatorial_coordinates_at_midday
|
70
|
+
.compute_hour_angle(time: period_time, longitude: @observer.longitude)
|
71
|
+
|
72
|
+
term1 = zenith_angle.cos -
|
73
|
+
@observer.latitude.sin *
|
74
|
+
@equatorial_coordinates_at_midday.declination.sin
|
75
|
+
term2 = @observer.latitude.cos *
|
76
|
+
equatorial_coordinates_at_midday.declination.cos
|
77
|
+
hour_angle_ratio_at_twilight = term1 / term2
|
78
|
+
|
79
|
+
return unless hour_angle_ratio_at_twilight.between?(-1, 1)
|
80
|
+
|
81
|
+
hour_angle_at_twilight = Angle.acos(hour_angle_ratio_at_twilight)
|
82
|
+
time_sign = -1
|
83
|
+
|
84
|
+
if period_of_the_day == MORNING
|
85
|
+
hour_angle_at_twilight = Angle.from_degrees(
|
86
|
+
Constants::DEGREES_PER_CIRCLE - hour_angle_at_twilight.degrees
|
87
|
+
)
|
88
|
+
time_sign = 1
|
89
|
+
end
|
90
|
+
|
91
|
+
twilight_in_hours =
|
92
|
+
time_sign * (hour_angle_at_twilight - hour_angle_at_period).hours *
|
93
|
+
GreenwichSiderealTime::SIDEREAL_MINUTE_IN_UT_MINUTE
|
94
|
+
twilight_in_seconds = time_sign *
|
95
|
+
twilight_in_hours *
|
96
|
+
Constants::SECONDS_PER_HOUR
|
97
|
+
|
98
|
+
(period_time + twilight_in_seconds).round
|
99
|
+
end
|
100
|
+
|
101
|
+
def observation_events
|
102
|
+
@observation_events ||= @sun.observation_events(observer: @observer)
|
103
|
+
end
|
104
|
+
|
105
|
+
def midday
|
106
|
+
date = @sun.time.to_date
|
107
|
+
Time.utc(date.year, date.month, date.day, 12)
|
108
|
+
end
|
109
|
+
|
110
|
+
def sun_at_midday
|
111
|
+
Sun.new(time: midday)
|
112
|
+
end
|
113
|
+
|
114
|
+
def equatorial_coordinates_at_midday
|
115
|
+
@equatorial_coordinates_at_midday ||= sun_at_midday
|
116
|
+
.apparent_ecliptic_coordinates
|
117
|
+
.to_apparent_equatorial(epoch: Epoch.from_time(midday))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -9,8 +9,8 @@ module Astronoby
|
|
9
9
|
# Chapter: 39 - Calculating correction for parallax
|
10
10
|
|
11
11
|
ASTRONOMICAL_UNIT_IN_METERS = 149_597_870_700
|
12
|
-
EARTH_FLATTENING_CORRECTION =
|
13
|
-
EARTH_EQUATORIAL_RADIUS =
|
12
|
+
EARTH_FLATTENING_CORRECTION = 0.996647
|
13
|
+
EARTH_EQUATORIAL_RADIUS = 6378140.0
|
14
14
|
|
15
15
|
# Equatorial horizontal parallax
|
16
16
|
# @param distance [Numeric] Distance of the body from the center of the
|
@@ -14,14 +14,14 @@ module Astronoby
|
|
14
14
|
def self.for_epoch(epoch)
|
15
15
|
return obliquity_of_reference if epoch == EPOCH_OF_REFERENCE
|
16
16
|
|
17
|
-
t = (epoch - EPOCH_OF_REFERENCE) /
|
17
|
+
t = (epoch - EPOCH_OF_REFERENCE) / Constants::DAYS_PER_JULIAN_CENTURY
|
18
18
|
|
19
19
|
Angle.from_degrees(
|
20
20
|
obliquity_of_reference.degrees - (
|
21
21
|
46.815 * t -
|
22
22
|
0.0006 * t * t +
|
23
23
|
0.00181 * t * t * t
|
24
|
-
) /
|
24
|
+
) / Constants::SECONDS_PER_DEGREE
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
data/lib/astronoby/nutation.rb
CHANGED
@@ -45,18 +45,20 @@ module Astronoby
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def julian_centuries
|
48
|
-
(@epoch - Epoch::J1900) /
|
48
|
+
(@epoch - Epoch::J1900) / Constants::DAYS_PER_JULIAN_CENTURY
|
49
49
|
end
|
50
50
|
|
51
51
|
def sun_mean_longitude
|
52
52
|
Angle.from_degrees(
|
53
|
-
(279.6967 +
|
53
|
+
(279.6967 + Constants::DEGREES_PER_CIRCLE * (centuries_a - centuries_a.to_i)) %
|
54
|
+
Constants::DEGREES_PER_CIRCLE
|
54
55
|
)
|
55
56
|
end
|
56
57
|
|
57
58
|
def moon_ascending_node_longitude
|
58
59
|
Angle.from_degrees(
|
59
|
-
(259.1833 -
|
60
|
+
(259.1833 - Constants::DEGREES_PER_CIRCLE * (centuries_b - centuries_b.to_i)) %
|
61
|
+
Constants::DEGREES_PER_CIRCLE
|
60
62
|
)
|
61
63
|
end
|
62
64
|
|
data/lib/astronoby/observer.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
module Astronoby
|
4
4
|
class Observer
|
5
5
|
DEFAULT_ELEVATION = 0
|
6
|
-
DEFAULT_TEMPERATURE =
|
7
|
-
PRESSURE_AT_SEA_LEVEL =
|
8
|
-
PASCAL_PER_MILLIBAR =
|
9
|
-
EARTH_GRAVITATIONAL_ACCELERATION =
|
10
|
-
MOLAR_MASS_OF_AIR =
|
11
|
-
UNIVERSAL_GAS_CONSTANT =
|
6
|
+
DEFAULT_TEMPERATURE = 283.15
|
7
|
+
PRESSURE_AT_SEA_LEVEL = 1013.25
|
8
|
+
PASCAL_PER_MILLIBAR = 0.01
|
9
|
+
EARTH_GRAVITATIONAL_ACCELERATION = 9.80665
|
10
|
+
MOLAR_MASS_OF_AIR = 0.0289644
|
11
|
+
UNIVERSAL_GAS_CONSTANT = 8.31432
|
12
12
|
|
13
|
-
attr_reader :latitude, :longitude, :elevation, :temperature
|
13
|
+
attr_reader :latitude, :longitude, :elevation, :temperature, :pressure
|
14
14
|
|
15
15
|
# @param latitude [Angle] geographic latitude of the observer
|
16
16
|
# @param longitude [Angle] geographic longitude of the observer
|
@@ -31,19 +31,38 @@ module Astronoby
|
|
31
31
|
@longitude = longitude
|
32
32
|
@elevation = elevation
|
33
33
|
@temperature = temperature
|
34
|
-
@pressure = pressure
|
34
|
+
@pressure = pressure || compute_pressure
|
35
35
|
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
def ==(other)
|
38
|
+
return false unless other.is_a?(self.class)
|
39
|
+
|
40
|
+
@latitude == other.latitude &&
|
41
|
+
@longitude == other.longitude &&
|
42
|
+
@elevation == other.elevation &&
|
43
|
+
@temperature == other.temperature &&
|
44
|
+
@pressure == other.pressure
|
45
|
+
end
|
46
|
+
alias_method :eql?, :==
|
47
|
+
|
48
|
+
def hash
|
49
|
+
[
|
50
|
+
self.class,
|
51
|
+
@latitude,
|
52
|
+
@longitude,
|
53
|
+
@elevation,
|
54
|
+
@temperature,
|
55
|
+
@pressure
|
56
|
+
].hash
|
43
57
|
end
|
44
58
|
|
45
59
|
private
|
46
60
|
|
61
|
+
# @return [Float] the atmospheric pressure in millibars.
|
62
|
+
def compute_pressure
|
63
|
+
@pressure ||= PRESSURE_AT_SEA_LEVEL * pressure_ratio
|
64
|
+
end
|
65
|
+
|
47
66
|
# Source:
|
48
67
|
# Barometric formula
|
49
68
|
# https://en.wikipedia.org/wiki/Barometric_formula
|
data/lib/astronoby/precession.rb
CHANGED
@@ -45,7 +45,7 @@ module Astronoby
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def matrix_for_epoch(epoch)
|
48
|
-
t = (epoch - Epoch::DEFAULT_EPOCH) /
|
48
|
+
t = (epoch - Epoch::DEFAULT_EPOCH) / Constants::DAYS_PER_JULIAN_CENTURY
|
49
49
|
|
50
50
|
zeta = Angle.from_degrees(
|
51
51
|
0.6406161 * t + 0.0000839 * t * t + 0.000005 * t * t * t
|
@@ -3,11 +3,13 @@
|
|
3
3
|
module Astronoby
|
4
4
|
class GreenwichSiderealTime
|
5
5
|
JULIAN_CENTURIES_EXPONENTS = [
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
6.697374558,
|
7
|
+
2400.051336,
|
8
|
+
0.000025862
|
9
9
|
].freeze
|
10
10
|
|
11
|
+
SIDEREAL_MINUTE_IN_UT_MINUTE = 0.9972695663
|
12
|
+
|
11
13
|
attr_reader :date, :time
|
12
14
|
|
13
15
|
# Source:
|
@@ -18,20 +20,20 @@ module Astronoby
|
|
18
20
|
def self.from_utc(utc)
|
19
21
|
date = utc.to_date
|
20
22
|
julian_day = utc.to_date.ajd
|
21
|
-
t = (julian_day - Epoch::J2000) /
|
23
|
+
t = (julian_day - Epoch::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
|
22
24
|
t0 = (
|
23
25
|
(JULIAN_CENTURIES_EXPONENTS[0] +
|
24
26
|
(JULIAN_CENTURIES_EXPONENTS[1] * t) +
|
25
|
-
(JULIAN_CENTURIES_EXPONENTS[2] * t * t)) %
|
27
|
+
(JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % Constants::HOURS_PER_DAY
|
26
28
|
).abs
|
27
29
|
|
28
30
|
ut_in_hours = utc.hour +
|
29
|
-
utc.min /
|
30
|
-
(utc.sec + utc.subsec) /
|
31
|
+
utc.min / Constants::MINUTES_PER_HOUR +
|
32
|
+
(utc.sec + utc.subsec) / Constants::SECONDS_PER_HOUR
|
31
33
|
|
32
|
-
gmst =
|
33
|
-
gmst +=
|
34
|
-
gmst -=
|
34
|
+
gmst = 1.002737909 * ut_in_hours + t0
|
35
|
+
gmst += Constants::HOURS_PER_DAY if gmst.negative?
|
36
|
+
gmst -= Constants::HOURS_PER_DAY if gmst > Constants::HOURS_PER_DAY
|
35
37
|
|
36
38
|
new(date: date, time: gmst)
|
37
39
|
end
|
@@ -49,38 +51,25 @@ module Astronoby
|
|
49
51
|
def to_utc
|
50
52
|
date = @date
|
51
53
|
julian_day = @date.ajd
|
52
|
-
t = (julian_day - Epoch::J2000) /
|
54
|
+
t = (julian_day - Epoch::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
|
53
55
|
|
54
56
|
t0 = (
|
55
57
|
(JULIAN_CENTURIES_EXPONENTS[0] +
|
56
58
|
(JULIAN_CENTURIES_EXPONENTS[1] * t) +
|
57
|
-
(JULIAN_CENTURIES_EXPONENTS[2] * t * t)) %
|
59
|
+
(JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % Constants::HOURS_PER_DAY
|
58
60
|
).abs
|
59
61
|
|
60
62
|
a = @time - t0
|
61
|
-
a +=
|
62
|
-
a -=
|
63
|
+
a += Constants::HOURS_PER_DAY if a.negative?
|
64
|
+
a -= Constants::HOURS_PER_DAY if a > Constants::HOURS_PER_DAY
|
63
65
|
|
64
|
-
utc =
|
66
|
+
utc = SIDEREAL_MINUTE_IN_UT_MINUTE * a
|
65
67
|
|
66
|
-
decimal_hour_to_time(date, utc)
|
68
|
+
Util::Time.decimal_hour_to_time(date, utc)
|
67
69
|
end
|
68
70
|
|
69
71
|
def to_lst(longitude:)
|
70
72
|
LocalSiderealTime.from_gst(gst: self, longitude: longitude)
|
71
73
|
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def decimal_hour_to_time(date, decimal)
|
76
|
-
absolute_hour = decimal.abs
|
77
|
-
hour = absolute_hour.floor
|
78
|
-
decimal_minute = 60 * (absolute_hour - hour)
|
79
|
-
absolute_decimal_minute = (60 * (absolute_hour - hour)).abs
|
80
|
-
minute = decimal_minute.floor
|
81
|
-
second = 60 * (absolute_decimal_minute - absolute_decimal_minute.floor)
|
82
|
-
|
83
|
-
::Time.utc(date.year, date.month, date.day, hour, minute, second).round
|
84
|
-
end
|
85
74
|
end
|
86
75
|
end
|
@@ -12,8 +12,8 @@ module Astronoby
|
|
12
12
|
def self.from_gst(gst:, longitude:)
|
13
13
|
date = gst.date
|
14
14
|
time = gst.time + longitude.hours
|
15
|
-
time +=
|
16
|
-
time -=
|
15
|
+
time += Constants::HOURS_PER_DAY if time.negative?
|
16
|
+
time -= Constants::HOURS_PER_DAY if time > Constants::HOURS_PER_DAY
|
17
17
|
|
18
18
|
new(date: date, time: time, longitude: longitude)
|
19
19
|
end
|
@@ -32,8 +32,8 @@ module Astronoby
|
|
32
32
|
def to_gst
|
33
33
|
date = @date
|
34
34
|
time = @time - @longitude.hours
|
35
|
-
time +=
|
36
|
-
time -=
|
35
|
+
time += Constants::HOURS_PER_DAY if time.negative?
|
36
|
+
time -= Constants::HOURS_PER_DAY if time > Constants::HOURS_PER_DAY
|
37
37
|
|
38
38
|
GreenwichSiderealTime.new(date: date, time: time)
|
39
39
|
end
|