astronoby 0.6.0 → 0.7.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/.standard.yml +1 -0
  4. data/CHANGELOG.md +116 -0
  5. data/Gemfile.lock +45 -23
  6. data/README.md +42 -285
  7. data/UPGRADING.md +238 -0
  8. data/lib/astronoby/aberration.rb +56 -31
  9. data/lib/astronoby/angle.rb +20 -16
  10. data/lib/astronoby/angles/dms.rb +2 -2
  11. data/lib/astronoby/angles/hms.rb +2 -2
  12. data/lib/astronoby/bodies/earth.rb +56 -0
  13. data/lib/astronoby/bodies/jupiter.rb +11 -0
  14. data/lib/astronoby/bodies/mars.rb +11 -0
  15. data/lib/astronoby/bodies/mercury.rb +11 -0
  16. data/lib/astronoby/bodies/moon.rb +50 -290
  17. data/lib/astronoby/bodies/neptune.rb +11 -0
  18. data/lib/astronoby/bodies/saturn.rb +11 -0
  19. data/lib/astronoby/bodies/solar_system_body.rb +122 -0
  20. data/lib/astronoby/bodies/sun.rb +16 -220
  21. data/lib/astronoby/bodies/uranus.rb +11 -0
  22. data/lib/astronoby/bodies/venus.rb +11 -0
  23. data/lib/astronoby/constants.rb +13 -1
  24. data/lib/astronoby/coordinates/ecliptic.rb +2 -37
  25. data/lib/astronoby/coordinates/equatorial.rb +25 -7
  26. data/lib/astronoby/coordinates/horizontal.rb +0 -46
  27. data/lib/astronoby/corrections/light_time_delay.rb +90 -0
  28. data/lib/astronoby/deflection.rb +187 -0
  29. data/lib/astronoby/distance.rb +9 -0
  30. data/lib/astronoby/ephem.rb +39 -0
  31. data/lib/astronoby/equinox_solstice.rb +21 -18
  32. data/lib/astronoby/errors.rb +4 -0
  33. data/lib/astronoby/events/moon_phases.rb +2 -1
  34. data/lib/astronoby/events/rise_transit_set_calculator.rb +352 -0
  35. data/lib/astronoby/events/rise_transit_set_event.rb +13 -0
  36. data/lib/astronoby/events/rise_transit_set_events.rb +13 -0
  37. data/lib/astronoby/events/twilight_calculator.rb +166 -0
  38. data/lib/astronoby/events/twilight_event.rb +28 -0
  39. data/lib/astronoby/instant.rb +171 -0
  40. data/lib/astronoby/mean_obliquity.rb +23 -10
  41. data/lib/astronoby/nutation.rb +227 -42
  42. data/lib/astronoby/observer.rb +55 -0
  43. data/lib/astronoby/precession.rb +91 -17
  44. data/lib/astronoby/reference_frame.rb +49 -0
  45. data/lib/astronoby/reference_frames/apparent.rb +60 -0
  46. data/lib/astronoby/reference_frames/astrometric.rb +21 -0
  47. data/lib/astronoby/reference_frames/geometric.rb +20 -0
  48. data/lib/astronoby/reference_frames/mean_of_date.rb +38 -0
  49. data/lib/astronoby/reference_frames/topocentric.rb +82 -0
  50. data/lib/astronoby/true_obliquity.rb +2 -1
  51. data/lib/astronoby/util/maths.rb +70 -73
  52. data/lib/astronoby/util/time.rb +454 -31
  53. data/lib/astronoby/vector.rb +36 -0
  54. data/lib/astronoby/velocity.rb +116 -0
  55. data/lib/astronoby/version.rb +1 -1
  56. data/lib/astronoby.rb +26 -5
  57. metadata +61 -16
  58. data/.tool-versions +0 -1
  59. data/lib/astronoby/astronomical_models/ephemeride_lunaire_parisienne.rb +0 -143
  60. data/lib/astronoby/events/observation_events.rb +0 -285
  61. data/lib/astronoby/events/rise_transit_set_iteration.rb +0 -218
  62. data/lib/astronoby/events/twilight_events.rb +0 -121
  63. data/lib/astronoby/util/astrodynamics.rb +0 -60
@@ -2,72 +2,257 @@
2
2
 
3
3
  module Astronoby
4
4
  class Nutation
5
- # Source:
6
- # Title: Practical Astronomy with your Calculator or Spreadsheet
7
- # Authors: Peter Duffett-Smith and Jonathan Zwart
8
- # Edition: Cambridge University Press
9
- # Chapter: 35 - Nutation
5
+ # IAU 2000B model corrections (in microarcseconds)
6
+ IAU2000B_DPSI_CORRECTION = -0.000135e7
7
+ IAU2000B_DEPS_CORRECTION = 0.000388e7
10
8
 
11
- def initialize(epoch)
12
- @epoch = epoch
13
- end
9
+ # Nutation terms from IAU 2000B model:
10
+ # 77 most significant terms from the IAU 2000A model
11
+ # 0..4: fundamental argument coefficients
12
+ # 5..7: longitude coefficients
13
+ # 8..10: obliquity coefficients
14
+ NUTATION_TERMS = [
15
+ [0, 0, 0, 0, 1, -172064161, -174666, 33386, 92052331, 9086, 15377],
16
+ [0, 0, 2, -2, 2, -13170906, -1675, -13696, 5730336, -3015, -4587],
17
+ [0, 0, 2, 0, 2, -2276413, -234, 2796, 978459, -485, 1374],
18
+ [0, 0, 0, 0, 2, 2074554, 207, -698, -897492, 470, -291],
19
+ [0, 1, 0, 0, 0, 1475877, -3633, 11817, 73871, -184, -1924],
20
+ [0, 1, 2, -2, 2, -516821, 1226, -524, 224386, -677, -174],
21
+ [1, 0, 0, 0, 0, 711159, 73, -872, -6750, 0, 358],
22
+ [0, 0, 2, 0, 1, -387298, -367, 380, 200728, 18, 318],
23
+ [1, 0, 2, 0, 2, -301461, -36, 816, 129025, -63, 367],
24
+ [0, -1, 2, -2, 2, 215829, -494, 111, -95929, 299, 132],
25
+ [0, 0, 2, -2, 1, 128227, 137, 181, -68982, -9, 39],
26
+ [-1, 0, 2, 0, 2, 123457, 11, 19, -53311, 32, -4],
27
+ [-1, 0, 0, 2, 0, 156994, 10, -168, -1235, 0, 82],
28
+ [1, 0, 0, 0, 1, 63110, 63, 27, -33228, 0, -9],
29
+ [-1, 0, 0, 0, 1, -57976, -63, -189, 31429, 0, -75],
30
+ [-1, 0, 2, 2, 2, -59641, -11, 149, 25543, -11, 66],
31
+ [1, 0, 2, 0, 1, -51613, -42, 129, 26366, 0, 78],
32
+ [-2, 0, 2, 0, 1, 45893, 50, 31, -24236, -10, 20],
33
+ [0, 0, 0, 2, 0, 63384, 11, -150, -1220, 0, 29],
34
+ [0, 0, 2, 2, 2, -38571, -1, 158, 16452, -11, 68],
35
+ [0, -2, 2, -2, 2, 32481, 0, 0, -13870, 0, 0],
36
+ [-2, 0, 0, 2, 0, -47722, 0, -18, 477, 0, -25],
37
+ [2, 0, 2, 0, 2, -31046, -1, 131, 13238, -11, 59],
38
+ [1, 0, 2, -2, 2, 28593, 0, -1, -12338, 10, -3],
39
+ [-1, 0, 2, 0, 1, 20441, 21, 10, -10758, 0, -3],
40
+ [2, 0, 0, 0, 0, 29243, 0, -74, -609, 0, 13],
41
+ [0, 0, 2, 0, 0, 25887, 0, -66, -550, 0, 11],
42
+ [0, 1, 0, 0, 1, -14053, -25, 79, 8551, -2, -45],
43
+ [-1, 0, 0, 2, 1, 15164, 10, 11, -8001, 0, -1],
44
+ [0, 2, 2, -2, 2, -15794, 72, -16, 6850, -42, -5],
45
+ [0, 0, -2, 2, 0, 21783, 0, 13, -167, 0, 13],
46
+ [1, 0, 0, -2, 1, -12873, -10, -37, 6953, 0, -14],
47
+ [0, -1, 0, 0, 1, -12654, 11, 63, 6415, 0, 26],
48
+ [-1, 0, 2, 2, 1, -10204, 0, 25, 5222, 0, 15],
49
+ [0, 2, 0, 0, 0, 16707, -85, -10, 168, -1, 10],
50
+ [1, 0, 2, 2, 2, -7691, 0, 44, 3268, 0, 19],
51
+ [-2, 0, 2, 0, 0, -11024, 0, -14, 104, 0, 2],
52
+ [0, 1, 2, 0, 2, 7566, -21, -11, -3250, 0, -5],
53
+ [0, 0, 2, 2, 1, -6637, -11, 25, 3353, 0, 14],
54
+ [0, -1, 2, 0, 2, -7141, 21, 8, 3070, 0, 4],
55
+ [0, 0, 0, 2, 1, -6302, -11, 2, 3272, 0, 4],
56
+ [1, 0, 2, -2, 1, 5800, 10, 2, -3045, 0, -1],
57
+ [2, 0, 2, -2, 2, 6443, 0, -7, -2768, 0, -4],
58
+ [-2, 0, 0, 2, 1, -5774, -11, -15, 3041, 0, -5],
59
+ [2, 0, 2, 0, 1, -5350, 0, 21, 2695, 0, 12],
60
+ [0, -1, 2, -2, 1, -4752, -11, -3, 2719, 0, -3],
61
+ [0, 0, 0, -2, 1, -4940, -11, -21, 2720, 0, -9],
62
+ [-1, -1, 0, 2, 0, 7350, 0, -8, -51, 0, 4],
63
+ [2, 0, 0, -2, 1, 4065, 0, 6, -2206, 0, 1],
64
+ [1, 0, 0, 2, 0, 6579, 0, -24, -199, 0, 2],
65
+ [0, 1, 2, -2, 1, 3579, 0, 5, -1900, 0, 1],
66
+ [1, -1, 0, 0, 0, 4725, 0, -6, -41, 0, 3],
67
+ [-2, 0, 2, 0, 2, -3075, 0, -2, 1313, 0, -1],
68
+ [3, 0, 2, 0, 2, -2904, 0, 15, 1233, 0, 7],
69
+ [0, -1, 0, 2, 0, 4348, 0, -10, -81, 0, 2],
70
+ [1, -1, 2, 0, 2, -2878, 0, 8, 1232, 0, 4],
71
+ [0, 0, 0, 1, 0, -4230, 0, 5, -20, 0, -2],
72
+ [-1, -1, 2, 2, 2, -2819, 0, 7, 1207, 0, 3],
73
+ [-1, 0, 2, 0, 0, -4056, 0, 5, 40, 0, -2],
74
+ [0, -1, 2, 2, 2, -2647, 0, 11, 1129, 0, 5],
75
+ [-2, 0, 0, 0, 1, -2294, 0, -10, 1266, 0, -4],
76
+ [1, 1, 2, 0, 2, 2481, 0, -7, -1062, 0, -3],
77
+ [2, 0, 0, 0, 1, 2179, 0, -2, -1129, 0, -2],
78
+ [-1, 1, 0, 1, 0, 3276, 0, 1, -9, 0, 0],
79
+ [1, 1, 0, 0, 0, -3389, 0, 5, 35, 0, -2],
80
+ [1, 0, 2, 0, 0, 3339, 0, -13, -107, 0, 1],
81
+ [-1, 0, 2, -2, 1, -1987, 0, -6, 1073, 0, -2],
82
+ [1, 0, 0, 0, 2, -1981, 0, 0, 854, 0, 0],
83
+ [-1, 0, 0, 1, 0, 4026, 0, -353, -553, 0, -139],
84
+ [0, 0, 2, 1, 2, 1660, 0, -5, -710, 0, -2],
85
+ [-1, 0, 2, 4, 2, -1521, 0, 9, 647, 0, 4],
86
+ [-1, 1, 0, 1, 1, 1314, 0, 0, -700, 0, 0],
87
+ [0, -2, 2, -2, 1, -1283, 0, 0, 672, 0, 0],
88
+ [1, 0, 2, 2, 1, -1331, 0, 8, 663, 0, 4],
89
+ [-2, 0, 2, 2, 2, 1383, 0, -2, -594, 0, -2],
90
+ [-1, 0, 0, 0, 2, 1405, 0, 4, -610, 0, 2],
91
+ [1, 1, 2, -2, 2, 1290, 0, 0, -556, 0, 0]
92
+ ]
14
93
 
15
- def self.for_ecliptic_longitude(epoch:)
16
- new(epoch).for_ecliptic_longitude
94
+ # @param instant [Astronoby::Instant] The time instant
95
+ # @return [Matrix] The nutation matrix
96
+ def self.matrix_for(instant)
97
+ new(instant: instant).matrix
17
98
  end
18
99
 
19
- def self.for_obliquity_of_the_ecliptic(epoch:)
20
- new(epoch).for_obliquity_of_the_ecliptic
100
+ # @param instant [Astronoby::Instant] The time instant
101
+ def initialize(instant:)
102
+ @instant = instant
21
103
  end
22
104
 
23
- def for_ecliptic_longitude
24
- Angle.from_dms(
25
- 0,
26
- 0,
27
- (
28
- -17.2 * moon_ascending_node_longitude.sin -
29
- 1.3 * Math.sin(2 * sun_mean_longitude.radians)
30
- )
105
+ # @return [Matrix] The nutation matrix
106
+ def matrix
107
+ mean_obliquity = MeanObliquity.for_epoch(@instant.tt)
108
+ true_obliquity = mean_obliquity + nutation_in_obliquity
109
+ build_nutation_matrix(
110
+ mean_obliquity: mean_obliquity,
111
+ true_obliquity: true_obliquity,
112
+ psi: nutation_in_longitude
31
113
  )
32
114
  end
33
115
 
34
- def for_obliquity_of_the_ecliptic
35
- Angle.from_dms(
36
- 0,
37
- 0,
38
- (
39
- 9.2 * moon_ascending_node_longitude.cos +
40
- 0.5 * Math.cos(2 * sun_mean_longitude.radians)
41
- )
42
- )
116
+ # @return [Astronoby::Angle] Nutation angle in longitude
117
+ def nutation_in_longitude
118
+ iau2000b_angles.first
119
+ end
120
+
121
+ # @return [Astronoby::Angle] Nutation angle in obliquity
122
+ def nutation_in_obliquity
123
+ iau2000b_angles.last
43
124
  end
44
125
 
45
126
  private
46
127
 
128
+ def iau2000a
129
+ a = fundamental_arguments
130
+
131
+ dpsi = 0.0
132
+ deps = 0.0
133
+
134
+ NUTATION_TERMS.each do |term|
135
+ # Extract the fundamental argument coefficients
136
+ arg_coef = term[0..4]
137
+
138
+ # Calculate the argument
139
+ arg = Util::Maths.dot_product(arg_coef, a.map(&:radians))
140
+
141
+ sin_arg = Math.sin(arg)
142
+ cos_arg = Math.cos(arg)
143
+
144
+ # Extract longitude coefficients
145
+ long_coef = term[5..7]
146
+
147
+ # Extract obliquity coefficients
148
+ obl_coef = term[8..10]
149
+
150
+ # Update dpsi using longitude coefficients
151
+ dpsi += long_coef[0] * sin_arg
152
+ dpsi += long_coef[1] * sin_arg * julian_centuries
153
+ dpsi += long_coef[2] * cos_arg
154
+
155
+ # Update deps using obliquity coefficients
156
+ deps += obl_coef[0] * cos_arg
157
+ deps += obl_coef[1] * cos_arg * julian_centuries
158
+ deps += obl_coef[2] * sin_arg
159
+ end
160
+
161
+ [dpsi, deps] # in microarcseconds
162
+ end
163
+
164
+ def iau2000b
165
+ dpsi, deps = iau2000a
166
+
167
+ # Apply corrections for IAU 2000B model
168
+ dpsi += IAU2000B_DPSI_CORRECTION
169
+ deps += IAU2000B_DEPS_CORRECTION
170
+
171
+ [dpsi, deps]
172
+ end
173
+
174
+ def iau2000b_angles
175
+ @iau2000b_angles ||= begin
176
+ dpsi, deps = iau2000b
177
+ dpsi = Angle.from_degree_arcseconds(dpsi / 1e7)
178
+ deps = Angle.from_degree_arcseconds(deps / 1e7)
179
+
180
+ [dpsi, deps]
181
+ end
182
+ end
183
+
184
+ def build_nutation_matrix(mean_obliquity:, true_obliquity:, psi:)
185
+ cobm = mean_obliquity.cos
186
+ sobm = mean_obliquity.sin
187
+ cobt = true_obliquity.cos
188
+ sobt = true_obliquity.sin
189
+ cpsi = psi.cos
190
+ spsi = psi.sin
191
+
192
+ Matrix[
193
+ [cpsi, -spsi * cobm, -spsi * sobm],
194
+ [
195
+ spsi * cobt,
196
+ cpsi * cobm * cobt + sobm * sobt,
197
+ cpsi * sobm * cobt - cobm * sobt
198
+ ],
199
+ [
200
+ spsi * sobt,
201
+ cpsi * cobm * sobt - sobm * cobt,
202
+ cpsi * sobm * sobt + cobm * cobt
203
+ ]
204
+ ]
205
+ end
206
+
47
207
  def julian_centuries
48
- (@epoch - Epoch::J1900) / Constants::DAYS_PER_JULIAN_CENTURY
208
+ @julian_centuries ||=
209
+ (@instant.tt - Epoch::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
49
210
  end
50
211
 
51
- def sun_mean_longitude
52
- Angle.from_degrees(
53
- (279.6967 + Constants::DEGREES_PER_CIRCLE * (centuries_a - centuries_a.to_i)) %
54
- Constants::DEGREES_PER_CIRCLE
212
+ # IAU 2006/2000A formula for the mean anomaly of the Moon
213
+ def mean_anomaly_moon
214
+ Angle.from_degree_arcseconds(
215
+ 485868.249036 + 1717915923.2178 * julian_centuries
55
216
  )
56
217
  end
57
218
 
58
- def moon_ascending_node_longitude
59
- Angle.from_degrees(
60
- (259.1833 - Constants::DEGREES_PER_CIRCLE * (centuries_b - centuries_b.to_i)) %
61
- Constants::DEGREES_PER_CIRCLE
219
+ # IAU 2006/2000A formula for the mean anomaly of the Sun
220
+ def mean_anomaly_sun
221
+ Angle.from_degree_arcseconds(
222
+ 1287104.79305 + 129596581.0481 * julian_centuries
62
223
  )
63
224
  end
64
225
 
65
- def centuries_a
66
- 100.002136 * julian_centuries
226
+ # IAU 2006/2000A formula for the mean argument of latitude of the Moon
227
+ def mean_argument_latitude_moon
228
+ Angle.from_degree_arcseconds(
229
+ 335779.526232 + 1739527262.8478 * julian_centuries
230
+ )
231
+ end
232
+
233
+ # IAU 2006/2000A formula for the mean elongation of the Moon from the Sun
234
+ def mean_elongation_moon_sun
235
+ Angle.from_degree_arcseconds(
236
+ 1072260.70369 + 1602961601.2090 * julian_centuries
237
+ )
67
238
  end
68
239
 
69
- def centuries_b
70
- 5.372617 * julian_centuries
240
+ # IAU 2006/2000A formula for the mean longitude of the ascending node of the
241
+ # Moon
242
+ def longitude_ascending_node_moon
243
+ Angle.from_degree_arcseconds(
244
+ 450160.398036 - 6962890.5431 * julian_centuries
245
+ )
246
+ end
247
+
248
+ def fundamental_arguments
249
+ l = mean_anomaly_moon
250
+ lp = mean_anomaly_sun
251
+ f = mean_argument_latitude_moon
252
+ d = mean_elongation_moon_sun
253
+ omega = longitude_ascending_node_moon
254
+
255
+ [l, lp, f, d, omega]
71
256
  end
72
257
  end
73
258
  end
@@ -42,6 +42,45 @@ module Astronoby
42
42
  @pressure = pressure || compute_pressure
43
43
  end
44
44
 
45
+ def geocentric_position
46
+ n = earth_prime_vertical_radius_of_curvature
47
+ x = (n + @elevation.m) * @latitude.cos * @longitude.cos
48
+ y = (n + @elevation.m) * @latitude.cos * @longitude.sin
49
+ z = (n * (1 - Constants::WGS84_ECCENTICITY_SQUARED) + @elevation.m) *
50
+ @latitude.sin
51
+ Distance.vector_from_meters([x, y, z])
52
+ end
53
+
54
+ def geocentric_velocity
55
+ r = projected_radius
56
+ vx = -Constants::EARTH_ANGULAR_VELOCITY_RAD_PER_S * r * @longitude.sin
57
+ vy = Constants::EARTH_ANGULAR_VELOCITY_RAD_PER_S * r * @longitude.cos
58
+ vz = 0.0
59
+ Velocity.vector_from_mps([vx, vy, vz])
60
+ end
61
+
62
+ def earth_fixed_rotation_matrix_for(instant)
63
+ dpsi = Nutation.new(instant: instant).nutation_in_longitude
64
+
65
+ mean_obliquity = MeanObliquity.for_epoch(instant.tt)
66
+
67
+ gast = Angle.from_radians(
68
+ Angle.from_hours(instant.gmst).radians +
69
+ dpsi.radians * mean_obliquity.cos
70
+ )
71
+
72
+ earth_rotation_matrix = Matrix[
73
+ [gast.cos, -gast.sin, 0],
74
+ [gast.sin, gast.cos, 0],
75
+ [0, 0, 1]
76
+ ]
77
+
78
+ nutation_matrix = Nutation.matrix_for(instant)
79
+ precession_matrix = Precession.matrix_for(instant)
80
+
81
+ earth_rotation_matrix * nutation_matrix * precession_matrix
82
+ end
83
+
45
84
  def ==(other)
46
85
  return false unless other.is_a?(self.class)
47
86
 
@@ -84,5 +123,21 @@ module Astronoby
84
123
 
85
124
  Math.exp(-term1 / term2)
86
125
  end
126
+
127
+ def earth_prime_vertical_radius_of_curvature
128
+ Constants::WGS84_EARTH_EQUATORIAL_RADIUS_IN_METERS./(
129
+ Math.sqrt(
130
+ 1 -
131
+ Constants::WGS84_ECCENTICITY_SQUARED * @latitude.sin * @latitude.sin
132
+ )
133
+ )
134
+ end
135
+
136
+ def projected_radius
137
+ Math.sqrt(
138
+ geocentric_position.x.m * geocentric_position.x.m +
139
+ geocentric_position.y.m * geocentric_position.y.m
140
+ )
141
+ end
87
142
  end
88
143
  end
@@ -1,16 +1,74 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "matrix"
4
-
5
3
  module Astronoby
6
4
  class Precession
7
- def self.for_equatorial_coordinates(coordinates:, epoch:)
8
- new(coordinates, epoch).precess
5
+ def self.matrix_for(instant)
6
+ new(instant: instant).matrix
7
+ end
8
+
9
+ def initialize(instant:)
10
+ @instant = instant
11
+ end
12
+
13
+ def matrix
14
+ # Source:
15
+ # IAU resolution in 2006 in favor of the P03 astronomical model
16
+ # https://syrte.obspm.fr/iau2006/aa03_412_P03.pdf
17
+ # P(t) = R3(χA) R1(−ωA) R3(−ψA) R1(ϵ0)
18
+
19
+ # Precession in right ascension
20
+ psi_a = ((((
21
+ -0.0000000951 * t +
22
+ +0.000132851) * t +
23
+ -0.00114045) * t +
24
+ -1.0790069) * t +
25
+ +5038.481507) * t
26
+
27
+ # Precession in declination
28
+ omega_a = ((((
29
+ +0.0000003337 * t +
30
+ -0.000000467) * t +
31
+ -0.00772503) * t +
32
+ +0.0512623) * t +
33
+ -0.025754) * t +
34
+ eps0
35
+
36
+ # Precession of the ecliptic
37
+ chi_a = ((((
38
+ -0.0000000560 * t +
39
+ +0.000170663) * t +
40
+ -0.00121197) * t +
41
+ -2.3814292) * t +
42
+ +10.556403) * t
43
+
44
+ psi_a = Angle.from_degree_arcseconds(psi_a)
45
+ omega_a = Angle.from_degree_arcseconds(omega_a)
46
+ chi_a = Angle.from_degree_arcseconds(chi_a)
47
+
48
+ r3_psi = rotation_z(-psi_a)
49
+ r1_omega = rotation_x(-omega_a)
50
+ r3_chi = rotation_z(chi_a)
51
+ r1_eps0 = rotation_x(MeanObliquity.obliquity_of_reference)
52
+
53
+ r3_chi * r1_omega * r3_psi * r1_eps0
9
54
  end
10
55
 
11
- def initialize(coordinates, epoch)
12
- @coordinates = coordinates
13
- @epoch = epoch
56
+ def rotation_x(angle)
57
+ c, s = angle.cos, angle.sin
58
+ Matrix[
59
+ [1, 0, 0],
60
+ [0, c, s],
61
+ [0, -s, c]
62
+ ]
63
+ end
64
+
65
+ def rotation_z(angle)
66
+ c, s = angle.cos, angle.sin
67
+ Matrix[
68
+ [c, s, 0],
69
+ [-s, c, 0],
70
+ [0, 0, 1]
71
+ ]
14
72
  end
15
73
 
16
74
  # Source:
@@ -18,14 +76,19 @@ module Astronoby
18
76
  # Authors: Peter Duffett-Smith and Jonathan Zwart
19
77
  # Edition: Cambridge University Press
20
78
  # Chapter: 34 - Precession
21
- def precess
22
- matrix_a = matrix_for_epoch(@coordinates.epoch)
23
- matrix_b = matrix_for_epoch(@epoch).transpose
79
+
80
+ def self.for_equatorial_coordinates(coordinates:, epoch:)
81
+ precess(coordinates, epoch)
82
+ end
83
+
84
+ def self.precess(coordinates, epoch)
85
+ matrix_a = matrix_for_epoch(coordinates.epoch)
86
+ matrix_b = matrix_for_epoch(epoch).transpose
24
87
 
25
88
  vector = Vector[
26
- @coordinates.right_ascension.cos * @coordinates.declination.cos,
27
- @coordinates.right_ascension.sin * @coordinates.declination.cos,
28
- @coordinates.declination.sin
89
+ coordinates.right_ascension.cos * coordinates.declination.cos,
90
+ coordinates.right_ascension.sin * coordinates.declination.cos,
91
+ coordinates.declination.sin
29
92
  ]
30
93
 
31
94
  s = matrix_a * vector
@@ -38,13 +101,11 @@ module Astronoby
38
101
  Angle.atan(w[1] / w[0])
39
102
  ),
40
103
  declination: Angle.asin(w[2]),
41
- epoch: @epoch
104
+ epoch: epoch
42
105
  )
43
106
  end
44
107
 
45
- private
46
-
47
- def matrix_for_epoch(epoch)
108
+ def self.matrix_for_epoch(epoch)
48
109
  t = (epoch - Epoch::DEFAULT_EPOCH) / Constants::DAYS_PER_JULIAN_CENTURY
49
110
 
50
111
  zeta = Angle.from_degrees(
@@ -82,5 +143,18 @@ module Astronoby
82
143
  ]
83
144
  ]
84
145
  end
146
+
147
+ private
148
+
149
+ def t
150
+ @t ||= Rational(
151
+ @instant.tdb - Epoch::DEFAULT_EPOCH,
152
+ Constants::DAYS_PER_JULIAN_CENTURY
153
+ )
154
+ end
155
+
156
+ def eps0
157
+ @eps0 ||= MeanObliquity.obliquity_of_reference_in_milliarcseconds
158
+ end
85
159
  end
86
160
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class ReferenceFrame
5
+ attr_reader :position,
6
+ :velocity,
7
+ :instant,
8
+ :center_identifier,
9
+ :target_body
10
+
11
+ def initialize(
12
+ position:,
13
+ velocity:,
14
+ instant:,
15
+ center_identifier:,
16
+ target_body:
17
+ )
18
+ @position = position
19
+ @velocity = velocity
20
+ @instant = instant
21
+ @center_identifier = center_identifier
22
+ @target_body = target_body
23
+ end
24
+
25
+ def equatorial
26
+ @equatorial ||= begin
27
+ return Coordinates::Equatorial.zero if distance.zero?
28
+
29
+ Coordinates::Equatorial.from_position_vector(@position)
30
+ end
31
+ end
32
+
33
+ def ecliptic
34
+ @ecliptic ||= begin
35
+ return Coordinates::Ecliptic.zero if distance.zero?
36
+
37
+ equatorial.to_ecliptic(epoch: Epoch::J2000)
38
+ end
39
+ end
40
+
41
+ def distance
42
+ @distance ||= begin
43
+ return Distance.zero if @position.zero?
44
+
45
+ @position.magnitude
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Apparent < ReferenceFrame
5
+ def self.build_from_astrometric(
6
+ instant:,
7
+ target_astrometric:,
8
+ earth_geometric:,
9
+ target_body:
10
+ )
11
+ position = target_astrometric.position
12
+ velocity = target_astrometric.velocity
13
+ precession_matrix = Precession.matrix_for(instant)
14
+ nutation_matrix = Nutation.matrix_for(instant)
15
+
16
+ corrected_position = Distance.vector_from_meters(
17
+ precession_matrix * nutation_matrix * position.map(&:m)
18
+ )
19
+ corrected_position = Aberration.new(
20
+ astrometric_position: corrected_position,
21
+ observer_velocity: earth_geometric.velocity
22
+ ).corrected_position
23
+ # In theory, here we should also apply light deflection. However, so far
24
+ # the deflection algorithm hasn't shown any significant changes to the
25
+ # apparent position. Therefore, for now, we are saving some computation
26
+ # time by not applying it, and we will investigate if the algorithm is
27
+ # correct or if the deflection is indeed negligible.
28
+
29
+ corrected_velocity = Velocity.vector_from_mps(
30
+ precession_matrix * nutation_matrix * velocity.map(&:mps)
31
+ )
32
+
33
+ new(
34
+ position: corrected_position,
35
+ velocity: corrected_velocity,
36
+ instant: instant,
37
+ center_identifier: SolarSystemBody::EARTH,
38
+ target_body: target_body
39
+ )
40
+ end
41
+
42
+ def ecliptic
43
+ @ecliptic ||= begin
44
+ return Coordinates::Ecliptic.zero if distance.zero?
45
+
46
+ equatorial.to_ecliptic(epoch: @instant.tdb)
47
+ end
48
+ end
49
+
50
+ def angular_diameter
51
+ @angular_radius ||= begin
52
+ return Angle.zero if @position.zero?
53
+
54
+ Angle.from_radians(
55
+ Math.atan(@target_body.class::EQUATORIAL_RADIUS.m / distance.m) * 2
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Astrometric < ReferenceFrame
5
+ def self.build_from_geometric(
6
+ instant:,
7
+ earth_geometric:,
8
+ light_time_corrected_position:,
9
+ light_time_corrected_velocity:,
10
+ target_body:
11
+ )
12
+ new(
13
+ position: light_time_corrected_position - earth_geometric.position,
14
+ velocity: light_time_corrected_velocity - earth_geometric.velocity,
15
+ instant: instant,
16
+ center_identifier: SolarSystemBody::EARTH,
17
+ target_body: target_body
18
+ )
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Geometric < ReferenceFrame
5
+ def initialize(
6
+ position:,
7
+ velocity:,
8
+ instant:,
9
+ target_body:
10
+ )
11
+ super(
12
+ position: position,
13
+ velocity: velocity,
14
+ instant: instant,
15
+ center_identifier: SolarSystemBody::SOLAR_SYSTEM_BARYCENTER,
16
+ target_body: target_body
17
+ )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class MeanOfDate < ReferenceFrame
5
+ def self.build_from_geometric(
6
+ instant:,
7
+ target_geometric:,
8
+ earth_geometric:,
9
+ target_body:
10
+ )
11
+ position = target_geometric.position - earth_geometric.position
12
+ velocity = target_geometric.velocity - earth_geometric.velocity
13
+ precession_matrix = Precession.matrix_for(instant)
14
+ corrected_position = Distance.vector_from_m(
15
+ precession_matrix * position.map(&:m)
16
+ )
17
+ corrected_velocity = Velocity.vector_from_mps(
18
+ precession_matrix * velocity.map(&:mps)
19
+ )
20
+
21
+ new(
22
+ position: corrected_position,
23
+ velocity: corrected_velocity,
24
+ instant: instant,
25
+ center_identifier: SolarSystemBody::EARTH,
26
+ target_body: target_body
27
+ )
28
+ end
29
+
30
+ def ecliptic
31
+ @ecliptic ||= begin
32
+ return Coordinates::Ecliptic.zero if distance.zero?
33
+
34
+ equatorial.to_ecliptic(epoch: @instant.tdb)
35
+ end
36
+ end
37
+ end
38
+ end