astronoby 0.3.0 → 0.5.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 +132 -0
- data/Gemfile.lock +19 -17
- data/README.md +195 -25
- data/UPGRADING.md +72 -0
- data/lib/astronoby/aberration.rb +7 -5
- data/lib/astronoby/angle.rb +26 -34
- data/lib/astronoby/astronomical_models/ephemeride_lunaire_parisienne.rb +143 -0
- data/lib/astronoby/astronomical_models/moon_phases_periodic_terms.rb +249 -0
- data/lib/astronoby/bodies/moon.rb +335 -0
- data/lib/astronoby/bodies/sun.rb +129 -132
- data/lib/astronoby/constants.rb +31 -0
- data/lib/astronoby/coordinates/ecliptic.rb +4 -4
- data/lib/astronoby/coordinates/equatorial.rb +7 -5
- data/lib/astronoby/coordinates/horizontal.rb +24 -12
- data/lib/astronoby/distance.rb +83 -0
- data/lib/astronoby/epoch.rb +0 -2
- data/lib/astronoby/equinox_solstice.rb +3 -2
- data/lib/astronoby/events/moon_phases.rb +143 -0
- data/lib/astronoby/events/observation_events.rb +259 -0
- data/lib/astronoby/events/rise_transit_set_iteration.rb +215 -0
- data/lib/astronoby/events/twilight_events.rb +121 -0
- data/lib/astronoby/geocentric_parallax.rb +36 -56
- data/lib/astronoby/mean_obliquity.rb +2 -2
- data/lib/astronoby/moon_phase.rb +43 -0
- data/lib/astronoby/nutation.rb +5 -3
- data/lib/astronoby/observer.rb +39 -18
- data/lib/astronoby/precession.rb +1 -1
- data/lib/astronoby/refraction.rb +8 -10
- 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 +12 -1
- metadata +15 -4
- data/lib/astronoby/body.rb +0 -155
@@ -0,0 +1,215 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class RiseTransitSetIteration
|
5
|
+
EARTH_SIDEREAL_ROTATION_RATE = 360.98564736629
|
6
|
+
|
7
|
+
# Source:
|
8
|
+
# Title: Astronomical Algorithms
|
9
|
+
# Author: Jean Meeus
|
10
|
+
# Edition: 2nd edition
|
11
|
+
# Chapter: 15 - Rising, Transit, and Setting
|
12
|
+
|
13
|
+
# @param observer [Astronoby::Observer] Observer
|
14
|
+
# @param date [Date] Date of the event
|
15
|
+
# @param coordinates_of_the_previous_day [Astronoby::Coordinates::Equatorial]
|
16
|
+
# Coordinates of the body of the previous day
|
17
|
+
# @param coordinates_of_the_day [Astronoby::Coordinates::Equatorial]
|
18
|
+
# Coordinates of the body of the day
|
19
|
+
# @param coordinates_of_the_next_day [Astronoby::Coordinates::Equatorial]
|
20
|
+
# Coordinates of the body of the next day
|
21
|
+
# @param shift [Astronoby::Angle] Altitude shift
|
22
|
+
# @param initial_rising [Float] Initial rising
|
23
|
+
# @param initial_transit [Float] Initial transit
|
24
|
+
# @param initial_setting [Float] Initial setting
|
25
|
+
def initialize(
|
26
|
+
observer:,
|
27
|
+
date:,
|
28
|
+
coordinates_of_the_previous_day:,
|
29
|
+
coordinates_of_the_day:,
|
30
|
+
coordinates_of_the_next_day:,
|
31
|
+
shift:,
|
32
|
+
initial_rising:,
|
33
|
+
initial_transit:,
|
34
|
+
initial_setting:
|
35
|
+
)
|
36
|
+
@observer = observer
|
37
|
+
@date = date
|
38
|
+
@coordinates_of_the_previous_day = coordinates_of_the_previous_day
|
39
|
+
@coordinates_of_the_day = coordinates_of_the_day
|
40
|
+
@coordinates_of_the_next_day = coordinates_of_the_next_day
|
41
|
+
@shift = shift
|
42
|
+
@initial_rising = initial_rising
|
43
|
+
@initial_transit = initial_transit
|
44
|
+
@initial_setting = initial_setting
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Array<Float>] Iteration results
|
48
|
+
def iterate
|
49
|
+
[
|
50
|
+
delta_m_rising,
|
51
|
+
delta_m_transit,
|
52
|
+
delta_m_setting
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def delta_m_rising
|
59
|
+
(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
|
+
end
|
66
|
+
|
67
|
+
def delta_m_transit
|
68
|
+
-local_hour_angle_transit.degrees / Constants::DEGREES_PER_CIRCLE
|
69
|
+
end
|
70
|
+
|
71
|
+
def delta_m_setting
|
72
|
+
(local_horizontal_altitude_setting - @shift).degrees./(
|
73
|
+
Constants::DEGREES_PER_CIRCLE *
|
74
|
+
declination_setting.cos *
|
75
|
+
@observer.latitude.cos *
|
76
|
+
local_hour_angle_setting.sin
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def observer_longitude
|
81
|
+
# Longitude must be treated positively westwards from the meridian of
|
82
|
+
# Greenwich, and negatively to the east
|
83
|
+
@observer_longitude ||= -@observer.longitude
|
84
|
+
end
|
85
|
+
|
86
|
+
def apparent_gst_at_midnight
|
87
|
+
@apparent_gst_at_midnight ||= Angle.from_hours(
|
88
|
+
GreenwichSiderealTime.from_utc(
|
89
|
+
Time.utc(@date.year, @date.month, @date.day)
|
90
|
+
).time
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def gst_rising
|
95
|
+
@gst_rising ||= Angle.from_degrees(
|
96
|
+
apparent_gst_at_midnight.degrees +
|
97
|
+
EARTH_SIDEREAL_ROTATION_RATE * @initial_rising
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def gst_transit
|
102
|
+
@gst_transit ||= Angle.from_degrees(
|
103
|
+
apparent_gst_at_midnight.degrees +
|
104
|
+
EARTH_SIDEREAL_ROTATION_RATE * @initial_transit
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
def gst_setting
|
109
|
+
@gst_setting ||= Angle.from_degrees(
|
110
|
+
apparent_gst_at_midnight.degrees +
|
111
|
+
EARTH_SIDEREAL_ROTATION_RATE * @initial_setting
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
def leap_day_portion
|
116
|
+
@leap_day_portion ||= begin
|
117
|
+
leap_seconds = Util::Time.terrestrial_universal_time_delta(@date)
|
118
|
+
leap_seconds / Constants::SECONDS_PER_DAY
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def local_hour_angle_rising
|
123
|
+
@local_hour_angle_rising ||=
|
124
|
+
gst_rising - observer_longitude - right_ascension_rising
|
125
|
+
end
|
126
|
+
|
127
|
+
def local_hour_angle_transit
|
128
|
+
gst_transit - observer_longitude - right_ascension_transit
|
129
|
+
end
|
130
|
+
|
131
|
+
def local_hour_angle_setting
|
132
|
+
@local_hour_angle_setting ||=
|
133
|
+
gst_setting - observer_longitude - right_ascension_setting
|
134
|
+
end
|
135
|
+
|
136
|
+
def local_horizontal_altitude_rising
|
137
|
+
Angle.asin(
|
138
|
+
@observer.latitude.sin * declination_rising.sin +
|
139
|
+
@observer.latitude.cos * declination_rising.cos * local_hour_angle_rising.cos
|
140
|
+
)
|
141
|
+
end
|
142
|
+
|
143
|
+
def local_horizontal_altitude_setting
|
144
|
+
Angle.asin(
|
145
|
+
@observer.latitude.sin * declination_setting.sin +
|
146
|
+
@observer.latitude.cos * declination_setting.cos * local_hour_angle_setting.cos
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
def right_ascension_rising
|
151
|
+
Angle.from_degrees(
|
152
|
+
Util::Maths.interpolate(
|
153
|
+
[
|
154
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
155
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
156
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
157
|
+
],
|
158
|
+
@initial_rising + leap_day_portion
|
159
|
+
)
|
160
|
+
)
|
161
|
+
end
|
162
|
+
|
163
|
+
def right_ascension_transit
|
164
|
+
Angle.from_degrees(
|
165
|
+
Util::Maths.interpolate(
|
166
|
+
[
|
167
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
168
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
169
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
170
|
+
],
|
171
|
+
@initial_transit + leap_day_portion
|
172
|
+
)
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
176
|
+
def right_ascension_setting
|
177
|
+
Angle.from_degrees(
|
178
|
+
Util::Maths.interpolate(
|
179
|
+
[
|
180
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
181
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
182
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
183
|
+
],
|
184
|
+
@initial_setting + leap_day_portion
|
185
|
+
)
|
186
|
+
)
|
187
|
+
end
|
188
|
+
|
189
|
+
def declination_rising
|
190
|
+
Angle.from_degrees(
|
191
|
+
Util::Maths.interpolate(
|
192
|
+
[
|
193
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
194
|
+
@coordinates_of_the_day.declination.degrees,
|
195
|
+
@coordinates_of_the_next_day.declination.degrees
|
196
|
+
],
|
197
|
+
@initial_rising + leap_day_portion
|
198
|
+
)
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
202
|
+
def declination_setting
|
203
|
+
Angle.from_degrees(
|
204
|
+
Util::Maths.interpolate(
|
205
|
+
[
|
206
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
207
|
+
@coordinates_of_the_day.declination.degrees,
|
208
|
+
@coordinates_of_the_next_day.declination.degrees
|
209
|
+
],
|
210
|
+
@initial_setting + leap_day_portion
|
211
|
+
)
|
212
|
+
)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
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
|
@@ -8,104 +8,82 @@ module Astronoby
|
|
8
8
|
# Edition: Cambridge University Press
|
9
9
|
# Chapter: 39 - Calculating correction for parallax
|
10
10
|
|
11
|
-
ASTRONOMICAL_UNIT_IN_METERS = 149_597_870_700
|
12
|
-
EARTH_FLATTENING_CORRECTION = BigDecimal("0.996647")
|
13
|
-
EARTH_EQUATORIAL_RADIUS = BigDecimal("6378140")
|
14
|
-
|
15
11
|
# Equatorial horizontal parallax
|
16
|
-
# @param distance [
|
17
|
-
#
|
12
|
+
# @param distance [Astronoby::Distance] Distance of the body from the center
|
13
|
+
# of the Earth
|
18
14
|
# @return [Astronoby::Angle] Equatorial horizontal parallax angle
|
19
15
|
def self.angle(distance:)
|
20
|
-
|
21
|
-
Angle.asin(1 / distance_in_earth_radius)
|
16
|
+
Angle.from_radians(Angle.from_dms(0, 0, 8.794).sin / distance.au)
|
22
17
|
end
|
23
18
|
|
24
19
|
# Correct equatorial coordinates with the equatorial horizontal parallax
|
25
|
-
# @param
|
26
|
-
# @param longitude [Astronoby::Angle] Observer's longitude
|
27
|
-
# @param elevation [Numeric] Observer's elevation above sea level in meters
|
20
|
+
# @param observer [Astronoby::Observer] Observer
|
28
21
|
# @param time [Time] Date-time of the observation
|
29
22
|
# @param coordinates [Astronoby::Coordinates::Equatorial]
|
30
23
|
# Equatorial coordinates of the observed body
|
31
|
-
# @param distance [
|
32
|
-
# the
|
24
|
+
# @param distance [Astronoby::Distance] Distance of the observed body from
|
25
|
+
# the center of the Earth
|
33
26
|
# @return [Astronoby::Coordinates::Equatorial] Apparent equatorial
|
34
27
|
# coordinates with equatorial horizontal parallax
|
35
28
|
def self.for_equatorial_coordinates(
|
36
|
-
|
37
|
-
longitude:,
|
38
|
-
elevation:,
|
29
|
+
observer:,
|
39
30
|
time:,
|
40
31
|
coordinates:,
|
41
32
|
distance:
|
42
33
|
)
|
43
34
|
new(
|
44
|
-
|
45
|
-
longitude,
|
46
|
-
elevation,
|
35
|
+
observer,
|
47
36
|
time,
|
48
37
|
coordinates,
|
49
38
|
distance
|
50
39
|
).apply
|
51
40
|
end
|
52
41
|
|
53
|
-
# @param
|
54
|
-
# @param longitude [Astronoby::Angle] Observer's longitude
|
55
|
-
# @param elevation [Numeric] Observer's elevation above sea level in meters
|
42
|
+
# @param observer [Astronoby::Observer] Observer
|
56
43
|
# @param time [Time] Date-time of the observation
|
57
44
|
# @param coordinates [Astronoby::Coordinates::Equatorial] Equatorial
|
58
45
|
# coordinates of the observed body
|
59
|
-
# @param distance [
|
60
|
-
# the
|
46
|
+
# @param distance [Astronoby::Distance] Distance of the observed body from
|
47
|
+
# the center of the Earth
|
61
48
|
def initialize(
|
62
|
-
|
63
|
-
longitude,
|
64
|
-
elevation,
|
49
|
+
observer,
|
65
50
|
time,
|
66
51
|
coordinates,
|
67
52
|
distance
|
68
53
|
)
|
69
|
-
@
|
70
|
-
@longitude = longitude
|
71
|
-
@elevation = elevation
|
54
|
+
@observer = observer
|
72
55
|
@time = time
|
73
56
|
@coordinates = coordinates
|
74
57
|
@distance = distance
|
75
58
|
end
|
76
59
|
|
77
60
|
def apply
|
78
|
-
term1 = Angle.atan(EARTH_FLATTENING_CORRECTION *
|
79
|
-
quantity1 = term1.cos + elevation_ratio *
|
80
|
-
quantity2 = EARTH_FLATTENING_CORRECTION * term1.sin +
|
81
|
-
elevation_ratio *
|
82
|
-
|
83
|
-
term2 = quantity1 * hour_angle.sin
|
84
|
-
term3 = distance_in_earth_radius * declination.cos -
|
85
|
-
quantity1 * hour_angle.cos
|
61
|
+
term1 = Angle.atan(Constants::EARTH_FLATTENING_CORRECTION * latitude.tan)
|
62
|
+
quantity1 = term1.cos + elevation_ratio * latitude.cos
|
63
|
+
quantity2 = Constants::EARTH_FLATTENING_CORRECTION * term1.sin +
|
64
|
+
elevation_ratio * latitude.sin
|
86
65
|
|
87
|
-
|
66
|
+
term1 = -quantity1 * equatorial_horizontal_parallax.sin * hour_angle.sin
|
67
|
+
term2 = declination.cos - quantity1 * equatorial_horizontal_parallax.sin * hour_angle.cos
|
68
|
+
delta_right_ascension = Angle.atan(term1 / term2)
|
88
69
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
(
|
93
|
-
apparent_hour_angle.cos *
|
94
|
-
(distance_in_earth_radius * declination.sin - quantity2)
|
95
|
-
) / (
|
96
|
-
distance_in_earth_radius * declination.cos * hour_angle.cos - quantity1
|
97
|
-
)
|
98
|
-
)
|
70
|
+
term1 = (declination.sin - quantity2 * equatorial_horizontal_parallax.sin) * delta_right_ascension.cos
|
71
|
+
term2 = declination.cos - quantity1 * equatorial_horizontal_parallax.sin * hour_angle.cos
|
72
|
+
new_declination = Angle.atan(term1 / term2)
|
99
73
|
|
100
74
|
Coordinates::Equatorial.new(
|
101
|
-
right_ascension:
|
102
|
-
declination:
|
75
|
+
right_ascension: delta_right_ascension + right_ascension,
|
76
|
+
declination: new_declination,
|
103
77
|
epoch: @coordinates.epoch
|
104
78
|
)
|
105
79
|
end
|
106
80
|
|
107
81
|
private
|
108
82
|
|
83
|
+
def latitude
|
84
|
+
@observer.latitude
|
85
|
+
end
|
86
|
+
|
109
87
|
def right_ascension
|
110
88
|
@coordinates.right_ascension
|
111
89
|
end
|
@@ -115,16 +93,18 @@ module Astronoby
|
|
115
93
|
end
|
116
94
|
|
117
95
|
def hour_angle
|
118
|
-
@_hour_angle ||=
|
119
|
-
|
96
|
+
@_hour_angle ||= @coordinates.compute_hour_angle(
|
97
|
+
time: @time,
|
98
|
+
longitude: @observer.longitude
|
99
|
+
)
|
120
100
|
end
|
121
101
|
|
122
102
|
def elevation_ratio
|
123
|
-
@elevation /
|
103
|
+
@observer.elevation.meters / Constants::EARTH_EQUATORIAL_RADIUS_IN_METERS.to_f
|
124
104
|
end
|
125
105
|
|
126
|
-
def
|
127
|
-
@distance
|
106
|
+
def equatorial_horizontal_parallax
|
107
|
+
self.class.angle(distance: @distance)
|
128
108
|
end
|
129
109
|
end
|
130
110
|
end
|
@@ -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
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class MoonPhase
|
5
|
+
NEW_MOON = :new_moon
|
6
|
+
FIRST_QUARTER = :first_quarter
|
7
|
+
FULL_MOON = :full_moon
|
8
|
+
LAST_QUARTER = :last_quarter
|
9
|
+
|
10
|
+
attr_reader :time, :phase
|
11
|
+
|
12
|
+
# @param time [Time] Time of the Moon phase
|
13
|
+
# @return [Astronoby::MoonPhase] New Moon phase
|
14
|
+
def self.new_moon(time)
|
15
|
+
new(time: time, phase: NEW_MOON)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param time [Time] Time of the Moon phase
|
19
|
+
# @return [Astronoby::MoonPhase] First quarter Moon phase
|
20
|
+
def self.first_quarter(time)
|
21
|
+
new(time: time, phase: FIRST_QUARTER)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param time [Time] Time of the Moon phase
|
25
|
+
# @return [Astronoby::MoonPhase] Full Moon phase
|
26
|
+
def self.full_moon(time)
|
27
|
+
new(time: time, phase: FULL_MOON)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param time [Time] Time of the Moon phase
|
31
|
+
# @return [Astronoby::MoonPhase] Last quarter Moon phase
|
32
|
+
def self.last_quarter(time)
|
33
|
+
new(time: time, phase: LAST_QUARTER)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param time [Time] Time of the Moon phase
|
37
|
+
# @param phase [Symbol] Moon phase
|
38
|
+
def initialize(time:, phase:)
|
39
|
+
@time = time
|
40
|
+
@phase = phase
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
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
@@ -2,20 +2,20 @@
|
|
2
2
|
|
3
3
|
module Astronoby
|
4
4
|
class Observer
|
5
|
-
DEFAULT_ELEVATION =
|
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 =
|
5
|
+
DEFAULT_ELEVATION = Distance.zero
|
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
|
17
|
-
# @param elevation [
|
18
|
-
# observer above sea level
|
17
|
+
# @param elevation [Astronoby::Distance] geographic elevation (or altitude)
|
18
|
+
# of the observer above sea level
|
19
19
|
# @param temperature [Numeric] temperature at the observer's location in
|
20
20
|
# kelvins
|
21
21
|
# @param pressure [Numeric] atmospheric pressure at the observer's
|
@@ -31,24 +31,45 @@ 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
|
50
69
|
def pressure_ratio
|
51
|
-
term1 = EARTH_GRAVITATIONAL_ACCELERATION *
|
70
|
+
term1 = EARTH_GRAVITATIONAL_ACCELERATION *
|
71
|
+
MOLAR_MASS_OF_AIR *
|
72
|
+
@elevation.meters
|
52
73
|
term2 = UNIVERSAL_GAS_CONSTANT * @temperature
|
53
74
|
|
54
75
|
Math.exp(-term1 / term2)
|
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
|