diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d4da96..1f2e426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,33 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.5.0] - 2026-05-18 + +### Removed + +- Dropped the C++ satellite/dynamics adapter surface, including + `include/siderust/dynamics.hpp`, the vendored `siderust-ffi` dynamics + exports, and the associated C++ dynamics tests (`tests/test_dynamics.cpp`). + Specifying the `satellite` CMake feature now produces a `FATAL_ERROR`. +- Removed backward-compatible `(start, end)` two-argument overloads from + `altitude.hpp`, `azimuth.hpp`, `lunar_phase.hpp`, `subject.hpp`, and + `target.hpp`. Callers must construct a `Period(start, end)` explicitly and + pass it to the `Period`-taking overload. +- Removed `#include ` from the umbrella + `include/siderust/siderust.hpp` header. + +### Changed + +- Submodule `siderust` advanced to v0.8.0 (from v0.7.0): brings qtty 0.8, + tempoch 0.6, `siderust::JulianDate` / `ModifiedJulianDate` crate-root + re-exports, and removal of the FFI dynamics layer. +- Submodule `tempoch-cpp` advanced to v0.5.0 (from v0.4.x). ## [0.4.0] - 2026-05-15 ### Added - `include/siderust/constops.hpp` and generated `include/siderust/constops.h` with JSON-oriented wrappers for the constops FFI surface, including route constants and ground-asset ID helpers. -- `include/siderust/dynamics.hpp` with RAII wrappers for the dynamics FFI, including `DynamicsContext`, `OrbitState`, and two-body propagation support. ### Changed diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fd1b05..c4992e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(siderust_cpp VERSION 0.4.0 LANGUAGES CXX) +project(siderust_cpp VERSION 0.5.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -26,10 +26,11 @@ elseif(NOT "${SIDERUST_FFI_FEATURES}" STREQUAL "") set(_SIDERUST_FEATURES_ARGS --features ${SIDERUST_FFI_FEATURES}) endif() -# Detect whether the satellite feature is enabled so C++ dynamics.hpp is armed. -set(_SIDERUST_SATELLITE OFF) if(SIDERUST_FFI_FEATURES MATCHES "(^|,)satellite(,|$)") - set(_SIDERUST_SATELLITE ON) + message(FATAL_ERROR + "The satellite feature and C++ dynamics adapter have been removed from siderust-cpp. " + "Remove 'satellite' from SIDERUST_FFI_FEATURES." + ) endif() # --------------------------------------------------------------------------- @@ -104,9 +105,6 @@ target_link_libraries(siderust_cpp INTERFACE $ $ ) -if(_SIDERUST_SATELLITE) - target_compile_definitions(siderust_cpp INTERFACE SIDERUST_SATELLITE=1) -endif() add_dependencies(siderust_cpp build_siderust_ffi) # --------------------------------------------------------------------------- @@ -213,9 +211,6 @@ set(TEST_SOURCES tests/test_context.cpp tests/test_twilight.cpp ) -if(_SIDERUST_SATELLITE) - list(APPEND TEST_SOURCES tests/test_dynamics.cpp) -endif() add_executable(test_siderust ${TEST_SOURCES}) target_link_libraries(test_siderust PRIVATE siderust_cpp GTest::gtest) diff --git a/examples/05_target_tracking.cpp b/examples/05_target_tracking.cpp index 3513424..4de6239 100644 --- a/examples/05_target_tracking.cpp +++ b/examples/05_target_tracking.cpp @@ -130,8 +130,7 @@ void section_target_snapshots(const TTJD &jd, const TTJD &jd_next) { /// Computes: RA' = RA + μα* · Δt / cos(dec), Dec' = Dec + μδ · Δt /// where Δt is in Julian years since the reference epoch. inline spherical::direction::ICRS apply_proper_motion(const spherical::direction::ICRS &pos, - const ProperMotion &pm, - const TTJD &epoch, + const ProperMotion &pm, const TTJD &epoch, const TTJD &target_epoch) { constexpr double JULIAN_YEAR = 365.25; // days double dt_years = (target_epoch.value() - epoch.value()) / JULIAN_YEAR; diff --git a/examples/06_night_events.cpp b/examples/06_night_events.cpp index 12c2994..0f44793 100644 --- a/examples/06_night_events.cpp +++ b/examples/06_night_events.cpp @@ -64,8 +64,7 @@ static void print_events_for_type(const Geodetic &site, const TTMjdPeriod &week, std::cout << " summary: down=" << downs << " raise=" << raises << std::endl; } -static void print_periods_for_type(const Geodetic &site, const TTMjdPeriod &week, - const char *name, +static void print_periods_for_type(const Geodetic &site, const TTMjdPeriod &week, const char *name, qtty::Degree threshold) { auto periods = sun::below_threshold(site, week, threshold); std::cout << std::left << std::setw(18) << name << " night periods (Sun < " << std::fixed diff --git a/examples/10_time_periods.cpp b/examples/10_time_periods.cpp index 2141040..f18c70d 100644 --- a/examples/10_time_periods.cpp +++ b/examples/10_time_periods.cpp @@ -73,8 +73,10 @@ int main() { CivilPeriod civil_window(to_utc_civil(jd_window.start()), to_utc_civil(jd_window.end())); std::cout << "Periods over the same half-day window:\n"; - std::cout << " JD(TT) " << jd_window << " duration=" << jd_window.duration() << "\n"; - std::cout << " MJD(TT) " << mjd_window << " duration=" << mjd_window.duration() << "\n"; + std::cout << " JD(TT) " << jd_window << " duration=" << jd_window.duration() + << "\n"; + std::cout << " MJD(TT) " << mjd_window << " duration=" << mjd_window.duration() + << "\n"; std::cout << " Civil " << civil_window << " duration=" << civil_window.duration() << "\n"; diff --git a/include/siderust/altitude.hpp b/include/siderust/altitude.hpp index 6b8041c..cf67ed5 100644 --- a/include/siderust/altitude.hpp +++ b/include/siderust/altitude.hpp @@ -42,7 +42,8 @@ struct CulminationEvent { CulminationKind kind; static CulminationEvent from_c(const siderust_culmination_event_t &c) { - return {ModifiedJulianDate(c.mjd), qtty::Degree(c.altitude_deg), static_cast(c.kind)}; + return {ModifiedJulianDate(c.mjd), qtty::Degree(c.altitude_deg), + static_cast(c.kind)}; } }; @@ -148,14 +149,6 @@ inline std::vector above_threshold(const Geodetic &obs, const Period &wi return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector above_threshold(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, const SearchOptions &opts = {}) { - return above_threshold(obs, Period(start, end), threshold, opts); -} - /** * @brief Find periods when the Sun is below a threshold altitude. */ @@ -170,14 +163,6 @@ inline std::vector below_threshold(const Geodetic &obs, const Period &wi return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector below_threshold(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, const SearchOptions &opts = {}) { - return below_threshold(obs, Period(start, end), threshold, opts); -} - /** * @brief Find threshold-crossing events for the Sun. */ @@ -192,15 +177,6 @@ inline std::vector crossings(const Geodetic &obs, const Period &w return detail::crossings_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector crossings(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, - const SearchOptions &opts = {}) { - return crossings(obs, Period(start, end), threshold, opts); -} - /** * @brief Find culmination events for the Sun. */ @@ -214,14 +190,6 @@ inline std::vector culminations(const Geodetic &obs, const Per return detail::culminations_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector culminations(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, const SearchOptions &opts = {}) { - return culminations(obs, Period(start, end), opts); -} - /** * @brief Find periods when the Sun's altitude is within [min, max]. */ @@ -237,21 +205,6 @@ inline std::vector altitude_periods(const Geodetic &obs, const Period &w return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector altitude_periods(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree min_alt, qtty::Degree max_alt) { - siderust_altitude_query_t q = {obs.to_c(), start.value(), end.value(), min_alt.value(), - max_alt.value()}; - tempoch_period_mjd_t *ptr = nullptr; - uintptr_t count = 0; - check_status( - siderust_altitude_periods(detail::make_body_subject(SIDERUST_BODY_SUN), q, &ptr, &count), - "sun::altitude_periods"); - return detail::periods_from_c(ptr, count); -} - } // namespace sun // ============================================================================ @@ -285,14 +238,6 @@ inline std::vector above_threshold(const Geodetic &obs, const Period &wi return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector above_threshold(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, const SearchOptions &opts = {}) { - return above_threshold(obs, Period(start, end), threshold, opts); -} - /** * @brief Find periods when the Moon is below a threshold altitude. */ @@ -307,14 +252,6 @@ inline std::vector below_threshold(const Geodetic &obs, const Period &wi return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector below_threshold(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, const SearchOptions &opts = {}) { - return below_threshold(obs, Period(start, end), threshold, opts); -} - /** * @brief Find threshold-crossing events for the Moon. */ @@ -329,15 +266,6 @@ inline std::vector crossings(const Geodetic &obs, const Period &w return detail::crossings_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector crossings(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, - const SearchOptions &opts = {}) { - return crossings(obs, Period(start, end), threshold, opts); -} - /** * @brief Find culmination events for the Moon. */ @@ -351,14 +279,6 @@ inline std::vector culminations(const Geodetic &obs, const Per return detail::culminations_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector culminations(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, const SearchOptions &opts = {}) { - return culminations(obs, Period(start, end), opts); -} - /** * @brief Find periods when the Moon's altitude is within [min, max]. */ @@ -374,21 +294,6 @@ inline std::vector altitude_periods(const Geodetic &obs, const Period &w return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector altitude_periods(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree min_alt, qtty::Degree max_alt) { - siderust_altitude_query_t q = {obs.to_c(), start.value(), end.value(), min_alt.value(), - max_alt.value()}; - tempoch_period_mjd_t *ptr = nullptr; - uintptr_t count = 0; - check_status( - siderust_altitude_periods(detail::make_body_subject(SIDERUST_BODY_MOON), q, &ptr, &count), - "moon::altitude_periods"); - return detail::periods_from_c(ptr, count); -} - } // namespace moon // ============================================================================ @@ -422,15 +327,6 @@ inline std::vector above_threshold(const Star &s, const Geodetic &obs, c return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector above_threshold(const Star &s, const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree threshold, - const SearchOptions &opts = {}) { - return above_threshold(s, obs, Period(start, end), threshold, opts); -} - /** * @brief Find periods when a star is below a threshold altitude. */ @@ -445,15 +341,6 @@ inline std::vector below_threshold(const Star &s, const Geodetic &obs, c return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector below_threshold(const Star &s, const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree threshold, - const SearchOptions &opts = {}) { - return below_threshold(s, obs, Period(start, end), threshold, opts); -} - /** * @brief Find threshold-crossing events for a star. */ @@ -468,15 +355,6 @@ inline std::vector crossings(const Star &s, const Geodetic &obs, return detail::crossings_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector crossings(const Star &s, const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree threshold, - const SearchOptions &opts = {}) { - return crossings(s, obs, Period(start, end), threshold, opts); -} - /** * @brief Find culmination events for a star. */ @@ -491,15 +369,6 @@ inline std::vector culminations(const Star &s, const Geodetic return detail::culminations_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector culminations(const Star &s, const Geodetic &obs, - const ModifiedJulianDate &start, const ModifiedJulianDate &end, - const SearchOptions &opts = {}) { - return culminations(s, obs, Period(start, end), opts); -} - } // namespace star_altitude // ============================================================================ @@ -543,16 +412,6 @@ inline std::vector above_threshold(const spherical::direction::ICRS &dir return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible RA/Dec + [start, end] overload. - */ -inline std::vector above_threshold(qtty::Degree ra, qtty::Degree dec, const Geodetic &obs, - const ModifiedJulianDate &start, const ModifiedJulianDate &end, qtty::Degree threshold, - const SearchOptions &opts = {}) { - return above_threshold(spherical::direction::ICRS(ra, dec), obs, Period(start, end), threshold, - opts); -} - /** * @brief Find periods when a fixed ICRS direction is below a threshold. */ @@ -568,16 +427,6 @@ inline std::vector below_threshold(const spherical::direction::ICRS &dir return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible RA/Dec + [start, end] overload. - */ -inline std::vector below_threshold(qtty::Degree ra, qtty::Degree dec, const Geodetic &obs, - const ModifiedJulianDate &start, const ModifiedJulianDate &end, qtty::Degree threshold, - const SearchOptions &opts = {}) { - return below_threshold(spherical::direction::ICRS(ra, dec), obs, Period(start, end), threshold, - opts); -} - } // namespace icrs_altitude } // namespace siderust diff --git a/include/siderust/azimuth.hpp b/include/siderust/azimuth.hpp index 35a2c25..d64b5df 100644 --- a/include/siderust/azimuth.hpp +++ b/include/siderust/azimuth.hpp @@ -44,7 +44,7 @@ enum class AzimuthExtremumKind : int32_t { * @brief An azimuth bearing-crossing event. */ struct AzimuthCrossingEvent { - ModifiedJulianDate time; ///< Epoch of the crossing (ModifiedJulianDate). + ModifiedJulianDate time; ///< Epoch of the crossing (ModifiedJulianDate). CrossingDirection direction; ///< Whether the azimuth is increasing or decreasing. static AzimuthCrossingEvent from_c(const siderust_azimuth_crossing_event_t &c) { @@ -56,12 +56,13 @@ struct AzimuthCrossingEvent { * @brief An azimuth extremum event. */ struct AzimuthExtremum { - ModifiedJulianDate time; ///< Epoch of the extremum (ModifiedJulianDate). + ModifiedJulianDate time; ///< Epoch of the extremum (ModifiedJulianDate). qtty::Degree azimuth; ///< Azimuth at the extremum (degrees, N-clockwise). AzimuthExtremumKind kind; ///< Maximum or minimum. static AzimuthExtremum from_c(const siderust_azimuth_extremum_t &c) { - return {ModifiedJulianDate(c.mjd), qtty::Degree(c.azimuth_deg), static_cast(c.kind)}; + return {ModifiedJulianDate(c.mjd), qtty::Degree(c.azimuth_deg), + static_cast(c.kind)}; } }; @@ -128,15 +129,6 @@ inline std::vector azimuth_crossings(const Geodetic &obs, return detail::az_crossings_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector azimuth_crossings(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree bearing, - const SearchOptions &opts = {}) { - return azimuth_crossings(obs, Period(start, end), bearing, opts); -} - /** * @brief Find azimuth extrema (northernmost / southernmost) for the Sun. */ @@ -150,15 +142,6 @@ inline std::vector azimuth_extrema(const Geodetic &obs, const P return detail::az_extrema_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector azimuth_extrema(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, - const SearchOptions &opts = {}) { - return azimuth_extrema(obs, Period(start, end), opts); -} - /** * @brief Find periods when the Sun's azimuth is within [min_bearing, * max_bearing]. @@ -175,15 +158,6 @@ inline std::vector in_azimuth_range(const Geodetic &obs, const Period &w return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector in_azimuth_range(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree min_bearing, qtty::Degree max_bearing, - const SearchOptions &opts = {}) { - return in_azimuth_range(obs, Period(start, end), min_bearing, max_bearing, opts); -} - /** * @brief Find periods when the Sun's azimuth is outside [min_bearing, * max_bearing]. @@ -200,16 +174,6 @@ inline std::vector outside_azimuth_range(const Geodetic &obs, const Peri return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector outside_azimuth_range(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree min_bearing, - qtty::Degree max_bearing, - const SearchOptions &opts = {}) { - return outside_azimuth_range(obs, Period(start, end), min_bearing, max_bearing, opts); -} - } // namespace sun // ============================================================================ @@ -246,15 +210,6 @@ inline std::vector azimuth_crossings(const Geodetic &obs, return detail::az_crossings_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector azimuth_crossings(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree bearing, - const SearchOptions &opts = {}) { - return azimuth_crossings(obs, Period(start, end), bearing, opts); -} - /** * @brief Find azimuth extrema (northernmost / southernmost) for the Moon. */ @@ -268,15 +223,6 @@ inline std::vector azimuth_extrema(const Geodetic &obs, const P return detail::az_extrema_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector azimuth_extrema(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, - const SearchOptions &opts = {}) { - return azimuth_extrema(obs, Period(start, end), opts); -} - /** * @brief Find periods when the Moon's azimuth is within [min_bearing, * max_bearing]. @@ -293,15 +239,6 @@ inline std::vector in_azimuth_range(const Geodetic &obs, const Period &w return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector in_azimuth_range(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree min_bearing, qtty::Degree max_bearing, - const SearchOptions &opts = {}) { - return in_azimuth_range(obs, Period(start, end), min_bearing, max_bearing, opts); -} - /** * @brief Find periods when the Moon's azimuth is outside [min_bearing, * max_bearing]. @@ -318,16 +255,6 @@ inline std::vector outside_azimuth_range(const Geodetic &obs, const Peri return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector outside_azimuth_range(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree min_bearing, - qtty::Degree max_bearing, - const SearchOptions &opts = {}) { - return outside_azimuth_range(obs, Period(start, end), min_bearing, max_bearing, opts); -} - } // namespace moon // ============================================================================ @@ -364,16 +291,6 @@ inline std::vector azimuth_crossings(const Star &s, const return detail::az_crossings_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector azimuth_crossings(const Star &s, const Geodetic &obs, - const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree bearing, - const SearchOptions &opts = {}) { - return azimuth_crossings(s, obs, Period(start, end), bearing, opts); -} - /** * @brief Find periods when a star's azimuth is within [min, max] (degrees). */ @@ -390,16 +307,6 @@ inline std::vector in_azimuth_range(const Star &s, const Geodetic &obs, return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector in_azimuth_range(const Star &s, const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree min_bearing, - qtty::Degree max_bearing, - const SearchOptions &opts = {}) { - return in_azimuth_range(s, obs, Period(start, end), min_bearing, max_bearing, opts); -} - /** * @brief Find periods when a star's azimuth is outside [min, max] (degrees). */ @@ -416,16 +323,6 @@ inline std::vector outside_azimuth_range(const Star &s, const Geodetic & return detail::periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector outside_azimuth_range(const Star &s, const Geodetic &obs, - const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree min_bearing, qtty::Degree max_bearing, - const SearchOptions &opts = {}) { - return outside_azimuth_range(s, obs, Period(start, end), min_bearing, max_bearing, opts); -} - } // namespace star_altitude // ============================================================================ @@ -478,16 +375,6 @@ azimuth_crossings(qtty::Degree ra, qtty::Degree dec, const Geodetic &obs, const return azimuth_crossings(spherical::direction::ICRS(ra, dec), obs, window, bearing, opts); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector azimuth_crossings(const spherical::direction::ICRS &dir, - const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree bearing, - const SearchOptions &opts = {}) { - return azimuth_crossings(dir, obs, Period(start, end), bearing, opts); -} - } // namespace icrs_altitude // ============================================================================ diff --git a/include/siderust/constops.hpp b/include/siderust/constops.hpp index 2d86da4..7ecd39d 100644 --- a/include/siderust/constops.hpp +++ b/include/siderust/constops.hpp @@ -51,8 +51,8 @@ inline std::string call_json(ConstopsEntryPoint entrypoint, const std::string &r if (rc != CONSTOPS_OK) { const char *last = constops_last_error(); std::string detail = (last == nullptr) ? "unknown constops error" : last; - throw ConstopsError(std::string(operation_name) + " failed (" + std::to_string(rc) + "): " + - detail); + throw ConstopsError(std::string(operation_name) + " failed (" + std::to_string(rc) + + "): " + detail); } std::string response; diff --git a/include/siderust/dynamics.hpp b/include/siderust/dynamics.hpp deleted file mode 100644 index 5b79b60..0000000 --- a/include/siderust/dynamics.hpp +++ /dev/null @@ -1,354 +0,0 @@ -#pragma once - -#ifndef SIDERUST_SATELLITE -# error "dynamics.hpp requires the satellite feature. " \ - "Rebuild siderust-ffi with -DSIDERUST_FFI_FEATURES=satellite " \ - "and configure CMake with that flag so SIDERUST_SATELLITE is defined." -#endif - -/** - * @file dynamics.hpp - * @brief Orbit propagation wrappers for the siderust dynamics FFI. - * - * Exposes DynamicsContext, OrbitState, and TwoBodyPropagator — RAII wrappers - * over the opaque handles produced by the dynamics section of siderust-ffi. - * - * ## C FFI scope (current) - * - * The current FFI surface supports: - * - Two-body propagation (Earth WGS-84 GM or custom GM) via DOP853. - * - DynamicsContext with optional runtime-ephemeris, atmosphere-vtable, and - * gravity-field-vtable providers. - * - * ## Gaps vs. Rust API - * - * The following Rust force models are not yet exposed through the C FFI; they - * are available in the Python, Node, and WebAssembly adapters which bind - * directly to the Rust crate: - * - J2 first-zonal oblateness - * - Full spherical-harmonic geopotential (Geopotential) - * - Cannonball atmospheric drag (DragForce) - * - Solar radiation pressure with shadow models (CannonballSrp / ShadowModel) - * - Sun/Moon third-body perturbations (ThirdBody / ThirdBodySunMoon) - * - 1PN Schwarzschild relativity (CentralBodyRelativity1Pn) - * - User-supplied RTN empirical acceleration (EmpiricalAcceleration) - * - Integrator selection (RK4 / DOPRI5 — only DOP853 via C FFI) - * - Variational equations / state-transition matrix (propagate_with_stm) - * - Event-driven propagation (propagate_with_events) - * - * See docs/dynamics_gaps.md for the cross-adapter gap summary. - */ - -#include "ffi_core.hpp" -#include "runtime_ephemeris.hpp" - -#include -#include -#include - -namespace siderust { -namespace dynamics { - -// ============================================================================ -// DynamicsError -// ============================================================================ - -/// Exception class for dynamics-specific status codes. -class DynamicsError : public SiderustException { -public: - siderust_dynamics_status_t code; - - explicit DynamicsError(const std::string &msg, siderust_dynamics_status_t c) - : SiderustException(msg), code(c) {} -}; - -/// Throw a typed DynamicsError for a non-OK dynamics status code. -inline void check_dynamics(siderust_dynamics_status_t status, const char *op) { - if (status == SIDERUST_DYNAMICS_STATUS_T_OK) - return; - - const std::string msg = std::string(op) + " failed: "; - switch (status) { - case SIDERUST_DYNAMICS_STATUS_T_NULL_POINTER: - throw DynamicsError(msg + "null pointer argument", status); - case SIDERUST_DYNAMICS_STATUS_T_EPHEMERIS_UNAVAILABLE: - throw DynamicsError(msg + "no ephemeris provider configured in dynamics context", status); - case SIDERUST_DYNAMICS_STATUS_T_EOP_UNAVAILABLE: - throw DynamicsError(msg + "Earth Orientation Parameters unavailable for epoch", status); - case SIDERUST_DYNAMICS_STATUS_T_GRAVITY_COEFFICIENT_UNAVAILABLE: - throw DynamicsError(msg + "gravity spherical-harmonic coefficient unavailable", status); - case SIDERUST_DYNAMICS_STATUS_T_ALTITUDE_BELOW_SURFACE: - throw DynamicsError(msg + "spacecraft altitude below body surface", status); - case SIDERUST_DYNAMICS_STATUS_T_DEGENERATE_GEOMETRY: - throw DynamicsError(msg + "degenerate geometry (zero r/v or r ∥ v)", status); - case SIDERUST_DYNAMICS_STATUS_T_INVALID_STEP_REQUEST: - throw DynamicsError(msg + "integrator step violated a constraint", status); - case SIDERUST_DYNAMICS_STATUS_T_ATMOSPHERE_PROVIDER_ERROR: - throw DynamicsError(msg + "atmosphere density provider returned an error", status); - case SIDERUST_DYNAMICS_STATUS_T_PROVIDER_ERROR: - throw DynamicsError(msg + "provider error", status); - case SIDERUST_DYNAMICS_STATUS_T_GRAVITY_FIELD_UNAVAILABLE: - throw DynamicsError(msg + "no gravity-field provider configured in dynamics context", status); - case SIDERUST_DYNAMICS_STATUS_T_GEOPOTENTIAL_DEGREE_OUT_OF_RANGE: - throw DynamicsError(msg + "geopotential degree/order exceeds provider maximum", status); - case SIDERUST_DYNAMICS_STATUS_T_INTERNAL_PANIC: - throw DynamicsError(msg + "internal Rust panic at FFI boundary", status); - default: - throw DynamicsError( - msg + "unknown dynamics error (" + std::to_string(static_cast(status)) + ")", status); - } -} - -// ============================================================================ -// OrbitState -// ============================================================================ - -/// Cartesian Geocentric/GCRS state snapshot: position (km), velocity (km/s), -/// epoch (Julian Date TT). -struct StateVector { - double x_km{0}; ///< X position component, km. - double y_km{0}; ///< Y position component, km. - double z_km{0}; ///< Z position component, km. - double vx_km_s{0}; ///< X velocity component, km/s. - double vy_km_s{0}; ///< Y velocity component, km/s. - double vz_km_s{0}; ///< Z velocity component, km/s. - double epoch_jd{0}; ///< Epoch as Julian Date (TT). -}; - -/// RAII owner of an opaque `siderust_orbit_state_t` handle. -/// -/// Constructors take epoch (JD TT), Cartesian position (km), and Cartesian -/// velocity (km/s) in the Geocentric/GCRS frame. Accessors return individual -/// components or a [`StateVector`] value object. -class OrbitState { - siderust_orbit_state_t *handle_{nullptr}; - -public: - /// Construct from individual components. - OrbitState(double epoch_jd, double x, double y, double z, double vx, double vy, double vz) { - check_status(siderust_orbit_state_new(epoch_jd, x, y, z, vx, vy, vz, &handle_), - "siderust_orbit_state_new"); - } - - /// Construct from a StateVector. - explicit OrbitState(const StateVector &sv) - : OrbitState(sv.epoch_jd, sv.x_km, sv.y_km, sv.z_km, sv.vx_km_s, sv.vy_km_s, sv.vz_km_s) {} - - /// Adopt an already-allocated FFI handle (takes ownership; handle may be null). - explicit OrbitState(siderust_orbit_state_t *handle) noexcept : handle_(handle) {} - - ~OrbitState() { - if (handle_) - siderust_orbit_state_free(handle_); - } - - OrbitState(const OrbitState &) = delete; - OrbitState &operator=(const OrbitState &) = delete; - - OrbitState(OrbitState &&o) noexcept : handle_(o.handle_) { o.handle_ = nullptr; } - - OrbitState &operator=(OrbitState &&o) noexcept { - if (this != &o) { - if (handle_) - siderust_orbit_state_free(handle_); - handle_ = o.handle_; - o.handle_ = nullptr; - } - return *this; - } - - explicit operator bool() const noexcept { return handle_ != nullptr; } - - const siderust_orbit_state_t *raw() const noexcept { return handle_; } - - // ── Accessors ───────────────────────────────────────────────────────────── - - /// Position components [x, y, z] in km (Geocentric/GCRS). - std::array position() const { - double x{}, y{}, z{}; - check_status(siderust_orbit_state_position(handle_, &x, &y, &z), - "siderust_orbit_state_position"); - return {x, y, z}; - } - - /// Velocity components [vx, vy, vz] in km/s (GCRS). - std::array velocity() const { - double vx{}, vy{}, vz{}; - check_status(siderust_orbit_state_velocity(handle_, &vx, &vy, &vz), - "siderust_orbit_state_velocity"); - return {vx, vy, vz}; - } - - /// Epoch as a Julian Date (TT). - double epoch_jd() const { - double jd{}; - check_status(siderust_orbit_state_epoch_jd(handle_, &jd), "siderust_orbit_state_epoch_jd"); - return jd; - } - - /// Decode all fields into a StateVector value object. - StateVector to_vector() const { - const auto pos = position(); - const auto vel = velocity(); - return {pos[0], pos[1], pos[2], vel[0], vel[1], vel[2], epoch_jd()}; - } -}; - -// ============================================================================ -// DynamicsContext -// ============================================================================ - -/// RAII owner of an opaque `siderust_dynamics_context_t` handle. -/// -/// A default-constructed context carries no providers. Attach providers with -/// `with_ephemeris()`, `with_atmosphere()`, or `with_gravity_field()`. -/// -/// The context is optional: pass `nullptr` to `TwoBodyPropagator::propagate()` -/// when no providers are needed. -class DynamicsContext { - siderust_dynamics_context_t *handle_{nullptr}; - -public: - /// Create an empty dynamics context (no providers attached). - DynamicsContext() { - check_status(siderust_dynamics_context_new(&handle_), "siderust_dynamics_context_new"); - } - - /// Adopt an already-allocated FFI handle (takes ownership). - explicit DynamicsContext(siderust_dynamics_context_t *h) noexcept : handle_(h) {} - - ~DynamicsContext() { - if (handle_) - siderust_dynamics_context_free(handle_); - } - - DynamicsContext(const DynamicsContext &) = delete; - DynamicsContext &operator=(const DynamicsContext &) = delete; - - DynamicsContext(DynamicsContext &&o) noexcept : handle_(o.handle_) { o.handle_ = nullptr; } - - DynamicsContext &operator=(DynamicsContext &&o) noexcept { - if (this != &o) { - if (handle_) - siderust_dynamics_context_free(handle_); - handle_ = o.handle_; - o.handle_ = nullptr; - } - return *this; - } - - explicit operator bool() const noexcept { return handle_ != nullptr; } - - const siderust_dynamics_context_t *raw() const noexcept { return handle_; } - - // ── Provider attachment (fluent API) ────────────────────────────────────── - - /// Attach a runtime ephemeris for third-body perturbations. - /// - /// The ephemeris handle must remain alive for the lifetime of this context. - DynamicsContext &with_ephemeris(const RuntimeEphemeris &eph) { - check_status(siderust_dynamics_context_with_ephemeris(handle_, eph.raw()), - "dynamics_context_with_ephemeris"); - return *this; - } - - /// Attach a C-callback atmosphere density provider. - /// - /// The vtable (and any `user_data` it points to) must remain alive for the - /// lifetime of this context. - DynamicsContext &with_atmosphere(const siderust_atmosphere_vtable_t &vtable) { - check_status(siderust_dynamics_context_with_atmosphere(handle_, &vtable), - "dynamics_context_with_atmosphere"); - return *this; - } - - /// Attach a C-callback gravity-field coefficient provider. - /// - /// The vtable (and any `user_data` it points to) must remain alive for the - /// lifetime of this context. - DynamicsContext &with_gravity_field(const siderust_gravity_vtable_t &vtable) { - check_status(siderust_dynamics_context_with_gravity_field(handle_, &vtable), - "dynamics_context_with_gravity_field"); - return *this; - } -}; - -// ============================================================================ -// TwoBodyPropagator -// ============================================================================ - -/// DOP853 two-body orbit propagator. -/// -/// Wraps a `siderust_propagator_t` handle configured with a TwoBody force -/// model (pure Newtonian central gravity). Propagation is forward or backward -/// in time and returns a new `OrbitState`. -/// -/// ## Force model gap -/// -/// Only the two-body model is available via C FFI. For J2, drag, SRP, -/// geopotential, third-body, relativity, empirical RTN, or variational STM, -/// use the Python or JavaScript/WebAssembly adapters which bind directly to -/// the Rust crate. See docs/dynamics_gaps.md. -class TwoBodyPropagator { - siderust_propagator_t *handle_{nullptr}; - -public: - /// Earth two-body propagator (WGS-84 / EGM2008 GM = 398 600.441 8 km³/s²). - TwoBodyPropagator() { - check_status(siderust_propagator_two_body_earth_new(&handle_), - "siderust_propagator_two_body_earth_new"); - } - - /// Custom-GM two-body propagator. - /// - /// @param gm_km3_s2 Gravitational parameter in km³/s² - /// (e.g. 1.327 124 4e11 for Sun, 4902.8 for Moon). - explicit TwoBodyPropagator(double gm_km3_s2) { - check_status(siderust_propagator_two_body_new(gm_km3_s2, &handle_), - "siderust_propagator_two_body_new"); - } - - ~TwoBodyPropagator() { - if (handle_) - siderust_propagator_free(handle_); - } - - TwoBodyPropagator(const TwoBodyPropagator &) = delete; - TwoBodyPropagator &operator=(const TwoBodyPropagator &) = delete; - - TwoBodyPropagator(TwoBodyPropagator &&o) noexcept : handle_(o.handle_) { o.handle_ = nullptr; } - - TwoBodyPropagator &operator=(TwoBodyPropagator &&o) noexcept { - if (this != &o) { - if (handle_) - siderust_propagator_free(handle_); - handle_ = o.handle_; - o.handle_ = nullptr; - } - return *this; - } - - explicit operator bool() const noexcept { return handle_ != nullptr; } - - // ── Propagation ─────────────────────────────────────────────────────────── - - /// Propagate `state` by `dt_s` seconds (negative for backward propagation). - /// - /// @param state Initial orbit state (Geocentric/GCRS, km / km/s / JD TT). - /// @param dt_s Propagation interval in seconds. Negative = backward. - /// @param ctx Optional dynamics context. Pass `nullptr` for pure two-body. - /// - /// @return Final orbit state. - /// @throws DynamicsError on propagation failure. - OrbitState propagate(const OrbitState &state, double dt_s, - const DynamicsContext *ctx = nullptr) const { - siderust_orbit_state_t *out = nullptr; - const siderust_dynamics_context_t *raw_ctx = ctx ? ctx->raw() : nullptr; - check_dynamics(siderust_propagator_propagate(handle_, state.raw(), dt_s, raw_ctx, &out), - "siderust_propagator_propagate"); - return OrbitState(out); - } -}; - -} // namespace dynamics -} // namespace siderust diff --git a/include/siderust/lunar_phase.hpp b/include/siderust/lunar_phase.hpp index 11aec59..0b0e32f 100644 --- a/include/siderust/lunar_phase.hpp +++ b/include/siderust/lunar_phase.hpp @@ -71,8 +71,8 @@ struct MoonPhaseGeometry { * @brief A principal lunar phase event (new moon, first quarter, etc.). */ struct PhaseEvent { - ModifiedJulianDate time; ///< Epoch of the event (ModifiedJulianDate). - PhaseKind kind; ///< Which principal phase occurred. + ModifiedJulianDate time; ///< Epoch of the event (ModifiedJulianDate). + PhaseKind kind; ///< Which principal phase occurred. static PhaseEvent from_c(const siderust_phase_event_t &c) { return {ModifiedJulianDate(c.mjd), static_cast(c.kind)}; @@ -100,7 +100,8 @@ inline std::vector illum_periods_from_c(tempoch_period_mjd_t *ptr, uintp std::vector result; result.reserve(count); for (uintptr_t i = 0; i < count; ++i) { - result.push_back(Period(ModifiedJulianDate(ptr[i].start_mjd), ModifiedJulianDate(ptr[i].end_mjd))); + result.push_back( + Period(ModifiedJulianDate(ptr[i].start_mjd), ModifiedJulianDate(ptr[i].end_mjd))); } siderust_periods_free(ptr, count); return result; @@ -168,14 +169,6 @@ inline std::vector find_phase_events(const Period &window, return detail::phase_events_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector find_phase_events(const ModifiedJulianDate &start, const ModifiedJulianDate &end, - const SearchOptions &opts = {}) { - return find_phase_events(Period(start, end), opts); -} - /** * @brief Find periods when illuminated fraction is ≥ k_min. * @@ -192,14 +185,6 @@ inline std::vector illumination_above(const Period &window, double k_min return detail::illum_periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector illumination_above(const ModifiedJulianDate &start, const ModifiedJulianDate &end, double k_min, - const SearchOptions &opts = {}) { - return illumination_above(Period(start, end), k_min, opts); -} - /** * @brief Find periods when illuminated fraction is ≤ k_max. * @@ -216,14 +201,6 @@ inline std::vector illumination_below(const Period &window, double k_max return detail::illum_periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector illumination_below(const ModifiedJulianDate &start, const ModifiedJulianDate &end, double k_max, - const SearchOptions &opts = {}) { - return illumination_below(Period(start, end), k_max, opts); -} - /** * @brief Find periods when illuminated fraction is within [k_min, k_max]. * @@ -242,14 +219,6 @@ inline std::vector illumination_range(const Period &window, double k_min return detail::illum_periods_from_c(ptr, count); } -/** - * @brief Backward-compatible [start, end] overload. - */ -inline std::vector illumination_range(const ModifiedJulianDate &start, const ModifiedJulianDate &end, double k_min, - double k_max, const SearchOptions &opts = {}) { - return illumination_range(Period(start, end), k_min, k_max, opts); -} - } // namespace moon // ============================================================================ diff --git a/include/siderust/siderust.hpp b/include/siderust/siderust.hpp index 8adef1d..79662f2 100644 --- a/include/siderust/siderust.hpp +++ b/include/siderust/siderust.hpp @@ -49,6 +49,3 @@ #include "target.hpp" #include "time.hpp" #include "twilight.hpp" -#ifdef SIDERUST_SATELLITE -# include "dynamics.hpp" -#endif diff --git a/include/siderust/subject.hpp b/include/siderust/subject.hpp index 8b2dfff..e25e642 100644 --- a/include/siderust/subject.hpp +++ b/include/siderust/subject.hpp @@ -145,7 +145,8 @@ class Subject { /** * @brief Altitude at an instant (radians) for any subject. */ -inline qtty::Radian altitude_at(const Subject &subj, const Geodetic &obs, const ModifiedJulianDate &mjd) { +inline qtty::Radian altitude_at(const Subject &subj, const Geodetic &obs, + const ModifiedJulianDate &mjd) { double out; check_status(siderust_altitude_at(subj.c_inner(), obs.to_c(), mjd.value(), &out), "altitude_at(Subject)"); @@ -228,7 +229,8 @@ inline std::vector altitude_periods(const Subject &subj, const Geodetic /** * @brief Azimuth at an instant (degrees, N-clockwise) for any subject. */ -inline qtty::Degree azimuth_at(const Subject &subj, const Geodetic &obs, const ModifiedJulianDate &mjd) { +inline qtty::Degree azimuth_at(const Subject &subj, const Geodetic &obs, + const ModifiedJulianDate &mjd) { double out; check_status(siderust_azimuth_at(subj.c_inner(), obs.to_c(), mjd.value(), &out), "azimuth_at(Subject)"); diff --git a/include/siderust/target.hpp b/include/siderust/target.hpp index fc54760..ff93875 100644 --- a/include/siderust/target.hpp +++ b/include/siderust/target.hpp @@ -265,13 +265,6 @@ template class DirectionTarget : public Target { return detail_periods_from_c(ptr, count); } - /// Backward-compatible [start, end] overload. - std::vector above_threshold(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, - const SearchOptions &opts = {}) const { - return above_threshold(obs, Period(start, end), threshold, opts); - } - /** * @brief Find periods when the target is below a threshold altitude. */ @@ -287,13 +280,6 @@ template class DirectionTarget : public Target { return detail_periods_from_c(ptr, count); } - /// Backward-compatible [start, end] overload. - std::vector below_threshold(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, - const SearchOptions &opts = {}) const { - return below_threshold(obs, Period(start, end), threshold, opts); - } - /** * @brief Find threshold-crossing events (rising / setting). */ @@ -308,13 +294,6 @@ template class DirectionTarget : public Target { return detail::crossings_from_c(ptr, count); } - /// Backward-compatible [start, end] overload. - std::vector crossings(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - qtty::Degree threshold, - const SearchOptions &opts = {}) const { - return crossings(obs, Period(start, end), threshold, opts); - } - /** * @brief Find culmination (local altitude extremum) events. */ @@ -328,12 +307,6 @@ template class DirectionTarget : public Target { return detail::culminations_from_c(ptr, count); } - /// Backward-compatible [start, end] overload. - std::vector culminations(const Geodetic &obs, const ModifiedJulianDate &start, const ModifiedJulianDate &end, - const SearchOptions &opts = {}) const { - return culminations(obs, Period(start, end), opts); - } - // ------------------------------------------------------------------ // Azimuth queries (implements Trackable) // ------------------------------------------------------------------ @@ -364,13 +337,6 @@ template class DirectionTarget : public Target { return detail::az_crossings_from_c(ptr, count); } - /// Backward-compatible [start, end] overload. - std::vector azimuth_crossings(const Geodetic &obs, const ModifiedJulianDate &start, - const ModifiedJulianDate &end, qtty::Degree bearing, - const SearchOptions &opts = {}) const { - return azimuth_crossings(obs, Period(start, end), bearing, opts); - } - /// Access the underlying C handle (advanced use). const SiderustGenericTarget *c_handle() const { return handle_; } @@ -400,7 +366,8 @@ template class DirectionTarget : public Target { std::vector result; result.reserve(count); for (uintptr_t i = 0; i < count; ++i) { - result.push_back(Period(ModifiedJulianDate(ptr[i].start_mjd), ModifiedJulianDate(ptr[i].end_mjd))); + result.push_back( + Period(ModifiedJulianDate(ptr[i].start_mjd), ModifiedJulianDate(ptr[i].end_mjd))); } siderust_periods_free(ptr, count); return result; @@ -625,7 +592,8 @@ class ProperMotionTarget : public Target { std::vector result; result.reserve(count); for (uintptr_t i = 0; i < count; ++i) { - result.push_back(Period(ModifiedJulianDate(ptr[i].start_mjd), ModifiedJulianDate(ptr[i].end_mjd))); + result.push_back( + Period(ModifiedJulianDate(ptr[i].start_mjd), ModifiedJulianDate(ptr[i].end_mjd))); } siderust_periods_free(ptr, count); return result; diff --git a/siderust b/siderust index 1d7c5b5..752677f 160000 --- a/siderust +++ b/siderust @@ -1 +1 @@ -Subproject commit 1d7c5b5986b5e750bbd5df929d625d36abafa7a9 +Subproject commit 752677f7dd7ccee0b72d75619d843b476578d60e diff --git a/tempoch-cpp b/tempoch-cpp index 6ef864b..a64c588 160000 --- a/tempoch-cpp +++ b/tempoch-cpp @@ -1 +1 @@ -Subproject commit 6ef864b63b7d5a511882809f9f2c8631d0c6c746 +Subproject commit a64c58856626f75e72843f21384cc51257167420 diff --git a/tests/test_dynamics.cpp b/tests/test_dynamics.cpp deleted file mode 100644 index b77731e..0000000 --- a/tests/test_dynamics.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -// Copyright (C) 2026 Vallés Puig, Ramon - -#include -#include -#include - -using namespace siderust; -using namespace siderust::dynamics; - -namespace { - -constexpr double J2000 = 2451545.0; - -/// ISS-like LEO orbit: 7000 km circular, equatorial. -OrbitState iss_like_state() { - return OrbitState(J2000, 7000.0, 0.0, 0.0, 0.0, 7.545831, 0.0); -} - -/// One-hour propagation step. -constexpr double ONE_HOUR_S = 3600.0; - -} // namespace - -// ============================================================================ -// DynamicsContext lifecycle -// ============================================================================ - -TEST(DynamicsContext, DefaultConstructionSucceeds) { - EXPECT_NO_THROW(DynamicsContext ctx); -} - -// ============================================================================ -// OrbitState lifecycle and accessors -// ============================================================================ - -TEST(OrbitState, ConstructionAndPositionRoundTrip) { - OrbitState s(J2000, 7000.0, 0.0, 0.0, 0.0, 7.5, 0.0); - auto pos = s.position(); - - EXPECT_NEAR(pos[0], 7000.0, 1e-9); - EXPECT_NEAR(pos[1], 0.0, 1e-9); - EXPECT_NEAR(pos[2], 0.0, 1e-9); -} - -TEST(OrbitState, VelocityRoundTrip) { - OrbitState s(J2000, 0.0, 7000.0, 0.0, 0.0, 0.0, 7.5); - auto vel = s.velocity(); - - EXPECT_NEAR(vel[0], 0.0, 1e-9); - EXPECT_NEAR(vel[1], 0.0, 1e-9); - EXPECT_NEAR(vel[2], 7.5, 1e-9); -} - -TEST(OrbitState, EpochRoundTrip) { - OrbitState s(J2000 + 10.5, 7000.0, 0.0, 0.0, 0.0, 7.5, 0.0); - EXPECT_NEAR(s.epoch_jd(), J2000 + 10.5, 1e-9); -} - -TEST(OrbitState, StateVectorConstructorRoundTrip) { - StateVector sv; - sv.epoch_jd = J2000; - sv.x_km = 6800.0; - sv.y_km = 100.0; - sv.z_km = -200.0; - sv.vx_km_s = 0.1; - sv.vy_km_s = 7.6; - sv.vz_km_s = -0.3; - - OrbitState s(sv); - auto got = s.to_vector(); - - EXPECT_NEAR(got.epoch_jd, sv.epoch_jd, 1e-12); - EXPECT_NEAR(got.x_km, sv.x_km, 1e-9); - EXPECT_NEAR(got.y_km, sv.y_km, 1e-9); - EXPECT_NEAR(got.z_km, sv.z_km, 1e-9); - EXPECT_NEAR(got.vx_km_s, sv.vx_km_s, 1e-9); - EXPECT_NEAR(got.vy_km_s, sv.vy_km_s, 1e-9); - EXPECT_NEAR(got.vz_km_s, sv.vz_km_s, 1e-9); -} - -// ============================================================================ -// TwoBodyPropagator lifecycle -// ============================================================================ - -TEST(TwoBodyPropagator, DefaultEarthConstructionSucceeds) { - EXPECT_NO_THROW(TwoBodyPropagator p); -} - -TEST(TwoBodyPropagator, CustomGmConstructionSucceeds) { - EXPECT_NO_THROW(TwoBodyPropagator p(398600.4418)); -} - -// ============================================================================ -// Two-body propagation correctness -// ============================================================================ - -TEST(TwoBodyPropagator, PropagatedStateHasFiniteComponents) { - TwoBodyPropagator prop; - auto final_s = prop.propagate(iss_like_state(), ONE_HOUR_S); - - const auto pos = final_s.position(); - const auto vel = final_s.velocity(); - - EXPECT_TRUE(std::isfinite(pos[0])); - EXPECT_TRUE(std::isfinite(pos[1])); - EXPECT_TRUE(std::isfinite(pos[2])); - EXPECT_TRUE(std::isfinite(vel[0])); - EXPECT_TRUE(std::isfinite(vel[1])); - EXPECT_TRUE(std::isfinite(vel[2])); -} - -TEST(TwoBodyPropagator, RadiusConservation) { - TwoBodyPropagator prop; - auto s0 = iss_like_state(); - auto s1 = prop.propagate(s0, ONE_HOUR_S); - - auto p0 = s0.position(); - auto p1 = s1.position(); - - double r0 = std::sqrt(p0[0]*p0[0] + p0[1]*p0[1] + p0[2]*p0[2]); - double r1 = std::sqrt(p1[0]*p1[0] + p1[1]*p1[1] + p1[2]*p1[2]); - - // For a circular orbit the radius stays within 1 km over one period. - EXPECT_NEAR(r0, r1, 1.0); -} - -TEST(TwoBodyPropagator, EpochAdvancesByDt) { - TwoBodyPropagator prop; - auto s0 = iss_like_state(); - auto s1 = prop.propagate(s0, ONE_HOUR_S); - - constexpr double one_hour_jd = ONE_HOUR_S / 86400.0; - EXPECT_NEAR(s1.epoch_jd() - s0.epoch_jd(), one_hour_jd, 1e-9); -} - -TEST(TwoBodyPropagator, BackwardPropagationRoundTrip) { - TwoBodyPropagator prop; - auto s0 = iss_like_state(); - auto s1 = prop.propagate(s0, ONE_HOUR_S); - auto s_back = prop.propagate(s1, -ONE_HOUR_S); - - auto p0 = s0.position(); - auto p2 = s_back.position(); - - EXPECT_NEAR(p2[0], p0[0], 1e-6); - EXPECT_NEAR(p2[1], p0[1], 1e-6); - EXPECT_NEAR(p2[2], p0[2], 1e-6); -} - -TEST(TwoBodyPropagator, PropagationWithEmptyContextMatchesNullContext) { - TwoBodyPropagator prop; - DynamicsContext ctx; - - auto s_null = prop.propagate(iss_like_state(), ONE_HOUR_S, nullptr); - auto s_ctx = prop.propagate(iss_like_state(), ONE_HOUR_S, &ctx); - - auto p_null = s_null.position(); - auto p_ctx = s_ctx.position(); - - EXPECT_NEAR(p_ctx[0], p_null[0], 1e-9); - EXPECT_NEAR(p_ctx[1], p_null[1], 1e-9); - EXPECT_NEAR(p_ctx[2], p_null[2], 1e-9); -} - -TEST(TwoBodyPropagator, CustomGmGivesConsistentRadius) { - // Sun GM (km³/s²): Earth-like orbit but around the Sun. - constexpr double GM_SUN = 1.327124400e11; - TwoBodyPropagator solar_prop(GM_SUN); - - OrbitState earth_like(J2000, 149597870.7, 0.0, 0.0, 0.0, 29.783, 0.0); - auto final_s = solar_prop.propagate(earth_like, 86400.0 * 30.0); - - auto p = final_s.position(); - double r = std::sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]); - - // Radius stays within 1% of 1 AU for a near-circular solar orbit. - EXPECT_NEAR(r, 149597870.7, 1.5e6); -} - -TEST(TwoBodyPropagator, MoveSemantics) { - TwoBodyPropagator p1; - TwoBodyPropagator p2 = std::move(p1); - EXPECT_FALSE(static_cast(p1)); // NOLINT: intentional moved-from check - EXPECT_TRUE(static_cast(p2)); -} diff --git a/tests/test_time.cpp b/tests/test_time.cpp index 7654f92..1d4bfef 100644 --- a/tests/test_time.cpp +++ b/tests/test_time.cpp @@ -7,8 +7,8 @@ using namespace siderust; TEST(Time, ReexportsExposeExplicitScaleTypes) { static_assert(std::is_same_v>); - static_assert(std::is_same_v>); + static_assert( + std::is_same_v>); } TEST(Time, TtJulianDateRoundtripsThroughUtcConvenience) {