astronoby 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|