astronoby 0.7.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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +145 -3
  4. data/README.md +59 -33
  5. data/UPGRADING.md +75 -21
  6. data/docs/README.md +224 -0
  7. data/docs/angles.md +137 -0
  8. data/docs/configuration.md +98 -0
  9. data/docs/coordinates.md +167 -0
  10. data/docs/deep_sky_bodies.md +101 -0
  11. data/docs/ephem.md +85 -0
  12. data/docs/equinoxes_solstices_times.md +31 -0
  13. data/docs/glossary.md +152 -0
  14. data/docs/instant.md +139 -0
  15. data/docs/moon_phases.md +79 -0
  16. data/docs/observer.md +65 -0
  17. data/docs/reference_frames.md +138 -0
  18. data/docs/rise_transit_set_times.md +119 -0
  19. data/docs/solar_system_bodies.md +107 -0
  20. data/docs/twilight_times.md +123 -0
  21. data/lib/astronoby/angle.rb +6 -2
  22. data/lib/astronoby/angular_velocity.rb +76 -0
  23. data/lib/astronoby/bodies/deep_sky_object.rb +44 -0
  24. data/lib/astronoby/bodies/deep_sky_object_position.rb +127 -0
  25. data/lib/astronoby/bodies/earth.rb +12 -2
  26. data/lib/astronoby/bodies/jupiter.rb +17 -0
  27. data/lib/astronoby/bodies/mars.rb +17 -0
  28. data/lib/astronoby/bodies/mercury.rb +21 -0
  29. data/lib/astronoby/bodies/moon.rb +50 -36
  30. data/lib/astronoby/bodies/neptune.rb +21 -0
  31. data/lib/astronoby/bodies/saturn.rb +26 -0
  32. data/lib/astronoby/bodies/solar_system_body.rb +162 -27
  33. data/lib/astronoby/bodies/sun.rb +25 -2
  34. data/lib/astronoby/bodies/uranus.rb +5 -0
  35. data/lib/astronoby/bodies/venus.rb +25 -0
  36. data/lib/astronoby/cache.rb +189 -0
  37. data/lib/astronoby/configuration.rb +92 -0
  38. data/lib/astronoby/constants.rb +11 -3
  39. data/lib/astronoby/constellation.rb +12 -0
  40. data/lib/astronoby/constellations/data.rb +42 -0
  41. data/lib/astronoby/constellations/finder.rb +35 -0
  42. data/lib/astronoby/constellations/repository.rb +20 -0
  43. data/lib/astronoby/coordinates/equatorial.rb +5 -8
  44. data/lib/astronoby/data/constellations/constellation_names.dat +88 -0
  45. data/lib/astronoby/data/constellations/indexed_abbreviations.dat +88 -0
  46. data/lib/astronoby/data/constellations/radec_to_index.dat +238 -0
  47. data/lib/astronoby/data/constellations/sorted_declinations.dat +202 -0
  48. data/lib/astronoby/data/constellations/sorted_right_ascensions.dat +237 -0
  49. data/lib/astronoby/distance.rb +6 -0
  50. data/lib/astronoby/equinox_solstice.rb +2 -2
  51. data/lib/astronoby/events/extremum_calculator.rb +233 -0
  52. data/lib/astronoby/events/extremum_event.rb +15 -0
  53. data/lib/astronoby/events/moon_phases.rb +15 -14
  54. data/lib/astronoby/events/rise_transit_set_calculator.rb +39 -12
  55. data/lib/astronoby/events/twilight_calculator.rb +116 -61
  56. data/lib/astronoby/events/twilight_events.rb +28 -0
  57. data/lib/astronoby/instant.rb +34 -6
  58. data/lib/astronoby/julian_date.rb +78 -0
  59. data/lib/astronoby/mean_obliquity.rb +8 -10
  60. data/lib/astronoby/nutation.rb +11 -3
  61. data/lib/astronoby/observer.rb +1 -1
  62. data/lib/astronoby/precession.rb +48 -38
  63. data/lib/astronoby/reference_frame.rb +2 -1
  64. data/lib/astronoby/reference_frames/apparent.rb +1 -11
  65. data/lib/astronoby/reference_frames/mean_of_date.rb +1 -1
  66. data/lib/astronoby/reference_frames/topocentric.rb +2 -12
  67. data/lib/astronoby/stellar_propagation.rb +162 -0
  68. data/lib/astronoby/time/greenwich_apparent_sidereal_time.rb +22 -0
  69. data/lib/astronoby/time/greenwich_mean_sidereal_time.rb +64 -0
  70. data/lib/astronoby/time/greenwich_sidereal_time.rb +20 -58
  71. data/lib/astronoby/time/local_apparent_sidereal_time.rb +42 -0
  72. data/lib/astronoby/time/local_mean_sidereal_time.rb +42 -0
  73. data/lib/astronoby/time/local_sidereal_time.rb +35 -26
  74. data/lib/astronoby/time/sidereal_time.rb +42 -0
  75. data/lib/astronoby/true_obliquity.rb +2 -3
  76. data/lib/astronoby/util/time.rb +62 -44
  77. data/lib/astronoby/velocity.rb +5 -0
  78. data/lib/astronoby/version.rb +1 -1
  79. data/lib/astronoby.rb +19 -1
  80. metadata +71 -11
  81. data/Gemfile +0 -5
  82. data/Gemfile.lock +0 -102
  83. data/benchmark/README.md +0 -131
  84. data/benchmark/benchmark.rb +0 -259
  85. data/benchmark/data/imcce.csv.zip +0 -0
  86. data/benchmark/data/sun_calc.csv.zip +0 -0
  87. data/lib/astronoby/epoch.rb +0 -22
@@ -0,0 +1,138 @@
1
+ # Reference Frames
2
+
3
+ A given body at a given time can be perceived at different positions, depending
4
+ on the reference frame and the corrections applied.
5
+
6
+ Astronoby provides five reference frames for each celestial body:
7
+
8
+ * Geometric
9
+ * Astrometric
10
+ * Mean of date
11
+ * Apparent
12
+ * Topocentric
13
+
14
+ All reference frames provide this common interface:
15
+
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`)
21
+
22
+ ## Geometric
23
+
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.
28
+
29
+ ```rb
30
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
31
+ time = Time.utc(1962, 7, 24)
32
+ instant = Astronoby::Instant.from_time(time)
33
+
34
+ moon = Astronoby::Moon.new(ephem: ephem, instant: instant)
35
+ geometric = moon.geometric
36
+ # => #<Astronoby::Geometric:0x000000011e7ffd40
37
+
38
+ geometric.distance.au
39
+ # => 1.0095091198501744
40
+
41
+ geometric.equatorial.right_ascension.str(:hms, precision: 0)
42
+ # => "20h 13m 52s"
43
+ ```
44
+
45
+ ## Astrometric
46
+
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.
52
+
53
+ ```rb
54
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
55
+ time = Time.utc(1962, 7, 24)
56
+ instant = Astronoby::Instant.from_time(time)
57
+
58
+ moon = Astronoby::Moon.new(ephem: ephem, instant: instant)
59
+ astrometric = moon.astrometric
60
+
61
+ astrometric.distance.km.round
62
+ # => 371187
63
+
64
+ astrometric.equatorial.right_ascension.str(:hms, precision: 0)
65
+ # => "1h 54m 27s"
66
+ ```
67
+
68
+ ## Mean of date
69
+
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).
73
+
74
+
75
+ ```rb
76
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
77
+ time = Time.utc(1962, 7, 24)
78
+ instant = Astronoby::Instant.from_time(time)
79
+
80
+ moon = Astronoby::Moon.new(ephem: ephem, instant: instant)
81
+ mean_of_date = moon.mean_of_date
82
+
83
+ mean_of_date.equatorial.right_ascension.str(:hms, precision: 0)
84
+ # => "1h 52m 29s"
85
+ ```
86
+
87
+ ## Apparent
88
+
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.
94
+
95
+ ```rb
96
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
97
+ time = Time.utc(1962, 7, 24)
98
+ instant = Astronoby::Instant.from_time(time)
99
+
100
+ moon = Astronoby::Moon.new(ephem: ephem, instant: instant)
101
+ apparent = moon.apparent
102
+
103
+ apparent.equatorial.right_ascension.str(:hms, precision: 0)
104
+ # => "1h 52m 28s"
105
+ ```
106
+
107
+ ## Topocentric
108
+
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`).
113
+
114
+ ```rb
115
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
116
+ time = Time.utc(1962, 7, 24)
117
+ instant = Astronoby::Instant.from_time(time)
118
+ observer = Astronoby::Observer.new(
119
+ latitude: Astronoby::Angle.from_degrees(48.838),
120
+ longitude: Astronoby::Angle.from_degrees(2.4843)
121
+ )
122
+
123
+ moon = Astronoby::Moon.new(ephem: ephem, instant: instant)
124
+ topocentric = moon.observed_by(observer)
125
+
126
+ topocentric.horizontal.azimuth.str(:dms, precision: 0)
127
+ # => "+90° 14′ 19″"
128
+ ```
129
+
130
+ You can learn more about observers on the [Observer page].
131
+
132
+ ## See also
133
+ - [Coordinates](coordinates.md) - for understanding coordinate systems
134
+ - [Observer](observer.md) - for location setup
135
+ - [Solar System Bodies](solar_system_bodies.md) - for object positions
136
+ - [Ephemerides](ephem.md) - for data sources
137
+
138
+ [Observer page]: observer.md
@@ -0,0 +1,119 @@
1
+ # Rise, Transit and Set Times
2
+
3
+ Astronoby provides a calculator to compute all the rise, transit and set times
4
+ that will happen for a celestial body as observed from Earth during a period
5
+ of time: `Astronoby::RiseTransitSetCalculator`.
6
+
7
+ ## Initialization
8
+
9
+ Once instantiated, the calculator doesn't do anything yet, it waits for your
10
+ instruction.
11
+
12
+ It takes as key arguments:
13
+ * `body` (`Astronoby::SolarSystemBody` or `Astronoby::DeepSkyObject`): any
14
+ 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
17
+
18
+ You can learn more about [Solar System bodies] and [ephemerides].
19
+
20
+ ```rb
21
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
22
+
23
+ observer = Astronoby::Observer.new(
24
+ latitude: Astronoby::Angle.from_degrees(41.0082),
25
+ longitude: Astronoby::Angle.from_degrees(28.9784),
26
+ elevation: Astronoby::Distance.from_meters(40)
27
+ )
28
+
29
+ calculator = Astronoby::RiseTransitSetCalculator.new(
30
+ body: Astronoby::Saturn,
31
+ observer: observer,
32
+ ephem: ephem
33
+ )
34
+ ```
35
+
36
+ You can learn more about observers on the
37
+ [Observer page](https://github.com/rhannequin/astronoby/wiki/Observer).
38
+
39
+ ## `#events_between`
40
+
41
+ This is the main method of the calculator. It provides all the rising, transit
42
+ and setting times that will happen between two dates. It returns a
43
+ `Astronoby::RiseTransitSetEvents` object which exposes the methods
44
+ `#rising_times`, `#transit_times` and `#setting_times`.
45
+
46
+ ```rb
47
+ events = calculator.events_between(
48
+ Time.utc(2025, 5, 1),
49
+ Time.utc(2025, 5, 3)
50
+ )
51
+
52
+ events.rising_times
53
+ # => [2025-05-01 01:28:48 UTC, 2025-05-02 01:25:07 UTC]
54
+
55
+ events.transit_times
56
+ # => [2025-05-01 07:21:34 UTC, 2025-05-02 07:18:01 UTC]
57
+
58
+ events.setting_times
59
+ # => [2025-05-01 13:14:24 UTC, 2025-05-02 13:10:59 UTC]
60
+ ```
61
+
62
+ ## `#events_on`
63
+
64
+ You can call `#events_on` to compute the event times that will happen during a
65
+ civil day. You can provide a UTC offset to specify the boundaries of the civil
66
+ day for your location.
67
+
68
+ This method also returns a `Astronoby::RiseTransitSetEvents` object because some
69
+ celestial bodies could occasionally have the same event happen multiple times in
70
+ a single day. This is the case for the Moon, for example, which can seem to rise
71
+ twice in the same civil day because of its quick motion around the Earth.
72
+
73
+ ```rb
74
+ events = calculator.events_on(Date.new(2025, 5, 1))
75
+
76
+ events.rising_times
77
+ # => [2025-05-01 01:28:48 UTC]
78
+
79
+ events.transit_times
80
+ # => [2025-05-01 07:21:34 UTC]
81
+
82
+ events.setting_times
83
+ # => [2025-05-01 13:14:24 UTC]
84
+ ```
85
+
86
+ ## `#event_on`
87
+
88
+ For convenience, `Astronoby::RiseTransitSetCalculator` also exposes a
89
+ `#event_on` method that behaves the same way as `#events_on` but returns the
90
+ first time of rising, transit and setting for the civil date, as these events
91
+ only happen once in most cases. It returns a `Astronoby::RiseTransitSetEvent`
92
+ which exposes the instance methods `#rising_time`, `#transit_time` and
93
+ `#setting_time`.
94
+
95
+ ```rb
96
+ utc_offset = "+03:00"
97
+ event = calculator.event_on(
98
+ Date.new(2025, 5, 1),
99
+ utc_offset: utc_offset
100
+ )
101
+
102
+ event.rising_time.localtime(utc_offset)
103
+ # => 2025-05-01 04:28:48 +0300
104
+
105
+ event.transit_time.localtime(utc_offset)
106
+ # => 2025-05-01 10:21:34 +0300
107
+
108
+ event.setting_time.localtime(utc_offset)
109
+ # => 2025-05-01 16:14:24 +0300
110
+ ```
111
+
112
+ [Solar System bodies]: solar_system_bodies.md
113
+ [ephemerides]: ephem.md
114
+
115
+ ## See also
116
+ - [Twilight Times](twilight_times.md) - for sun-related events
117
+ - [Solar System Bodies](solar_system_bodies.md) - for object information
118
+ - [Observer](observer.md) - for location setup
119
+ - [Ephemerides](ephem.md) - for data sources
@@ -0,0 +1,107 @@
1
+ # Solar System Bodies
2
+
3
+ Currently, Astronoby only supports the following major bodies of the Solar
4
+ System:
5
+ * the Sun (`Astronoby::Sun`)
6
+ * planets from Mercury to Neptune, including the Earth (`Astronoby::Earth`, ...)
7
+ * the Moon (`Astronoby::Moon`)
8
+
9
+ Given an ephemeris (`Astronoby::Ephem`) and an instant object
10
+ (`Astronoby::Instant`), these classes enable you to get instances which provide
11
+ positions in different reference frames.
12
+
13
+ You can learn more about [ephemerides] and [reference frames].
14
+
15
+ ```rb
16
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
17
+ time = Time.utc(2021, 7, 8)
18
+ instant = Astronoby::Instant.from_time(time)
19
+
20
+ venus = Astronoby::Venus.new(ephem: ephem, instant: instant)
21
+ apparent_position = venus.apparent.position
22
+
23
+ apparent_position.x.km.round
24
+ # => -148794622
25
+ ```
26
+
27
+ Each of these bodies also provides its own equatorial radius
28
+ (`Astronoby::Distance`).
29
+
30
+ ```rb
31
+ Astronoby::Venus::EQUATORIAL_RADIUS.meters
32
+ # => 6051800
33
+ ```
34
+
35
+ ## Attributes of planets
36
+
37
+ For all Solar System bodies, except the Sun and the Earth, the following
38
+ attributes are available. Note that dynamic values are accessible through
39
+ instance methods, while absolute values are accessible through class methods.
40
+
41
+ ### `#angular_diameter`
42
+
43
+ Size of the body in the sky. Returns a `Astronoby::Angle` object.
44
+
45
+ ```rb
46
+ venus.angular_diameter.str(:dms)
47
+ # => "+0° 0′ 11.4587″"
48
+ ```
49
+
50
+ ### `#constellation`
51
+
52
+ Constellation where the body appears in the sky as seen from Earth. Returns
53
+ a `Astronoby::Constellation` object.
54
+
55
+ ```rb
56
+ venus.constellation.name
57
+ # => "Cancer"
58
+
59
+ venus.constellation.abbreviation
60
+ # => "Cnc"
61
+ ```
62
+
63
+ ### `#phase_angle`
64
+
65
+ "Sun-object-Earth" angle. Returns a `Astronoby::Angle` object.
66
+
67
+ ```rb
68
+ venus.phase_angle.degrees.round
69
+ # => 40
70
+ ```
71
+
72
+ ### `#illuminated_fraction`
73
+
74
+ Fraction of the object's disk illuminated as seen from Earth. Returns a `Float`
75
+ between `0` and `1`.
76
+
77
+ ```rb
78
+ venus.illuminated_fraction.round(2)
79
+ # => 0.88
80
+ ```
81
+
82
+ ### `#apparent_magnitude`
83
+
84
+ Apparent brightness of the body. Returns a `Float`.
85
+
86
+ ```rb
87
+ venus.apparent_magnitude.round(2)
88
+ # => -3.89
89
+ ```
90
+
91
+ ### `::absolute_magnitude`
92
+
93
+ Absolute brightness of the body. Returns a `Float`.
94
+
95
+ ```rb
96
+ Astronoby::Venus.absolute_magnitude
97
+ # => -4.384
98
+ ```
99
+
100
+ [ephemerides]: ephem.md
101
+ [reference frames]: reference_frames.md
102
+
103
+ ## See also
104
+ - [Ephemerides](ephem.md) - for understanding data sources
105
+ - [Reference Frames](reference_frames.md) - for coordinate systems
106
+ - [Observer](observer.md) - for setting up observation locations
107
+ - [Angles](angles.md) - for working with angular measurements
@@ -0,0 +1,123 @@
1
+ # Twilight times
2
+
3
+ In astronomy, twilight is a period of time when the Sun is already set but
4
+ some of its light still illuminates the atmosphere, making the sky brighter than
5
+ during full night.
6
+
7
+ We usually define 4 moments when talking about twilight:
8
+ * sunrise/sunset: right when the Sun goes above the horizon or right after it
9
+ goes below the horizon. The Sun's horizon angle is 0°.
10
+ * civil twilight: when the horizon angle is between 0° and -6°. Usually, during
11
+ this time, artificial light is not needed yet.
12
+ * nautical twilight: when the horizon angle is between -6° and -12°. When the
13
+ nautical twilight starts, the difference between the horizon at sea and the
14
+ sky cannot be seen clearly anymore.
15
+ * astronomical twilight: when the horizon angle is between -12° and -18°. Some
16
+ stars can be seen during this time.
17
+
18
+ These moments change every day and depend on the observer's location. They can
19
+ be computed using `Astronoby::TwilightCalculator`.
20
+
21
+ ## Initialization
22
+
23
+ Once instantiated, the calculator doesn't do anything yet, it waits for your
24
+ instruction.
25
+
26
+ It takes as key arguments:
27
+ * `observer` (`Astronoby::Observer`): location on Earth of the observer
28
+ * `ephem`: ephemeris to provide the initial raw data
29
+
30
+ You can learn more about ephemerides on the [Ephem page].
31
+
32
+ ```rb
33
+ ephem = Astronoby::Ephem.load("inpop19a.bsp")
34
+
35
+ observer = Astronoby::Observer.new(
36
+ latitude: Astronoby::Angle.from_degrees(41.0082),
37
+ longitude: Astronoby::Angle.from_degrees(28.9784),
38
+ elevation: Astronoby::Distance.from_meters(40)
39
+ )
40
+
41
+ calculator = Astronoby::TwilightCalculator.new(
42
+ observer: observer,
43
+ ephem: ephem
44
+ )
45
+ ```
46
+
47
+ You can learn more about observers on the [Observer page].
48
+
49
+ ## `events_between`
50
+
51
+ This is the main method of the calculator. It provides all the twilight times
52
+ that will happen between two dates.
53
+
54
+ It returns a `Astronoby::TwilightEvents` object which exposes the 6 following
55
+ instance methods:
56
+ * `#morning_astronomical_twilight_times`: when the rising Sun reaches 18° below
57
+ the horizon
58
+ * `#morning_nautical_twilight_times`: when the rising Sun reaches 12° below the
59
+ horizon
60
+ * `#morning_civil_twilight_times`: when the rising Sun reaches 6° below the
61
+ horizon
62
+ * `#evening_civil_twilight_times`: when the setting Sun reaches 6° below the
63
+ horizon
64
+ * `#evening_nautical_twilight_times`: when the setting Sun reaches 12° below the
65
+ horizon
66
+ * `#evening_astronomical_twilight_times`: when the setting Sun reaches 18° below
67
+ the horizon
68
+
69
+ ```rb
70
+ events = calculator.events_between(
71
+ Time.utc(2025, 8, 1),
72
+ Time.utc(2025, 8, 8)
73
+ )
74
+
75
+ events.morning_civil_twilight_times
76
+ # =>
77
+ # [2025-08-01 02:29:17 UTC,
78
+ # 2025-08-02 02:30:21 UTC,
79
+ # 2025-08-03 02:31:26 UTC,
80
+ # 2025-08-04 02:32:30 UTC,
81
+ # 2025-08-05 02:33:35 UTC,
82
+ # 2025-08-06 02:34:40 UTC,
83
+ # 2025-08-07 02:35:45 UTC]
84
+ ```
85
+
86
+ ## `#event_on`
87
+
88
+ The calculator exposes the instance method `#event_on` to compute the twilight
89
+ times for a given `date` (`Date`) parameter.
90
+
91
+ It returns a `Astronoby::TwilightEvent` object which exposes the 6 following
92
+ instance methods: `#morning_astronomical_twilight_time`,
93
+ `#morning_nautical_twilight_time`, `#morning_civil_twilight_time`,
94
+ `#evening_civil_twilight_time`, `#evening_nautical_twilight_time` and
95
+ `#evening_astronomical_twilight_time`.
96
+
97
+ ```rb
98
+ event = calculator.event_on(Date.new(2025, 5, 1))
99
+
100
+ event.morning_astronomical_twilight_time
101
+ # => 2025-05-01 01:17:18 UTC
102
+
103
+ event.morning_nautical_twilight_time
104
+ # => 2025-05-01 01:56:48 UTC
105
+
106
+ event.evening_civil_twilight_time
107
+ # => 2025-05-01 17:29:41 UTC
108
+
109
+ event.evening_nautical_twilight_time
110
+ # => 2025-05-01 18:06:08 UTC
111
+
112
+ event.evening_astronomical_twilight_time
113
+ # => 2025-05-01 18:45:38 UTC
114
+ ```
115
+
116
+ [Ephem page]: ephem.md
117
+ [Observer page]: observer.md
118
+
119
+ ## See also
120
+ - [Rise, Transit and Set Times](rise_transit_set_times.md) - for sun and moon events
121
+ - [Observer](observer.md) - for location setup
122
+ - [Ephemerides](ephem.md) - for data sources
123
+ - [Moon Phases](moon_phases.md) - for lunar events
@@ -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