astronoby 0.10.0 → 0.11.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 +134 -0
- data/README.md +13 -0
- data/UPGRADING.md +186 -0
- data/docs/README.md +32 -8
- data/docs/altitude_crossings.md +92 -0
- data/docs/configuration.md +8 -0
- data/docs/deep_sky_bodies.md +45 -0
- data/docs/glossary.md +2 -0
- data/docs/instant.md +14 -7
- data/docs/lunar_eclipses.md +128 -13
- data/docs/twilight_times.md +26 -11
- data/lib/astronoby/bodies/deep_sky_object_position.rb +6 -24
- data/lib/astronoby/cache.rb +13 -0
- data/lib/astronoby/constants.rb +1 -2
- data/lib/astronoby/constellations/data.rb +1 -1
- data/lib/astronoby/coordinates/equatorial.rb +15 -5
- data/lib/astronoby/delta_t.rb +44 -0
- data/lib/astronoby/events/altitude_crossing.rb +29 -0
- data/lib/astronoby/events/altitude_crossing_calculator.rb +511 -0
- data/lib/astronoby/events/altitude_extreme.rb +31 -0
- data/lib/astronoby/events/lunar_eclipse.rb +49 -37
- data/lib/astronoby/events/lunar_eclipse_calculator.rb +143 -31
- data/lib/astronoby/events/lunar_eclipse_geometry.rb +115 -0
- data/lib/astronoby/events/lunar_eclipse_phase.rb +45 -0
- data/lib/astronoby/events/lunar_eclipse_visibility.rb +296 -0
- data/lib/astronoby/events/rise_transit_set_calculator.rb +9 -13
- data/lib/astronoby/events/twilight_calculator.rb +37 -147
- data/lib/astronoby/horizon.rb +40 -0
- data/lib/astronoby/horizontal_transformation.rb +112 -0
- data/lib/astronoby/instant.rb +50 -13
- data/lib/astronoby/precession.rb +1 -1
- data/lib/astronoby/reference_frame.rb +8 -3
- data/lib/astronoby/reference_frames/apparent.rb +20 -8
- data/lib/astronoby/reference_frames/mean_of_date.rb +6 -0
- data/lib/astronoby/reference_frames/teme.rb +6 -0
- data/lib/astronoby/reference_frames/topocentric.rb +6 -0
- data/lib/astronoby/root_finder.rb +16 -9
- data/lib/astronoby/time/greenwich_apparent_sidereal_time.rb +10 -8
- data/lib/astronoby/time/greenwich_mean_sidereal_time.rb +11 -9
- data/lib/astronoby/util/time.rb +0 -22
- data/lib/astronoby/version.rb +1 -1
- data/lib/astronoby.rb +9 -1
- metadata +40 -5
- data/lib/astronoby/events/eclipse_phase.rb +0 -27
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Astronoby
|
|
4
|
+
# Transforms equatorial coordinates at J2000.0 into horizontal coordinates
|
|
5
|
+
# for one observer at one instant.
|
|
6
|
+
#
|
|
7
|
+
# Everything that depends only on the observer and the instant is computed
|
|
8
|
+
# once at construction: the rotation from the ICRS to the true equator and
|
|
9
|
+
# equinox of date (nutation * precession, with the ICRS frame bias), and the
|
|
10
|
+
# local apparent sidereal time. Converting a point then costs a matrix
|
|
11
|
+
# product and a few trigonometric functions, instead of rebuilding the
|
|
12
|
+
# reference frame chain for each point. It is meant for converting many
|
|
13
|
+
# points for the same observer and instant, such as the stars of a catalogue
|
|
14
|
+
# for a sky chart.
|
|
15
|
+
#
|
|
16
|
+
# It plays the role of the "star-independent astrometry parameters" of SOFA
|
|
17
|
+
# and ERFA, restricted to what Astronoby applies without an ephemeris. The
|
|
18
|
+
# result is the same as
|
|
19
|
+
# `DeepSkyObject#at(instant).observed_by(observer).horizontal`, but the
|
|
20
|
+
# points are treated as fixed directions: no aberration, no proper motion and
|
|
21
|
+
# no parallax. The topocentric correction is not applied either, as it is
|
|
22
|
+
# nil for objects at stellar distances. Atmospheric refraction is optional.
|
|
23
|
+
#
|
|
24
|
+
# @example Convert the stars of a catalogue for one observer and instant
|
|
25
|
+
# transformation = Astronoby::HorizontalTransformation.new(
|
|
26
|
+
# observer: observer,
|
|
27
|
+
# instant: instant
|
|
28
|
+
# )
|
|
29
|
+
# horizontals = catalogue.map { |star| transformation.apply(star) }
|
|
30
|
+
class HorizontalTransformation
|
|
31
|
+
# @return [Astronoby::Observer] the observer
|
|
32
|
+
attr_reader :observer
|
|
33
|
+
|
|
34
|
+
# @return [Astronoby::Instant] the instant
|
|
35
|
+
attr_reader :instant
|
|
36
|
+
|
|
37
|
+
# @param observer [Astronoby::Observer] the observer
|
|
38
|
+
# @param instant [Astronoby::Instant] the instant of the observation
|
|
39
|
+
def initialize(observer:, instant:)
|
|
40
|
+
@observer = observer
|
|
41
|
+
@instant = instant
|
|
42
|
+
@rotation_rows = (
|
|
43
|
+
Nutation.matrix_for(instant) * Precession.matrix_for(instant)
|
|
44
|
+
).to_a.freeze
|
|
45
|
+
@local_apparent_sidereal_time = LocalApparentSiderealTime.from_utc(
|
|
46
|
+
instant.to_time,
|
|
47
|
+
longitude: observer.longitude
|
|
48
|
+
).time
|
|
49
|
+
freeze
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @param equatorial_coordinates [Astronoby::Coordinates::Equatorial]
|
|
53
|
+
# equatorial coordinates at epoch J2000.0
|
|
54
|
+
# @param refraction [Boolean] whether to apply atmospheric refraction
|
|
55
|
+
# correction (default: false)
|
|
56
|
+
# @return [Astronoby::Coordinates::Horizontal] horizontal coordinates
|
|
57
|
+
# @raise [Astronoby::IncompatibleArgumentsError] if the coordinates are not
|
|
58
|
+
# referred to epoch J2000.0
|
|
59
|
+
def apply(equatorial_coordinates, refraction: false)
|
|
60
|
+
ensure_j2000!(equatorial_coordinates)
|
|
61
|
+
|
|
62
|
+
horizontal = apparent_equatorial(equatorial_coordinates).to_horizontal(
|
|
63
|
+
time: @instant.to_time,
|
|
64
|
+
observer: @observer
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if refraction
|
|
68
|
+
Refraction.correct_horizontal_coordinates(coordinates: horizontal)
|
|
69
|
+
else
|
|
70
|
+
horizontal
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Equatorial coordinates of date, with the hour angle already known so that
|
|
77
|
+
# the sidereal time is not computed again.
|
|
78
|
+
def apparent_equatorial(coordinates)
|
|
79
|
+
declination = coordinates.declination
|
|
80
|
+
right_ascension = coordinates.right_ascension
|
|
81
|
+
cos_declination = declination.cos
|
|
82
|
+
x = cos_declination * right_ascension.cos
|
|
83
|
+
y = cos_declination * right_ascension.sin
|
|
84
|
+
z = declination.sin
|
|
85
|
+
|
|
86
|
+
row_x, row_y, row_z = @rotation_rows
|
|
87
|
+
apparent_x = row_x[0] * x + row_x[1] * y + row_x[2] * z
|
|
88
|
+
apparent_y = row_y[0] * x + row_y[1] * y + row_y[2] * z
|
|
89
|
+
apparent_z = row_z[0] * x + row_z[1] * y + row_z[2] * z
|
|
90
|
+
|
|
91
|
+
apparent_right_ascension =
|
|
92
|
+
Angle.from_radians(Math.atan2(apparent_y, apparent_x))
|
|
93
|
+
|
|
94
|
+
Coordinates::Equatorial.new(
|
|
95
|
+
right_ascension: apparent_right_ascension,
|
|
96
|
+
declination: Angle.asin(apparent_z.clamp(-1.0, 1.0)),
|
|
97
|
+
hour_angle:
|
|
98
|
+
Angle.from_hours(@local_apparent_sidereal_time) -
|
|
99
|
+
apparent_right_ascension,
|
|
100
|
+
epoch: @instant.tt
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def ensure_j2000!(coordinates)
|
|
105
|
+
return if coordinates.epoch == JulianDate::J2000
|
|
106
|
+
|
|
107
|
+
raise IncompatibleArgumentsError,
|
|
108
|
+
"Expected equatorial coordinates at epoch J2000.0, got epoch " \
|
|
109
|
+
"#{coordinates.epoch}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
data/lib/astronoby/instant.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "date"
|
|
4
|
+
require "horologium"
|
|
4
5
|
|
|
5
6
|
module Astronoby
|
|
6
7
|
# Represents a specific instant in time using Terrestrial Time (TT) as its
|
|
@@ -8,9 +9,15 @@ module Astronoby
|
|
|
8
9
|
# time scales commonly used in astronomy:
|
|
9
10
|
# - Terrestrial Time (TT)
|
|
10
11
|
# - International Atomic Time (TAI)
|
|
12
|
+
# - Barycentric Dynamical Time (TDB)
|
|
11
13
|
# - Universal Time Coordinated (UTC)
|
|
12
14
|
# - Greenwich Mean Sidereal Time (GMST)
|
|
13
15
|
#
|
|
16
|
+
# TAI and TDB are delegated to the horologium gem, which owns the scales
|
|
17
|
+
# that convert by definition or by model. UT1 is not: it rests on
|
|
18
|
+
# observations of the rotation of the Earth, and comes from
|
|
19
|
+
# {Astronoby::DeltaT}.
|
|
20
|
+
#
|
|
14
21
|
# @example Create an instant from the current time
|
|
15
22
|
# instant = Astronoby::Instant.from_time(Time.now)
|
|
16
23
|
# instant.tai # Get International Atomic Time
|
|
@@ -28,6 +35,16 @@ module Astronoby
|
|
|
28
35
|
# midnight. This constant adds 0.5 days (12 hours) to make the conversion.
|
|
29
36
|
DATETIME_JD_EPOCH_ADJUSTMENT = 0.5
|
|
30
37
|
|
|
38
|
+
# Astronoby pins the precision it asks horologium for rather than reading
|
|
39
|
+
# horologium's global default, so a host application configuring
|
|
40
|
+
# horologium for itself cannot change what Astronoby computes.
|
|
41
|
+
#
|
|
42
|
+
# Exact rather than standard: standard would round the Julian Date into a
|
|
43
|
+
# two-part float on the way in, and TAI is a fixed offset from TT, so
|
|
44
|
+
# there is nothing to round it for. It is also the cheaper of the two
|
|
45
|
+
# here, having no conversion to make in either direction.
|
|
46
|
+
PRECISION = :exact
|
|
47
|
+
|
|
31
48
|
class << self
|
|
32
49
|
# Creates a new Instant from a Terrestrial Time value
|
|
33
50
|
#
|
|
@@ -44,7 +61,7 @@ module Astronoby
|
|
|
44
61
|
# @param time [Time] a Time object to convert
|
|
45
62
|
# @return [Astronoby::Instant] a new Instant object
|
|
46
63
|
def from_time(time)
|
|
47
|
-
delta_t =
|
|
64
|
+
delta_t = DeltaT.at(time)
|
|
48
65
|
terrestrial_time = time.utc.to_datetime.ajd +
|
|
49
66
|
Rational(delta_t, Constants::SECONDS_PER_DAY)
|
|
50
67
|
from_terrestrial_time(terrestrial_time)
|
|
@@ -55,7 +72,7 @@ module Astronoby
|
|
|
55
72
|
# @param julian_date [Numeric] the Julian Date in UTC
|
|
56
73
|
# @return [Astronoby::Instant] a new Instant object
|
|
57
74
|
def from_utc_julian_date(julian_date)
|
|
58
|
-
delta_t =
|
|
75
|
+
delta_t = DeltaT.at(julian_date)
|
|
59
76
|
terrestrial_time = julian_date +
|
|
60
77
|
Rational(delta_t, Constants::SECONDS_PER_DAY)
|
|
61
78
|
from_terrestrial_time(terrestrial_time)
|
|
@@ -120,8 +137,7 @@ module Astronoby
|
|
|
120
137
|
#
|
|
121
138
|
# @return [Numeric] Delta T in seconds
|
|
122
139
|
def delta_t
|
|
123
|
-
@memo[:delta_t] ||=
|
|
124
|
-
Util::Time.terrestrial_universal_time_delta(@terrestrial_time)
|
|
140
|
+
@memo[:delta_t] ||= DeltaT.at(@terrestrial_time)
|
|
125
141
|
end
|
|
126
142
|
|
|
127
143
|
# Get the Greenwich Mean Sidereal Time
|
|
@@ -156,21 +172,26 @@ module Astronoby
|
|
|
156
172
|
|
|
157
173
|
# Get the International Atomic Time (TAI)
|
|
158
174
|
#
|
|
159
|
-
#
|
|
175
|
+
# Read as a Rational rather than a Float. A Julian Date is around 2.46
|
|
176
|
+
# million, so a single Float rounds to about 47 microseconds of the day,
|
|
177
|
+
# which would coarsen an instant that TT carries exactly.
|
|
178
|
+
#
|
|
179
|
+
# @return [Rational] TAI as Julian Date
|
|
160
180
|
def tai
|
|
161
|
-
@memo[:tai] ||=
|
|
162
|
-
|
|
181
|
+
@memo[:tai] ||= scale_free_instant
|
|
182
|
+
.as(:julian_date, scale: :tai, as: :rational)
|
|
163
183
|
end
|
|
164
184
|
|
|
165
185
|
# Get the Barycentric Dynamical Time (TDB)
|
|
166
|
-
# Note: Currently approximated as equal to TT
|
|
167
186
|
#
|
|
168
|
-
#
|
|
187
|
+
# TDB - TT comes from the Fairhead and Bretagnon series ERFA uses. It
|
|
188
|
+
# peaks around 1.7 milliseconds, so a Float Julian Date would quantise
|
|
189
|
+
# most of the correction away; see {#tai} on the shape.
|
|
190
|
+
#
|
|
191
|
+
# @return [Rational] TDB as Julian Date
|
|
169
192
|
def tdb
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
# doesn't support it and consider they are the same value.
|
|
173
|
-
@memo[:tdb] ||= @terrestrial_time
|
|
193
|
+
@memo[:tdb] ||= scale_free_instant
|
|
194
|
+
.as(:julian_date, scale: :tdb, as: :rational)
|
|
174
195
|
end
|
|
175
196
|
|
|
176
197
|
# Get the offset between TT and UTC for this instant
|
|
@@ -198,5 +219,21 @@ module Astronoby
|
|
|
198
219
|
@terrestrial_time <=> other.terrestrial_time
|
|
199
220
|
end
|
|
200
221
|
alias_method :eql?, :==
|
|
222
|
+
|
|
223
|
+
private
|
|
224
|
+
|
|
225
|
+
# The instant as horologium holds it: a point on the timeline with no
|
|
226
|
+
# scale of its own. Building it costs more than reading a Julian Date back
|
|
227
|
+
# out of it, so it is built on the first scale conversion that needs it
|
|
228
|
+
# and never on the paths that only want TT.
|
|
229
|
+
#
|
|
230
|
+
# @return [Horologium::Instant]
|
|
231
|
+
def scale_free_instant
|
|
232
|
+
@memo[:scale_free_instant] ||= Horologium::Instant.from_julian_date(
|
|
233
|
+
@terrestrial_time,
|
|
234
|
+
scale: :tt,
|
|
235
|
+
precision: PRECISION
|
|
236
|
+
)
|
|
237
|
+
end
|
|
201
238
|
end
|
|
202
239
|
end
|
data/lib/astronoby/precession.rb
CHANGED
|
@@ -39,13 +39,18 @@ module Astronoby
|
|
|
39
39
|
@target_body = target_body
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# @return [Numeric] Julian Date epoch
|
|
43
|
+
def epoch
|
|
44
|
+
JulianDate::DEFAULT_EPOCH
|
|
45
|
+
end
|
|
46
|
+
|
|
42
47
|
# @return [Astronoby::Coordinates::Equatorial] equatorial coordinates
|
|
43
|
-
# derived from the position vector
|
|
48
|
+
# derived from the position vector, referred to the frame's epoch
|
|
44
49
|
def equatorial
|
|
45
50
|
@equatorial ||= begin
|
|
46
|
-
return Coordinates::Equatorial.zero if distance.zero?
|
|
51
|
+
return Coordinates::Equatorial.zero(epoch: epoch) if distance.zero?
|
|
47
52
|
|
|
48
|
-
Coordinates::Equatorial.from_position_vector(@position)
|
|
53
|
+
Coordinates::Equatorial.from_position_vector(@position, epoch: epoch)
|
|
49
54
|
end
|
|
50
55
|
end
|
|
51
56
|
|
|
@@ -6,29 +6,35 @@ module Astronoby
|
|
|
6
6
|
class Apparent < ReferenceFrame
|
|
7
7
|
# Builds an apparent frame from an astrometric frame by applying
|
|
8
8
|
# aberration in GCRS, then rotating by nutation * precession (with ICRS
|
|
9
|
-
# frame bias).
|
|
9
|
+
# frame bias). Without Earth's geometric frame there is no observer
|
|
10
|
+
# velocity to derive the aberration from, so only the rotation is applied.
|
|
10
11
|
#
|
|
11
12
|
# @param instant [Astronoby::Instant] the time instant
|
|
12
13
|
# @param target_astrometric [Astronoby::Astrometric] target's astrometric
|
|
13
14
|
# frame
|
|
14
|
-
# @param earth_geometric [Astronoby::Geometric] Earth's geometric
|
|
15
|
+
# @param earth_geometric [Astronoby::Geometric, nil] Earth's geometric
|
|
16
|
+
# frame
|
|
15
17
|
# @param target_body [Astronoby::Body, nil] the target body
|
|
16
18
|
# @return [Astronoby::Apparent] a new apparent frame
|
|
17
19
|
def self.build_from_astrometric(
|
|
18
20
|
instant:,
|
|
19
21
|
target_astrometric:,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
target_body:,
|
|
23
|
+
earth_geometric: nil
|
|
22
24
|
)
|
|
23
25
|
position = target_astrometric.position
|
|
24
26
|
velocity = target_astrometric.velocity
|
|
25
27
|
precession_matrix = Precession.matrix_for(instant)
|
|
26
28
|
nutation_matrix = Nutation.matrix_for(instant)
|
|
27
29
|
|
|
28
|
-
corrected_position =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
corrected_position = if earth_geometric
|
|
31
|
+
Aberration.new(
|
|
32
|
+
astrometric_position: position,
|
|
33
|
+
observer_velocity: earth_geometric.velocity
|
|
34
|
+
).corrected_position
|
|
35
|
+
else
|
|
36
|
+
position
|
|
37
|
+
end
|
|
32
38
|
|
|
33
39
|
# In theory, here we should also apply light deflection. However, so far
|
|
34
40
|
# the deflection algorithm hasn't shown any significant changes to the
|
|
@@ -53,6 +59,12 @@ module Astronoby
|
|
|
53
59
|
)
|
|
54
60
|
end
|
|
55
61
|
|
|
62
|
+
# @return [Numeric] Julian Date epoch of the frame's coordinates, which
|
|
63
|
+
# are referred to the true equator and equinox of date
|
|
64
|
+
def epoch
|
|
65
|
+
instant.tt
|
|
66
|
+
end
|
|
67
|
+
|
|
56
68
|
# @return [Astronoby::Coordinates::Ecliptic] ecliptic coordinates at the
|
|
57
69
|
# current instant (true ecliptic and equinox of date)
|
|
58
70
|
def ecliptic
|
|
@@ -37,6 +37,12 @@ module Astronoby
|
|
|
37
37
|
)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# @return [Numeric] Julian Date epoch of the frame's coordinates, which
|
|
41
|
+
# are referred to the mean equator and equinox of date
|
|
42
|
+
def epoch
|
|
43
|
+
instant.tt
|
|
44
|
+
end
|
|
45
|
+
|
|
40
46
|
# @return [Astronoby::Coordinates::Ecliptic] ecliptic coordinates at the
|
|
41
47
|
# current instant (mean equinox of date)
|
|
42
48
|
def ecliptic
|
|
@@ -43,6 +43,12 @@ module Astronoby
|
|
|
43
43
|
)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# @return [Numeric] Julian Date epoch of the frame's coordinates, which
|
|
47
|
+
# are referred to the true equator and mean equinox of date
|
|
48
|
+
def epoch
|
|
49
|
+
instant.tt
|
|
50
|
+
end
|
|
51
|
+
|
|
46
52
|
# Converts TEME position and velocity to ECEF using the canonical
|
|
47
53
|
# Vallado formulation with R₃(GMST).
|
|
48
54
|
#
|
|
@@ -64,6 +64,12 @@ module Astronoby
|
|
|
64
64
|
@observer = observer
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
# @return [Numeric] Julian Date epoch of the frame's coordinates, which
|
|
68
|
+
# are referred to the true equator and equinox of date
|
|
69
|
+
def epoch
|
|
70
|
+
instant.tt
|
|
71
|
+
end
|
|
72
|
+
|
|
67
73
|
# @return [Astronoby::Coordinates::Ecliptic] ecliptic coordinates at the
|
|
68
74
|
# current instant (true ecliptic and equinox of date)
|
|
69
75
|
def ecliptic
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Astronoby
|
|
4
4
|
class RootFinder
|
|
5
5
|
MIN_SAMPLES_PER_PERIOD = 20
|
|
6
|
-
BISECTION_TOLERANCE_DAYS = 1e-
|
|
6
|
+
BISECTION_TOLERANCE_DAYS = 1e-8
|
|
7
7
|
|
|
8
8
|
# @param value_at [#call] callable mapping a Julian Date (Terrestrial Time)
|
|
9
9
|
# to a Float
|
|
@@ -22,21 +22,28 @@ module Astronoby
|
|
|
22
22
|
# @return [Array<Float>] root times in Julian Date (Terrestrial Time),
|
|
23
23
|
# sorted by time
|
|
24
24
|
def roots(start_jd, end_jd, accept: nil)
|
|
25
|
-
|
|
26
|
-
located =
|
|
25
|
+
samples = collect_samples(start_jd, end_jd)
|
|
26
|
+
located = (
|
|
27
|
+
samples_on_root(samples) +
|
|
28
|
+
find_brackets(samples).map { |a, b, f_a| bisect(a, b, f_a) }
|
|
29
|
+
).uniq.sort
|
|
27
30
|
located.select { |jd| accept.nil? || accept.call(jd) }
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
private
|
|
31
34
|
|
|
32
|
-
def
|
|
33
|
-
samples
|
|
35
|
+
def samples_on_root(samples)
|
|
36
|
+
samples.filter_map { |sample| sample[:jd] if sample[:value].zero? }
|
|
37
|
+
end
|
|
34
38
|
|
|
39
|
+
def find_brackets(samples)
|
|
35
40
|
(0...samples.length - 1).filter_map do |i|
|
|
36
41
|
current = samples[i]
|
|
37
42
|
following = samples[i + 1]
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
if sign_change?(current, following)
|
|
45
|
+
[current[:jd], following[:jd], current[:value]]
|
|
46
|
+
end
|
|
40
47
|
end
|
|
41
48
|
end
|
|
42
49
|
|
|
@@ -61,11 +68,11 @@ module Astronoby
|
|
|
61
68
|
[base_samples, MIN_SAMPLES_PER_PERIOD].max
|
|
62
69
|
end
|
|
63
70
|
|
|
64
|
-
def bisect(a, b)
|
|
65
|
-
f_a = @value_at.call(a)
|
|
66
|
-
|
|
71
|
+
def bisect(a, b, f_a)
|
|
67
72
|
while (b - a).abs > BISECTION_TOLERANCE_DAYS
|
|
68
73
|
midpoint = (a + b) / 2
|
|
74
|
+
break if midpoint == a || midpoint == b
|
|
75
|
+
|
|
69
76
|
f_midpoint = @value_at.call(midpoint)
|
|
70
77
|
return midpoint if f_midpoint.zero?
|
|
71
78
|
|
|
@@ -10,16 +10,18 @@ module Astronoby
|
|
|
10
10
|
# @param utc [Time] the UTC time
|
|
11
11
|
# @return [Astronoby::GreenwichApparentSiderealTime]
|
|
12
12
|
def self.from_utc(utc)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
Astronoby.cache.fetch(CacheKey.generate_for_utc(:gast, utc)) do
|
|
14
|
+
gmst = GreenwichMeanSiderealTime.from_utc(utc)
|
|
15
|
+
instant = Instant.from_time(utc)
|
|
16
|
+
nutation = Nutation.new(instant: instant)
|
|
17
|
+
mean_obliquity = MeanObliquity.at(instant)
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
equation_of_equinoxes = nutation.nutation_in_longitude.hours *
|
|
20
|
+
mean_obliquity.cos
|
|
21
|
+
gast_time = normalize_time(gmst.time + equation_of_equinoxes)
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
new(date: gmst.date, time: gast_time)
|
|
24
|
+
end
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
# @param date [Date] the calendar date
|
|
@@ -27,16 +27,18 @@ module Astronoby
|
|
|
27
27
|
# @param utc [Time] the UTC time
|
|
28
28
|
# @return [Astronoby::GreenwichMeanSiderealTime]
|
|
29
29
|
def self.from_utc(utc)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
Astronoby.cache.fetch(CacheKey.generate_for_utc(:gmst, utc)) do
|
|
31
|
+
date = utc.to_date
|
|
32
|
+
gmst_hours = begin
|
|
33
|
+
gmst_radians = IERS::GMST.at(utc)
|
|
34
|
+
gmst_radians * 12.0 / Math::PI
|
|
35
|
+
rescue IERS::OutOfRangeError
|
|
36
|
+
from_utc_polynomial(utc)
|
|
37
|
+
end
|
|
38
|
+
gmst_hours = normalize_time(gmst_hours)
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
new(date: date, time: gmst_hours)
|
|
41
|
+
end
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
# Polynomial fallback for dates outside the IERS EOP data range.
|
data/lib/astronoby/util/time.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "iers"
|
|
4
|
-
|
|
5
3
|
module Astronoby
|
|
6
4
|
module Util
|
|
7
5
|
module Time
|
|
@@ -40,26 +38,6 @@ module Astronoby
|
|
|
40
38
|
|
|
41
39
|
::Time.utc(date.year, date.month, date.day, hour, minute, second).round
|
|
42
40
|
end
|
|
43
|
-
|
|
44
|
-
# @param instant [Numeric, Time, Date, DateTime]
|
|
45
|
-
# @return [Numeric] Delta T (TT - UT1) in seconds for the given instant
|
|
46
|
-
def self.terrestrial_universal_time_delta(instant)
|
|
47
|
-
case instant
|
|
48
|
-
when Numeric
|
|
49
|
-
IERS::DeltaT.at(jd: instant).delta_t
|
|
50
|
-
when ::Time, ::Date, ::DateTime
|
|
51
|
-
IERS::DeltaT.at(instant).delta_t
|
|
52
|
-
else
|
|
53
|
-
raise IncompatibleArgumentsError,
|
|
54
|
-
"Expected a Numeric, Time, Date or DateTime object, got #{instant.class}"
|
|
55
|
-
end
|
|
56
|
-
rescue IERS::OutOfRangeError => e
|
|
57
|
-
if e.available_range
|
|
58
|
-
IERS::DeltaT.at(mjd: e.available_range.end).delta_t
|
|
59
|
-
else
|
|
60
|
-
0
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
41
|
end
|
|
64
42
|
end
|
|
65
43
|
end
|
data/lib/astronoby/version.rb
CHANGED
data/lib/astronoby.rb
CHANGED
|
@@ -18,6 +18,7 @@ require "astronoby/body"
|
|
|
18
18
|
require "astronoby/position"
|
|
19
19
|
require "astronoby/ephem"
|
|
20
20
|
require "astronoby/julian_date"
|
|
21
|
+
require "astronoby/delta_t"
|
|
21
22
|
require "astronoby/instant"
|
|
22
23
|
require "astronoby/vector"
|
|
23
24
|
require "astronoby/velocity"
|
|
@@ -47,6 +48,8 @@ require "astronoby/equinox_solstice"
|
|
|
47
48
|
require "astronoby/errors"
|
|
48
49
|
require "astronoby/extremum_finder"
|
|
49
50
|
require "astronoby/root_finder"
|
|
51
|
+
require "astronoby/horizon"
|
|
52
|
+
require "astronoby/horizontal_transformation"
|
|
50
53
|
require "astronoby/events/conjunction"
|
|
51
54
|
require "astronoby/events/opposition"
|
|
52
55
|
require "astronoby/events/greatest_elongation"
|
|
@@ -55,15 +58,20 @@ require "astronoby/events/greatest_elongation_calculator"
|
|
|
55
58
|
require "astronoby/events/extremum_event"
|
|
56
59
|
require "astronoby/events/extremum_calculator"
|
|
57
60
|
require "astronoby/events/moon_phases"
|
|
61
|
+
require "astronoby/events/altitude_crossing"
|
|
62
|
+
require "astronoby/events/altitude_extreme"
|
|
63
|
+
require "astronoby/events/altitude_crossing_calculator"
|
|
58
64
|
require "astronoby/events/rise_transit_set_event"
|
|
59
65
|
require "astronoby/events/rise_transit_set_events"
|
|
60
66
|
require "astronoby/events/rise_transit_set_calculator"
|
|
61
67
|
require "astronoby/events/twilight_event"
|
|
62
68
|
require "astronoby/events/twilight_events"
|
|
63
69
|
require "astronoby/events/twilight_calculator"
|
|
64
|
-
require "astronoby/events/
|
|
70
|
+
require "astronoby/events/lunar_eclipse_phase"
|
|
71
|
+
require "astronoby/events/lunar_eclipse_geometry"
|
|
65
72
|
require "astronoby/events/lunar_eclipse"
|
|
66
73
|
require "astronoby/events/lunar_eclipse_calculator"
|
|
74
|
+
require "astronoby/events/lunar_eclipse_visibility"
|
|
67
75
|
require "astronoby/geocentric_parallax"
|
|
68
76
|
require "astronoby/libration"
|
|
69
77
|
require "astronoby/mean_obliquity"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: astronoby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rémy Hannequin
|
|
@@ -24,7 +24,7 @@ dependencies:
|
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0.5'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: horologium
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
@@ -37,6 +37,26 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0.1'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: iers
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.3'
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: 0.3.1
|
|
50
|
+
type: :runtime
|
|
51
|
+
prerelease: false
|
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - "~>"
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '0.3'
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: 0.3.1
|
|
40
60
|
- !ruby/object:Gem::Dependency
|
|
41
61
|
name: matrix
|
|
42
62
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -127,14 +147,20 @@ dependencies:
|
|
|
127
147
|
requirements:
|
|
128
148
|
- - "~>"
|
|
129
149
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '0
|
|
150
|
+
version: '1.0'
|
|
151
|
+
- - "<"
|
|
152
|
+
- !ruby/object:Gem::Version
|
|
153
|
+
version: '1.2'
|
|
131
154
|
type: :development
|
|
132
155
|
prerelease: false
|
|
133
156
|
version_requirements: !ruby/object:Gem::Requirement
|
|
134
157
|
requirements:
|
|
135
158
|
- - "~>"
|
|
136
159
|
- !ruby/object:Gem::Version
|
|
137
|
-
version: '0
|
|
160
|
+
version: '1.0'
|
|
161
|
+
- - "<"
|
|
162
|
+
- !ruby/object:Gem::Version
|
|
163
|
+
version: '1.2'
|
|
138
164
|
- !ruby/object:Gem::Dependency
|
|
139
165
|
name: standard
|
|
140
166
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -167,6 +193,7 @@ files:
|
|
|
167
193
|
- Rakefile
|
|
168
194
|
- UPGRADING.md
|
|
169
195
|
- docs/README.md
|
|
196
|
+
- docs/altitude_crossings.md
|
|
170
197
|
- docs/angles.md
|
|
171
198
|
- docs/configuration.md
|
|
172
199
|
- docs/coordinates.md
|
|
@@ -225,21 +252,27 @@ files:
|
|
|
225
252
|
- lib/astronoby/data/constellations/sorted_declinations.dat
|
|
226
253
|
- lib/astronoby/data/constellations/sorted_right_ascensions.dat
|
|
227
254
|
- lib/astronoby/deflection.rb
|
|
255
|
+
- lib/astronoby/delta_t.rb
|
|
228
256
|
- lib/astronoby/distance.rb
|
|
229
257
|
- lib/astronoby/duration.rb
|
|
230
258
|
- lib/astronoby/earth_rotation.rb
|
|
231
259
|
- lib/astronoby/ephem.rb
|
|
232
260
|
- lib/astronoby/equinox_solstice.rb
|
|
233
261
|
- lib/astronoby/errors.rb
|
|
262
|
+
- lib/astronoby/events/altitude_crossing.rb
|
|
263
|
+
- lib/astronoby/events/altitude_crossing_calculator.rb
|
|
264
|
+
- lib/astronoby/events/altitude_extreme.rb
|
|
234
265
|
- lib/astronoby/events/conjunction.rb
|
|
235
266
|
- lib/astronoby/events/conjunction_opposition_calculator.rb
|
|
236
|
-
- lib/astronoby/events/eclipse_phase.rb
|
|
237
267
|
- lib/astronoby/events/extremum_calculator.rb
|
|
238
268
|
- lib/astronoby/events/extremum_event.rb
|
|
239
269
|
- lib/astronoby/events/greatest_elongation.rb
|
|
240
270
|
- lib/astronoby/events/greatest_elongation_calculator.rb
|
|
241
271
|
- lib/astronoby/events/lunar_eclipse.rb
|
|
242
272
|
- lib/astronoby/events/lunar_eclipse_calculator.rb
|
|
273
|
+
- lib/astronoby/events/lunar_eclipse_geometry.rb
|
|
274
|
+
- lib/astronoby/events/lunar_eclipse_phase.rb
|
|
275
|
+
- lib/astronoby/events/lunar_eclipse_visibility.rb
|
|
243
276
|
- lib/astronoby/events/moon_phases.rb
|
|
244
277
|
- lib/astronoby/events/opposition.rb
|
|
245
278
|
- lib/astronoby/events/rise_transit_set_calculator.rb
|
|
@@ -250,6 +283,8 @@ files:
|
|
|
250
283
|
- lib/astronoby/events/twilight_events.rb
|
|
251
284
|
- lib/astronoby/extremum_finder.rb
|
|
252
285
|
- lib/astronoby/geocentric_parallax.rb
|
|
286
|
+
- lib/astronoby/horizon.rb
|
|
287
|
+
- lib/astronoby/horizontal_transformation.rb
|
|
253
288
|
- lib/astronoby/instant.rb
|
|
254
289
|
- lib/astronoby/julian_date.rb
|
|
255
290
|
- lib/astronoby/libration.rb
|