astronoby 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +235 -0
  4. data/README.md +19 -1
  5. data/UPGRADING.md +270 -0
  6. data/docs/README.md +105 -16
  7. data/docs/altitude_crossings.md +92 -0
  8. data/docs/angles.md +1 -0
  9. data/docs/configuration.md +28 -17
  10. data/docs/coordinates.md +72 -12
  11. data/docs/deep_sky_bodies.md +46 -1
  12. data/docs/ephem.md +5 -2
  13. data/docs/equinoxes_solstices_times.md +4 -3
  14. data/docs/glossary.md +99 -1
  15. data/docs/iers.md +40 -0
  16. data/docs/instant.md +33 -21
  17. data/docs/lunar_eclipses.md +208 -0
  18. data/docs/lunar_observation.md +87 -0
  19. data/docs/moon_phases.md +4 -1
  20. data/docs/observer.md +20 -6
  21. data/docs/planetary_phenomena.md +78 -0
  22. data/docs/reference_frames.md +192 -34
  23. data/docs/rise_transit_set_times.md +6 -4
  24. data/docs/solar_system_bodies.md +26 -4
  25. data/docs/twilight_times.md +43 -24
  26. data/lib/astronoby/angle.rb +63 -2
  27. data/lib/astronoby/angles/dms.rb +18 -1
  28. data/lib/astronoby/angles/hms.rb +14 -1
  29. data/lib/astronoby/angular_velocity.rb +21 -0
  30. data/lib/astronoby/bodies/deep_sky_object.rb +6 -1
  31. data/lib/astronoby/bodies/deep_sky_object_position.rb +35 -38
  32. data/lib/astronoby/bodies/earth.rb +7 -44
  33. data/lib/astronoby/bodies/jupiter.rb +10 -0
  34. data/lib/astronoby/bodies/mars.rb +10 -0
  35. data/lib/astronoby/bodies/mercury.rb +10 -0
  36. data/lib/astronoby/bodies/moon.rb +158 -32
  37. data/lib/astronoby/bodies/neptune.rb +10 -0
  38. data/lib/astronoby/bodies/saturn.rb +10 -0
  39. data/lib/astronoby/bodies/solar_system_body.rb +240 -61
  40. data/lib/astronoby/bodies/sun.rb +79 -4
  41. data/lib/astronoby/bodies/uranus.rb +10 -0
  42. data/lib/astronoby/bodies/venus.rb +10 -0
  43. data/lib/astronoby/body.rb +6 -0
  44. data/lib/astronoby/cache.rb +13 -0
  45. data/lib/astronoby/center.rb +84 -0
  46. data/lib/astronoby/constants.rb +1 -2
  47. data/lib/astronoby/constellation.rb +9 -1
  48. data/lib/astronoby/constellations/data.rb +1 -1
  49. data/lib/astronoby/coordinates/ecliptic.rb +10 -1
  50. data/lib/astronoby/coordinates/equatorial.rb +79 -13
  51. data/lib/astronoby/coordinates/geodetic.rb +102 -0
  52. data/lib/astronoby/coordinates/horizontal.rb +13 -1
  53. data/lib/astronoby/delta_t.rb +44 -0
  54. data/lib/astronoby/distance.rb +35 -0
  55. data/lib/astronoby/duration.rb +116 -0
  56. data/lib/astronoby/earth_rotation.rb +70 -0
  57. data/lib/astronoby/equinox_solstice.rb +31 -8
  58. data/lib/astronoby/errors.rb +11 -0
  59. data/lib/astronoby/events/altitude_crossing.rb +29 -0
  60. data/lib/astronoby/events/altitude_crossing_calculator.rb +511 -0
  61. data/lib/astronoby/events/altitude_extreme.rb +31 -0
  62. data/lib/astronoby/events/conjunction.rb +51 -0
  63. data/lib/astronoby/events/conjunction_opposition_calculator.rb +84 -0
  64. data/lib/astronoby/events/extremum_calculator.rb +23 -176
  65. data/lib/astronoby/events/greatest_elongation.rb +58 -0
  66. data/lib/astronoby/events/greatest_elongation_calculator.rb +56 -0
  67. data/lib/astronoby/events/lunar_eclipse.rb +111 -0
  68. data/lib/astronoby/events/lunar_eclipse_calculator.rb +397 -0
  69. data/lib/astronoby/events/lunar_eclipse_geometry.rb +115 -0
  70. data/lib/astronoby/events/lunar_eclipse_phase.rb +45 -0
  71. data/lib/astronoby/events/lunar_eclipse_visibility.rb +296 -0
  72. data/lib/astronoby/events/opposition.rb +19 -0
  73. data/lib/astronoby/events/rise_transit_set_calculator.rb +9 -13
  74. data/lib/astronoby/events/rise_transit_set_event.rb +12 -1
  75. data/lib/astronoby/events/rise_transit_set_events.rb +12 -1
  76. data/lib/astronoby/events/twilight_calculator.rb +37 -147
  77. data/lib/astronoby/events/twilight_event.rb +24 -6
  78. data/lib/astronoby/events/twilight_events.rb +26 -6
  79. data/lib/astronoby/extremum_finder.rb +148 -0
  80. data/lib/astronoby/horizon.rb +40 -0
  81. data/lib/astronoby/horizontal_transformation.rb +112 -0
  82. data/lib/astronoby/instant.rb +56 -16
  83. data/lib/astronoby/libration.rb +25 -0
  84. data/lib/astronoby/mean_obliquity.rb +8 -0
  85. data/lib/astronoby/moon_orientation_ephemeris.rb +69 -0
  86. data/lib/astronoby/moon_physical_ephemeris.rb +263 -0
  87. data/lib/astronoby/nutation.rb +10 -20
  88. data/lib/astronoby/observer.rb +67 -49
  89. data/lib/astronoby/orientation.rb +107 -0
  90. data/lib/astronoby/position.rb +16 -0
  91. data/lib/astronoby/precession.rb +62 -61
  92. data/lib/astronoby/reference_frame.rb +80 -9
  93. data/lib/astronoby/reference_frames/apparent.rb +43 -12
  94. data/lib/astronoby/reference_frames/astrometric.rb +14 -1
  95. data/lib/astronoby/reference_frames/geometric.rb +7 -1
  96. data/lib/astronoby/reference_frames/mean_of_date.rb +19 -1
  97. data/lib/astronoby/reference_frames/teme.rb +159 -0
  98. data/lib/astronoby/reference_frames/topocentric.rb +36 -4
  99. data/lib/astronoby/refraction.rb +26 -5
  100. data/lib/astronoby/root_finder.rb +90 -0
  101. data/lib/astronoby/rotation.rb +49 -0
  102. data/lib/astronoby/time/greenwich_apparent_sidereal_time.rb +19 -8
  103. data/lib/astronoby/time/greenwich_mean_sidereal_time.rb +44 -5
  104. data/lib/astronoby/time/greenwich_sidereal_time.rb +21 -0
  105. data/lib/astronoby/time/local_apparent_sidereal_time.rb +21 -0
  106. data/lib/astronoby/time/local_mean_sidereal_time.rb +21 -0
  107. data/lib/astronoby/time/local_sidereal_time.rb +24 -0
  108. data/lib/astronoby/time/sidereal_time.rb +23 -1
  109. data/lib/astronoby/true_obliquity.rb +4 -0
  110. data/lib/astronoby/util/maths.rb +8 -0
  111. data/lib/astronoby/util/time.rb +0 -497
  112. data/lib/astronoby/vector.rb +10 -0
  113. data/lib/astronoby/velocity.rb +39 -0
  114. data/lib/astronoby/version.rb +1 -1
  115. data/lib/astronoby.rb +30 -0
  116. metadata +80 -5
@@ -0,0 +1,87 @@
1
+ # Lunar Observation
2
+
3
+ Beyond phases and apsides, Astronoby exposes the quantities lunar observers rely on to describe the Moon's appearance and orientation in the sky: its libration, the position angle of its rotation axis, the position angle of its bright limb, and the parallactic angle. By default they follow Jean Meeus, *Astronomical Algorithms*, 2nd edition, and are derived from the Moon's and the Sun's apparent positions, which Astronoby already computes from the ephemeris. The libration and the position angle of the axis can additionally use the integrated lunar orientation from a JPL DE kernel to reach arcsecond accuracy, as described below.
4
+
5
+ All of these are available directly on a `Astronoby::Moon` instance.
6
+
7
+ ```rb
8
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
9
+ instant = Astronoby::Instant.from_time(Time.utc(2025, 3, 1))
10
+ moon = Astronoby::Moon.new(instant: instant, ephem: ephem)
11
+ ```
12
+
13
+ ## Libration
14
+
15
+ Libration is the apparent oscillation that lets an observer on Earth see slightly more than half of the lunar surface over time. `Astronoby::Moon#libration` returns a `Astronoby::Libration` value object holding the total (optical plus physical) libration in longitude and latitude, both as `Astronoby::Angle` objects. A positive longitude exposes more of the Moon's eastern limb (Mare Crisium side); a positive latitude exposes more of its northern limb.
16
+
17
+ ```rb
18
+ libration = moon.libration
19
+
20
+ libration.longitude.str(:dms) # => "-2° 5′ 4.4219″"
21
+ libration.latitude.str(:dms) # => "+0° 28′ 45.9616″"
22
+ ```
23
+
24
+ By default this is the analytic optical plus physical libration from Meeus's series. Compared with the lunar orientation integrated in the JPL DE solution (as reported by JPL Horizons), the libration in longitude and the position angle of the axis agree to within a few arcseconds, while the libration in latitude agrees to about 0.02 degrees. That residual is the well known difference between the mean lunar equator used by the classical Meeus method and the DE mean-Earth frame. It is eliminated by loading an orientation kernel.
25
+
26
+ ## Arcsecond accuracy with an orientation kernel
27
+
28
+ For the libration and the position angle of the axis, you can reach the accuracy of JPL Horizons, Skyfield and IMCCE by also loading a binary PCK lunar orientation kernel, such as `moon_pa_de440_200625.bpc`, which carries the integrated DE440 orientation of the Moon. Load it once with `Astronoby::Orientation` and pass it to the Moon alongside the ephemeris.
29
+
30
+ ```rb
31
+ ephem = Astronoby::Ephem.load("de440.bsp")
32
+ orientation = Astronoby::Orientation.load("moon_pa_de440_200625.bpc")
33
+
34
+ moon = Astronoby::Moon.new(instant: instant, ephem: ephem, orientation: orientation)
35
+
36
+ moon.libration.latitude.str(:dms) # => "+0° 27′ 27.7258″"
37
+ moon.position_angle_of_axis.str(:dms) # => "-21° 48′ 32.9625″"
38
+ ```
39
+
40
+ The libration is then computed as the selenographic longitude and latitude of the sub-Earth point in the Moon's mean-Earth body-fixed frame, and matches JPL Horizons to better than an arcsecond. Without an orientation kernel the Moon keeps using the analytic method, so this is an opt-in refinement, not a requirement. The `bright_limb_position_angle` and `parallactic_angle` are unaffected and do not need a kernel.
41
+
42
+ If you do not already have the kernel, Astronoby can download it for you.
43
+
44
+ ```rb
45
+ Astronoby::Orientation.download(
46
+ name: "moon_pa_de440_200625.bpc",
47
+ target: "moon_pa_de440_200625.bpc"
48
+ )
49
+ ```
50
+
51
+ ## Position angle of the axis
52
+
53
+ `Astronoby::Moon#position_angle_of_axis` returns the position angle of the Moon's axis of rotation, the angle of the projection of the lunar north pole, measured eastward from the north point of the disk. It is returned as an `Astronoby::Angle`.
54
+
55
+ ```rb
56
+ moon.position_angle_of_axis.str(:dms) # => "-21° 48′ 46.657″"
57
+ ```
58
+
59
+ ## Position angle of the bright limb
60
+
61
+ `Astronoby::Moon#bright_limb_position_angle` returns the position angle of the midpoint of the illuminated limb, measured eastward from the north point of the disk, between 0 and 360 degrees. It is a geocentric quantity computed from the apparent equatorial coordinates of the Moon and the Sun, and it points towards the Sun, since the bright limb is the limb facing the Sun.
62
+
63
+ ```rb
64
+ moon.bright_limb_position_angle.str(:dms) # => "+248° 1′ 57.9467″"
65
+ ```
66
+
67
+ ## Parallactic angle
68
+
69
+ The parallactic angle is the angle at the Moon between the direction of the north celestial pole and the direction of the observer's zenith. Unlike the previous quantities it depends on the observer, so it is computed from the topocentric place, where lunar parallax is significant. `Astronoby::Moon#parallactic_angle` takes an observer and returns an `Astronoby::Angle`.
70
+
71
+ ```rb
72
+ observer = Astronoby::Observer.new(
73
+ latitude: Astronoby::Angle.from_degrees(48.8566),
74
+ longitude: Astronoby::Angle.from_degrees(2.3522)
75
+ )
76
+
77
+ moon.parallactic_angle(observer: observer).str(:dms) # => "+11° 42′ 47.1246″"
78
+ ```
79
+
80
+ ## See also
81
+
82
+ - [Solar System Bodies](solar_system_bodies.md) - for the Moon's other properties
83
+ - [Moon Phases](moon_phases.md) - for lunar phase events
84
+ - [Lunar Eclipses](lunar_eclipses.md) - for eclipses of the Moon
85
+ - [Observer](observer.md) - for setting up observation locations
86
+ - [Reference Frames](reference_frames.md) - for coordinate systems
87
+ - [Angles](angles.md) - for working with angular measurements
data/docs/moon_phases.md CHANGED
@@ -69,11 +69,14 @@ phases.each { puts "#{_1.phase}: #{_1.time}" }
69
69
  # new_moon: 2024-05-08 03:21:56 UTC
70
70
  # first_quarter: 2024-05-15 11:48:02 UTC
71
71
  # full_moon: 2024-05-23 13:53:12 UTC
72
- # last_quarter: 2024-05-30 17:12:43 UTC
72
+ # last_quarter: 2024-05-30 17:12:42 UTC
73
73
  ```
74
74
 
75
75
  ## See also
76
+
76
77
  - [Twilight Times](twilight_times.md) - for sun-related events
77
78
  - [Rise, Transit and Set Times](rise_transit_set_times.md) - for moon events
79
+ - [Lunar Observation](lunar_observation.md) - for libration, axis and limb angles
80
+ - [Lunar Eclipses](lunar_eclipses.md) - for eclipses of the Moon
78
81
  - [Solar System Bodies](solar_system_bodies.md) - for moon object details
79
82
  - [Ephemerides](ephem.md) - for data sources
data/docs/observer.md CHANGED
@@ -6,19 +6,31 @@ events computed by Astronoby are location and date based.
6
6
  ## Initialization
7
7
 
8
8
  The two required key arguments to instantiate an observer are:
9
- * `latitude` (`Astronoby::Angle`): the angle from the equator to the observer,
9
+
10
+ - `latitude` (`Astronoby::Angle`): the angle from the equator to the observer,
10
11
  from 90° to -90°, with positive angles for the Northern Hemisphere.
11
- * `longitude` (`Astronoby::Angle`): the angle from the Greenwich meridian to the
12
+ - `longitude` (`Astronoby::Angle`): the angle from the Greenwich meridian to the
12
13
  observer, from 180° to -180°, with positive angles eastward of the Greenwich
13
14
  meridian.
14
15
 
15
- Latitude and longitude are defined according to the [World Geodetic System].
16
- In other words, they are the same as those used for the [GPS].
16
+ Latitude and longitude are defined according to the [World Geodetic System]
17
+ (WGS-84). In other words, they are the same as those used for the [GPS].
18
+
19
+ Internally, the observer's geodetic coordinates are converted to geocentric
20
+ Cartesian coordinates in the **International Terrestrial Reference System
21
+ (ITRS)**, also known as Earth-Centered Earth-Fixed (ECEF). These are then
22
+ rotated into the celestial frame using Earth rotation (GAST) via
23
+ `Astronoby::EarthRotation` and IERS polar motion corrections. See
24
+ [Reference Frames](reference_frames.md) for details.
25
+
26
+ The reverse conversion (ECEF → geodetic) is available via
27
+ [`Astronoby::Coordinates::Geodetic.from_ecef`][Coordinates page].
17
28
 
18
29
  It is also possible to give the following optional key arguments:
19
- * `elevation` (`Astronoby::Distance`): the distance above or below the average
30
+
31
+ - `elevation` (`Astronoby::Distance`): the distance above or below the average
20
32
  sea level
21
- * `utc_offset`: local time difference with UTC. Check the [timezone specifiers]
33
+ - `utc_offset`: local time difference with UTC. Check the [timezone specifiers]
22
34
  for the format.
23
35
 
24
36
  ```rb
@@ -57,8 +69,10 @@ observer1 == observer2
57
69
  [GPS]: https://en.wikipedia.org/wiki/GPS
58
70
  [timezone specifiers]: https://ruby-doc.org/3.4.1/Time.html#class-Time-label-Timezone+Specifiers
59
71
  [Angles page]: angles.md
72
+ [Coordinates page]: coordinates.md#geodetic
60
73
 
61
74
  ## See also
75
+
62
76
  - [Angles](angles.md) - for working with latitude and longitude
63
77
  - [Coordinates](coordinates.md) - for understanding position systems
64
78
  - [Reference Frames](reference_frames.md) - for topocentric calculations
@@ -0,0 +1,78 @@
1
+ # Planetary Phenomena
2
+
3
+ Astronoby computes the classic planetary phenomena relative to the Sun: conjunctions, oppositions, and greatest elongations. They are all defined in the apparent geocentric ecliptic frame of date.
4
+
5
+ The available phenomena depend on the planet. Inferior planets (Mercury and Venus) have conjunctions and greatest elongations. Superior planets (Mars to Neptune) have conjunctions and oppositions. Requesting a phenomenon that cannot occur for a given body raises an `Astronoby::UnsupportedEventError`.
6
+
7
+ Each method takes an ephemeris and a time range, and returns the events found within that range.
8
+
9
+ ## Oppositions
10
+
11
+ An opposition is the instant when a superior planet and the Sun have opposite apparent ecliptic longitudes (a difference of 180 degrees), so the planet is opposite the Sun in the sky.
12
+
13
+ ```rb
14
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
15
+
16
+ opposition = Astronoby::Mars.opposition_events(
17
+ ephem: ephem,
18
+ start_time: Time.utc(2025, 1, 1),
19
+ end_time: Time.utc(2026, 1, 1)
20
+ ).first
21
+
22
+ opposition.instant.to_time # => 2025-01-16 02:38:35 UTC
23
+ opposition.body # => Astronoby::Mars
24
+ ```
25
+
26
+ ## Conjunctions
27
+
28
+ A conjunction is the instant when a planet and the Sun share the same apparent ecliptic longitude. Each conjunction is classified as inferior (the planet passes between the Earth and the Sun) or superior (the planet passes behind the Sun), determined from the apparent distances. Superior planets only ever have superior conjunctions.
29
+
30
+ ```rb
31
+ conjunction = Astronoby::Venus.conjunction_events(
32
+ ephem: ephem,
33
+ start_time: Time.utc(2025, 1, 1),
34
+ end_time: Time.utc(2026, 1, 1)
35
+ ).first
36
+
37
+ conjunction.instant.to_time # => 2025-03-23 01:07:30 UTC
38
+ conjunction.inferior? # => true
39
+ conjunction.superior? # => false
40
+ ```
41
+
42
+ ## Greatest elongations
43
+
44
+ A greatest elongation is the instant when an inferior planet reaches its maximum apparent angular separation from the Sun. It is eastern when the planet is east of the Sun (visible in the evening sky) and western when it is west of the Sun (visible in the morning sky).
45
+
46
+ ```rb
47
+ elongation = Astronoby::Mercury.greatest_elongation_events(
48
+ ephem: ephem,
49
+ start_time: Time.utc(2025, 1, 1),
50
+ end_time: Time.utc(2026, 1, 1)
51
+ ).first
52
+
53
+ elongation.instant.to_time # => 2025-03-08 06:09:18 UTC
54
+ elongation.angle.degrees # => 18.25
55
+ elongation.eastern? # => true
56
+ elongation.western? # => false
57
+ ```
58
+
59
+ ## Elongation of a body
60
+
61
+ The elongation of any Solar System body, the apparent Sun-Earth-body angle, is also available directly on a body instance, along with the side of the Sun it lies on.
62
+
63
+ ```rb
64
+ instant = Astronoby::Instant.from_time(Time.utc(2025, 7, 14))
65
+ venus = Astronoby::Venus.new(instant: instant, ephem: ephem)
66
+
67
+ venus.elongation.degrees # => 41.47
68
+ venus.eastern? # => false
69
+ venus.western? # => true
70
+ ```
71
+
72
+ ## See also
73
+
74
+ - [Solar System Bodies](solar_system_bodies.md) - for planets and their properties
75
+ - [Equinoxes and Solstices Times](equinoxes_solstices_times.md) - for solar events
76
+ - [Moon Phases](moon_phases.md) - for lunar events
77
+ - [Reference Frames](reference_frames.md) - for coordinate systems
78
+ - [Ephemerides](ephem.md) - for data sources
@@ -3,28 +3,42 @@
3
3
  A given body at a given time can be perceived at different positions, depending
4
4
  on the reference frame and the corrections applied.
5
5
 
6
- Astronoby provides five reference frames for each celestial body:
6
+ Astronoby provides five reference frames for celestial bodies, forming a
7
+ transformation chain:
7
8
 
8
- * Geometric
9
- * Astrometric
10
- * Mean of date
11
- * Apparent
12
- * Topocentric
9
+ ```
10
+ Geometric (BCRS)
11
+ → Astrometric (GCRS)
12
+ → Mean of date
13
+ → Apparent
14
+ → Topocentric
15
+ ```
16
+
17
+ Each step applies physical corrections to move closer to what an observer
18
+ actually sees. The standard reference systems used follow the IAU/IERS
19
+ conventions (see [Standard reference systems](#standard-reference-systems)
20
+ below).
21
+
22
+ A separate **TEME** (True Equator, Mean Equinox) frame is also available for
23
+ satellite tracking. See [TEME](#teme) below.
13
24
 
14
25
  All reference frames provide this common interface:
15
26
 
16
- * `#position`: Vector of position as x,y,z `Astronoby::Distance` objects
17
- * `#velocity`: Vector of velocity as x,y,z `Astronoby::Velocity` objects
18
- * `#distance`: Distance from the centre (`Astronoby::Distance`)
19
- * `#equatorial`: Equatorial coordinates (`Astronoby::Coordinates::Equatorial`)
20
- * `#ecliptic`: Ecliptic coordinates (`Astronoby::Coordinates::Ecliptic`)
27
+ - `#position`: Vector of position as x,y,z `Astronoby::Distance` objects
28
+ - `#velocity`: Vector of velocity as x,y,z `Astronoby::Velocity` objects
29
+ - `#distance`: Distance from the centre (`Astronoby::Distance`)
30
+ - `#center`: The centre the frame is relative to (`Astronoby::Center`)
31
+ - `#equatorial`: Equatorial coordinates (`Astronoby::Coordinates::Equatorial`)
32
+ - `#ecliptic`: Ecliptic coordinates (`Astronoby::Coordinates::Ecliptic`)
33
+ - `#separation_from(other)`: Angular separation to another frame
34
+ (`Astronoby::Angle`)
21
35
 
22
36
  ## Geometric
23
37
 
24
- Also called "mean J2000", this reference frame is related to the mean ecliptic
25
- or terrestrial equator and the mean equinox of the reference date (J2000). It is
26
- the strict position computed from the ephemeris file in a reference frame
27
- centered on the Solar System barycentre, with no corrections applied.
38
+ The geometric frame corresponds to the **Barycentric Celestial Reference System
39
+ (BCRS)**, centered on the Solar System barycentre, with axes aligned to the
40
+ **International Celestial Reference System (ICRS)**. It is the raw position
41
+ read from the ephemeris file, with no corrections applied.
28
42
 
29
43
  ```rb
30
44
  ephem = Astronoby::Ephem.load("inpop19a.bsp")
@@ -36,7 +50,7 @@ geometric = moon.geometric
36
50
  # => #<Astronoby::Geometric:0x000000011e7ffd40
37
51
 
38
52
  geometric.distance.au
39
- # => 1.0095091198501744
53
+ # => 1.0095091267969827
40
54
 
41
55
  geometric.equatorial.right_ascension.str(:hms, precision: 0)
42
56
  # => "20h 13m 52s"
@@ -44,11 +58,11 @@ geometric.equatorial.right_ascension.str(:hms, precision: 0)
44
58
 
45
59
  ## Astrometric
46
60
 
47
- Also called "astrometric J2000", this reference frame is related to the ecliptic
48
- or the mean terrestrial equator and the mean equinox of the reference date
49
- (J2000). It applies light-time correction between the celestial body and the
50
- observer. The frame is centred on the Earth's centre, as are all the following
51
- reference frames.
61
+ The astrometric frame corresponds to the **Geocentric Celestial Reference System
62
+ (GCRS)**, centered on the Earth's centre, with axes still aligned to the ICRS.
63
+ It is obtained by subtracting the Earth's barycentric position from the target's
64
+ light-time-corrected barycentric position. All subsequent frames are also
65
+ Earth-centered.
52
66
 
53
67
  ```rb
54
68
  ephem = Astronoby::Ephem.load("inpop19a.bsp")
@@ -67,10 +81,13 @@ astrometric.equatorial.right_ascension.str(:hms, precision: 0)
67
81
 
68
82
  ## Mean of date
69
83
 
70
- This reference frame is related to the ecliptic or the mean equator and the mean
71
- equinox of the date. It provides the geometric position corrected for the
72
- precessional motion of the Earth's rotation axis (precession and nutation).
84
+ This frame is referred to the **mean equator and mean equinox of the date**. It
85
+ is obtained by applying the precession-bias matrix to the geocentric ICRS
86
+ position, rotating it from the ICRS axes to the mean pole and equinox at the
87
+ given instant.
73
88
 
89
+ Astronoby uses the **IAU 2006 precession** model in its Fukushima-Williams
90
+ four-angle parameterization, which includes the ICRS-to-J2000 frame bias.
74
91
 
75
92
  ```rb
76
93
  ephem = Astronoby::Ephem.load("inpop19a.bsp")
@@ -86,11 +103,17 @@ mean_of_date.equatorial.right_ascension.str(:hms, precision: 0)
86
103
 
87
104
  ## Apparent
88
105
 
89
- This reference frame is related to the true ecliptic or equator and the true
90
- equinox of the date. It is the actual position in the sky of a celestial object
91
- as seen from the centre of the Earth. It applies several corrections to the
92
- astrometric position:the deflection of light, the aberration, the precession and
93
- the nutation.
106
+ This frame is referred to the **true equator and true equinox of the date**. It
107
+ represents the position of a celestial object as seen from the centre of the
108
+ Earth. The following corrections are applied to the astrometric position:
109
+
110
+ - **Aberration**: relativistic stellar aberration due to Earth's velocity
111
+ (Explanatory Supplement, Ch. 7.2.3)
112
+ - **Precession**: IAU 2006 Fukushima-Williams precession-bias matrix
113
+ - **Nutation**: IAU 2000B 77-term nutation model
114
+
115
+ The combined N \* PB matrix rotates the aberration-corrected GCRS position into
116
+ the true equator and equinox frame of the date.
94
117
 
95
118
  ```rb
96
119
  ephem = Astronoby::Ephem.load("inpop19a.bsp")
@@ -106,10 +129,16 @@ apparent.equatorial.right_ascension.str(:hms, precision: 0)
106
129
 
107
130
  ## Topocentric
108
131
 
109
- This reference frame is the final transformation of a position. It provides the
110
- apparent position of a celestial body as seen from a location on Earth. It can
111
- only be produced given an observer (`Astronoby::Observer`). It provides another
112
- set of coordinates: horizontal (`Astronoby::Coordinates::Horizontal`).
132
+ This is the final frame in the chain, providing the position of a celestial body
133
+ as seen from a specific location on Earth's surface. It can only be produced
134
+ given an observer (`Astronoby::Observer`). It provides an additional set of
135
+ coordinates: horizontal (`Astronoby::Coordinates::Horizontal`).
136
+
137
+ To go from the apparent frame (true equator and equinox) to the topocentric
138
+ frame, the observer's position must be transformed from the **International
139
+ Terrestrial Reference System (ITRS)** into the same true-equinox frame. This is
140
+ done by applying the **IERS polar motion matrix** (W) followed by the Earth
141
+ rotation via **Greenwich Apparent Sidereal Time (GAST)**.
113
142
 
114
143
  ```rb
115
144
  ephem = Astronoby::Ephem.load("inpop19a.bsp")
@@ -124,12 +153,141 @@ moon = Astronoby::Moon.new(ephem: ephem, instant: instant)
124
153
  topocentric = moon.observed_by(observer)
125
154
 
126
155
  topocentric.horizontal.azimuth.str(:dms, precision: 0)
127
- # => "+90° 14′ 19″"
156
+ # => "+90° 14′ 24″"
128
157
  ```
129
158
 
130
159
  You can learn more about observers on the [Observer page].
131
160
 
161
+ ## Angular separation
162
+
163
+ Any two frames can be compared with `#separation_from`, which returns the
164
+ angular separation (an `Astronoby::Angle`, between 0° and 180°) between the two
165
+ targets' directions as seen from their shared centre.
166
+
167
+ The two frames may belong to different kinds of objects (a Solar System body
168
+ and a deep-sky object, for example) as long as they are in the same reference
169
+ frame and at the same instant. Otherwise the separation is not physically
170
+ meaningful and an `Astronoby::IncompatibleArgumentsError` is raised.
171
+
172
+ ```rb
173
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
174
+ instant = Astronoby::Instant.from_time(Time.utc(2020, 12, 21, 18))
175
+
176
+ jupiter = Astronoby::Jupiter.new(ephem: ephem, instant: instant)
177
+ saturn = Astronoby::Saturn.new(ephem: ephem, instant: instant)
178
+
179
+ # The 2020 "Great Conjunction", the closest since 1623
180
+ separation = jupiter.apparent.separation_from(saturn.apparent)
181
+
182
+ separation.str(:dms, precision: 0)
183
+ # => "+0° 6′ 6″"
184
+ ```
185
+
186
+ ## TEME
187
+
188
+ The **TEME (True Equator, Mean Equinox)** frame is the output frame of the
189
+ SGP4/SDP4 satellite orbit propagators. Unlike the five frames above, TEME is
190
+ not part of the celestial body chain — it is constructed directly from SGP4
191
+ output and provides its own conversion methods.
192
+
193
+ TEME shares the true equator with the apparent frame but uses the mean equinox
194
+ (tracked by GMST) instead of the true equinox (tracked by GAST). The
195
+ difference between the two is the equation of the equinoxes.
196
+
197
+ ### Conversions
198
+
199
+ `Astronoby::Teme` provides three conversion methods:
200
+
201
+ - `#to_ecef` - ECEF position and velocity, using R₃(GMST) and the ω×r
202
+ velocity correction. The returned `EcefCoordinates` object also provides
203
+ a `#geodetic` method to convert to WGS-84 geodetic coordinates
204
+ (`Astronoby::Coordinates::Geodetic`)
205
+ - `#to_gcrs` - GCRS position and velocity (returns an `Astronoby::Astrometric`
206
+ frame), using the transposed precession and nutation matrices
207
+ - `#observed_by(observer)` - topocentric position as seen from a specific
208
+ observer (returns an `Astronoby::Topocentric` frame)
209
+
210
+ ```rb
211
+ instant = Astronoby::Instant.from_time(Time.utc(2025, 6, 15, 12))
212
+
213
+ # Construct from SGP4 output
214
+ teme = Astronoby::Teme.new(
215
+ position: Astronoby::Distance.vector_from_meters(
216
+ [4_154_639.35, 768_141.62, -5_327_440.29]
217
+ ),
218
+ velocity: Astronoby::Velocity.vector_from_mps(
219
+ [-1152.15, 7561.14, 192.76]
220
+ ),
221
+ instant: instant
222
+ )
223
+
224
+ # Convert to ECEF
225
+ ecef = teme.to_ecef
226
+ ecef.position[0].km
227
+ # => 1196.49...
228
+
229
+ # Convert ECEF to geodetic (WGS-84)
230
+ geodetic = ecef.geodetic
231
+ geodetic.latitude.str(:dms)
232
+ # => "+10° 29′ 51.7526″"
233
+
234
+ # Convert to GCRS
235
+ gcrs = teme.to_gcrs
236
+ gcrs.equatorial.right_ascension.str(:hms, precision: 0)
237
+ # => "0h 40m 42s"
238
+
239
+ # Observe from a location
240
+ observer = Astronoby::Observer.new(
241
+ latitude: Astronoby::Angle.from_degrees(48.8566),
242
+ longitude: Astronoby::Angle.from_degrees(2.3522)
243
+ )
244
+ topocentric = teme.observed_by(observer)
245
+ topocentric.horizontal.azimuth.str(:dms, precision: 0)
246
+ # => "+223° 53′ 41″"
247
+ ```
248
+
249
+ ## Earth rotation
250
+
251
+ `Astronoby::EarthRotation` provides standalone Earth rotation matrices:
252
+
253
+ - `EarthRotation.matrix_for(instant)` — R₃(GAST), the apparent rotation
254
+ matrix (used for observer placement in the true-equinox frame)
255
+ - `EarthRotation.mean_matrix_for(instant)` — R₃(GMST), the mean rotation
256
+ matrix (used for TEME ↔ ECEF conversions)
257
+
258
+ Both return 3×3 orthogonal rotation matrices that transform ECEF coordinates
259
+ into the corresponding celestial frame.
260
+
261
+ ## Standard reference systems
262
+
263
+ The table below maps Astronoby's reference frames to standard IAU/IERS
264
+ reference systems and lists the corrections applied at each step.
265
+
266
+ | Astronoby frame | Standard system | Origin | Axes | Corrections applied |
267
+ | --------------- | --------------- | ----------------------- | ----------------------------------- | ----------------------------------------------------------------------- |
268
+ | Geometric | BCRS | Solar System barycentre | ICRS | None (raw ephemeris) |
269
+ | Astrometric | GCRS | Earth centre | ICRS | Light-time, origin shift |
270
+ | Mean of date | — | Earth centre | Mean equator & equinox of date | IAU 2006 precession (Fukushima-Williams, incl. frame bias) |
271
+ | Apparent | — | Earth centre | True equator & equinox of date | Aberration, precession, IAU 2000B nutation |
272
+ | Topocentric | — | Observer | True equator & equinox of date | GAST earth rotation, IERS polar motion, observer position (WGS-84/ITRS) |
273
+ | TEME | — | Earth centre | True equator & mean equinox of date | SGP4/SDP4 output frame; converts to ECEF, GCRS, or topocentric |
274
+
275
+ ### Models and references
276
+
277
+ - **Precession**: IAU 2006 P03, Fukushima-Williams parameterization including
278
+ ICRS frame bias (IERS Conventions 2010, Section 5.6.4; ERFA `eraPfw06` /
279
+ `eraFw2m`)
280
+ - **Nutation**: IAU 2000B, 77-term truncation of the IAU 2000A model (IERS
281
+ Conventions 2010, Section 5.5.2)
282
+ - **Aberration**: Relativistic stellar aberration (Explanatory Supplement to the
283
+ Astronomical Almanac, Ch. 7.2.3)
284
+ - **Earth rotation**: Greenwich Apparent Sidereal Time (GAST) computed as GMST +
285
+ equation of equinoxes (IERS Conventions 2010, Section 5.5.7)
286
+ - **Polar motion**: IERS Earth Orientation Parameters via the `iers` gem
287
+ - **Observer coordinates**: WGS-84 geodetic to geocentric (ITRS/ECEF) conversion
288
+
132
289
  ## See also
290
+
133
291
  - [Coordinates](coordinates.md) - for understanding coordinate systems
134
292
  - [Observer](observer.md) - for location setup
135
293
  - [Solar System Bodies](solar_system_bodies.md) - for object positions
@@ -10,10 +10,11 @@ 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` or `Astronoby::DeepSkyObject`): any
13
+
14
+ - `body` (`Astronoby::SolarSystemBody` or `Astronoby::DeepSkyObject`): any
14
15
  supported celestial body, e.g. `Astronoby::Sun`
15
- * `observer` (`Astronoby::Observer`): location on Earth of the observer
16
- * `ephem`: ephemeris to provide the initial raw data
16
+ - `observer` (`Astronoby::Observer`): location on Earth of the observer
17
+ - `ephem`: ephemeris to provide the initial raw data
17
18
 
18
19
  You can learn more about [Solar System bodies] and [ephemerides].
19
20
 
@@ -56,7 +57,7 @@ events.transit_times
56
57
  # => [2025-05-01 07:21:34 UTC, 2025-05-02 07:18:01 UTC]
57
58
 
58
59
  events.setting_times
59
- # => [2025-05-01 13:14:24 UTC, 2025-05-02 13:10:59 UTC]
60
+ # => [2025-05-01 13:14:24 UTC, 2025-05-02 13:10:58 UTC]
60
61
  ```
61
62
 
62
63
  ## `#events_on`
@@ -113,6 +114,7 @@ event.setting_time.localtime(utc_offset)
113
114
  [ephemerides]: ephem.md
114
115
 
115
116
  ## See also
117
+
116
118
  - [Twilight Times](twilight_times.md) - for sun-related events
117
119
  - [Solar System Bodies](solar_system_bodies.md) - for object information
118
120
  - [Observer](observer.md) - for location setup
@@ -2,9 +2,10 @@
2
2
 
3
3
  Currently, Astronoby only supports the following major bodies of the Solar
4
4
  System:
5
- * the Sun (`Astronoby::Sun`)
6
- * planets from Mercury to Neptune, including the Earth (`Astronoby::Earth`, ...)
7
- * the Moon (`Astronoby::Moon`)
5
+
6
+ - the Sun (`Astronoby::Sun`)
7
+ - planets from Mercury to Neptune, including the Earth (`Astronoby::Earth`, ...)
8
+ - the Moon (`Astronoby::Moon`)
8
9
 
9
10
  Given an ephemeris (`Astronoby::Ephem`) and an instant object
10
11
  (`Astronoby::Instant`), these classes enable you to get instances which provide
@@ -21,7 +22,7 @@ venus = Astronoby::Venus.new(ephem: ephem, instant: instant)
21
22
  apparent_position = venus.apparent.position
22
23
 
23
24
  apparent_position.x.km.round
24
- # => -148794622
25
+ # => -148794597
25
26
  ```
26
27
 
27
28
  Each of these bodies also provides its own equatorial radius
@@ -60,6 +61,26 @@ venus.constellation.abbreviation
60
61
  # => "Cnc"
61
62
  ```
62
63
 
64
+ ### `#elongation`
65
+
66
+ Apparent angular distance between the body and the Sun, as seen from Earth
67
+ ("Sun-Earth-object" angle). Returns a `Astronoby::Angle` object.
68
+
69
+ ```rb
70
+ venus.elongation.degrees.round
71
+ # => 41
72
+ ```
73
+
74
+ ### `#eastern?` and `#western?`
75
+
76
+ Whether the body lies east of the Sun (visible in the evening) or west of it
77
+ (visible in the morning). Both return a `Boolean`.
78
+
79
+ ```rb
80
+ venus.eastern? # => false
81
+ venus.western? # => true
82
+ ```
83
+
63
84
  ### `#phase_angle`
64
85
 
65
86
  "Sun-object-Earth" angle. Returns a `Astronoby::Angle` object.
@@ -101,6 +122,7 @@ Astronoby::Venus.absolute_magnitude
101
122
  [reference frames]: reference_frames.md
102
123
 
103
124
  ## See also
125
+
104
126
  - [Ephemerides](ephem.md) - for understanding data sources
105
127
  - [Reference Frames](reference_frames.md) - for coordinate systems
106
128
  - [Observer](observer.md) - for setting up observation locations