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
@@ -1,6 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
+ # Represents an angle with radians as its internal representation.
5
+ # Provides conversions between radians, degrees, hours, and sexagesimal
6
+ # formats (DMS and HMS), as well as trigonometric operations.
7
+ #
8
+ # @example Create an angle from degrees
9
+ # angle = Astronoby::Angle.from_degrees(180)
10
+ # angle.radians # => Math::PI
11
+ #
12
+ # @example Create an angle from hours, minutes, seconds
13
+ # angle = Astronoby::Angle.from_hms(12, 30, 0)
14
+ #
4
15
  class Angle
5
16
  include Comparable
6
17
 
@@ -8,29 +19,42 @@ module Astronoby
8
19
  FORMATS = %i[dms hms].freeze
9
20
 
10
21
  class << self
22
+ # @return [Astronoby::Angle] a zero angle
11
23
  def zero
12
24
  new(0)
13
25
  end
14
26
 
27
+ # @param radians [Numeric] the angle in radians
28
+ # @return [Astronoby::Angle] a new Angle, normalized to (-2π, 2π)
15
29
  def from_radians(radians)
16
30
  normalized_radians = radians.remainder(Constants::RADIANS_PER_CIRCLE)
17
31
  new(normalized_radians)
18
32
  end
19
33
 
34
+ # @param degrees [Numeric] the angle in degrees
35
+ # @return [Astronoby::Angle] a new Angle
20
36
  def from_degrees(degrees)
21
37
  radians = degrees / Constants::PI_IN_DEGREES * Math::PI
22
38
  from_radians(radians)
23
39
  end
24
40
 
41
+ # @param arcseconds [Numeric] the angle in arcseconds
42
+ # @return [Astronoby::Angle] a new Angle
25
43
  def from_degree_arcseconds(arcseconds)
26
44
  from_dms(0, 0, arcseconds)
27
45
  end
28
46
 
47
+ # @param hours [Numeric] the angle in hour-angle hours
48
+ # @return [Astronoby::Angle] a new Angle
29
49
  def from_hours(hours)
30
50
  radians = hours * Constants::RADIAN_PER_HOUR
31
51
  from_radians(radians)
32
52
  end
33
53
 
54
+ # @param hour [Numeric] hours component
55
+ # @param minute [Numeric] minutes component
56
+ # @param second [Numeric] seconds component
57
+ # @return [Astronoby::Angle] a new Angle
34
58
  def from_hms(hour, minute, second)
35
59
  hours = hour +
36
60
  minute / Constants::MINUTES_PER_HOUR +
@@ -38,85 +62,118 @@ module Astronoby
38
62
  from_hours(hours)
39
63
  end
40
64
 
65
+ # @param degree [Numeric] degrees component (sign determines overall sign)
66
+ # @param minute [Numeric] arcminutes component
67
+ # @param second [Numeric] arcseconds component
68
+ # @return [Astronoby::Angle] a new Angle
41
69
  def from_dms(degree, minute, second)
42
70
  sign = degree.negative? ? -1 : 1
43
71
  degrees = degree.abs +
44
- minute / Constants::MINUTES_PER_HOUR +
45
- second / Constants::SECONDS_PER_HOUR
72
+ minute / Constants::ARCMINUTES_PER_DEGREE +
73
+ second / Constants::ARCSECONDS_PER_DEGREE
46
74
  from_degrees(sign * degrees)
47
75
  end
48
76
 
77
+ # @param ratio [Numeric] the sine value (-1..1)
78
+ # @return [Astronoby::Angle] the arcsine
49
79
  def asin(ratio)
50
80
  radians = Math.asin(ratio)
51
81
  from_radians(radians)
52
82
  end
53
83
 
84
+ # @param ratio [Numeric] the cosine value (-1..1)
85
+ # @return [Astronoby::Angle] the arccosine
54
86
  def acos(ratio)
55
87
  radians = Math.acos(ratio)
56
88
  from_radians(radians)
57
89
  end
58
90
 
91
+ # @param ratio [Numeric] the tangent value
92
+ # @return [Astronoby::Angle] the arctangent
59
93
  def atan(ratio)
60
94
  radians = Math.atan(ratio)
61
95
  from_radians(radians)
62
96
  end
63
97
  end
64
98
 
99
+ # @return [Numeric] the angle in radians
65
100
  attr_reader :radians
66
101
 
102
+ # @param radians [Numeric] the angle in radians
67
103
  def initialize(radians)
68
104
  @radians = radians
69
105
  freeze
70
106
  end
71
107
 
108
+ # @return [Float] the angle in degrees
72
109
  def degrees
73
110
  @radians * Constants::PI_IN_DEGREES / Math::PI
74
111
  end
75
112
 
113
+ # @return [Float] the angle in milliarcseconds
114
+ def degree_milliarcseconds
115
+ degrees * Constants::MILLIARCSECONDS_PER_DEGREE
116
+ end
117
+
118
+ # @return [Float] the angle in hour-angle hours
76
119
  def hours
77
120
  @radians / Constants::RADIAN_PER_HOUR
78
121
  end
79
122
 
123
+ # @param other [Astronoby::Angle] angle to add
124
+ # @return [Astronoby::Angle] the sum
80
125
  def +(other)
81
126
  self.class.from_radians(radians + other.radians)
82
127
  end
83
128
 
129
+ # @param other [Astronoby::Angle] angle to subtract
130
+ # @return [Astronoby::Angle] the difference
84
131
  def -(other)
85
132
  self.class.from_radians(@radians - other.radians)
86
133
  end
87
134
 
135
+ # @return [Astronoby::Angle] the negated angle
88
136
  def -@
89
137
  self.class.from_radians(-@radians)
90
138
  end
91
139
 
140
+ # @return [Float] the sine of the angle
92
141
  def sin
93
142
  Math.sin(radians)
94
143
  end
95
144
 
145
+ # @return [Float] the cosine of the angle
96
146
  def cos
97
147
  Math.cos(radians)
98
148
  end
99
149
 
150
+ # @return [Float] the tangent of the angle
100
151
  def tan
101
152
  Math.tan(radians)
102
153
  end
103
154
 
155
+ # @return [Boolean] true if the angle is positive
104
156
  def positive?
105
157
  radians > 0
106
158
  end
107
159
 
160
+ # @return [Boolean] true if the angle is negative
108
161
  def negative?
109
162
  radians < 0
110
163
  end
111
164
 
165
+ # @return [Boolean] true if the angle is zero
112
166
  def zero?
113
167
  radians.zero?
114
168
  end
115
169
 
170
+ # @return [Integer] hash value
116
171
  def hash
117
172
  [radians, self.class].hash
118
173
  end
119
174
 
175
+ # @param other [Astronoby::Angle] angle to compare with
176
+ # @return [Integer, nil] -1, 0, or 1; nil if not comparable
120
177
  def <=>(other)
121
178
  return unless other.is_a?(self.class)
122
179
 
@@ -124,6 +181,12 @@ module Astronoby
124
181
  end
125
182
  alias_method :eql?, :==
126
183
 
184
+ # Formats the angle as a string in the given format.
185
+ #
186
+ # @param format [Symbol] :dms or :hms
187
+ # @param precision [Integer] decimal places for seconds
188
+ # @return [String] the formatted angle
189
+ # @raise [Astronoby::UnsupportedFormatError] if format is not :dms or :hms
127
190
  def str(format, precision: 4)
128
191
  case format
129
192
  when :dms then to_dms.format(precision: precision)
@@ -135,14 +198,15 @@ module Astronoby
135
198
  end
136
199
  end
137
200
 
201
+ # @return [Astronoby::Dms] the angle in degrees, arcminutes, arcseconds
138
202
  def to_dms
139
203
  sign = degrees.negative? ? "-" : "+"
140
204
  absolute_degrees = degrees.abs
141
205
  deg = absolute_degrees.floor
142
- decimal_minutes = Constants::MINUTES_PER_DEGREE *
206
+ decimal_minutes = Constants::ARCMINUTES_PER_DEGREE *
143
207
  (absolute_degrees - deg)
144
208
  absolute_decimal_minutes = (
145
- Constants::MINUTES_PER_DEGREE * (absolute_degrees - deg)
209
+ Constants::ARCMINUTES_PER_DEGREE * (absolute_degrees - deg)
146
210
  ).abs
147
211
  minutes = decimal_minutes.floor
148
212
  seconds = Constants::SECONDS_PER_MINUTE * (
@@ -152,6 +216,7 @@ module Astronoby
152
216
  Dms.new(sign, deg, minutes, seconds)
153
217
  end
154
218
 
219
+ # @return [Astronoby::Hms] the angle in hours, minutes, seconds
155
220
  def to_hms
156
221
  absolute_hours = hours.abs
157
222
  hrs = absolute_hours.floor
@@ -1,9 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
+ # Represents an angle in degrees, arcminutes, arcseconds (DMS) notation.
4
5
  class Dms
5
- attr_reader :sign, :degrees, :minutes, :seconds
6
+ # @return [String] "+" or "-"
7
+ attr_reader :sign
6
8
 
9
+ # @return [Integer] degrees component
10
+ attr_reader :degrees
11
+
12
+ # @return [Integer] arcminutes component
13
+ attr_reader :minutes
14
+
15
+ # @return [Float] arcseconds component
16
+ attr_reader :seconds
17
+
18
+ # @param sign [String] "+" or "-"
19
+ # @param degrees [Integer] degrees component
20
+ # @param minutes [Integer] arcminutes component
21
+ # @param seconds [Float] arcseconds component
7
22
  def initialize(sign, degrees, minutes, seconds)
8
23
  @sign = sign
9
24
  @degrees = degrees
@@ -11,6 +26,8 @@ module Astronoby
11
26
  @seconds = seconds
12
27
  end
13
28
 
29
+ # @param precision [Integer] decimal places for the seconds component
30
+ # @return [String] the formatted DMS string (e.g., "+45° 30′ 15.0000″")
14
31
  def format(precision: 4)
15
32
  "#{sign}#{degrees}° #{minutes}′ #{seconds.floor(precision)}″"
16
33
  end
@@ -1,15 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
+ # Represents an angle in hours, minutes, seconds (HMS) notation.
4
5
  class Hms
5
- attr_reader :hours, :minutes, :seconds
6
+ # @return [Integer] hours component
7
+ attr_reader :hours
6
8
 
9
+ # @return [Integer] minutes component
10
+ attr_reader :minutes
11
+
12
+ # @return [Float] seconds component
13
+ attr_reader :seconds
14
+
15
+ # @param hours [Integer] hours component
16
+ # @param minutes [Integer] minutes component
17
+ # @param seconds [Float] seconds component
7
18
  def initialize(hours, minutes, seconds)
8
19
  @hours = hours
9
20
  @minutes = minutes
10
21
  @seconds = seconds
11
22
  end
12
23
 
24
+ # @param precision [Integer] decimal places for the seconds component
25
+ # @return [String] the formatted HMS string (e.g., "12h 30m 45.0000s")
13
26
  def format(precision: 4)
14
27
  "#{hours}h #{minutes}m #{seconds.floor(precision)}s"
15
28
  end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ # Represents an angular velocity with radians per second as its internal
5
+ # representation. Used primarily for stellar proper motion.
6
+ class AngularVelocity
7
+ include Comparable
8
+
9
+ class << self
10
+ # @return [Astronoby::AngularVelocity] a zero angular velocity
11
+ def zero
12
+ new(0)
13
+ end
14
+
15
+ # @param radians_per_second [Numeric] the angular velocity in rad/s
16
+ # @return [Astronoby::AngularVelocity] a new AngularVelocity
17
+ def from_radians_per_second(radians_per_second)
18
+ new(radians_per_second)
19
+ end
20
+
21
+ # @param mas_per_year [Numeric] the angular velocity in mas/yr
22
+ # @return [Astronoby::AngularVelocity] a new AngularVelocity
23
+ def from_milliarcseconds_per_year(mas_per_year)
24
+ angle = Angle.from_degree_arcseconds(mas_per_year / 1000.0)
25
+ radians_per_second = angle.radians / Constants::SECONDS_PER_JULIAN_YEAR
26
+ new(radians_per_second)
27
+ end
28
+ end
29
+
30
+ # @return [Numeric] the angular velocity in radians per second
31
+ attr_reader :radians_per_second
32
+ alias_method :rps, :radians_per_second
33
+
34
+ # @param radians_per_second [Numeric] the angular velocity in rad/s
35
+ def initialize(radians_per_second)
36
+ @radians_per_second = radians_per_second
37
+ freeze
38
+ end
39
+
40
+ # @return [Float] the angular velocity in milliarcseconds per year
41
+ def milliarcseconds_per_year
42
+ angle = Angle.from_radians(@radians_per_second)
43
+ angle.degree_milliarcseconds * Constants::SECONDS_PER_JULIAN_YEAR
44
+ end
45
+ alias_method :mas_per_year, :milliarcseconds_per_year
46
+
47
+ # @param other [Astronoby::AngularVelocity] angular velocity to add
48
+ # @return [Astronoby::AngularVelocity] the sum
49
+ def +(other)
50
+ self.class.from_radians_per_second(
51
+ @radians_per_second + other.radians_per_second
52
+ )
53
+ end
54
+
55
+ # @param other [Astronoby::AngularVelocity] angular velocity to subtract
56
+ # @return [Astronoby::AngularVelocity] the difference
57
+ def -(other)
58
+ self.class.from_radians_per_second(
59
+ @radians_per_second - other.radians_per_second
60
+ )
61
+ end
62
+
63
+ # @return [Astronoby::AngularVelocity] the negated angular velocity
64
+ def -@
65
+ self.class.from_radians_per_second(-@radians_per_second)
66
+ end
67
+
68
+ # @return [Boolean] true if the angular velocity is positive
69
+ def positive?
70
+ @radians_per_second > 0
71
+ end
72
+
73
+ # @return [Boolean] true if the angular velocity is negative
74
+ def negative?
75
+ @radians_per_second < 0
76
+ end
77
+
78
+ # @return [Boolean] true if the angular velocity is zero
79
+ def zero?
80
+ @radians_per_second.zero?
81
+ end
82
+
83
+ # @return [Integer] hash value
84
+ def hash
85
+ [@radians_per_second, self.class].hash
86
+ end
87
+
88
+ # @param other [Astronoby::AngularVelocity] angular velocity to compare with
89
+ # @return [Integer, nil] -1, 0, or 1; nil if not comparable
90
+ def <=>(other)
91
+ return unless other.is_a?(self.class)
92
+
93
+ @radians_per_second <=> other.radians_per_second
94
+ end
95
+ alias_method :eql?, :==
96
+ end
97
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ # Represents a deep-sky object (star, galaxy, nebula, etc.) with optional
5
+ # proper motion and parallax data.
6
+ class DeepSkyObject
7
+ include Body
8
+
9
+ # @param equatorial_coordinates [Astronoby::Coordinates::Equatorial]
10
+ # Equatorial coordinates at epoch J2000.0
11
+ # @param proper_motion_ra [Astronoby::AngularVelocity, nil] Proper motion in
12
+ # right ascension
13
+ # @param proper_motion_dec [Astronoby::AngularVelocity, nil] Proper motion
14
+ # in declination
15
+ # @param parallax [Astronoby::Angle, nil] Parallax angle
16
+ # @param radial_velocity [Astronoby::Velocity, nil] Radial velocity
17
+ def initialize(
18
+ equatorial_coordinates:,
19
+ proper_motion_ra: nil,
20
+ proper_motion_dec: nil,
21
+ parallax: nil,
22
+ radial_velocity: nil
23
+ )
24
+ @initial_equatorial_coordinates = equatorial_coordinates
25
+ @proper_motion_ra = proper_motion_ra
26
+ @proper_motion_dec = proper_motion_dec
27
+ @parallax = parallax
28
+ @radial_velocity = radial_velocity
29
+ end
30
+
31
+ # @param instant [Astronoby::Instant] Instant of the observation
32
+ # @param ephem [Astronoby::Ephemeris, nil] Ephemeris to use for Earth
33
+ # position calculation
34
+ # @return [Astronoby::DeepSkyObjectPosition] Position of the deep-sky object
35
+ # at the given instant
36
+ def at(instant, ephem: nil)
37
+ DeepSkyObjectPosition.new(
38
+ instant: instant,
39
+ equatorial_coordinates: @initial_equatorial_coordinates,
40
+ ephem: ephem,
41
+ proper_motion_ra: @proper_motion_ra,
42
+ proper_motion_dec: @proper_motion_dec,
43
+ parallax: @parallax,
44
+ radial_velocity: @radial_velocity,
45
+ deep_sky_object: self
46
+ )
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ # Represents the computed position of a deep-sky object at a specific
5
+ # instant, providing astrometric, apparent, and topocentric reference frames.
6
+ class DeepSkyObjectPosition
7
+ include Position
8
+
9
+ DEFAULT_DISTANCE = Distance.from_parsecs(1e9)
10
+
11
+ # @return [Astronoby::Instant] the time instant
12
+ attr_reader :instant
13
+
14
+ # @return [Astronoby::Apparent] the apparent reference frame
15
+ attr_reader :apparent
16
+
17
+ # @return [Astronoby::DeepSkyObject, nil] the body definition
18
+ attr_reader :body
19
+
20
+ # @param instant [Astronoby::Instant] Instant of the observation
21
+ # @param equatorial_coordinates [Astronoby::Coordinates::Equatorial]
22
+ # Equatorial coordinates at epoch J2000.0
23
+ # @param ephem [::Ephem::SPK, nil] Ephemeris data source for Earth position
24
+ # @param proper_motion_ra [Astronoby::AngularVelocity, nil] Proper motion in
25
+ # right ascension
26
+ # @param proper_motion_dec [Astronoby::AngularVelocity, nil] Proper motion
27
+ # in declination
28
+ # @param parallax [Astronoby::Angle, nil] Parallax angle
29
+ # @param radial_velocity [Astronoby::Velocity, nil] Radial velocity
30
+ # @param deep_sky_object [Astronoby::DeepSkyObject, nil] the body definition
31
+ def initialize(
32
+ instant:,
33
+ equatorial_coordinates:,
34
+ ephem: nil,
35
+ proper_motion_ra: nil,
36
+ proper_motion_dec: nil,
37
+ parallax: nil,
38
+ radial_velocity: nil,
39
+ deep_sky_object: nil
40
+ )
41
+ @instant = instant
42
+ @initial_equatorial_coordinates = equatorial_coordinates
43
+ @proper_motion_ra = proper_motion_ra
44
+ @proper_motion_dec = proper_motion_dec
45
+ @parallax = parallax
46
+ @radial_velocity = radial_velocity
47
+ @body = deep_sky_object
48
+ if ephem
49
+ @earth_geometric = Earth.geometric(ephem: ephem, instant: @instant)
50
+ end
51
+ compute_apparent
52
+ end
53
+
54
+ # @return [Astronoby::Astrometric] Astrometric position of the object
55
+ def astrometric
56
+ @astrometric ||= Astrometric.new(
57
+ instant: @instant,
58
+ position: astrometric_position,
59
+ velocity: astrometric_velocity,
60
+ center: Center.geocentric,
61
+ target_body: body
62
+ )
63
+ end
64
+
65
+ private
66
+
67
+ def astrometric_position
68
+ @astrometric_position ||=
69
+ if @earth_geometric
70
+ barycentric_position - @earth_geometric.position
71
+ else
72
+ barycentric_position
73
+ end
74
+ end
75
+
76
+ def barycentric_position
77
+ if use_stellar_propagation?
78
+ stellar_propagation.position
79
+ else
80
+ astronomical_distance = DEFAULT_DISTANCE.meters
81
+ right_ascension = @initial_equatorial_coordinates.right_ascension
82
+ declination = @initial_equatorial_coordinates.declination
83
+ Distance.vector_from_meters([
84
+ declination.cos * right_ascension.cos * astronomical_distance,
85
+ declination.cos * right_ascension.sin * astronomical_distance,
86
+ declination.sin * astronomical_distance
87
+ ])
88
+ end
89
+ end
90
+
91
+ def astrometric_velocity
92
+ @astrometric_velocity ||= if use_stellar_propagation?
93
+ stellar_propagation.velocity_vector
94
+ else
95
+ Velocity.vector_from_meters_per_second([0.0, 0.0, 0.0])
96
+ end
97
+ end
98
+
99
+ def use_stellar_propagation?
100
+ @proper_motion_ra && @proper_motion_dec && @parallax && @radial_velocity
101
+ end
102
+
103
+ def stellar_propagation
104
+ @stellar_propagation ||= StellarPropagation.new(
105
+ equatorial_coordinates: @initial_equatorial_coordinates,
106
+ proper_motion_ra: @proper_motion_ra,
107
+ proper_motion_dec: @proper_motion_dec,
108
+ parallax: @parallax,
109
+ radial_velocity: @radial_velocity,
110
+ instant: @instant,
111
+ earth_geometric: @earth_geometric
112
+ )
113
+ end
114
+
115
+ def compute_apparent
116
+ @apparent = if @earth_geometric
117
+ Apparent.build_from_astrometric(
118
+ instant: @instant,
119
+ target_astrometric: astrometric,
120
+ earth_geometric: @earth_geometric,
121
+ target_body: body
122
+ )
123
+ else
124
+ precession_matrix = Precession.matrix_for(@instant)
125
+ nutation_matrix = Nutation.matrix_for(@instant)
126
+ corrected_position = Distance.vector_from_meters(
127
+ precession_matrix * nutation_matrix * astrometric.position.map(&:m)
128
+ )
129
+ corrected_velocity = Velocity.vector_from_mps(
130
+ precession_matrix * nutation_matrix * astrometric.velocity.map(&:mps)
131
+ )
132
+ Apparent.new(
133
+ position: corrected_position,
134
+ velocity: corrected_velocity,
135
+ instant: @instant,
136
+ center: Center.geocentric,
137
+ target_body: body
138
+ )
139
+ end
140
+ end
141
+ end
142
+ end
@@ -1,7 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
+ # Represents the Earth. Provides ephemeris segments for computing Earth's
5
+ # geometric position.
4
6
  class Earth < SolarSystemBody
7
+ ORBITAL_PERIOD = 365.256
8
+
9
+ # @param ephem_source [Symbol] the ephemeris source type
10
+ # @return [Array<Array>] ephemeris segment identifiers
5
11
  def self.ephemeris_segments(ephem_source)
6
12
  if ephem_source == ::Ephem::SPK::JPL_DE
7
13
  [
@@ -15,48 +21,9 @@ module Astronoby
15
21
  end
16
22
  end
17
23
 
18
- private
19
-
20
- # Attributes that require Sun data like phase angle or magnitude are not
21
- # applicable for Earth.
22
- def requires_sun_data?
23
- false
24
- end
25
-
26
- def compute_astrometric(_ephem)
27
- Astrometric.new(
28
- position: Vector[
29
- Distance.zero,
30
- Distance.zero,
31
- Distance.zero
32
- ],
33
- velocity: Vector[
34
- Velocity.zero,
35
- Velocity.zero,
36
- Velocity.zero
37
- ],
38
- instant: @instant,
39
- center_identifier: EARTH,
40
- target_body: self.class
41
- )
42
- end
43
-
44
- def compute_mean_of_date(_ephem)
45
- MeanOfDate.new(
46
- position: Vector[
47
- Distance.zero,
48
- Distance.zero,
49
- Distance.zero
50
- ],
51
- velocity: Vector[
52
- Velocity.zero,
53
- Velocity.zero,
54
- Velocity.zero
55
- ],
56
- instant: @instant,
57
- center_identifier: EARTH,
58
- target_body: self.class
59
- )
24
+ # @return [nil] Earth has no phase angle as seen from itself
25
+ def phase_angle
26
+ nil
60
27
  end
61
28
  end
62
29
  end
@@ -1,14 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
+ # Represents Jupiter.
4
5
  class Jupiter < SolarSystemBody
5
6
  EQUATORIAL_RADIUS = Distance.from_meters(71_492_000)
6
7
  ABSOLUTE_MAGNITUDE = -9.395
8
+ ORBITAL_PERIOD = 4332.59
7
9
 
10
+ # @return [Boolean] true; Jupiter is a superior planet
11
+ def self.superior_planet?
12
+ true
13
+ end
14
+
15
+ # @param _ephem_source [Symbol] the ephemeris source type
16
+ # @return [Array<Array>] ephemeris segment identifiers
8
17
  def self.ephemeris_segments(_ephem_source)
9
18
  [[SOLAR_SYSTEM_BARYCENTER, JUPITER_BARYCENTER]]
10
19
  end
11
20
 
21
+ # @return [Float] absolute magnitude
12
22
  def self.absolute_magnitude
13
23
  ABSOLUTE_MAGNITUDE
14
24
  end
@@ -1,14 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
+ # Represents Mars.
4
5
  class Mars < SolarSystemBody
5
6
  EQUATORIAL_RADIUS = Distance.from_meters(3_396_200)
6
7
  ABSOLUTE_MAGNITUDE = -1.601
8
+ ORBITAL_PERIOD = 686.98
7
9
 
10
+ # @return [Boolean] true; Mars is a superior planet
11
+ def self.superior_planet?
12
+ true
13
+ end
14
+
15
+ # @param _ephem_source [Symbol] the ephemeris source type
16
+ # @return [Array<Array>] ephemeris segment identifiers
8
17
  def self.ephemeris_segments(_ephem_source)
9
18
  [[SOLAR_SYSTEM_BARYCENTER, MARS_BARYCENTER]]
10
19
  end
11
20
 
21
+ # @return [Float] absolute magnitude
12
22
  def self.absolute_magnitude
13
23
  ABSOLUTE_MAGNITUDE
14
24
  end