Skip to content

File Date.cpp

File List > astrea > astro > astro > time > Date.cpp

Go to the documentation of this file

/*
 * The GNU Lesser General Public License (LGPL)
 *
 * Copyright (c) 2025 Jay Iuliano
 *
 * This file is part of Astrea.
 * Astrea is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * Astrea is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should
 * have received a copy of the GNU General Public License along with Astrea. If not, see <https://www.gnu.org/licenses/>.
 */

#include <astro/time/Date.hpp>

#include <chrono>
#include <format>
#include <iomanip>
#include <iostream>
#include <sstream>

#include <date/date.h> // NOTE: This is standard in std::chrono as of GNU 13.2
#include <mp-units/math.h>

#include <astro/systems/planetary_bodies/Earth/Earth.hpp>
#include <astro/utilities/conversions.hpp>

using namespace mp_units;

namespace astrea {
namespace astro {

// Stream
std::ostream& operator<<(std::ostream& os, const Date& obj)
{
    os << obj.epoch();
    return os;
}

// Utility operators
Date Date::operator+(const Time& time) const
{
    const auto newTime = _julianDate.time_since_epoch() + JulianDateClock::duration(time);
    return Date(JulianDate(newTime));
}
Date& Date::operator+=(const Time& time)
{
    const auto newTime = _julianDate.time_since_epoch() + JulianDateClock::duration(time);
    _julianDate        = JulianDate(newTime);
    return *this;
}
Date Date::operator-(const Time& time) const
{
    const auto newTime = _julianDate.time_since_epoch() - JulianDateClock::duration(time);
    return Date(JulianDate(newTime));
}
Date& Date::operator-=(const Time& time)
{
    const auto newTime = _julianDate.time_since_epoch() - JulianDateClock::duration(time);
    _julianDate        = JulianDate(newTime);
    return *this;
}

Time Date::operator-(const Date& other) const { return _julianDate - other._julianDate; }

// Conversions
std::string Date::epoch() const
{
    using namespace std::chrono;

    const auto sysTime = round<std::chrono::milliseconds>(clock_cast<system_clock>(_julianDate));
    std::stringstream ss;
    ss << sysTime;
    return ss.str();
}

Angle Date::gmst() const { return julian_date_to_sidereal_time(_julianDate); }

// General conversions
JulianDate epoch_to_julian_date(const std::string& epoch, const std::string format)
{
    using namespace std::chrono;

    // Stream date string into time point
    std::istringstream epochStream{ epoch };
    sys_time<std::chrono::milliseconds> systemTime;
    epochStream >> date::parse(format, systemTime);

    // Convert with clock cast
    return round<std::chrono::milliseconds>(clock_cast<JulianDateClock>(systemTime));
}


const Date Date::now() noexcept { return JulianDateClock::now(); }


Angle julian_date_to_sidereal_time(const JulianDate& _julianDate)
{
    using mp_units::angular::unit_symbols::deg;
    using mp_units::non_si::day;

    const Time julianDay = _julianDate.time_since_epoch().count() * day;

    // UT = (fraction of current Julian Day since 00:00:00 in days) / (body rotation rate in deg/day ratioed to Earth's)
    static const Time halfDay = 0.5 * day;
    const Time universalTime  = (julianDay - (floor<day>(julianDay + halfDay) - halfDay));

    // Greenwich Universal Time
    const Time julianDay0 = julianDay - universalTime; // julian day number on this julian date

    // TODO: This difference can be done, somehow, with mp_units mechanisms
    const auto T0JulianCenturies = julianDay0 - J2000.time_since_epoch().count() * day;
    const auto T0                = T0JulianCenturies / JulianCentury;

    const Angle greenwichUniversalTime =
        (100.4606184 * one + 36000.77005361 * T0 + 0.00038793 * T0 * T0 - 2.583e-8 * T0 * T0 * T0) * deg;

    // GST
    static const AngularRate earthRotRate = planetary_bodies::Earth().get_rotation_rate(); // in rad/s
    const Angle greenwichSiderealTime     = wrap_angle(greenwichUniversalTime + earthRotRate * universalTime);

    return greenwichSiderealTime;
}

} // namespace astro
} // namespace astrea