astronoby 0.8.0 → 0.10.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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +159 -0
  4. data/README.md +12 -5
  5. data/UPGRADING.md +109 -0
  6. data/docs/README.md +109 -16
  7. data/docs/angles.md +2 -1
  8. data/docs/configuration.md +20 -17
  9. data/docs/coordinates.md +73 -13
  10. data/docs/deep_sky_bodies.md +101 -0
  11. data/docs/ephem.md +6 -3
  12. data/docs/equinoxes_solstices_times.md +4 -3
  13. data/docs/glossary.md +97 -1
  14. data/docs/iers.md +40 -0
  15. data/docs/instant.md +21 -16
  16. data/docs/lunar_eclipses.md +93 -0
  17. data/docs/lunar_observation.md +87 -0
  18. data/docs/moon_phases.md +5 -2
  19. data/docs/observer.md +21 -7
  20. data/docs/planetary_phenomena.md +78 -0
  21. data/docs/reference_frames.md +193 -35
  22. data/docs/rise_transit_set_times.md +10 -8
  23. data/docs/{celestial_bodies.md → solar_system_bodies.md} +27 -5
  24. data/docs/twilight_times.md +25 -21
  25. data/lib/astronoby/angle.rb +69 -4
  26. data/lib/astronoby/angles/dms.rb +18 -1
  27. data/lib/astronoby/angles/hms.rb +14 -1
  28. data/lib/astronoby/angular_velocity.rb +97 -0
  29. data/lib/astronoby/bodies/deep_sky_object.rb +49 -0
  30. data/lib/astronoby/bodies/deep_sky_object_position.rb +142 -0
  31. data/lib/astronoby/bodies/earth.rb +9 -42
  32. data/lib/astronoby/bodies/jupiter.rb +10 -0
  33. data/lib/astronoby/bodies/mars.rb +10 -0
  34. data/lib/astronoby/bodies/mercury.rb +10 -0
  35. data/lib/astronoby/bodies/moon.rb +162 -15
  36. data/lib/astronoby/bodies/neptune.rb +10 -0
  37. data/lib/astronoby/bodies/saturn.rb +10 -0
  38. data/lib/astronoby/bodies/solar_system_body.rb +257 -53
  39. data/lib/astronoby/bodies/sun.rb +79 -4
  40. data/lib/astronoby/bodies/uranus.rb +10 -0
  41. data/lib/astronoby/bodies/venus.rb +10 -0
  42. data/lib/astronoby/body.rb +6 -0
  43. data/lib/astronoby/cache.rb +1 -0
  44. data/lib/astronoby/center.rb +84 -0
  45. data/lib/astronoby/constants.rb +7 -2
  46. data/lib/astronoby/constellation.rb +9 -1
  47. data/lib/astronoby/coordinates/ecliptic.rb +10 -1
  48. data/lib/astronoby/coordinates/equatorial.rb +66 -13
  49. data/lib/astronoby/coordinates/geodetic.rb +102 -0
  50. data/lib/astronoby/coordinates/horizontal.rb +13 -1
  51. data/lib/astronoby/distance.rb +41 -0
  52. data/lib/astronoby/duration.rb +116 -0
  53. data/lib/astronoby/earth_rotation.rb +70 -0
  54. data/lib/astronoby/equinox_solstice.rb +31 -8
  55. data/lib/astronoby/errors.rb +11 -0
  56. data/lib/astronoby/events/conjunction.rb +51 -0
  57. data/lib/astronoby/events/conjunction_opposition_calculator.rb +84 -0
  58. data/lib/astronoby/events/eclipse_phase.rb +27 -0
  59. data/lib/astronoby/events/extremum_calculator.rb +80 -0
  60. data/lib/astronoby/events/extremum_event.rb +15 -0
  61. data/lib/astronoby/events/greatest_elongation.rb +58 -0
  62. data/lib/astronoby/events/greatest_elongation_calculator.rb +56 -0
  63. data/lib/astronoby/events/lunar_eclipse.rb +99 -0
  64. data/lib/astronoby/events/lunar_eclipse_calculator.rb +285 -0
  65. data/lib/astronoby/events/opposition.rb +19 -0
  66. data/lib/astronoby/events/rise_transit_set_calculator.rb +9 -6
  67. data/lib/astronoby/events/rise_transit_set_event.rb +12 -1
  68. data/lib/astronoby/events/rise_transit_set_events.rb +12 -1
  69. data/lib/astronoby/events/twilight_calculator.rb +1 -1
  70. data/lib/astronoby/events/twilight_event.rb +24 -6
  71. data/lib/astronoby/events/twilight_events.rb +26 -6
  72. data/lib/astronoby/extremum_finder.rb +148 -0
  73. data/lib/astronoby/instant.rb +35 -9
  74. data/lib/astronoby/libration.rb +25 -0
  75. data/lib/astronoby/mean_obliquity.rb +8 -0
  76. data/lib/astronoby/moon_orientation_ephemeris.rb +69 -0
  77. data/lib/astronoby/moon_physical_ephemeris.rb +263 -0
  78. data/lib/astronoby/nutation.rb +10 -20
  79. data/lib/astronoby/observer.rb +67 -49
  80. data/lib/astronoby/orientation.rb +107 -0
  81. data/lib/astronoby/position.rb +16 -0
  82. data/lib/astronoby/precession.rb +61 -60
  83. data/lib/astronoby/reference_frame.rb +73 -7
  84. data/lib/astronoby/reference_frames/apparent.rb +25 -16
  85. data/lib/astronoby/reference_frames/astrometric.rb +14 -1
  86. data/lib/astronoby/reference_frames/geometric.rb +7 -1
  87. data/lib/astronoby/reference_frames/mean_of_date.rb +13 -1
  88. data/lib/astronoby/reference_frames/teme.rb +153 -0
  89. data/lib/astronoby/reference_frames/topocentric.rb +31 -5
  90. data/lib/astronoby/refraction.rb +26 -5
  91. data/lib/astronoby/root_finder.rb +83 -0
  92. data/lib/astronoby/rotation.rb +49 -0
  93. data/lib/astronoby/stellar_propagation.rb +162 -0
  94. data/lib/astronoby/time/greenwich_apparent_sidereal_time.rb +31 -0
  95. data/lib/astronoby/time/greenwich_mean_sidereal_time.rb +101 -0
  96. data/lib/astronoby/time/greenwich_sidereal_time.rb +41 -58
  97. data/lib/astronoby/time/local_apparent_sidereal_time.rb +63 -0
  98. data/lib/astronoby/time/local_mean_sidereal_time.rb +63 -0
  99. data/lib/astronoby/time/local_sidereal_time.rb +59 -26
  100. data/lib/astronoby/time/sidereal_time.rb +64 -0
  101. data/lib/astronoby/true_obliquity.rb +4 -0
  102. data/lib/astronoby/util/maths.rb +8 -0
  103. data/lib/astronoby/util/time.rb +10 -467
  104. data/lib/astronoby/vector.rb +10 -0
  105. data/lib/astronoby/velocity.rb +44 -0
  106. data/lib/astronoby/version.rb +1 -1
  107. data/lib/astronoby.rb +33 -0
  108. metadata +58 -6
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class StellarPropagation
5
+ # @return [Astronoby::Vector] Propagated position vector of
6
+ # Astronoby::Distance components
7
+ def self.position_for(**kwargs)
8
+ new(**kwargs).position
9
+ end
10
+
11
+ # @return [Astronoby::Vector] Propagated position vector of
12
+ # Astronoby::Velocity components
13
+ def self.velocity_vector_for(**kwargs)
14
+ new(**kwargs).velocity_vector
15
+ end
16
+
17
+ # @return [Astronoby::Coordinates::Equatorial] Propagated equatorial
18
+ # coordinates
19
+ def self.equatorial_coordinates_for(**kwargs)
20
+ new(**kwargs).equatorial_coordinates
21
+ end
22
+
23
+ # @param instant [Astronoby::Instant] Instant of the observation
24
+ # @param equatorial_coordinates [Astronoby::Coordinates::Equatorial]
25
+ # Equatorial coordinates at epoch J2000.0
26
+ # @param proper_motion_ra [Astronoby::AngularVelocity] Proper motion in
27
+ # right ascension
28
+ # @param proper_motion_dec [Astronoby::AngularVelocity] Proper motion in
29
+ # declination
30
+ # @param parallax [Astronoby::Angle] Parallax angle
31
+ # @param radial_velocity [Astronoby::Velocity] Radial velocity
32
+ # @param earth_geometric [Astronoby::ReferenceFrame::Geometric, nil]
33
+ # Geometric reference frame of the Earth
34
+ def initialize(
35
+ instant:,
36
+ equatorial_coordinates:,
37
+ proper_motion_ra:,
38
+ proper_motion_dec:,
39
+ parallax:,
40
+ radial_velocity:,
41
+ earth_geometric: nil
42
+ )
43
+ @instant = instant
44
+ @right_ascension = equatorial_coordinates.right_ascension
45
+ @declination = equatorial_coordinates.declination
46
+ @initial_epoch = equatorial_coordinates.epoch
47
+ @proper_motion_ra = proper_motion_ra
48
+ @proper_motion_dec = proper_motion_dec
49
+ @parallax = parallax
50
+ @radial_velocity = radial_velocity
51
+ @earth_geometric = earth_geometric
52
+ end
53
+
54
+ # @return [Astronoby::Vector] Propagated position vector of
55
+ # Astronoby::Distance components
56
+ def position
57
+ @position ||= Distance.vector_from_meters(
58
+ initial_position_vector +
59
+ tangential_velocity.map(&:mps) * time_elapsed_seconds
60
+ )
61
+ end
62
+
63
+ # @return [Astronoby::Vector] Propagated position vector of
64
+ # Astronoby::Velocity components
65
+ def velocity_vector
66
+ @velocity_vector ||= if @earth_geometric
67
+ @earth_geometric.velocity - tangential_velocity
68
+ else
69
+ tangential_velocity
70
+ end
71
+ end
72
+
73
+ # @return [Astronoby::Coordinates::Equatorial] Propagated equatorial
74
+ # coordinates
75
+ def equatorial_coordinates
76
+ @equatorial_coordinates ||= begin
77
+ right_ascension = Util::Trigonometry.adjustement_for_arctangent(
78
+ position.y.m,
79
+ position.x.m,
80
+ Angle.atan(position.y.m / position.x.m)
81
+ )
82
+ declination = Angle.asin(position.z.m / position.magnitude.m)
83
+
84
+ Coordinates::Equatorial.new(
85
+ right_ascension: right_ascension,
86
+ declination: declination,
87
+ epoch: @instant.tt
88
+ )
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def distance
95
+ @distance ||= Distance.from_parsecs(
96
+ 1 / (@parallax.degrees * Constants::ARCSECONDS_PER_DEGREE)
97
+ )
98
+ end
99
+
100
+ def unit_position_vector
101
+ @unit_position_vector ||= Vector[
102
+ @right_ascension.cos * @declination.cos,
103
+ @right_ascension.sin * @declination.cos,
104
+ @declination.sin
105
+ ]
106
+ end
107
+
108
+ def right_ascension_unit_vector
109
+ @right_ascension_unit_vector ||= Vector[
110
+ -@right_ascension.sin,
111
+ @right_ascension.cos,
112
+ 0.0
113
+ ]
114
+ end
115
+
116
+ def declination_unit_vector
117
+ @declination_unit_vector ||= Vector[
118
+ -@right_ascension.cos * @declination.sin,
119
+ -@right_ascension.sin * @declination.sin,
120
+ @declination.cos
121
+ ]
122
+ end
123
+
124
+ def initial_position_vector
125
+ @initial_position_vector ||= unit_position_vector * distance.meters
126
+ end
127
+
128
+ def tangential_velocity
129
+ @tangential_velocity ||= begin
130
+ # Doppler factor for light travel time correction
131
+ k = 1.0 / (1.0 - @radial_velocity.kmps / Velocity.light_speed.kmps)
132
+
133
+ proper_motion_ra_component =
134
+ @proper_motion_ra.mas_per_year / (
135
+ @parallax.degree_milliarcseconds * Constants::DAYS_PER_JULIAN_YEAR
136
+ ) * k
137
+ proper_motion_dec_component =
138
+ @proper_motion_dec.mas_per_year / (
139
+ @parallax.degree_milliarcseconds * Constants::DAYS_PER_JULIAN_YEAR
140
+ ) * k
141
+ radial_velocity_component = Velocity
142
+ .from_kmps(@radial_velocity.kmps * k)
143
+
144
+ Velocity.vector_from_astronomical_units_per_day([
145
+ -proper_motion_ra_component * @right_ascension.sin -
146
+ proper_motion_dec_component * @declination.sin * @right_ascension.cos +
147
+ radial_velocity_component.aupd * @declination.cos * @right_ascension.cos,
148
+ proper_motion_ra_component * @right_ascension.cos -
149
+ proper_motion_dec_component * @declination.sin * @right_ascension.sin +
150
+ radial_velocity_component.aupd * @declination.cos * @right_ascension.sin,
151
+ proper_motion_dec_component * @declination.cos +
152
+ radial_velocity_component.aupd * @declination.sin
153
+ ])
154
+ end
155
+ end
156
+
157
+ def time_elapsed_seconds
158
+ @time_elapsed_seconds ||=
159
+ (@instant.tt - @initial_epoch) * Constants::SECONDS_PER_DAY
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ # Greenwich Apparent Sidereal Time (GAST). Derived from GMST by applying
5
+ # the equation of the equinoxes (nutation correction).
6
+ class GreenwichApparentSiderealTime < GreenwichSiderealTime
7
+ # Creates GAST from UTC by computing GMST and applying the equation
8
+ # of the equinoxes.
9
+ #
10
+ # @param utc [Time] the UTC time
11
+ # @return [Astronoby::GreenwichApparentSiderealTime]
12
+ def self.from_utc(utc)
13
+ gmst = GreenwichMeanSiderealTime.from_utc(utc)
14
+ instant = Instant.from_time(utc)
15
+ nutation = Nutation.new(instant: instant)
16
+ mean_obliquity = MeanObliquity.at(instant)
17
+
18
+ equation_of_equinoxes = nutation.nutation_in_longitude.hours *
19
+ mean_obliquity.cos
20
+ gast_time = normalize_time(gmst.time + equation_of_equinoxes)
21
+
22
+ new(date: gmst.date, time: gast_time)
23
+ end
24
+
25
+ # @param date [Date] the calendar date
26
+ # @param time [Numeric] GAST in hours
27
+ def initialize(date:, time:)
28
+ super(date: date, time: time, type: APPARENT)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "iers"
4
+
5
+ module Astronoby
6
+ # Greenwich Mean Sidereal Time (GMST). Computed from UTC using the IERS
7
+ # Conventions 2010 ERA-based expression, with a polynomial fallback for
8
+ # dates outside the IERS EOP data range.
9
+ class GreenwichMeanSiderealTime < GreenwichSiderealTime
10
+ JULIAN_CENTURIES_EXPONENTS = [
11
+ 6.697374558,
12
+ 2400.051336,
13
+ 0.000025862
14
+ ].freeze
15
+
16
+ SIDEREAL_MINUTE_IN_UT_MINUTE = 0.9972695663
17
+
18
+ UT_TO_SIDEREAL_RATIO = 1.002737909
19
+
20
+ # Creates GMST from UTC using the IERS Conventions 2010 ERA-based
21
+ # expression.
22
+ #
23
+ # Source:
24
+ # Title: IERS Conventions (2010)
25
+ # Chapter: 5.5.7 - ERA based expressions for Greenwich Sidereal Time
26
+ #
27
+ # @param utc [Time] the UTC time
28
+ # @return [Astronoby::GreenwichMeanSiderealTime]
29
+ def self.from_utc(utc)
30
+ date = utc.to_date
31
+ gmst_hours = begin
32
+ gmst_radians = IERS::GMST.at(utc)
33
+ gmst_radians * 12.0 / Math::PI
34
+ rescue IERS::OutOfRangeError
35
+ from_utc_polynomial(utc)
36
+ end
37
+ gmst_hours = normalize_time(gmst_hours)
38
+
39
+ new(date: date, time: gmst_hours)
40
+ end
41
+
42
+ # Polynomial fallback for dates outside the IERS EOP data range.
43
+ #
44
+ # Source:
45
+ # Title: Practical Astronomy with your Calculator or Spreadsheet
46
+ # Authors: Peter Duffett-Smith and Jonathan Zwart
47
+ # Edition: Cambridge University Press
48
+ # Chapter: 12 - Conversion of UT to Greenwich sidereal time (GST)
49
+ #
50
+ # @param utc [Time] the UTC time
51
+ # @return [Numeric] GMST in hours
52
+ def self.from_utc_polynomial(utc)
53
+ julian_day = utc.to_date.ajd
54
+ t = (julian_day - JulianDate::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
55
+ t0 = (
56
+ (JULIAN_CENTURIES_EXPONENTS[0] +
57
+ (JULIAN_CENTURIES_EXPONENTS[1] * t) +
58
+ (JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % Constants::HOURS_PER_DAY
59
+ ).abs
60
+
61
+ ut_in_hours = utc.hour +
62
+ utc.min / Constants::MINUTES_PER_HOUR +
63
+ (utc.sec + utc.subsec) / Constants::SECONDS_PER_HOUR
64
+
65
+ UT_TO_SIDEREAL_RATIO * ut_in_hours + t0
66
+ end
67
+
68
+ # @param date [Date] the calendar date
69
+ # @param time [Numeric] GMST in hours
70
+ def initialize(date:, time:)
71
+ super(date: date, time: time, type: MEAN)
72
+ end
73
+
74
+ # Converts GMST back to UTC.
75
+ #
76
+ # Source:
77
+ # Title: Practical Astronomy with your Calculator or Spreadsheet
78
+ # Authors: Peter Duffett-Smith and Jonathan Zwart
79
+ # Edition: Cambridge University Press
80
+ # Chapter: 13 - Conversion of GST to UT
81
+ #
82
+ # @return [Time] the UTC time
83
+ def to_utc
84
+ date = @date
85
+ julian_day = @date.ajd
86
+ t = (julian_day - JulianDate::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
87
+
88
+ t0 = (
89
+ (JULIAN_CENTURIES_EXPONENTS[0] +
90
+ (JULIAN_CENTURIES_EXPONENTS[1] * t) +
91
+ (JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % Constants::HOURS_PER_DAY
92
+ ).abs
93
+
94
+ a = normalize_time(@time - t0)
95
+
96
+ utc = SIDEREAL_MINUTE_IN_UT_MINUTE * a
97
+
98
+ Util::Time.decimal_hour_to_time(date, 0, utc)
99
+ end
100
+ end
101
+ end
@@ -1,73 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
- class GreenwichSiderealTime
5
- JULIAN_CENTURIES_EXPONENTS = [
6
- 6.697374558,
7
- 2400.051336,
8
- 0.000025862
9
- ].freeze
10
-
11
- SIDEREAL_MINUTE_IN_UT_MINUTE = 0.9972695663
12
-
13
- attr_reader :date, :time
14
-
15
- # Source:
16
- # Title: Practical Astronomy with your Calculator or Spreadsheet
17
- # Authors: Peter Duffett-Smith and Jonathan Zwart
18
- # Edition: Cambridge University Press
19
- # Chapter: 12 - Conversion of UT to Greenwich sidereal time (GST)
20
- def self.from_utc(utc)
21
- date = utc.to_date
22
- julian_day = utc.to_date.ajd
23
- t = (julian_day - JulianDate::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
24
- t0 = (
25
- (JULIAN_CENTURIES_EXPONENTS[0] +
26
- (JULIAN_CENTURIES_EXPONENTS[1] * t) +
27
- (JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % Constants::HOURS_PER_DAY
28
- ).abs
29
-
30
- ut_in_hours = utc.hour +
31
- utc.min / Constants::MINUTES_PER_HOUR +
32
- (utc.sec + utc.subsec) / Constants::SECONDS_PER_HOUR
33
-
34
- gmst = 1.002737909 * ut_in_hours + t0
35
- gmst += Constants::HOURS_PER_DAY if gmst.negative?
36
- gmst -= Constants::HOURS_PER_DAY if gmst > Constants::HOURS_PER_DAY
4
+ # Greenwich Sidereal Time base class. Dispatches to mean or apparent
5
+ # subclasses.
6
+ class GreenwichSiderealTime < SiderealTime
7
+ # Creates a Greenwich Sidereal Time from UTC.
8
+ #
9
+ # @param utc [Time] the UTC time
10
+ # @param type [Symbol] :mean or :apparent
11
+ # @return [Astronoby::GreenwichMeanSiderealTime,
12
+ # Astronoby::GreenwichApparentSiderealTime]
13
+ def self.from_utc(utc, type: MEAN)
14
+ validate_type!(type)
15
+ case type
16
+ when MEAN
17
+ GreenwichMeanSiderealTime.from_utc(utc)
18
+ when APPARENT
19
+ GreenwichApparentSiderealTime.from_utc(utc)
20
+ end
21
+ end
37
22
 
38
- new(date: date, time: gmst)
23
+ # @param utc [Time] the UTC time
24
+ # @return [Astronoby::GreenwichMeanSiderealTime]
25
+ def self.mean_from_utc(utc)
26
+ GreenwichMeanSiderealTime.from_utc(utc)
39
27
  end
40
28
 
41
- def initialize(date:, time:)
42
- @date = date
43
- @time = time
29
+ # @param utc [Time] the UTC time
30
+ # @return [Astronoby::GreenwichApparentSiderealTime]
31
+ def self.apparent_from_utc(utc)
32
+ GreenwichApparentSiderealTime.from_utc(utc)
44
33
  end
45
34
 
46
- # Source:
47
- # Title: Practical Astronomy with your Calculator or Spreadsheet
48
- # Authors: Peter Duffett-Smith and Jonathan Zwart
49
- # Edition: Cambridge University Press
50
- # Chapter: 13 - Conversion of GST to UT
35
+ # Converts to UTC.
36
+ #
37
+ # @return [Time] the UTC time
38
+ # @raise [NotImplementedError] if this is apparent sidereal time
51
39
  def to_utc
52
- date = @date
53
- julian_day = @date.ajd
54
- t = (julian_day - JulianDate::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
55
-
56
- t0 = (
57
- (JULIAN_CENTURIES_EXPONENTS[0] +
58
- (JULIAN_CENTURIES_EXPONENTS[1] * t) +
59
- (JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % Constants::HOURS_PER_DAY
60
- ).abs
61
-
62
- a = @time - t0
63
- a += Constants::HOURS_PER_DAY if a.negative?
64
- a -= Constants::HOURS_PER_DAY if a > Constants::HOURS_PER_DAY
65
-
66
- utc = SIDEREAL_MINUTE_IN_UT_MINUTE * a
40
+ unless mean?
41
+ raise NotImplementedError,
42
+ "UTC conversion only supported for mean sidereal time"
43
+ end
67
44
 
68
- Util::Time.decimal_hour_to_time(date, 0, utc)
45
+ gmst = GreenwichMeanSiderealTime.new(date: @date, time: @time)
46
+ gmst.to_utc
69
47
  end
70
48
 
49
+ # Converts to Local Sidereal Time for a given longitude.
50
+ #
51
+ # @param longitude [Astronoby::Angle] the observer's longitude
52
+ # @return [Astronoby::LocalMeanSiderealTime,
53
+ # Astronoby::LocalApparentSiderealTime]
71
54
  def to_lst(longitude:)
72
55
  LocalSiderealTime.from_gst(gst: self, longitude: longitude)
73
56
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ # Local Apparent Sidereal Time (LAST). Derived from GAST by adding the
5
+ # observer's longitude.
6
+ class LocalApparentSiderealTime < LocalSiderealTime
7
+ # Creates LAST from a Greenwich Apparent Sidereal Time and longitude.
8
+ #
9
+ # Source:
10
+ # Title: Practical Astronomy with your Calculator or Spreadsheet
11
+ # Authors: Peter Duffett-Smith and Jonathan Zwart
12
+ # Edition: Cambridge University Press
13
+ # Chapter: 14 - Local sidereal time (LST)
14
+ #
15
+ # @param gst [Astronoby::GreenwichApparentSiderealTime] GAST
16
+ # @param longitude [Astronoby::Angle] the observer's longitude
17
+ # @return [Astronoby::LocalApparentSiderealTime]
18
+ # @raise [ArgumentError] if gst is not apparent sidereal time
19
+ def self.from_gst(gst:, longitude:)
20
+ unless gst.apparent?
21
+ raise ArgumentError, "GST must be apparent sidereal time"
22
+ end
23
+
24
+ date = gst.date
25
+ time = normalize_time(gst.time + longitude.hours)
26
+
27
+ new(date: date, time: time, longitude: longitude)
28
+ end
29
+
30
+ # Creates LAST from UTC and longitude.
31
+ #
32
+ # @param utc [Time] the UTC time
33
+ # @param longitude [Astronoby::Angle] the observer's longitude
34
+ # @return [Astronoby::LocalApparentSiderealTime]
35
+ def self.from_utc(utc, longitude:)
36
+ gast = GreenwichApparentSiderealTime.from_utc(utc)
37
+ from_gst(gst: gast, longitude: longitude)
38
+ end
39
+
40
+ # @param date [Date] the calendar date
41
+ # @param time [Numeric] LAST in hours
42
+ # @param longitude [Astronoby::Angle] the observer's longitude
43
+ def initialize(date:, time:, longitude:)
44
+ super(date: date, time: time, longitude: longitude, type: APPARENT)
45
+ end
46
+
47
+ # Converts to Greenwich Apparent Sidereal Time.
48
+ #
49
+ # Source:
50
+ # Title: Practical Astronomy with your Calculator or Spreadsheet
51
+ # Authors: Peter Duffett-Smith and Jonathan Zwart
52
+ # Edition: Cambridge University Press
53
+ # Chapter: 15 - Converting LST to GST
54
+ #
55
+ # @return [Astronoby::GreenwichApparentSiderealTime]
56
+ def to_gst
57
+ date = @date
58
+ time = normalize_time(@time - @longitude.hours)
59
+
60
+ GreenwichApparentSiderealTime.new(date: date, time: time)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ # Local Mean Sidereal Time (LMST). Derived from GMST by adding the
5
+ # observer's longitude.
6
+ class LocalMeanSiderealTime < LocalSiderealTime
7
+ # Creates LMST from a Greenwich Mean Sidereal Time and longitude.
8
+ #
9
+ # Source:
10
+ # Title: Practical Astronomy with your Calculator or Spreadsheet
11
+ # Authors: Peter Duffett-Smith and Jonathan Zwart
12
+ # Edition: Cambridge University Press
13
+ # Chapter: 14 - Local sidereal time (LST)
14
+ #
15
+ # @param gst [Astronoby::GreenwichMeanSiderealTime] GMST
16
+ # @param longitude [Astronoby::Angle] the observer's longitude
17
+ # @return [Astronoby::LocalMeanSiderealTime]
18
+ # @raise [ArgumentError] if gst is not mean sidereal time
19
+ def self.from_gst(gst:, longitude:)
20
+ unless gst.mean?
21
+ raise ArgumentError, "GST must be mean sidereal time"
22
+ end
23
+
24
+ date = gst.date
25
+ time = normalize_time(gst.time + longitude.hours)
26
+
27
+ new(date: date, time: time, longitude: longitude)
28
+ end
29
+
30
+ # Creates LMST from UTC and longitude.
31
+ #
32
+ # @param utc [Time] the UTC time
33
+ # @param longitude [Astronoby::Angle] the observer's longitude
34
+ # @return [Astronoby::LocalMeanSiderealTime]
35
+ def self.from_utc(utc, longitude:)
36
+ gmst = GreenwichMeanSiderealTime.from_utc(utc)
37
+ from_gst(gst: gmst, longitude: longitude)
38
+ end
39
+
40
+ # @param date [Date] the calendar date
41
+ # @param time [Numeric] LMST in hours
42
+ # @param longitude [Astronoby::Angle] the observer's longitude
43
+ def initialize(date:, time:, longitude:)
44
+ super(date: date, time: time, longitude: longitude, type: MEAN)
45
+ end
46
+
47
+ # Converts to Greenwich Mean Sidereal Time.
48
+ #
49
+ # Source:
50
+ # Title: Practical Astronomy with your Calculator or Spreadsheet
51
+ # Authors: Peter Duffett-Smith and Jonathan Zwart
52
+ # Edition: Cambridge University Press
53
+ # Chapter: 15 - Converting LST to GST
54
+ #
55
+ # @return [Astronoby::GreenwichMeanSiderealTime]
56
+ def to_gst
57
+ date = @date
58
+ time = normalize_time(@time - @longitude.hours)
59
+
60
+ GreenwichMeanSiderealTime.new(date: date, time: time)
61
+ end
62
+ end
63
+ end
@@ -1,41 +1,74 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
- class LocalSiderealTime
5
- attr_reader :date, :time, :longitude
4
+ # Local Sidereal Time base class. Dispatches to mean or apparent subclasses
5
+ # based on the type of the source GST.
6
+ class LocalSiderealTime < SiderealTime
7
+ # @return [Astronoby::Angle] the observer's longitude
8
+ attr_reader :longitude
6
9
 
7
- # Source:
8
- # Title: Practical Astronomy with your Calculator or Spreadsheet
9
- # Authors: Peter Duffett-Smith and Jonathan Zwart
10
- # Edition: Cambridge University Press
11
- # Chapter: 14 - Local sidereal time (LST)
10
+ # Creates an LST from a Greenwich Sidereal Time and longitude.
11
+ #
12
+ # @param gst [Astronoby::GreenwichSiderealTime] Greenwich Sidereal Time
13
+ # @param longitude [Astronoby::Angle] the observer's longitude
14
+ # @return [Astronoby::LocalMeanSiderealTime,
15
+ # Astronoby::LocalApparentSiderealTime]
12
16
  def self.from_gst(gst:, longitude:)
13
- date = gst.date
14
- time = gst.time + longitude.hours
15
- time += Constants::HOURS_PER_DAY if time.negative?
16
- time -= Constants::HOURS_PER_DAY if time > Constants::HOURS_PER_DAY
17
+ case gst.type
18
+ when MEAN
19
+ LocalMeanSiderealTime.from_gst(gst: gst, longitude: longitude)
20
+ when APPARENT
21
+ LocalApparentSiderealTime.from_gst(gst: gst, longitude: longitude)
22
+ end
23
+ end
17
24
 
18
- new(date: date, time: time, longitude: longitude)
25
+ # Creates an LST from UTC and longitude.
26
+ #
27
+ # @param utc [Time] the UTC time
28
+ # @param longitude [Astronoby::Angle] the observer's longitude
29
+ # @param type [Symbol] :mean or :apparent
30
+ # @return [Astronoby::LocalMeanSiderealTime,
31
+ # Astronoby::LocalApparentSiderealTime]
32
+ def self.from_utc(utc, longitude:, type: MEAN)
33
+ validate_type!(type)
34
+ case type
35
+ when MEAN
36
+ LocalMeanSiderealTime.from_utc(utc, longitude: longitude)
37
+ when APPARENT
38
+ LocalApparentSiderealTime.from_utc(utc, longitude: longitude)
39
+ end
19
40
  end
20
41
 
21
- def initialize(date:, time:, longitude:)
22
- @date = date
23
- @time = time
42
+ # @param date [Date] the calendar date
43
+ # @param time [Numeric] the sidereal time in hours
44
+ # @param longitude [Astronoby::Angle] the observer's longitude
45
+ # @param type [Symbol] :mean or :apparent
46
+ def initialize(date:, time:, longitude:, type: MEAN)
47
+ super(date: date, time: time, type: type)
24
48
  @longitude = longitude
25
49
  end
26
50
 
27
- # Source:
28
- # Title: Practical Astronomy with your Calculator or Spreadsheet
29
- # Authors: Peter Duffett-Smith and Jonathan Zwart
30
- # Edition: Cambridge University Press
31
- # Chapter: 15 - Converting LST to GST
51
+ # Converts to Greenwich Sidereal Time.
52
+ #
53
+ # @return [Astronoby::GreenwichMeanSiderealTime,
54
+ # Astronoby::GreenwichApparentSiderealTime]
32
55
  def to_gst
33
- date = @date
34
- time = @time - @longitude.hours
35
- time += Constants::HOURS_PER_DAY if time.negative?
36
- time -= Constants::HOURS_PER_DAY if time > Constants::HOURS_PER_DAY
37
-
38
- GreenwichSiderealTime.new(date: date, time: time)
56
+ case @type
57
+ when MEAN
58
+ lst = LocalMeanSiderealTime.new(
59
+ date: @date,
60
+ time: @time,
61
+ longitude: @longitude
62
+ )
63
+ lst.to_gst
64
+ when APPARENT
65
+ last = LocalApparentSiderealTime.new(
66
+ date: @date,
67
+ time: @time,
68
+ longitude: @longitude
69
+ )
70
+ last.to_gst
71
+ end
39
72
  end
40
73
  end
41
74
  end