Program Listing for File TimeKeeper.h

Program Listing for File TimeKeeper.h#

Return to documentation for file (include/Karana/SOADyn/TimeKeeper.h)

/*
 * Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
 *
 * NOTICE TO USER:
 *
 * This source code and/or documentation (the "Licensed Materials") is
 * the confidential and proprietary information of Karana Dynamics Inc.
 * Use of these Licensed Materials is governed by the terms and conditions
 * of a separate software license agreement between Karana Dynamics and the
 * Licensee ("License Agreement"). Unless expressly permitted under that
 * agreement, any reproduction, modification, distribution, or disclosure
 * of the Licensed Materials, in whole or in part, to any third party
 * without the prior written consent of Karana Dynamics is strictly prohibited.
 *
 * THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
 * KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
 * INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
 * OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
 *
 * U.S. Government End Users: The Licensed Materials are a "commercial item"
 * as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
 * only as a commercial end item under the terms of this license.
 *
 * Any use of the Licensed Materials in individual or commercial software must
 * include, in the user documentation and internal source code comments,
 * this Notice, Disclaimer, and U.S. Government Use Provision.
 */

/**
 * @file
 * @brief Contains the declarations for the TimeKeeper classes.
 */

#pragma once
#include "Karana/Frame/FrameContainer.h"
#include "Karana/Math/Defs.h"

namespace Karana::Dynamics {
    namespace km = Karana::Math;
    namespace kc = Karana::Core;
    namespace kf = Karana::Frame;

    // Forward declaration
    class TimeKeeper;

    /**
     * @brief The time types that TimeKeeper can convert to/from.
     */
    enum class TimeType {
        /**
         * @brief ET ephemeris time in seconds. This is the number of seconds past the J2000 epoch
         *        in TDB (Barycentric Dynamical Time).
         */
        ET,
        TAI,    ///< International Atomic Time seconds since the ephemeris J2000 epoch in TAI.
        UTC,    ///< Coordinated Universal Time seconds since the J2000 epoch in UTC.
        GPS,    ///< GPS Time seconds since the J2000 epoch in GPS.
        UT1,    ///< UT1 seconds since the J2000 epoch in UT1.
        TT,     ///< Terrestrial Time seconds since the J2000 epoch in TT.
        JDTT,   ///< Terrestrial Time Julian Date in days since J2000.
        JDUTC,  ///< Coordinated Universal Time Julian Date in days since J2000.
        MJDTT,  ///< Terrestrial Time Modified Julian Date in days since J2000.
        MJDUTC, ///< Coordinated Universal Time Modified Julian Date in days since J2000.
    };

    /**
     * @class TimeKeeperVars
     * @brief Vars for the TimeKeeper class.
     */
    class TimeKeeperVars : public kc::BaseVars {
      public:
        /**
         * @brief TimeKeeperVars constructor. The constructor is not meant to be
         *        called directly. Please use the create(...) method instead to create
         *        an instance.
         *
         * @param time_keeper The TimeKeeper associated with these vars.
         */
        TimeKeeperVars(const kc::ks_ptr<const TimeKeeper> time_keeper);

        /**
         * @brief Destructor.
         */
        ~TimeKeeperVars();

        /**
         * @brief Create a new instanced of TimeKeeperVars.
         *
         * @param time_keeper The TimeKeeper associated with these vars.
         * @returns A pointer to the newly created instance of TimeKeeperVars.
         */
        static kc::ks_ptr<TimeKeeperVars> create(const kc::ks_ptr<const TimeKeeper> time_keeper);

        /// The current time.
        kc::ks_ptr<kc::Var_T<double>> time;

        /**
         * @brief Get all the Vars that this TimeKeeperVars has.
         *
         * @returns A NestedVars struct with all of this TimeKeeperVars's vars.
         */
        kc::NestedVars getAllVars() const override;
    };

    /**
     * @brief Keeps track of time.
     */
    class TimeKeeper : public kc::BaseWithVars {
      public:
        /**
         * @brief System-clock time point with microsecond storage.
         */
        using TimePoint =
            std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds>;

        /**
         * @brief TimeKeeper constructor. The constructor is not meant to be called
         *        directly. Please use the corresponding create(...) method instead to create an
         * instance.
         *
         * @param fc FrameContainer. Used to update ephemeris in derived classes.
         *        The _initial_ephemeris will also be set to the FrameContainer's current ephemeris.
         */
        TimeKeeper(const kc::ks_ptr<kf::FrameContainer> &fc);

        /**
         * @brief Create a new instance of TimeKeeper.
         *
         * @param fc FrameContainer. Used to update ephemeris in derived classes.
         *        The _initial_ephemeris will also be set to the FrameContainer's current ephemeris.
         * @returns A pointer to the newly created instance of TimeKeeper.
         */
        static kc::ks_ptr<TimeKeeper> create(const kc::ks_ptr<kf::FrameContainer> &fc);

        /**
         * @brief Copy constructor.
         *
         * @param other The TimeKeeper instance to copy from.
         */
        TimeKeeper(const TimeKeeper &other);

        /**
         * @brief Get the time since the initial ephemeris from TimeKeeper.
         *
         * This is the time from the initial ephemeris in ET time.
         *
         * @return The current time in seconds since the initial ephemeris.
         */
        const km::Ktime &getTime() const;

        /**
         * @brief Get the current time in the requested representation.
         *
         * @param time_type Requested output representation.
         * @return The number of seconds or days since the J2000 epoch.
         *         Seconds for second-based types and days for Julian-date-like.
         *         types.
         */
        double getTime(TimeType time_type) const;

        /**
         * @brief Get the current time as a UTC-like system clock time.
         *
         * @return The current time converted to a UTC-like system clock time.
         */
        TimePoint getDateTime() const;

        /**
         * @brief Set the TimeKeeper's time.
         *
         * @param time The current time.
         */
        virtual void setTime(const km::Ktime &time);

        /**
         * @brief Determine if the TimeKeeper is initialized.
         *
         * @return true if initialized, false otherwise.
         */
        bool isReady() const override;

        /**
         * @brief Set the initial ephemeris.
         *
         * @param initial_ephemeris The initial ephemeris. This is in seconds for second-based
         *        types, and days for `JD` and `MJD`.
         * @param type Time representation of `initial_ephemeris`.
         */
        void setInitialEphemeris(double initial_ephemeris, TimeType type);

        /**
         * @brief Set the initial ephemeris from a UTC-like time point.
         *
         * @param time_point The UTC time point to use as the initial ephemeris.
         */
        void setInitialEphemeris(const TimePoint &time_point);

        /**
         * @brief Set \f$\Delta UT1 = UT1 - UTC\f$.
         *
         * @param delta_ut1_seconds UT1 offset in seconds.
         */
        void setUT1Offset(double delta_ut1_seconds);

        /**
         * @brief Get the configured \f$\Delta UT1 = UT1 - UTC\f$.
         *
         * @return UT1 offset in seconds.
         * @throws std::runtime_error if the value is still uninitialized.
         */
        double getUT1Offset() const;

        /**
         * @brief Enable or disable the periodic relativistic correction.
         *
         * @param enable If true, inputs/outputs include the periodic ET-to-TT
         *               correction; if false, ET and TT seconds are treated as equal.
         */
        void setRelativisticCorrection(bool enable);

        /**
         * @brief Check whether the periodic relativistic correction is enabled.
         *
         * @return True when the relativistic correction is enabled; false otherwise.
         */
        bool getRelativisticCorrection() const;

        /**
         * @brief Set the leap seconds file.
         *
         * @param leap_seconds_file Filepath to the leap seconds file.
         */
        void setLeapSecondsFile(const std::filesystem::path &leap_seconds_file);

        /**
         * @brief Get the loaded leap-second filepath.
         *
         * @return Configured path to the leap seconds file.
         */
        const std::filesystem::path &getLeapSecondsFile() const;

      protected:
        Karana::Core::ks_ptr<Karana::Core::BaseVars> _getVars() const override;

        /// The current time.
        km::Ktime _time;

        /// The FrameContainer to keep in sync.
        kc::ks_ptr<kf::FrameContainer> _fc;

        /// Initial ephemeris.
        double _initial_ephemeris = km::notReadyNaN;

      private:
        /**
         * @brief Load the configured leap second table into memory.
         */
        void _loadLeapSeconds();

        /**
         * @brief Convert a user scalar input to ET seconds.
         *
         * @param value Input value. Seconds for second-based types, days for
         *              days-based types.
         * @param type Time representation of `value`.
         * @return ET elapsed seconds under the current conversion settings since
         *            the J2000 epoch.
         */
        [[nodiscard]] double _convertToEt(double value, TimeType type) const;

        /**
         * @brief Recompute the stored initial ephemeris from saved user input.
         *
         * This is used when conversion settings change after the user has already
         * provided an initial epoch.
         */
        void _updateInitialEphemeris();

        /**
         * @brief Saved scalar input value used to recompute `_initial_ephemeris`.
         */
        double _initial_input_value{Math::notReadyNaN};

        /**
         * @brief Saved scalar input type used to recompute `_initial_ephemeris`.
         */
        TimeType _initial_input_type{TimeType::ET};

        /**
         * @brief Configured \f$\Delta UT1 = UT1 - UTC\f$ in seconds.
         */
        double _ut1_offset_seconds{km::notReadyNaN};

        /**
         * @brief Whether to apply the periodic ET-TT relativistic correction.
         */
        bool _relativistic_correction_enabled{true};

        /**
         * @brief Path to the TAI-UTC leap-second table.
         */
        std::filesystem::path _leap_seconds_path{};
    };
} // namespace Karana::Dynamics