astronoby 0.7.0 → 0.9.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/.ruby-version +1 -1
- data/CHANGELOG.md +145 -3
- data/README.md +59 -33
- data/UPGRADING.md +75 -21
- data/docs/README.md +224 -0
- data/docs/angles.md +137 -0
- data/docs/configuration.md +98 -0
- data/docs/coordinates.md +167 -0
- data/docs/deep_sky_bodies.md +101 -0
- data/docs/ephem.md +85 -0
- data/docs/equinoxes_solstices_times.md +31 -0
- data/docs/glossary.md +152 -0
- data/docs/instant.md +139 -0
- data/docs/moon_phases.md +79 -0
- data/docs/observer.md +65 -0
- data/docs/reference_frames.md +138 -0
- data/docs/rise_transit_set_times.md +119 -0
- data/docs/solar_system_bodies.md +107 -0
- data/docs/twilight_times.md +123 -0
- data/lib/astronoby/angle.rb +6 -2
- data/lib/astronoby/angular_velocity.rb +76 -0
- data/lib/astronoby/bodies/deep_sky_object.rb +44 -0
- data/lib/astronoby/bodies/deep_sky_object_position.rb +127 -0
- data/lib/astronoby/bodies/earth.rb +12 -2
- data/lib/astronoby/bodies/jupiter.rb +17 -0
- data/lib/astronoby/bodies/mars.rb +17 -0
- data/lib/astronoby/bodies/mercury.rb +21 -0
- data/lib/astronoby/bodies/moon.rb +50 -36
- data/lib/astronoby/bodies/neptune.rb +21 -0
- data/lib/astronoby/bodies/saturn.rb +26 -0
- data/lib/astronoby/bodies/solar_system_body.rb +162 -27
- data/lib/astronoby/bodies/sun.rb +25 -2
- data/lib/astronoby/bodies/uranus.rb +5 -0
- data/lib/astronoby/bodies/venus.rb +25 -0
- data/lib/astronoby/cache.rb +189 -0
- data/lib/astronoby/configuration.rb +92 -0
- data/lib/astronoby/constants.rb +11 -3
- data/lib/astronoby/constellation.rb +12 -0
- data/lib/astronoby/constellations/data.rb +42 -0
- data/lib/astronoby/constellations/finder.rb +35 -0
- data/lib/astronoby/constellations/repository.rb +20 -0
- data/lib/astronoby/coordinates/equatorial.rb +5 -8
- data/lib/astronoby/data/constellations/constellation_names.dat +88 -0
- data/lib/astronoby/data/constellations/indexed_abbreviations.dat +88 -0
- data/lib/astronoby/data/constellations/radec_to_index.dat +238 -0
- data/lib/astronoby/data/constellations/sorted_declinations.dat +202 -0
- data/lib/astronoby/data/constellations/sorted_right_ascensions.dat +237 -0
- data/lib/astronoby/distance.rb +6 -0
- data/lib/astronoby/equinox_solstice.rb +2 -2
- data/lib/astronoby/events/extremum_calculator.rb +233 -0
- data/lib/astronoby/events/extremum_event.rb +15 -0
- data/lib/astronoby/events/moon_phases.rb +15 -14
- data/lib/astronoby/events/rise_transit_set_calculator.rb +39 -12
- data/lib/astronoby/events/twilight_calculator.rb +116 -61
- data/lib/astronoby/events/twilight_events.rb +28 -0
- data/lib/astronoby/instant.rb +34 -6
- data/lib/astronoby/julian_date.rb +78 -0
- data/lib/astronoby/mean_obliquity.rb +8 -10
- data/lib/astronoby/nutation.rb +11 -3
- data/lib/astronoby/observer.rb +1 -1
- data/lib/astronoby/precession.rb +48 -38
- data/lib/astronoby/reference_frame.rb +2 -1
- data/lib/astronoby/reference_frames/apparent.rb +1 -11
- data/lib/astronoby/reference_frames/mean_of_date.rb +1 -1
- data/lib/astronoby/reference_frames/topocentric.rb +2 -12
- data/lib/astronoby/stellar_propagation.rb +162 -0
- data/lib/astronoby/time/greenwich_apparent_sidereal_time.rb +22 -0
- data/lib/astronoby/time/greenwich_mean_sidereal_time.rb +64 -0
- data/lib/astronoby/time/greenwich_sidereal_time.rb +20 -58
- data/lib/astronoby/time/local_apparent_sidereal_time.rb +42 -0
- data/lib/astronoby/time/local_mean_sidereal_time.rb +42 -0
- data/lib/astronoby/time/local_sidereal_time.rb +35 -26
- data/lib/astronoby/time/sidereal_time.rb +42 -0
- data/lib/astronoby/true_obliquity.rb +2 -3
- data/lib/astronoby/util/time.rb +62 -44
- data/lib/astronoby/velocity.rb +5 -0
- data/lib/astronoby/version.rb +1 -1
- data/lib/astronoby.rb +19 -1
- metadata +71 -11
- data/Gemfile +0 -5
- data/Gemfile.lock +0 -102
- data/benchmark/README.md +0 -131
- data/benchmark/benchmark.rb +0 -259
- data/benchmark/data/imcce.csv.zip +0 -0
- data/benchmark/data/sun_calc.csv.zip +0 -0
- data/lib/astronoby/epoch.rb +0 -22
|
@@ -15,17 +15,19 @@ module Astronoby
|
|
|
15
15
|
# @param month [Integer] Requested month
|
|
16
16
|
# @return [Array<Astronoby::MoonPhase>] List of Moon phases
|
|
17
17
|
def self.phases_for(year:, month:)
|
|
18
|
-
[
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
Astronoby.cache.fetch([:moon_phases, year, month]) do
|
|
19
|
+
[
|
|
20
|
+
MoonPhase.first_quarter(new(year, month, :first_quarter, -0.75).time),
|
|
21
|
+
MoonPhase.full_moon(new(year, month, :full_moon, -0.5).time),
|
|
22
|
+
MoonPhase.last_quarter(new(year, month, :last_quarter, -0.25).time),
|
|
23
|
+
MoonPhase.new_moon(new(year, month, :new_moon, 0).time),
|
|
24
|
+
MoonPhase.first_quarter(new(year, month, :first_quarter, 0.25).time),
|
|
25
|
+
MoonPhase.full_moon(new(year, month, :full_moon, 0.5).time),
|
|
26
|
+
MoonPhase.last_quarter(new(year, month, :last_quarter, 0.75).time),
|
|
27
|
+
MoonPhase.new_moon(new(year, month, :new_moon, 1).time),
|
|
28
|
+
MoonPhase.first_quarter(new(year, month, :first_quarter, 1.25).time)
|
|
29
|
+
].select { _1.time.month == month }
|
|
30
|
+
end
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
# @param year [Integer] Requested year
|
|
@@ -43,13 +45,12 @@ module Astronoby
|
|
|
43
45
|
def time
|
|
44
46
|
correction = moon_phases_periodic_terms
|
|
45
47
|
.public_send(:"#{@phase}_correction")
|
|
46
|
-
terrestrial_time =
|
|
48
|
+
terrestrial_time = Instant.from_terrestrial_time(
|
|
47
49
|
julian_ephemeris_day +
|
|
48
50
|
correction +
|
|
49
51
|
moon_phases_periodic_terms.additional_corrections
|
|
50
52
|
)
|
|
51
|
-
|
|
52
|
-
(terrestrial_time - delta).round
|
|
53
|
+
terrestrial_time.to_time.round
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
private
|
|
@@ -23,7 +23,11 @@ module Astronoby
|
|
|
23
23
|
SUN_REFRACTION_ANGLE = -Angle.from_dms(0, 50, 0)
|
|
24
24
|
EVENT_TYPES = [:rising, :transit, :setting].freeze
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
# @param body [Astronoby::SolarSystemBody, Astronoby::DeepSkyObject]
|
|
27
|
+
# Celestial body for which to calculate events
|
|
28
|
+
# @param observer [Astronoby::Observer] Observer location
|
|
29
|
+
# @param ephem [::Ephem::SPK, nil] Ephemeris data source
|
|
30
|
+
def initialize(body:, observer:, ephem: nil)
|
|
27
31
|
@body = body
|
|
28
32
|
@observer = observer
|
|
29
33
|
@ephem = ephem
|
|
@@ -237,13 +241,14 @@ module Astronoby
|
|
|
237
241
|
)
|
|
238
242
|
|
|
239
243
|
# Calculate time differences
|
|
240
|
-
|
|
241
|
-
|
|
244
|
+
time_differences_in_days = new_instants.each_with_index.map do |instant, i|
|
|
245
|
+
instant.tt - old_instants[i].tt
|
|
242
246
|
end
|
|
243
247
|
|
|
244
248
|
# Calculate hour angle rate (radians per day)
|
|
245
249
|
hour_angle_rates = hour_angle_changes.each_with_index.map do |angle, i|
|
|
246
|
-
|
|
250
|
+
denominator = time_differences_in_days[i]
|
|
251
|
+
angle.radians / denominator
|
|
247
252
|
end
|
|
248
253
|
|
|
249
254
|
# Store current values for next iteration
|
|
@@ -255,7 +260,8 @@ module Astronoby
|
|
|
255
260
|
.each_with_index
|
|
256
261
|
.map do |angle, i|
|
|
257
262
|
ratio = angle.radians / hour_angle_rates[i]
|
|
258
|
-
|
|
263
|
+
time_adjustment = (ratio.nan? || ratio.infinite?) ? 0 : ratio
|
|
264
|
+
[time_adjustment, MIN_TIME_ADJUSTMENT].max
|
|
259
265
|
end
|
|
260
266
|
|
|
261
267
|
# Apply time adjustments
|
|
@@ -268,10 +274,31 @@ module Astronoby
|
|
|
268
274
|
end
|
|
269
275
|
|
|
270
276
|
def calculate_positions_at_instants(instants)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
.
|
|
274
|
-
|
|
277
|
+
if Astronoby.configuration.cache_enabled?
|
|
278
|
+
instants.map do |instant|
|
|
279
|
+
cache_key = CacheKey.generate(
|
|
280
|
+
:observed_by,
|
|
281
|
+
instant,
|
|
282
|
+
@body.to_s,
|
|
283
|
+
@observer.hash
|
|
284
|
+
)
|
|
285
|
+
Astronoby.cache.fetch(cache_key) do
|
|
286
|
+
@body
|
|
287
|
+
.at(instant, ephem: @ephem)
|
|
288
|
+
.observed_by(@observer)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
else
|
|
292
|
+
@positions_cache ||= {}
|
|
293
|
+
precision = Astronoby.configuration.cache_precision(:observed_by)
|
|
294
|
+
instants.map do |instant|
|
|
295
|
+
rounded_instant = Instant.from_terrestrial_time(
|
|
296
|
+
instant.tt.round(precision)
|
|
297
|
+
)
|
|
298
|
+
@positions_cache[rounded_instant] ||= @body
|
|
299
|
+
.at(rounded_instant, ephem: @ephem)
|
|
300
|
+
.observed_by(@observer)
|
|
301
|
+
end
|
|
275
302
|
end
|
|
276
303
|
end
|
|
277
304
|
|
|
@@ -329,10 +356,9 @@ module Astronoby
|
|
|
329
356
|
end
|
|
330
357
|
|
|
331
358
|
def horizon_angle(distance)
|
|
332
|
-
|
|
333
|
-
when "Astronoby::Sun"
|
|
359
|
+
if @body == Astronoby::Sun
|
|
334
360
|
SUN_REFRACTION_ANGLE
|
|
335
|
-
|
|
361
|
+
elsif @body == Astronoby::Moon
|
|
336
362
|
STANDARD_REFRACTION_ANGLE -
|
|
337
363
|
Angle.from_radians(Moon::EQUATORIAL_RADIUS.m / distance.m)
|
|
338
364
|
else
|
|
@@ -347,6 +373,7 @@ module Astronoby
|
|
|
347
373
|
@sample_instants = nil
|
|
348
374
|
@start_instant = nil
|
|
349
375
|
@end_instant = nil
|
|
376
|
+
@positions_cache = nil
|
|
350
377
|
end
|
|
351
378
|
end
|
|
352
379
|
end
|
|
@@ -24,72 +24,114 @@ module Astronoby
|
|
|
24
24
|
@ephem = ephem
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def event_on(date)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
morning_civil = compute_twilight_time(
|
|
34
|
-
MORNING,
|
|
35
|
-
TWILIGHT_ANGLES[CIVIL],
|
|
36
|
-
observation_events,
|
|
37
|
-
equatorial_coordinates
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
evening_civil = compute_twilight_time(
|
|
41
|
-
EVENING,
|
|
42
|
-
TWILIGHT_ANGLES[CIVIL],
|
|
43
|
-
observation_events,
|
|
44
|
-
equatorial_coordinates
|
|
45
|
-
)
|
|
27
|
+
def event_on(date, utc_offset: 0)
|
|
28
|
+
start_time = Time
|
|
29
|
+
.new(date.year, date.month, date.day, 0, 0, 0, utc_offset)
|
|
30
|
+
end_time = Time
|
|
31
|
+
.new(date.year, date.month, date.day, 23, 59, 59, utc_offset)
|
|
32
|
+
events = events_between(start_time, end_time)
|
|
46
33
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
34
|
+
TwilightEvent.new(
|
|
35
|
+
morning_civil_twilight_time:
|
|
36
|
+
events.morning_civil_twilight_times.first,
|
|
37
|
+
evening_civil_twilight_time:
|
|
38
|
+
events.evening_civil_twilight_times.first,
|
|
39
|
+
morning_nautical_twilight_time:
|
|
40
|
+
events.morning_nautical_twilight_times.first,
|
|
41
|
+
evening_nautical_twilight_time:
|
|
42
|
+
events.evening_nautical_twilight_times.first,
|
|
43
|
+
morning_astronomical_twilight_time:
|
|
44
|
+
events.morning_astronomical_twilight_times.first,
|
|
45
|
+
evening_astronomical_twilight_time:
|
|
46
|
+
events.evening_astronomical_twilight_times.first
|
|
59
47
|
)
|
|
48
|
+
end
|
|
60
49
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
50
|
+
def events_between(start_time, end_time)
|
|
51
|
+
rts_events = Astronoby::RiseTransitSetCalculator.new(
|
|
52
|
+
body: Sun,
|
|
53
|
+
observer: @observer,
|
|
54
|
+
ephem: @ephem
|
|
55
|
+
).events_between(start_time, end_time)
|
|
56
|
+
|
|
57
|
+
equatorial_by_time = {}
|
|
58
|
+
|
|
59
|
+
(rts_events.rising_times + rts_events.setting_times)
|
|
60
|
+
.compact
|
|
61
|
+
.each do |event_time|
|
|
62
|
+
rounded_time = event_time.round
|
|
63
|
+
next if equatorial_by_time.key?(rounded_time)
|
|
64
|
+
|
|
65
|
+
instant = Instant.from_time(rounded_time)
|
|
66
|
+
sun_at_time = Sun.new(instant: instant, ephem: @ephem)
|
|
67
|
+
equatorial_by_time[rounded_time] = sun_at_time.apparent.equatorial
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
morning_civil = []
|
|
71
|
+
evening_civil = []
|
|
72
|
+
morning_nautical = []
|
|
73
|
+
evening_nautical = []
|
|
74
|
+
morning_astronomical = []
|
|
75
|
+
evening_astronomical = []
|
|
76
|
+
|
|
77
|
+
arrays_by_period = {
|
|
78
|
+
MORNING => {
|
|
79
|
+
CIVIL => morning_civil,
|
|
80
|
+
NAUTICAL => morning_nautical,
|
|
81
|
+
ASTRONOMICAL => morning_astronomical
|
|
82
|
+
},
|
|
83
|
+
EVENING => {
|
|
84
|
+
CIVIL => evening_civil,
|
|
85
|
+
NAUTICAL => evening_nautical,
|
|
86
|
+
ASTRONOMICAL => evening_astronomical
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[
|
|
91
|
+
[rts_events.rising_times, MORNING],
|
|
92
|
+
[rts_events.setting_times, EVENING]
|
|
93
|
+
].each do |times, period|
|
|
94
|
+
times.each do |event_time|
|
|
95
|
+
next unless event_time
|
|
96
|
+
|
|
97
|
+
equatorial_coordinates = equatorial_by_time[event_time.round]
|
|
98
|
+
TWILIGHT_ANGLES.each do |twilight, angle|
|
|
99
|
+
arrays_by_period[period][twilight] << compute_twilight_time_from(
|
|
100
|
+
period,
|
|
101
|
+
angle,
|
|
102
|
+
event_time,
|
|
103
|
+
equatorial_coordinates
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
67
108
|
|
|
68
|
-
|
|
69
|
-
EVENING,
|
|
70
|
-
TWILIGHT_ANGLES[ASTRONOMICAL],
|
|
71
|
-
observation_events,
|
|
72
|
-
equatorial_coordinates
|
|
73
|
-
)
|
|
109
|
+
within_range = ->(time) { time && time >= start_time && time <= end_time }
|
|
74
110
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
111
|
+
TwilightEvents.new(
|
|
112
|
+
morning_civil.select(&within_range),
|
|
113
|
+
evening_civil.select(&within_range),
|
|
114
|
+
morning_nautical.select(&within_range),
|
|
115
|
+
evening_nautical.select(&within_range),
|
|
116
|
+
morning_astronomical.select(&within_range),
|
|
117
|
+
evening_astronomical.select(&within_range)
|
|
82
118
|
)
|
|
83
119
|
end
|
|
84
120
|
|
|
85
|
-
def time_for_zenith_angle(
|
|
121
|
+
def time_for_zenith_angle(
|
|
122
|
+
date:,
|
|
123
|
+
period_of_the_day:,
|
|
124
|
+
zenith_angle:,
|
|
125
|
+
utc_offset: 0
|
|
126
|
+
)
|
|
86
127
|
unless PERIODS_OF_THE_DAY.include?(period_of_the_day)
|
|
87
128
|
raise IncompatibleArgumentsError,
|
|
88
|
-
"Only #{PERIODS_OF_THE_DAY.join(" or ")} are allowed as
|
|
129
|
+
"Only #{PERIODS_OF_THE_DAY.join(" or ")} are allowed as " \
|
|
130
|
+
"period_of_the_day, got #{period_of_the_day}"
|
|
89
131
|
end
|
|
90
132
|
|
|
91
|
-
observation_events = get_observation_events(date)
|
|
92
|
-
midday_instant = create_midday_instant(date)
|
|
133
|
+
observation_events = get_observation_events(date, utc_offset: utc_offset)
|
|
134
|
+
midday_instant = create_midday_instant(date, utc_offset: utc_offset)
|
|
93
135
|
sun_at_midday = Sun.new(instant: midday_instant, ephem: @ephem)
|
|
94
136
|
equatorial_coordinates = sun_at_midday.apparent.equatorial
|
|
95
137
|
|
|
@@ -103,17 +145,17 @@ module Astronoby
|
|
|
103
145
|
|
|
104
146
|
private
|
|
105
147
|
|
|
106
|
-
def create_midday_instant(date)
|
|
107
|
-
time = Time.
|
|
148
|
+
def create_midday_instant(date, utc_offset: 0)
|
|
149
|
+
time = Time.new(date.year, date.month, date.day, 12, 0, 0, utc_offset)
|
|
108
150
|
Instant.from_time(time)
|
|
109
151
|
end
|
|
110
152
|
|
|
111
|
-
def get_observation_events(date)
|
|
153
|
+
def get_observation_events(date, utc_offset: 0)
|
|
112
154
|
Astronoby::RiseTransitSetCalculator.new(
|
|
113
155
|
body: Sun,
|
|
114
156
|
observer: @observer,
|
|
115
157
|
ephem: @ephem
|
|
116
|
-
).event_on(date)
|
|
158
|
+
).event_on(date, utc_offset: utc_offset)
|
|
117
159
|
end
|
|
118
160
|
|
|
119
161
|
def compute_twilight_time(
|
|
@@ -128,8 +170,21 @@ module Astronoby
|
|
|
128
170
|
observation_events.setting_time
|
|
129
171
|
end
|
|
130
172
|
|
|
131
|
-
|
|
132
|
-
|
|
173
|
+
compute_twilight_time_from(
|
|
174
|
+
period_of_the_day,
|
|
175
|
+
zenith_angle,
|
|
176
|
+
period_time,
|
|
177
|
+
equatorial_coordinates
|
|
178
|
+
)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def compute_twilight_time_from(
|
|
182
|
+
period_of_the_day,
|
|
183
|
+
zenith_angle,
|
|
184
|
+
period_time,
|
|
185
|
+
equatorial_coordinates
|
|
186
|
+
)
|
|
187
|
+
# If the sun doesn't rise or set on this day, we can't calculate twilight
|
|
133
188
|
return nil unless period_time
|
|
134
189
|
|
|
135
190
|
hour_angle_at_period = equatorial_coordinates
|
|
@@ -155,7 +210,7 @@ module Astronoby
|
|
|
155
210
|
|
|
156
211
|
twilight_in_hours =
|
|
157
212
|
time_sign * (hour_angle_at_twilight - hour_angle_at_period).hours *
|
|
158
|
-
|
|
213
|
+
GreenwichMeanSiderealTime::SIDEREAL_MINUTE_IN_UT_MINUTE
|
|
159
214
|
twilight_in_seconds = time_sign *
|
|
160
215
|
twilight_in_hours *
|
|
161
216
|
Constants::SECONDS_PER_HOUR
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Astronoby
|
|
4
|
+
class TwilightEvents
|
|
5
|
+
attr_reader :morning_civil_twilight_times,
|
|
6
|
+
:evening_civil_twilight_times,
|
|
7
|
+
:morning_nautical_twilight_times,
|
|
8
|
+
:evening_nautical_twilight_times,
|
|
9
|
+
:morning_astronomical_twilight_times,
|
|
10
|
+
:evening_astronomical_twilight_times
|
|
11
|
+
|
|
12
|
+
def initialize(
|
|
13
|
+
morning_civil,
|
|
14
|
+
evening_civil,
|
|
15
|
+
morning_nautical,
|
|
16
|
+
evening_nautical,
|
|
17
|
+
morning_astronomical,
|
|
18
|
+
evening_astronomical
|
|
19
|
+
)
|
|
20
|
+
@morning_civil_twilight_times = morning_civil
|
|
21
|
+
@evening_civil_twilight_times = evening_civil
|
|
22
|
+
@morning_nautical_twilight_times = morning_nautical
|
|
23
|
+
@evening_nautical_twilight_times = evening_nautical
|
|
24
|
+
@morning_astronomical_twilight_times = morning_astronomical
|
|
25
|
+
@evening_astronomical_twilight_times = evening_astronomical
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/astronoby/instant.rb
CHANGED
|
@@ -21,7 +21,12 @@ module Astronoby
|
|
|
21
21
|
class Instant
|
|
22
22
|
include Comparable
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# The adjustment value to align our noon-based Julian Date with the
|
|
25
|
+
# midnight-based epoch required by Ruby's `DateTime.jd` constructor.
|
|
26
|
+
# Our internal time values are standard astronomical Julian Dates, which
|
|
27
|
+
# start at noon. `DateTime.jd` expects a day that starts at the preceding
|
|
28
|
+
# midnight. This constant adds 0.5 days (12 hours) to make the conversion.
|
|
29
|
+
DATETIME_JD_EPOCH_ADJUSTMENT = 0.5
|
|
25
30
|
|
|
26
31
|
class << self
|
|
27
32
|
# Creates a new Instant from a Terrestrial Time value
|
|
@@ -89,8 +94,8 @@ module Astronoby
|
|
|
89
94
|
def to_datetime
|
|
90
95
|
DateTime.jd(
|
|
91
96
|
@terrestrial_time -
|
|
92
|
-
Rational(delta_t
|
|
93
|
-
|
|
97
|
+
Rational(delta_t, Constants::SECONDS_PER_DAY) +
|
|
98
|
+
DATETIME_JD_EPOCH_ADJUSTMENT
|
|
94
99
|
)
|
|
95
100
|
end
|
|
96
101
|
|
|
@@ -118,9 +123,32 @@ module Astronoby
|
|
|
118
123
|
|
|
119
124
|
# Get the Greenwich Mean Sidereal Time
|
|
120
125
|
#
|
|
121
|
-
# @return [Numeric] the sidereal time in
|
|
126
|
+
# @return [Numeric] the sidereal time in hours
|
|
122
127
|
def gmst
|
|
123
|
-
|
|
128
|
+
GreenwichMeanSiderealTime.from_utc(to_time).time
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Get the Greenwich Apparent Sidereal Time
|
|
132
|
+
#
|
|
133
|
+
# @return [Numeric] the sidereal time in hours
|
|
134
|
+
def gast
|
|
135
|
+
GreenwichApparentSiderealTime.from_utc(to_time).time
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Get the Local Mean Sidereal Time
|
|
139
|
+
#
|
|
140
|
+
# @param longitude [Astronoby::Angle] the observer's longitude
|
|
141
|
+
# @return [Numeric] the sidereal time in hours
|
|
142
|
+
def lmst(longitude:)
|
|
143
|
+
LocalMeanSiderealTime.from_utc(to_time, longitude: longitude).time
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Get the Local Apparent Sidereal Time
|
|
147
|
+
#
|
|
148
|
+
# @param longitude [Astronoby::Angle] the observer's longitude
|
|
149
|
+
# @return [Numeric] the sidereal time in hours
|
|
150
|
+
def last(longitude:)
|
|
151
|
+
LocalApparentSiderealTime.from_utc(to_time, longitude: longitude).time
|
|
124
152
|
end
|
|
125
153
|
|
|
126
154
|
# Get the International Atomic Time (TAI)
|
|
@@ -146,7 +174,7 @@ module Astronoby
|
|
|
146
174
|
#
|
|
147
175
|
# @return [Numeric] the offset in days
|
|
148
176
|
def utc_offset
|
|
149
|
-
|
|
177
|
+
Rational(delta_t / Constants::SECONDS_PER_DAY)
|
|
150
178
|
end
|
|
151
179
|
|
|
152
180
|
# Calculate hash value for the instant
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Astronoby
|
|
4
|
+
# @see https://en.wikipedia.org/wiki/Julian_day
|
|
5
|
+
# @see https://en.wikipedia.org/wiki/Epoch_(astronomy)
|
|
6
|
+
class JulianDate
|
|
7
|
+
# Starting year for Besselian epoch calculations
|
|
8
|
+
# @return [Integer] 1900
|
|
9
|
+
BESSELIAN_EPOCH_STARTING_YEAR = 1900
|
|
10
|
+
|
|
11
|
+
# Starting year for Julian epoch calculations
|
|
12
|
+
# @return [Integer] 2000
|
|
13
|
+
JULIAN_EPOCH_STARTING_YEAR = 2000
|
|
14
|
+
|
|
15
|
+
# Julian Date for Besselian epoch 1875.0
|
|
16
|
+
# @return [Float] 2405889.258550475
|
|
17
|
+
B1875 = 2405889.258550475
|
|
18
|
+
|
|
19
|
+
# Julian Date for Besselian epoch 1900.0
|
|
20
|
+
# @return [Float] 2415020.31352
|
|
21
|
+
B1900 = 2415020.31352
|
|
22
|
+
|
|
23
|
+
# Julian Date for Julian epoch 1950.0
|
|
24
|
+
# @return [Float] 2433282.5
|
|
25
|
+
J1950 = 2433282.5
|
|
26
|
+
|
|
27
|
+
# Julian Date for Julian epoch 2000.0 (current standard)
|
|
28
|
+
# @return [Float] 2451545.0
|
|
29
|
+
J2000 = 2451545.0
|
|
30
|
+
|
|
31
|
+
# Default epoch used by the library
|
|
32
|
+
# @return [Float] 2451545.0
|
|
33
|
+
DEFAULT_EPOCH = J2000
|
|
34
|
+
|
|
35
|
+
# Converts a Time object to Julian Date
|
|
36
|
+
#
|
|
37
|
+
# @param time [Time] the time to convert
|
|
38
|
+
# @return [Rational] the Julian Date
|
|
39
|
+
#
|
|
40
|
+
# @example
|
|
41
|
+
# JulianDate.from_time(Time.utc(2000, 1, 1, 12, 0, 0))
|
|
42
|
+
# # => 2451545.0
|
|
43
|
+
def self.from_time(time)
|
|
44
|
+
time.to_datetime.ajd
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Converts a Julian year to Julian Date
|
|
48
|
+
#
|
|
49
|
+
# Uses the formula: JD = J2000 + 365.25 * (year - 2000)
|
|
50
|
+
#
|
|
51
|
+
# @param julian_year [Float] the Julian year
|
|
52
|
+
# @return [Float] the Julian Date
|
|
53
|
+
#
|
|
54
|
+
# @example
|
|
55
|
+
# JulianDate.from_julian_year(2025.0)
|
|
56
|
+
# # => 2460676.25
|
|
57
|
+
def self.from_julian_year(julian_year)
|
|
58
|
+
J2000 + Constants::DAYS_PER_JULIAN_YEAR *
|
|
59
|
+
(julian_year - JULIAN_EPOCH_STARTING_YEAR)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Converts a Besselian year to Julian Date
|
|
63
|
+
#
|
|
64
|
+
# Uses the formula: JD = B1900 + 365.242198781 * (year - 1900)
|
|
65
|
+
# where 365.242198781 is the tropical year length at B1900.
|
|
66
|
+
#
|
|
67
|
+
# @param besselian_year [Float] the Besselian year
|
|
68
|
+
# @return [Float] the Julian Date
|
|
69
|
+
#
|
|
70
|
+
# @example
|
|
71
|
+
# JulianDate.from_besselian_year(1875.0)
|
|
72
|
+
# # => 2405889.258550475
|
|
73
|
+
def self.from_besselian_year(besselian_year)
|
|
74
|
+
B1900 + Constants::TROPICAL_YEAR_AT_B1900 *
|
|
75
|
+
(besselian_year - BESSELIAN_EPOCH_STARTING_YEAR)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -9,36 +9,34 @@ module Astronoby
|
|
|
9
9
|
# IAU resolution in 2006 in favor of the P03 astronomical model
|
|
10
10
|
# https://syrte.obspm.fr/iau2006/aa03_412_P03.pdf
|
|
11
11
|
|
|
12
|
-
EPOCH_OF_REFERENCE =
|
|
12
|
+
EPOCH_OF_REFERENCE = JulianDate::DEFAULT_EPOCH
|
|
13
13
|
OBLIQUITY_OF_REFERENCE = 23.4392794
|
|
14
14
|
|
|
15
|
-
def self.
|
|
16
|
-
return obliquity_of_reference if
|
|
15
|
+
def self.at(instant)
|
|
16
|
+
return obliquity_of_reference if instant.julian_date == EPOCH_OF_REFERENCE
|
|
17
17
|
|
|
18
18
|
t = Rational(
|
|
19
|
-
|
|
19
|
+
instant.julian_date - EPOCH_OF_REFERENCE,
|
|
20
20
|
Constants::DAYS_PER_JULIAN_CENTURY
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
-
epsilon0 =
|
|
23
|
+
epsilon0 = obliquity_of_reference_in_arcseconds
|
|
24
24
|
c1 = -46.836769
|
|
25
25
|
c2 = -0.0001831
|
|
26
26
|
c3 = 0.00200340
|
|
27
27
|
c4 = -0.000000576
|
|
28
28
|
c5 = -0.0000000434
|
|
29
29
|
|
|
30
|
-
Angle.
|
|
31
|
-
0,
|
|
32
|
-
0,
|
|
30
|
+
Angle.from_degree_arcseconds(
|
|
33
31
|
epsilon0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * c5))))
|
|
34
32
|
)
|
|
35
33
|
end
|
|
36
34
|
|
|
37
35
|
def self.obliquity_of_reference
|
|
38
|
-
Angle.
|
|
36
|
+
Angle.from_degree_arcseconds(obliquity_of_reference_in_arcseconds)
|
|
39
37
|
end
|
|
40
38
|
|
|
41
|
-
def self.
|
|
39
|
+
def self.obliquity_of_reference_in_arcseconds
|
|
42
40
|
84381.406
|
|
43
41
|
end
|
|
44
42
|
end
|
data/lib/astronoby/nutation.rb
CHANGED
|
@@ -104,7 +104,7 @@ module Astronoby
|
|
|
104
104
|
|
|
105
105
|
# @return [Matrix] The nutation matrix
|
|
106
106
|
def matrix
|
|
107
|
-
mean_obliquity = MeanObliquity.
|
|
107
|
+
mean_obliquity = MeanObliquity.at(@instant)
|
|
108
108
|
true_obliquity = mean_obliquity + nutation_in_obliquity
|
|
109
109
|
build_nutation_matrix(
|
|
110
110
|
mean_obliquity: mean_obliquity,
|
|
@@ -125,6 +125,14 @@ module Astronoby
|
|
|
125
125
|
|
|
126
126
|
private
|
|
127
127
|
|
|
128
|
+
def cache_key
|
|
129
|
+
@_cache_key ||= CacheKey.generate(:nutation, @instant)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def cache
|
|
133
|
+
Astronoby.cache
|
|
134
|
+
end
|
|
135
|
+
|
|
128
136
|
def iau2000a
|
|
129
137
|
a = fundamental_arguments
|
|
130
138
|
|
|
@@ -172,7 +180,7 @@ module Astronoby
|
|
|
172
180
|
end
|
|
173
181
|
|
|
174
182
|
def iau2000b_angles
|
|
175
|
-
|
|
183
|
+
cache.fetch(cache_key) do
|
|
176
184
|
dpsi, deps = iau2000b
|
|
177
185
|
dpsi = Angle.from_degree_arcseconds(dpsi / 1e7)
|
|
178
186
|
deps = Angle.from_degree_arcseconds(deps / 1e7)
|
|
@@ -206,7 +214,7 @@ module Astronoby
|
|
|
206
214
|
|
|
207
215
|
def julian_centuries
|
|
208
216
|
@julian_centuries ||=
|
|
209
|
-
(@instant.tt -
|
|
217
|
+
(@instant.tt - JulianDate::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
|
|
210
218
|
end
|
|
211
219
|
|
|
212
220
|
# IAU 2006/2000A formula for the mean anomaly of the Moon
|
data/lib/astronoby/observer.rb
CHANGED
|
@@ -62,7 +62,7 @@ module Astronoby
|
|
|
62
62
|
def earth_fixed_rotation_matrix_for(instant)
|
|
63
63
|
dpsi = Nutation.new(instant: instant).nutation_in_longitude
|
|
64
64
|
|
|
65
|
-
mean_obliquity = MeanObliquity.
|
|
65
|
+
mean_obliquity = MeanObliquity.at(instant)
|
|
66
66
|
|
|
67
67
|
gast = Angle.from_radians(
|
|
68
68
|
Angle.from_hours(instant.gmst).radians +
|