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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -0
- data/README.md +6 -4
- data/UPGRADING.md +25 -0
- data/docs/README.md +30 -2
- data/docs/angles.md +1 -1
- data/docs/coordinates.md +1 -1
- data/docs/deep_sky_bodies.md +101 -0
- data/docs/ephem.md +1 -1
- data/docs/instant.md +1 -1
- data/docs/moon_phases.md +1 -1
- data/docs/observer.md +1 -1
- data/docs/reference_frames.md +1 -1
- data/docs/rise_transit_set_times.md +5 -5
- data/docs/{celestial_bodies.md → solar_system_bodies.md} +1 -1
- data/lib/astronoby/angle.rb +6 -2
- data/lib/astronoby/angular_velocity.rb +76 -0
- data/lib/astronoby/bodies/deep_sky_object.rb +44 -0
- data/lib/astronoby/bodies/deep_sky_object_position.rb +127 -0
- data/lib/astronoby/bodies/earth.rb +5 -1
- data/lib/astronoby/bodies/moon.rb +21 -0
- data/lib/astronoby/bodies/solar_system_body.rb +25 -0
- data/lib/astronoby/cache.rb +1 -0
- data/lib/astronoby/constants.rb +7 -2
- data/lib/astronoby/coordinates/equatorial.rb +2 -5
- data/lib/astronoby/distance.rb +6 -0
- data/lib/astronoby/events/extremum_calculator.rb +233 -0
- data/lib/astronoby/events/extremum_event.rb +15 -0
- data/lib/astronoby/events/rise_transit_set_calculator.rb +9 -6
- data/lib/astronoby/events/twilight_calculator.rb +1 -1
- data/lib/astronoby/instant.rb +27 -4
- data/lib/astronoby/reference_frames/apparent.rb +0 -10
- data/lib/astronoby/reference_frames/topocentric.rb +1 -1
- data/lib/astronoby/stellar_propagation.rb +162 -0
- data/lib/astronoby/time/greenwich_apparent_sidereal_time.rb +22 -0
- data/lib/astronoby/time/greenwich_mean_sidereal_time.rb +64 -0
- data/lib/astronoby/time/greenwich_sidereal_time.rb +20 -58
- data/lib/astronoby/time/local_apparent_sidereal_time.rb +42 -0
- data/lib/astronoby/time/local_mean_sidereal_time.rb +42 -0
- data/lib/astronoby/time/local_sidereal_time.rb +35 -26
- data/lib/astronoby/time/sidereal_time.rb +42 -0
- data/lib/astronoby/util/time.rb +61 -43
- data/lib/astronoby/velocity.rb +5 -0
- data/lib/astronoby/version.rb +1 -1
- data/lib/astronoby.rb +11 -0
- metadata +14 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: becadc2ba01e7620f12b20a5192e8537ed9f717336bb43471734fd5be9561675
         | 
| 4 | 
            +
              data.tar.gz: 47c5743867f09c58f49093645da7534def3836135883f44929d4da15f73a441a
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 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 | 
            -
            - [ | 
| 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  | 
| 62 | 
            -
             | 
| 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 [ | 
| 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 | 
            -
            [ | 
| 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 | 
            -
            - [ | 
| 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 | 
            -
            - [ | 
| 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 | 
            -
            - [ | 
| 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 | 
            -
            - [ | 
| 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 | 
            -
            - [ | 
| 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 | 
            -
            - [ | 
| 65 | 
            +
            - [Solar System Bodies](solar_system_bodies.md) - for observing objects
         | 
    
        data/docs/reference_frames.md
    CHANGED
    
    | @@ -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 | 
            -
            - [ | 
| 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 | 
| 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 [ | 
| 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 | 
            -
            [ | 
| 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 | 
            -
            - [ | 
| 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
         | 
    
        data/lib/astronoby/angle.rb
    CHANGED
    
    | @@ -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:: | 
| 146 | 
            +
                  decimal_minutes = Constants::ARCMINUTES_PER_DEGREE *
         | 
| 143 147 | 
             
                    (absolute_degrees - deg)
         | 
| 144 148 | 
             
                  absolute_decimal_minutes = (
         | 
| 145 | 
            -
                    Constants:: | 
| 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
         |