astronoby 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/README.md +6 -4
  4. data/UPGRADING.md +25 -0
  5. data/docs/README.md +30 -2
  6. data/docs/angles.md +1 -1
  7. data/docs/coordinates.md +1 -1
  8. data/docs/deep_sky_bodies.md +101 -0
  9. data/docs/ephem.md +1 -1
  10. data/docs/instant.md +1 -1
  11. data/docs/moon_phases.md +1 -1
  12. data/docs/observer.md +1 -1
  13. data/docs/reference_frames.md +1 -1
  14. data/docs/rise_transit_set_times.md +5 -5
  15. data/docs/{celestial_bodies.md → solar_system_bodies.md} +1 -1
  16. data/lib/astronoby/angle.rb +6 -2
  17. data/lib/astronoby/angular_velocity.rb +76 -0
  18. data/lib/astronoby/bodies/deep_sky_object.rb +44 -0
  19. data/lib/astronoby/bodies/deep_sky_object_position.rb +127 -0
  20. data/lib/astronoby/bodies/earth.rb +5 -1
  21. data/lib/astronoby/bodies/moon.rb +21 -0
  22. data/lib/astronoby/bodies/solar_system_body.rb +25 -0
  23. data/lib/astronoby/cache.rb +1 -0
  24. data/lib/astronoby/constants.rb +7 -2
  25. data/lib/astronoby/coordinates/equatorial.rb +2 -5
  26. data/lib/astronoby/distance.rb +6 -0
  27. data/lib/astronoby/events/extremum_calculator.rb +233 -0
  28. data/lib/astronoby/events/extremum_event.rb +15 -0
  29. data/lib/astronoby/events/rise_transit_set_calculator.rb +9 -6
  30. data/lib/astronoby/events/twilight_calculator.rb +1 -1
  31. data/lib/astronoby/instant.rb +27 -4
  32. data/lib/astronoby/reference_frames/apparent.rb +0 -10
  33. data/lib/astronoby/reference_frames/topocentric.rb +1 -1
  34. data/lib/astronoby/stellar_propagation.rb +162 -0
  35. data/lib/astronoby/time/greenwich_apparent_sidereal_time.rb +22 -0
  36. data/lib/astronoby/time/greenwich_mean_sidereal_time.rb +64 -0
  37. data/lib/astronoby/time/greenwich_sidereal_time.rb +20 -58
  38. data/lib/astronoby/time/local_apparent_sidereal_time.rb +42 -0
  39. data/lib/astronoby/time/local_mean_sidereal_time.rb +42 -0
  40. data/lib/astronoby/time/local_sidereal_time.rb +35 -26
  41. data/lib/astronoby/time/sidereal_time.rb +42 -0
  42. data/lib/astronoby/util/time.rb +61 -43
  43. data/lib/astronoby/velocity.rb +5 -0
  44. data/lib/astronoby/version.rb +1 -1
  45. data/lib/astronoby.rb +11 -0
  46. metadata +14 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e26fab1e74e0fa27c784712cb44a5557c553c1104752eb5e63ddda0c48a95b52
4
- data.tar.gz: cfb3e98fc8a4f841b144a0ae2ea435ea764d7eafb85f150a0e3cc7495af3218e
3
+ metadata.gz: becadc2ba01e7620f12b20a5192e8537ed9f717336bb43471734fd5be9561675
4
+ data.tar.gz: 47c5743867f09c58f49093645da7534def3836135883f44929d4da15f73a441a
5
5
  SHA512:
6
- metadata.gz: 43b7bdbd274fc42b1c1c992a677abf71dc831e727abb2bf78b93190595a4746a07f266affececa6e495a534eda304164422623a09673fe779c9b1064be9d11d0
7
- data.tar.gz: b8a3dcd0992c55606c6e13b4f7783325268a1168d26343ac6a20d050514ca7b30b4fd3548a2903db5abfcbb8133efe79ae7868681cdcba8b61d284862f1f423c
6
+ metadata.gz: 2b04c971a79ac8201e21398dc0cc02b920cdadde538a0eb7d5d59dbc260661cbda51f3a72a434695b596634848f361a96b9533074f399d23d1e3d21fac89d772
7
+ data.tar.gz: f71973fd392c15a243a28ac09e521e813adf4900eefacfbc55712bd4f942c46b3351cfe44e183c1e809b0285b0ffb22ab320afd26b84c3d329be7fe79897378d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.0 - 2025-10-31
4
+
5
+ _If you are upgrading: please see [UPGRADING.md]._
6
+
7
+ ### Features
8
+
9
+ * Add `#approaching_primary?` and `#receding_from_primary?` to solar system bodies ([#211])
10
+ * Calculate apoapsis and periapsis events ([#213])
11
+ * Improve precision of ΔT ([#219])
12
+ * Deep Sky Object: Compute astrometric position ([#217])
13
+ * Deep Sky Object: Compute apparent position ([#220])
14
+ * Deep Sky Object: Handle velocities properly ([#222])
15
+ * Deep Sky Object: Compute topocentric position ([#226])
16
+ * Deep Sky Object: difference between the body and the position ([#227])
17
+ * Deep Sky Object: Add support for RiseTransitSetCalculator ([#228])
18
+
19
+ ### Improvements
20
+
21
+ * Drop `Astronoby::Apparent#angular_diameter` ([#221])
22
+ * Bump rubyzip from 3.0.2 to 3.2.1 by @dependabot ([#210], [#215], [#223], [#233])
23
+ * Bump standard from 1.50.0 to 1.51.1 by @dependabot ([#212], [#214])
24
+ * Be proud about the precision achieved ([#218])
25
+ * Use local apparent instead of local mean sidereal time for hour angle ([#225])
26
+ * Bump rspec from 3.13.1 to 3.13.2 by @dependabot ([#229])
27
+ * Bump benchmark from 0.4.1 to 0.5.0 by @dependabot ([#230])
28
+ * Add documentation for deep-sky objects ([#232])
29
+ * Bump rake from 13.3.0 to 13.3.1 by @dependabot ([#235])
30
+
31
+ ### Backward-incompatible changes
32
+
33
+ * Drop `Astronoby::Apparent#angular_diameter` ([#221])
34
+ * Use local apparent instead of local mean sidereal time for hour angle ([#225])
35
+
36
+ **Full Changelog**: https://github.com/rhannequin/astronoby/compare/v0.8.0...v0.9.0
37
+
38
+ [#210]: https://github.com/rhannequin/astronoby/pull/210
39
+ [#211]: https://github.com/rhannequin/astronoby/pull/211
40
+ [#212]: https://github.com/rhannequin/astronoby/pull/212
41
+ [#213]: https://github.com/rhannequin/astronoby/pull/213
42
+ [#214]: https://github.com/rhannequin/astronoby/pull/214
43
+ [#215]: https://github.com/rhannequin/astronoby/pull/215
44
+ [#217]: https://github.com/rhannequin/astronoby/pull/217
45
+ [#218]: https://github.com/rhannequin/astronoby/pull/218
46
+ [#219]: https://github.com/rhannequin/astronoby/pull/219
47
+ [#220]: https://github.com/rhannequin/astronoby/pull/220
48
+ [#221]: https://github.com/rhannequin/astronoby/pull/221
49
+ [#222]: https://github.com/rhannequin/astronoby/pull/222
50
+ [#223]: https://github.com/rhannequin/astronoby/pull/223
51
+ [#225]: https://github.com/rhannequin/astronoby/pull/225
52
+ [#226]: https://github.com/rhannequin/astronoby/pull/226
53
+ [#227]: https://github.com/rhannequin/astronoby/pull/227
54
+ [#228]: https://github.com/rhannequin/astronoby/pull/228
55
+ [#229]: https://github.com/rhannequin/astronoby/pull/229
56
+ [#230]: https://github.com/rhannequin/astronoby/pull/230
57
+ [#232]: https://github.com/rhannequin/astronoby/pull/232
58
+ [#233]: https://github.com/rhannequin/astronoby/pull/233
59
+ [#235]: https://github.com/rhannequin/astronoby/pull/235
60
+
3
61
  ## 0.8.0 - 2025-09-01
4
62
 
5
63
  _If you are upgrading: please see [UPGRADING.md]._
data/README.md CHANGED
@@ -41,7 +41,8 @@ previous versions, you can access the documentation in the README for each
41
41
 
42
42
  ### See also
43
43
  - [Quick Start Guide](docs/README.md) - for getting started examples
44
- - [Celestial Bodies](docs/celestial_bodies.md) - for understanding planets and objects
44
+ - [Solar System Bodies](docs/solar_system_bodies.md) - for understanding planets
45
+ and objects
45
46
  - [Reference Frames](docs/reference_frames.md) - for coordinate systems
46
47
  - [Observer Setup](docs/observer.md) - for location configuration
47
48
  - [Glossary](docs/glossary.md) - for astronomical and technical terms
@@ -58,11 +59,12 @@ described in the [UPGRADING] document.
58
59
  ## Precision
59
60
 
60
61
  The current precision for the major Solar System bodies' locations in the sky
61
- as seen from an observer on Earth is below 10 arc seconds. This corresponds to
62
- half the size of Saturn when it is closest to Earth.
62
+ as seen from an observer on Earth is around 1-2 arc seconds, with sub-arcsecond
63
+ often achieved. This corresponds to less than the apparent size of Neptune when
64
+ it is closest to Earth.
63
65
 
64
66
  While the precision is not enough for spacecraft navigation, it is enough for
65
- automated guiding of amateur telescopes.
67
+ automated guiding of amateur telescopes and some professional use-cases.
66
68
 
67
69
  The sources used for comparison are: [IMCCE], [JPL Horizons], [Stellarium],
68
70
  and the [Skyfield] library.
data/UPGRADING.md CHANGED
@@ -7,6 +7,31 @@ changes to it as long as a major version has not been released.
7
7
  If you are already using Astronoby and wish to follow the changes to its
8
8
  public API, please read the upgrading notes for each release.
9
9
 
10
+ ## Upgrading from 0.8.0 to 0.9.0
11
+
12
+ ### Constant `MINUTES_PER_DEGREE` renamed
13
+
14
+ The constant `Constants::MINUTES_PER_DEGREE` has been renamed to
15
+ `Constants::ARC_MINUTES_PER_DEGREE` to clarify its purpose. The value remains
16
+ unchanged (`60.0`).
17
+
18
+ ### Signature change in `RiseTransitSetCalculator`
19
+
20
+ The `RiseTransitSetCalculator` constructor's `ephem` key argument is not
21
+ mandatory anymore. This allows to support bodies that don't require ephemerides
22
+ (e.g. deep-sky objects).
23
+
24
+ ### Sidereal time API change
25
+
26
+ It is is now preferred to use `Instant#gmst`, `Instant#gast`, `Instant#lmst` and
27
+ `Instant#last` to get sidereal times.
28
+
29
+ It is also possible to use dedicated classes:
30
+ - `GreenwichMeanSiderealTime`
31
+ - `GreenwichApparentSiderealTime`
32
+ - `LocalMeanSiderealTime`
33
+ - `LocalApparentSiderealTime`
34
+
10
35
  ## Upgrading from 0.7.0 to 0.8.0
11
36
 
12
37
  ### Benchmark directory changed
data/docs/README.md CHANGED
@@ -34,7 +34,7 @@ You can learn more about time scales on the [Instant page].
34
34
  jupiter = Astronoby::Jupiter.new(instant: instant, ephem: ephem)
35
35
  ```
36
36
 
37
- You can learn more about planets and bodies on the [Celestial Bodies page].
37
+ You can learn more about planets and bodies on the [Solar System Bodies page].
38
38
 
39
39
  ## Define an observer from geographic coordinates
40
40
 
@@ -178,6 +178,33 @@ Astronoby::EquinoxSolstice.december_solstice(2025, ephem)
178
178
  You can learn more about equinoxes and solstices on the
179
179
  [Equinoxes and solstices times page].
180
180
 
181
+ ## Deep-sky objects
182
+
183
+ It is possible to manipulate any deep-sky possible, given equatorial coordinates
184
+ from a catalogue at J2000 epoch.
185
+
186
+ ```rb
187
+ time = Time.utc(2025, 10, 1)
188
+ instant = Astronoby::Instant.from_time(time)
189
+
190
+ vega_j2000 = Astronoby::Coordinates::Equatorial.new(
191
+ right_ascension: Astronoby::Angle.from_hms(18, 36, 56.33635),
192
+ declination: Astronoby::Angle.from_dms(38, 47, 1.2802),
193
+ epoch: Astronoby::JulianDate::J2000
194
+ )
195
+
196
+ # Build the body for star Vega
197
+ vega = Astronoby::DeepSkyObject.new(equatorial_coordinates: vega_j2000)
198
+
199
+ # Build the position of star Vega for a given instant
200
+ vega_position = vega.at(instant)
201
+
202
+ vega_position.apparent.equatorial.right_ascension.str(:hms)
203
+ # => "18h 36m 56.3363s"
204
+ ```
205
+
206
+ You can learn more about deep-sky objects on the [Deep-sky Bodies page].
207
+
181
208
  ## See also
182
209
  - [Glossary](glossary.md) - for astronomical and technical terms
183
210
  - [Configuration](configuration.md) - for performance tuning
@@ -185,7 +212,7 @@ You can learn more about equinoxes and solstices on the
185
212
 
186
213
  [Ephem page]: ephem.md
187
214
  [Instant page]: instant.md
188
- [Celestial Bodies page]: celestial_bodies.md
215
+ [Solar System Bodies page]: solar_system_bodies.md
189
216
  [Observer page]: observer.md
190
217
  [Reference Frames page]: reference_frames.md
191
218
  [Angles page]: angles.md
@@ -194,3 +221,4 @@ You can learn more about equinoxes and solstices on the
194
221
  [Twilight times page]: twilight_times.md
195
222
  [Moon phases page]: moon_phases.md
196
223
  [Equinoxes and solstices times page]: equinoxes_solstices_times.md
224
+ [Deep-sky Bodies page]: deep_sky_bodies.md
data/docs/angles.md CHANGED
@@ -134,4 +134,4 @@ angle1.negative? # => false
134
134
  - [Coordinates](coordinates.md) - for using angles in coordinate systems
135
135
  - [Observer](observer.md) - for latitude and longitude
136
136
  - [Reference Frames](reference_frames.md) - for position calculations
137
- - [Celestial Bodies](celestial_bodies.md) - for object positions
137
+ - [Solar System Bodies](solar_system_bodies.md) - for object positions
data/docs/coordinates.md CHANGED
@@ -164,4 +164,4 @@ horizontal.azimuth.str(:dms)
164
164
  - [Reference Frames](reference_frames.md) - for coordinate system details
165
165
  - [Angles](angles.md) - for working with angular measurements
166
166
  - [Observer](observer.md) - for location setup
167
- - [Celestial Bodies](celestial_bodies.md) - for object positions
167
+ - [Solar System Bodies](solar_system_bodies.md) - for object positions
@@ -0,0 +1,101 @@
1
+ # Deep-sky Bodies
2
+
3
+ Deep-sky objects represent celestial object that are not part of the Solar
4
+ System, like stars, nebulae, clusters, galaxies. They are not affected by any
5
+ body of the Solar System.
6
+
7
+ Because we know billions of there objects, it is impossible for Astronoby to
8
+ store a comprehensive catalogue. Therefore, it is up to the developer to build
9
+ the object they need, based on equatorial coordinates from official catalogues
10
+ at J2000 epoch. The [SIMBAD Astronomical Database] is an example of database
11
+ where such coordinates can be found.
12
+
13
+ Astronoby makes the difference between the body and the position.
14
+ `Astronoby::DeepSkyObject` represent the body in itself, defined by fixed
15
+ coordinates. It can also be support proper motion parameters, also given from
16
+ official catalogues, which provides a bit more precision.
17
+
18
+ ```rb
19
+ vega_j2000 = Astronoby::Coordinates::Equatorial.new(
20
+ right_ascension: Astronoby::Angle.from_hms(18, 36, 56.33635),
21
+ declination: Astronoby::Angle.from_dms(38, 47, 1.2802),
22
+ epoch: Astronoby::JulianDate::J2000
23
+ )
24
+
25
+ vega = Astronoby::DeepSkyObject.new(equatorial_coordinates: vega_j2000)
26
+
27
+ vega_with_proper_motion = Astronoby::DeepSkyObject.new(
28
+ equatorial_coordinates: vega_j2000,
29
+ proper_motion_ra: Astronoby::AngularVelocity
30
+ .from_milliarcseconds_per_year(200.94),
31
+ proper_motion_dec: Astronoby::AngularVelocity
32
+ .from_milliarcseconds_per_year(286.23),
33
+ parallax: Astronoby::Angle.from_degree_arcseconds(130.23 / 1000.0),
34
+ radial_velocity: Astronoby::Velocity.from_kilometers_per_second(-13.5)
35
+ )
36
+ ```
37
+
38
+ From a `DeepSkyObject` instance, it is possible to instantiate the position
39
+ of the body at a given instant. If given the optional `ephem` parameter,
40
+ precision will increase as some astronomical corrections will be
41
+ applied.
42
+
43
+ A `DeepSkyPosition` object exposes `astrometric`, `apparent` and `topocentric`
44
+ reference frames.
45
+
46
+ ```rb
47
+ time = Time.utc(2025, 10, 1)
48
+ instant = Astronoby::Instant.from_time(time)
49
+
50
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
51
+
52
+ vega
53
+ .at(instant)
54
+ .apparent
55
+ .equatorial
56
+ .right_ascension
57
+ .str(:hms)
58
+ # => "18h 37m 48.2804s"
59
+
60
+ vega_with_proper_motion
61
+ .at(instant, ephem: ephem)
62
+ .apparent
63
+ .equatorial
64
+ .right_ascension
65
+ .str(:hms)
66
+ # => "18h 37m 48.7215s"
67
+ ```
68
+
69
+ You can learn more about [ephemerides] and [reference frames].
70
+
71
+ A deep-sky body object can also be used in calculators.
72
+
73
+ ```rb
74
+ observer = Astronoby::Observer.new(
75
+ latitude: Astronoby::Angle.from_degrees(51.5072),
76
+ longitude: Astronoby::Angle.from_degrees(-0.1276)
77
+ )
78
+ date = Date.new(2025, 10, 1)
79
+
80
+ body = Astronoby::DeepSkyObject.new(
81
+ equatorial_coordinates: Astronoby::Coordinates::Equatorial.new(
82
+ right_ascension: Astronoby::Angle.from_hms(6, 45, 8.917),
83
+ declination: Astronoby::Angle.from_dms(-16, 42, 58.02)
84
+ )
85
+ )
86
+
87
+ calculator = Astronoby::RiseTransitSetCalculator.new(
88
+ body: body,
89
+ observer: observer,
90
+ ephem: ephem
91
+ )
92
+
93
+ event = calculator.event_on(date)
94
+
95
+ event.rising_time
96
+ # => 2025-10-01 01:31:25 UTC
97
+ ```
98
+
99
+ [ephemerides]: ephem.md
100
+ [reference frames]: reference_frames.md
101
+ [SIMBAD Astronomical Database]: https://simbad.u-strasbg.fr/simbad/
data/docs/ephem.md CHANGED
@@ -79,7 +79,7 @@ the target `399`.
79
79
  [IMCCE]: https://www.imcce.fr
80
80
 
81
81
  ## See also
82
- - [Celestial Bodies](celestial_bodies.md) - for using ephemeris data
82
+ - [Solar System Bodies](solar_system_bodies.md) - for using ephemeris data
83
83
  - [Reference Frames](reference_frames.md) - for coordinate calculations
84
84
  - [Observer](observer.md) - for location-based calculations
85
85
  - [Configuration](configuration.md) - for performance tuning
data/docs/instant.md CHANGED
@@ -134,6 +134,6 @@ instant1 < instant2
134
134
 
135
135
  ## See also
136
136
  - [Ephemerides](ephem.md) - for time-based calculations
137
- - [Celestial Bodies](celestial_bodies.md) - for object positions
137
+ - [Solar System Bodies](solar_system_bodies.md) - for object positions
138
138
  - [Reference Frames](reference_frames.md) - for coordinate systems
139
139
  - [Configuration](configuration.md) - for performance settings
data/docs/moon_phases.md CHANGED
@@ -75,5 +75,5 @@ phases.each { puts "#{_1.phase}: #{_1.time}" }
75
75
  ## See also
76
76
  - [Twilight Times](twilight_times.md) - for sun-related events
77
77
  - [Rise, Transit and Set Times](rise_transit_set_times.md) - for moon events
78
- - [Celestial Bodies](celestial_bodies.md) - for moon object details
78
+ - [Solar System Bodies](solar_system_bodies.md) - for moon object details
79
79
  - [Ephemerides](ephem.md) - for data sources
data/docs/observer.md CHANGED
@@ -62,4 +62,4 @@ observer1 == observer2
62
62
  - [Angles](angles.md) - for working with latitude and longitude
63
63
  - [Coordinates](coordinates.md) - for understanding position systems
64
64
  - [Reference Frames](reference_frames.md) - for topocentric calculations
65
- - [Celestial Bodies](celestial_bodies.md) - for observing objects
65
+ - [Solar System Bodies](solar_system_bodies.md) - for observing objects
@@ -132,7 +132,7 @@ You can learn more about observers on the [Observer page].
132
132
  ## See also
133
133
  - [Coordinates](coordinates.md) - for understanding coordinate systems
134
134
  - [Observer](observer.md) - for location setup
135
- - [Celestial Bodies](celestial_bodies.md) - for object positions
135
+ - [Solar System Bodies](solar_system_bodies.md) - for object positions
136
136
  - [Ephemerides](ephem.md) - for data sources
137
137
 
138
138
  [Observer page]: observer.md
@@ -10,12 +10,12 @@ Once instantiated, the calculator doesn't do anything yet, it waits for your
10
10
  instruction.
11
11
 
12
12
  It takes as key arguments:
13
- * `body` (`Astronoby::SolarSystemBody`): any supported celestial body,
14
- e.g. `Astronoby::Sun`
13
+ * `body` (`Astronoby::SolarSystemBody` or `Astronoby::DeepSkyObject`): any
14
+ supported celestial body, e.g. `Astronoby::Sun`
15
15
  * `observer` (`Astronoby::Observer`): location on Earth of the observer
16
16
  * `ephem`: ephemeris to provide the initial raw data
17
17
 
18
- You can learn more about [celestial bodies] and [ephemerides].
18
+ You can learn more about [Solar System bodies] and [ephemerides].
19
19
 
20
20
  ```rb
21
21
  ephem = Astronoby::Ephem.load("inpop19a.bsp")
@@ -109,11 +109,11 @@ event.setting_time.localtime(utc_offset)
109
109
  # => 2025-05-01 16:14:24 +0300
110
110
  ```
111
111
 
112
- [celestial bodies]: celestial_bodies.md
112
+ [Solar System bodies]: solar_system_bodies.md
113
113
  [ephemerides]: ephem.md
114
114
 
115
115
  ## See also
116
116
  - [Twilight Times](twilight_times.md) - for sun-related events
117
- - [Celestial Bodies](celestial_bodies.md) - for object information
117
+ - [Solar System Bodies](solar_system_bodies.md) - for object information
118
118
  - [Observer](observer.md) - for location setup
119
119
  - [Ephemerides](ephem.md) - for data sources
@@ -1,4 +1,4 @@
1
- # Celestial Bodies
1
+ # Solar System Bodies
2
2
 
3
3
  Currently, Astronoby only supports the following major bodies of the Solar
4
4
  System:
@@ -73,6 +73,10 @@ module Astronoby
73
73
  @radians * Constants::PI_IN_DEGREES / Math::PI
74
74
  end
75
75
 
76
+ def degree_milliarcseconds
77
+ degrees * Constants::MILLIARCSECONDS_PER_DEGREE
78
+ end
79
+
76
80
  def hours
77
81
  @radians / Constants::RADIAN_PER_HOUR
78
82
  end
@@ -139,10 +143,10 @@ module Astronoby
139
143
  sign = degrees.negative? ? "-" : "+"
140
144
  absolute_degrees = degrees.abs
141
145
  deg = absolute_degrees.floor
142
- decimal_minutes = Constants::MINUTES_PER_DEGREE *
146
+ decimal_minutes = Constants::ARCMINUTES_PER_DEGREE *
143
147
  (absolute_degrees - deg)
144
148
  absolute_decimal_minutes = (
145
- Constants::MINUTES_PER_DEGREE * (absolute_degrees - deg)
149
+ Constants::ARCMINUTES_PER_DEGREE * (absolute_degrees - deg)
146
150
  ).abs
147
151
  minutes = decimal_minutes.floor
148
152
  seconds = Constants::SECONDS_PER_MINUTE * (
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class AngularVelocity
5
+ include Comparable
6
+
7
+ class << self
8
+ def zero
9
+ new(0)
10
+ end
11
+
12
+ def from_radians_per_second(radians_per_second)
13
+ new(radians_per_second)
14
+ end
15
+
16
+ def from_milliarcseconds_per_year(mas_per_year)
17
+ angle = Angle.from_degree_arcseconds(mas_per_year / 1000.0)
18
+ radians_per_second = angle.radians / Constants::SECONDS_PER_JULIAN_YEAR
19
+ new(radians_per_second)
20
+ end
21
+ end
22
+
23
+ attr_reader :radians_per_second
24
+ alias_method :rps, :radians_per_second
25
+
26
+ def initialize(radians_per_second)
27
+ @radians_per_second = radians_per_second
28
+ freeze
29
+ end
30
+
31
+ def milliarcseconds_per_year
32
+ angle = Angle.from_radians(@radians_per_second)
33
+ angle.degree_milliarcseconds * Constants::SECONDS_PER_JULIAN_YEAR
34
+ end
35
+ alias_method :mas_per_year, :milliarcseconds_per_year
36
+
37
+ def +(other)
38
+ self.class.from_radians_per_second(
39
+ @radians_per_second + other.radians_per_second
40
+ )
41
+ end
42
+
43
+ def -(other)
44
+ self.class.from_radians_per_second(
45
+ @radians_per_second - other.radians_per_second
46
+ )
47
+ end
48
+
49
+ def -@
50
+ self.class.from_radians_per_second(-@radians_per_second)
51
+ end
52
+
53
+ def positive?
54
+ @radians_per_second > 0
55
+ end
56
+
57
+ def negative?
58
+ @radians_per_second < 0
59
+ end
60
+
61
+ def zero?
62
+ @radians_per_second.zero?
63
+ end
64
+
65
+ def hash
66
+ [@radians_per_second, self.class].hash
67
+ end
68
+
69
+ def <=>(other)
70
+ return unless other.is_a?(self.class)
71
+
72
+ @radians_per_second <=> other.radians_per_second
73
+ end
74
+ alias_method :eql?, :==
75
+ end
76
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class DeepSkyObject
5
+ # @param equatorial_coordinates [Astronoby::Coordinates::Equatorial]
6
+ # Equatorial coordinates at epoch J2000.0
7
+ # @param proper_motion_ra [Astronoby::AngularVelocity, nil] Proper motion in
8
+ # right ascension
9
+ # @param proper_motion_dec [Astronoby::AngularVelocity, nil] Proper motion
10
+ # in declination
11
+ # @param parallax [Astronoby::Angle, nil] Parallax angle
12
+ # @param radial_velocity [Astronoby::Velocity, nil] Radial velocity
13
+ def initialize(
14
+ equatorial_coordinates:,
15
+ proper_motion_ra: nil,
16
+ proper_motion_dec: nil,
17
+ parallax: nil,
18
+ radial_velocity: nil
19
+ )
20
+ @initial_equatorial_coordinates = equatorial_coordinates
21
+ @proper_motion_ra = proper_motion_ra
22
+ @proper_motion_dec = proper_motion_dec
23
+ @parallax = parallax
24
+ @radial_velocity = radial_velocity
25
+ end
26
+
27
+ # @param instant [Astronoby::Instant] Instant of the observation
28
+ # @param ephem [Astronoby::Ephemeris, nil] Ephemeris to use for Earth
29
+ # position calculation
30
+ # @return [Astronoby::DeepSkyObjectPosition] Position of the deep-sky object
31
+ # at the given instant
32
+ def at(instant, ephem: nil)
33
+ DeepSkyObjectPosition.new(
34
+ instant: instant,
35
+ equatorial_coordinates: @initial_equatorial_coordinates,
36
+ ephem: ephem,
37
+ proper_motion_ra: @proper_motion_ra,
38
+ proper_motion_dec: @proper_motion_dec,
39
+ parallax: @parallax,
40
+ radial_velocity: @radial_velocity
41
+ )
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class DeepSkyObjectPosition
5
+ DEFAULT_DISTANCE = Distance.from_parsecs(1e9)
6
+
7
+ attr_reader :instant, :apparent
8
+
9
+ # @param instant [Astronoby::Instant] Instant of the observation
10
+ # @param equatorial_coordinates [Astronoby::Coordinates::Equatorial]
11
+ # Equatorial coordinates at epoch J2000.0
12
+ # @param proper_motion_ra [Astronoby::AngularVelocity, nil] Proper motion in
13
+ # right ascension
14
+ # @param proper_motion_dec [Astronoby::AngularVelocity, nil] Proper motion
15
+ # in declination
16
+ # @param parallax [Astronoby::Angle, nil] Parallax angle
17
+ # @param radial_velocity [Astronoby::Velocity, nil] Radial velocity
18
+ def initialize(
19
+ instant:,
20
+ equatorial_coordinates:,
21
+ ephem: nil,
22
+ proper_motion_ra: nil,
23
+ proper_motion_dec: nil,
24
+ parallax: nil,
25
+ radial_velocity: nil
26
+ )
27
+ @instant = instant
28
+ @initial_equatorial_coordinates = equatorial_coordinates
29
+ @proper_motion_ra = proper_motion_ra
30
+ @proper_motion_dec = proper_motion_dec
31
+ @parallax = parallax
32
+ @radial_velocity = radial_velocity
33
+ if ephem
34
+ @earth_geometric = Earth.geometric(ephem: ephem, instant: @instant)
35
+ end
36
+ compute_apparent
37
+ end
38
+
39
+ # @return [Astronoby::Astrometric] Astrometric position of the object
40
+ def astrometric
41
+ @astrometric ||= Astrometric.new(
42
+ instant: @instant,
43
+ position: astrometric_position,
44
+ velocity: astrometric_velocity,
45
+ center_identifier: SolarSystemBody::EARTH,
46
+ target_body: self
47
+ )
48
+ end
49
+
50
+ def observed_by(observer)
51
+ Topocentric.build_from_apparent(
52
+ apparent: @apparent,
53
+ observer: observer,
54
+ instant: @instant,
55
+ target_body: self
56
+ )
57
+ end
58
+
59
+ private
60
+
61
+ def astrometric_position
62
+ @astrometric_position ||= if use_stellar_propagation?
63
+ stellar_propagation.position
64
+ else
65
+ astronomical_distance = DEFAULT_DISTANCE.meters
66
+ right_ascension = @initial_equatorial_coordinates.right_ascension
67
+ declination = @initial_equatorial_coordinates.declination
68
+ Distance.vector_from_meters([
69
+ declination.cos * right_ascension.cos * astronomical_distance,
70
+ declination.cos * right_ascension.sin * astronomical_distance,
71
+ declination.sin * astronomical_distance
72
+ ])
73
+ end
74
+ end
75
+
76
+ def astrometric_velocity
77
+ @astrometric_velocity ||= if use_stellar_propagation?
78
+ stellar_propagation.velocity_vector
79
+ else
80
+ Velocity.vector_from_meters_per_second([0.0, 0.0, 0.0])
81
+ end
82
+ end
83
+
84
+ def use_stellar_propagation?
85
+ @proper_motion_ra && @proper_motion_dec && @parallax && @radial_velocity
86
+ end
87
+
88
+ def stellar_propagation
89
+ @stellar_propagation ||= StellarPropagation.new(
90
+ equatorial_coordinates: @initial_equatorial_coordinates,
91
+ proper_motion_ra: @proper_motion_ra,
92
+ proper_motion_dec: @proper_motion_dec,
93
+ parallax: @parallax,
94
+ radial_velocity: @radial_velocity,
95
+ instant: @instant,
96
+ earth_geometric: @earth_geometric
97
+ )
98
+ end
99
+
100
+ def compute_apparent
101
+ @apparent = if @earth_geometric
102
+ Apparent.build_from_astrometric(
103
+ instant: @instant,
104
+ target_astrometric: astrometric,
105
+ earth_geometric: @earth_geometric,
106
+ target_body: self
107
+ )
108
+ else
109
+ precession_matrix = Precession.matrix_for(@instant)
110
+ nutation_matrix = Nutation.matrix_for(@instant)
111
+ corrected_position = Distance.vector_from_meters(
112
+ precession_matrix * nutation_matrix * astrometric.position.map(&:m)
113
+ )
114
+ corrected_velocity = Velocity.vector_from_mps(
115
+ precession_matrix * nutation_matrix * astrometric.velocity.map(&:mps)
116
+ )
117
+ Apparent.new(
118
+ position: corrected_position,
119
+ velocity: corrected_velocity,
120
+ instant: @instant,
121
+ center_identifier: SolarSystemBody::EARTH,
122
+ target_body: self
123
+ )
124
+ end
125
+ end
126
+ end
127
+ end