/* * Copyright 2013 Daniel Warner * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include "TimeSpan.h" #include "Util.h" namespace libsgp4 { namespace { static int daysInMonth[2][13] = { // 1 2 3 4 5 6 7 8 9 10 11 12 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static int cumulDaysInMonth[2][13] = { // 1 2 3 4 5 6 7 8 9 10 11 12 {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} }; } /** * @brief Represents an instance in time. */ class DateTime { public: /** * Default contructor * Initialise to 0001/01/01 00:00:00.000000 */ DateTime() { Initialise(1, 1, 1, 0, 0, 0, 0); } /** * Constructor * @param[in] ticks raw tick value */ explicit DateTime(int64_t ticks) : m_encoded(ticks) { } /** * Constructor * @param[in] year the year * @param[in] doy the day of the year */ DateTime(unsigned int year, double doy) { m_encoded = TimeSpan( static_cast(AbsoluteDays(year, doy) * TicksPerDay)).Ticks(); } /** * Constructor * @param[in] year the year * @param[in] month the month * @param[in] day the day */ DateTime(int year, int month, int day) { Initialise(year, month, day, 0, 0, 0, 0); } /** * Constructor * @param[in] year the year * @param[in] month the month * @param[in] day the day * @param[in] hour the hour * @param[in] minute the minute * @param[in] second the second */ DateTime(int year, int month, int day, int hour, int minute, int second) { Initialise(year, month, day, hour, minute, second, 0); } /** * Constructor * @param[in] year the year * @param[in] month the month * @param[in] day the day * @param[in] hour the hour * @param[in] minute the minute * @param[in] second the second * @param[in] microsecond the microsecond */ DateTime(int year, int month, int day, int hour, int minute, int second, int microsecond) { Initialise(year, month, day, hour, minute, second, microsecond); } /** * Initialise to the given data and time. * @param[in] year the year * @param[in] month the month * @param[in] day the day * @param[in] hour the hour * @param[in] minute the minute * @param[in] second the second * @param[in] microsecond the microsecond */ void Initialise(int year, int month, int day, int hour, int minute, int second, int microsecond) { if (!IsValidYearMonthDay(year, month, day) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59 || microsecond < 0 || microsecond > 999999) { assert(false && "Invalid date"); } m_encoded = TimeSpan( AbsoluteDays(year, month, day), hour, minute, second, microsecond).Ticks(); } /** * Return the current time * @param[in] microseconds whether to set the microsecond component * @returns a DateTime object set to the current date and time */ static DateTime Now(bool useMicroseconds = false) { using namespace std::chrono; if (useMicroseconds) { return DateTime(UnixEpoch + duration_cast(system_clock::now() .time_since_epoch()).count() * TicksPerMicrosecond); } else { return DateTime(UnixEpoch + duration_cast(system_clock::now() .time_since_epoch()).count() * TicksPerSecond); } } /** * Find whether a year is a leap year * @param[in] year the year to check * @returns whether the year is a leap year */ static bool IsLeapYear(int year) { if (!IsValidYear(year)) { assert(false && "Invalid year"); } return (((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0); } /** * Checks whether the given year is valid * @param[in] year the year to check * @returns whether the year is valid */ static bool IsValidYear(int year) { bool valid = true; if (year < 1 || year > 9999) { valid = false; } return valid; } /** * Check whether the year/month is valid * @param[in] year the year to check * @param[in] month the month to check * @returns whether the year/month is valid */ static bool IsValidYearMonth(int year, int month) { bool valid = true; if (IsValidYear(year)) { if (month < 1 || month > 12) { valid = false; } } else { valid = false; } return valid; } /** * Check whether the year/month/day is valid * @param[in] year the year to check * @param[in] month the month to check * @param[in] day the day to check * @returns whether the year/month/day is valid */ static bool IsValidYearMonthDay(int year, int month, int day) { bool valid = true; if (IsValidYearMonth(year, month)) { if (day < 1 || day > DaysInMonth(year, month)) { valid = false; } } else { valid = false; } return valid; } /** * Find the number of days in a month given the year/month * @param[in] year the year * @param[in] month the month * @returns the days in the given month */ static int DaysInMonth(int year, int month) { if (!IsValidYearMonth(year, month)) { assert(false && "Invalid year and month"); } const int* daysInMonthPtr; if (IsLeapYear(year)) { daysInMonthPtr = daysInMonth[1]; } else { daysInMonthPtr = daysInMonth[0]; } return daysInMonthPtr[month]; } /** * Find the day of the year given the year/month/day * @param[in] year the year * @param[in] month the month * @param[in] day the day * @returns the day of the year */ int DayOfYear(int year, int month, int day) const { if (!IsValidYearMonthDay(year, month, day)) { assert(false && "Invalid year, month and day"); } int daysThisYear = day; if (IsLeapYear(year)) { daysThisYear += cumulDaysInMonth[1][month]; } else { daysThisYear += cumulDaysInMonth[0][month]; } return daysThisYear; } /** * */ double AbsoluteDays(unsigned int year, double doy) const { int64_t previousYear = year - 1; /* * + days in previous years ignoring leap days * + Julian leap days before this year * - minus prior century years * + plus prior years divisible by 400 days */ int64_t daysSoFar = 365 * previousYear + previousYear / 4LL - previousYear / 100LL + previousYear / 400LL; return static_cast(daysSoFar) + doy - 1.0; } int AbsoluteDays(int year, int month, int day) const { int previousYear = year - 1; /* * days this year (0 - ...) * + days in previous years ignoring leap days * + Julian leap days before this year * - minus prior century years * + plus prior years divisible by 400 days */ int result = DayOfYear(year, month, day) - 1 + 365 * previousYear + previousYear / 4 - previousYear / 100 + previousYear / 400; return result; } TimeSpan TimeOfDay() const { return TimeSpan(Ticks() % TicksPerDay); } int DayOfWeek() const { /* * The fixed day 1 (January 1, 1 Gregorian) is Monday. * 0 Sunday * 1 Monday * 2 Tuesday * 3 Wednesday * 4 Thursday * 5 Friday * 6 Saturday */ return static_cast(((m_encoded / TicksPerDay) + 1LL) % 7LL); } bool Equals(const DateTime& dt) const { return (m_encoded == dt.m_encoded); } int Compare(const DateTime& dt) const { int ret = 0; if (m_encoded < dt.m_encoded) { return -1; } else if (m_encoded > dt.m_encoded) { return 1; } return ret; } DateTime AddYears(const int years) const { return AddMonths(years * 12); } DateTime AddMonths(const int months) const { int year; int month; int day; FromTicks(year, month, day); month += months % 12; year += months / 12; if (month < 1) { month += 12; --year; } else if (month > 12) { month -= 12; ++year; } int maxday = DaysInMonth(year, month); day = std::min(day, maxday); return DateTime(year, month, day).Add(TimeOfDay()); } /** * Add a TimeSpan to this DateTime * @param[in] t the TimeSpan to add * @returns a DateTime which has the given TimeSpan added */ DateTime Add(const TimeSpan& t) const { return AddTicks(t.Ticks()); } DateTime AddDays(const double days) const { return AddMicroseconds(days * 86400000000.0); } DateTime AddHours(const double hours) const { return AddMicroseconds(hours * 3600000000.0); } DateTime AddMinutes(const double minutes) const { return AddMicroseconds(minutes * 60000000.0); } DateTime AddSeconds(const double seconds) const { return AddMicroseconds(seconds * 1000000.0); } DateTime AddMicroseconds(const double microseconds) const { auto ticks = static_cast(microseconds * TicksPerMicrosecond); return AddTicks(ticks); } DateTime AddTicks(int64_t ticks) const { return DateTime(m_encoded + ticks); } /** * Get the number of ticks * @returns the number of ticks */ int64_t Ticks() const { return m_encoded; } void FromTicks(int& year, int& month, int& day) const { int totalDays = static_cast(m_encoded / TicksPerDay); /* * number of 400 year cycles */ int num400 = totalDays / 146097; totalDays -= num400 * 146097; /* * number of 100 year cycles */ int num100 = totalDays / 36524; if (num100 == 4) { /* * last day of the last leap century */ num100 = 3; } totalDays -= num100 * 36524; /* * number of 4 year cycles */ int num4 = totalDays / 1461; totalDays -= num4 * 1461; /* * number of years */ int num1 = totalDays / 365; if (num1 == 4) { /* * last day of the last leap olympiad */ num1 = 3; } totalDays -= num1 * 365; /* * find year */ year = (num400 * 400) + (num100 * 100) + (num4 * 4) + num1 + 1; /* * convert day of year to month/day */ const int* daysInMonthPtr; if (IsLeapYear(year)) { daysInMonthPtr = daysInMonth[1]; } else { daysInMonthPtr = daysInMonth[0]; } month = 1; while (totalDays >= daysInMonthPtr[month] && month <= 12) { totalDays -= daysInMonthPtr[month++]; } day = totalDays + 1; } int Year() const { int year; int month; int day; FromTicks(year, month, day); return year; } int Month() const { int year; int month; int day; FromTicks(year, month, day); return month; } int Day() const { int year; int month; int day; FromTicks(year, month, day); return day; } /** * Hour component * @returns the hour component */ int Hour() const { return static_cast(m_encoded % TicksPerDay / TicksPerHour); } /** * Minute component * @returns the minute component */ int Minute() const { return static_cast(m_encoded % TicksPerHour / TicksPerMinute); } /** * Second component * @returns the Second component */ int Second() const { return static_cast(m_encoded % TicksPerMinute / TicksPerSecond); } /** * Microsecond component * @returns the microsecond component */ int Microsecond() const { return static_cast(m_encoded % TicksPerSecond / TicksPerMicrosecond); } /** * Convert to a julian date * @returns the julian date */ double ToJulian() const { auto ts = TimeSpan(Ticks()); return ts.TotalDays() + 1721425.5; } /** * Convert to greenwich sidereal time * @returns the greenwich sidereal time */ double ToGreenwichSiderealTime() const { // julian date of previous midnight double jd0 = floor(ToJulian() + 0.5) - 0.5; // julian centuries since epoch double t = (jd0 - 2451545.0) / 36525.0; double jdf = ToJulian() - jd0; double gt = 24110.54841 + t * (8640184.812866 + t * (0.093104 - t * 6.2E-6)); gt += jdf * 1.00273790935 * 86400.0; // 360.0 / 86400.0 = 1.0 / 240.0 return Util::WrapTwoPI(Util::DegreesToRadians(gt / 240.0)); } /** * Return the modified julian date since the j2000 epoch * January 1, 2000, at 12:00 TT * @returns the modified julian date */ double ToJ2000() const { return ToJulian() - 2415020.0; } /** * Convert to local mean sidereal time (GMST plus the observer's longitude) * @param[in] lon observers longitude * @returns the local mean sidereal time */ double ToLocalMeanSiderealTime(const double lon) const { return Util::WrapTwoPI(ToGreenwichSiderealTime() + lon); } std::string ToString() const { std::stringstream ss; int year; int month; int day; FromTicks(year, month, day); ss << std::right << std::setfill('0'); ss << std::setw(4) << year << "-"; ss << std::setw(2) << month << "-"; ss << std::setw(2) << day << " "; ss << std::setw(2) << Hour() << ":"; ss << std::setw(2) << Minute() << ":"; ss << std::setw(2) << Second() << "."; ss << std::setw(6) << Microsecond() << " UTC"; return ss.str(); } private: int64_t m_encoded{}; }; inline std::ostream& operator<<(std::ostream& strm, const DateTime& dt) { return strm << dt.ToString(); } inline DateTime operator+(const DateTime& dt, TimeSpan ts) { return DateTime(dt.Ticks() + ts.Ticks()); } inline DateTime operator-(const DateTime& dt, const TimeSpan& ts) { return DateTime(dt.Ticks() - ts.Ticks()); } inline TimeSpan operator-(const DateTime& dt1, const DateTime& dt2) { return TimeSpan(dt1.Ticks() - dt2.Ticks()); } inline bool operator==(const DateTime& dt1, const DateTime& dt2) { return dt1.Equals(dt2); } inline bool operator>(const DateTime& dt1, const DateTime& dt2) { return (dt1.Compare(dt2) > 0); } inline bool operator>=(const DateTime& dt1, const DateTime& dt2) { return (dt1.Compare(dt2) >= 0); } inline bool operator!=(const DateTime& dt1, const DateTime& dt2) { return !dt1.Equals(dt2); } inline bool operator<(const DateTime& dt1, const DateTime& dt2) { return (dt1.Compare(dt2) < 0); } inline bool operator<=(const DateTime& dt1, const DateTime& dt2) { return (dt1.Compare(dt2) <= 0); } } // namespace libsgp4