astronoby 0.2.0 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +93 -2
- data/Gemfile.lock +3 -3
- data/README.md +107 -9
- data/UPGRADING.md +74 -0
- data/lib/astronoby/aberration.rb +12 -7
- data/lib/astronoby/angle.rb +42 -54
- data/lib/astronoby/bodies/sun.rb +146 -129
- data/lib/astronoby/constants.rb +25 -0
- data/lib/astronoby/coordinates/ecliptic.rb +18 -7
- data/lib/astronoby/coordinates/equatorial.rb +6 -5
- data/lib/astronoby/coordinates/horizontal.rb +8 -4
- data/lib/astronoby/epoch.rb +0 -2
- data/lib/astronoby/equinox_solstice.rb +8 -21
- 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 +4 -4
- data/lib/astronoby/nutation.rb +9 -7
- data/lib/astronoby/observer.rb +33 -14
- data/lib/astronoby/precession.rb +6 -6
- data/lib/astronoby/refraction.rb +4 -4
- data/lib/astronoby/time/greenwich_sidereal_time.rb +18 -29
- data/lib/astronoby/time/local_sidereal_time.rb +4 -4
- data/lib/astronoby/util/astrodynamics.rb +2 -2
- 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,19 +14,19 @@ 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
|
-
Angle.
|
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
|
|
28
28
|
def self.obliquity_of_reference
|
29
|
-
Angle.
|
29
|
+
Angle.from_dms(23, 26, 21.45)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
data/lib/astronoby/nutation.rb
CHANGED
@@ -21,7 +21,7 @@ module Astronoby
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def for_ecliptic_longitude
|
24
|
-
Angle.
|
24
|
+
Angle.from_dms(
|
25
25
|
0,
|
26
26
|
0,
|
27
27
|
(
|
@@ -32,7 +32,7 @@ module Astronoby
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def for_obliquity_of_the_ecliptic
|
35
|
-
Angle.
|
35
|
+
Angle.from_dms(
|
36
36
|
0,
|
37
37
|
0,
|
38
38
|
(
|
@@ -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
|
-
Angle.
|
53
|
-
(279.6967 +
|
52
|
+
Angle.from_degrees(
|
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
|
-
Angle.
|
59
|
-
(259.1833 -
|
59
|
+
Angle.from_degrees(
|
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
@@ -33,8 +33,8 @@ module Astronoby
|
|
33
33
|
|
34
34
|
Coordinates::Equatorial.new(
|
35
35
|
right_ascension: Util::Trigonometry.adjustement_for_arctangent(
|
36
|
-
Angle.
|
37
|
-
Angle.
|
36
|
+
Angle.from_radians(w[1]),
|
37
|
+
Angle.from_radians(w[0]),
|
38
38
|
Angle.atan(w[1] / w[0])
|
39
39
|
),
|
40
40
|
declination: Angle.asin(w[2]),
|
@@ -45,15 +45,15 @@ 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
|
-
zeta = Angle.
|
50
|
+
zeta = Angle.from_degrees(
|
51
51
|
0.6406161 * t + 0.0000839 * t * t + 0.000005 * t * t * t
|
52
52
|
)
|
53
|
-
z = Angle.
|
53
|
+
z = Angle.from_degrees(
|
54
54
|
0.6406161 * t + 0.0003041 * t * t + 0.0000051 * t * t * t
|
55
55
|
)
|
56
|
-
theta = Angle.
|
56
|
+
theta = Angle.from_degrees(
|
57
57
|
0.5567530 * t - 0.0001185 * t * t - 0.0000116 * t * t * t
|
58
58
|
)
|
59
59
|
|
data/lib/astronoby/refraction.rb
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module Astronoby
|
4
4
|
class Refraction
|
5
|
-
LOW_ALTITUDE_BODY_ANGLE = Angle.
|
6
|
-
ZENITH = Angle.
|
5
|
+
LOW_ALTITUDE_BODY_ANGLE = Angle.from_degrees(15)
|
6
|
+
ZENITH = Angle.from_degrees(90)
|
7
7
|
|
8
8
|
def self.angle(coordinates:, observer:)
|
9
9
|
new(coordinates, observer).refraction_angle
|
@@ -56,7 +56,7 @@ module Astronoby
|
|
56
56
|
|
57
57
|
def high_altitude_angle
|
58
58
|
zenith_angle = ZENITH - @coordinates.altitude
|
59
|
-
Angle.
|
59
|
+
Angle.from_degrees(0.00452 * pressure * zenith_angle.tan / temperature)
|
60
60
|
end
|
61
61
|
|
62
62
|
def low_altitude_angle
|
@@ -67,7 +67,7 @@ module Astronoby
|
|
67
67
|
1 + 0.505 * altitude_in_degrees + 0.0845 * altitude_in_degrees**2
|
68
68
|
)
|
69
69
|
|
70
|
-
Angle.
|
70
|
+
Angle.from_degrees(term1 / term2)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|