Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update time system library #548

Merged
merged 16 commits into from
Dec 6, 2023
2 changes: 2 additions & 0 deletions src/library/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ add_library(${PROJECT_NAME} STATIC
planet_rotation/moon_rotation_utilities.cpp

time_system/date_time_format.cpp
time_system/epoch_time.cpp
time_system/gps_time.cpp

external/igrf/igrf.cpp
external/inih/ini.c
Expand Down
31 changes: 31 additions & 0 deletions src/library/time_system/date_time_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,43 @@
* @brief Class to handle Gregorian date and time format
*/

#define _CRT_SECURE_NO_WARNINGS // for sscanf

#include "date_time_format.hpp"

#include <cmath>
#include <iomanip>
#include <sstream>

DateTime::DateTime(const std::string date_time) {
sscanf(date_time.c_str(), "%zu/%zu/%zu %zu:%zu:%lf", &year_, &month_, &day_, &hour_, &minute_, &second_);
}

DateTime::DateTime(const EpochTime epoch_time) {
// No leap second calculation
const int mday[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //!< Number of days in a month
int days, sec, mon, day;

// leap year if year%4==0 in 1901-2099
days = (int)(epoch_time.GetTime_s() / 86400);
sec = (int)(epoch_time.GetTime_s() - (time_t)days * 86400);

for (day = days % 1461, mon = 0; mon < 48; mon++) {
if (day >= mday[mon])
day -= mday[mon];
else
break;
}

year_ = 1970 + days / 1461 * 4 + mon / 12;
month_ = mon % 12 + 1;
day_ = day + 1;
hour_ = sec / 3600;
minute_ = sec % 3600 / 60;
second_ = sec % 60 + epoch_time.GetFraction_s();
}

std::string DateTime::GetAsString() const {
std::stringstream stream;
stream << std::setw(4) << std::setfill('0') << (int)year_ << "/";
Expand Down
15 changes: 10 additions & 5 deletions src/library/time_system/date_time_format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
* @brief Class to handle Gregorian date and time format
*/

#define _CRT_SECURE_NO_WARNINGS // for sscanf

#ifndef S2E_LIBRARY_TIME_SYSTEM_DATE_TIME_FORMAT_HPP_
#define S2E_LIBRARY_TIME_SYSTEM_DATE_TIME_FORMAT_HPP_

#include <string>

#include "epoch_time.hpp"

class EpochTime;

/**
*@class DateTime
* @brief Class to handle Gregorian date and time format
Expand All @@ -28,9 +30,12 @@ class DateTime {
* @brief Constructor initialized with string expression as YYYY/MM/DD hh:mm:ss.s
* @note TODO: Support other format like dd.mm.yyyy
*/
DateTime(const std::string date_time = "0000/01/01 00:00:00.0") {
sscanf(date_time.c_str(), "%zu/%zu/%zu %zu:%zu:%lf", &year_, &month_, &day_, &hour_, &minute_, &second_);
}
DateTime(const std::string date_time = "0000/01/01 00:00:00.0");
/**
* @fn DateTime
* @brief Constructor initialized with epoch time
*/
DateTime(const EpochTime epoch_time);

// Getters
inline size_t GetYear() const { return year_; }
Expand Down
75 changes: 75 additions & 0 deletions src/library/time_system/epoch_time.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @file epoch_time.cpp
* @brief A class to handle time like UNIX time with fractions
*/

#include "epoch_time.hpp"

#include <cmath>

EpochTime::EpochTime(const DateTime date_time) {
// No leap second calculation
const int doy[] = {1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; //!< Day of Year for the 1st day of each month
// Parse Calender
const size_t year = date_time.GetYear();
const size_t month = date_time.GetMonth();
const size_t day = date_time.GetDay();
const size_t hour = date_time.GetHour();
const size_t minute = date_time.GetMinute();
const size_t second = (size_t)std::floor(date_time.GetSecond());

// TODO: assertion
if (year < 1970 || month < 1 || 12 < month) return;
if (day < 1 || 32 <= day) return;
if (60 <= minute) return;
if (60 <= second) return;

// leap year if year%4==0 in 1901-2099
uint64_t days = (year - 1970) * 365 + (year - 1969) / 4 + doy[month - 1] + day - 2 + (year % 4 == 0 && month >= 3 ? 1 : 0);
time_s_ = (uint64_t)days * 86400 + hour * 3600 + minute * 60 + second;
fraction_s_ = date_time.GetSecond() - (double)second;
}

bool EpochTime::operator==(const EpochTime& target) const {
if (this->time_s_ != target.time_s_) return false;
if (this->fraction_s_ != target.fraction_s_) return false; // TODO: comparison of double
return true;
}

bool EpochTime::operator>(const EpochTime& right_side) const {
if (*this == right_side) return false;
if (this->time_s_ < right_side.time_s_) return false;
if (this->time_s_ > right_side.time_s_) return true;
if (this->fraction_s_ < right_side.fraction_s_) return false;
return true;
}

bool EpochTime::operator<(const EpochTime& right_side) const {
if (*this == right_side) return false;
if (this->time_s_ > right_side.time_s_) return false;
if (this->time_s_ < right_side.time_s_) return true;
if (this->fraction_s_ > right_side.fraction_s_) return false;
return true;
}

EpochTime EpochTime::operator+(const EpochTime& right_side) const {
time_t time_s = this->time_s_ + right_side.GetTime_s();
double fraction_s = this->fraction_s_ + right_side.GetFraction_s();
if (fraction_s > 1.0) {
fraction_s -= 1.0;
time_s += 1;
}
EpochTime result(time_s, fraction_s);
return result;
}

EpochTime EpochTime::operator-(const EpochTime& right_side) const {
time_t time_s = this->time_s_ - right_side.GetTime_s(); // TODO: assertion for negative value
double fraction_s = this->fraction_s_ - right_side.GetFraction_s();
if (fraction_s < 0.0) {
fraction_s += 1.0;
time_s -= 1;
}
EpochTime result(time_s, fraction_s);
return result;
}
70 changes: 70 additions & 0 deletions src/library/time_system/epoch_time.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* @file epoch_time.hpp
* @brief A class to handle time like UNIX time with fractions
*/

#ifndef S2E_LIBRARY_TIME_SYSTEM_EPOCH_TIME_HPP_
#define S2E_LIBRARY_TIME_SYSTEM_EPOCH_TIME_HPP_

#include <cstdint>

#include "date_time_format.hpp"

class DateTime;

/**
* @class EpochTime
* @brief A class to handle time like UNIX time with fractions.
* @note This class doesn't care leap seconds.
*/
class EpochTime {
public:
/**
* @fn EpochTime
* @brief Constructor initialized with week and second
*/
EpochTime(const uint64_t time_s = 0, const double fraction_s = 0.0) : time_s_(time_s), fraction_s_(fraction_s) {}
/**
* @fn EpochTime
* @brief Constructor initialized with date time expression
*/
EpochTime(const DateTime date_time);
/**
* @fn ~EpochTime
* @brief Destructor
*/
~EpochTime() {}

// Getter
/**
* @fn GetTime_s
* @return time [s]
*/
inline uint64_t GetTime_s() const { return time_s_; }
/**
* @fn GetFraction_s
* @return fraction time [s]
*/
inline double GetFraction_s() const { return fraction_s_; }
/**
* @fn GetTimeWithFraction_s
* @return time + fraction [s]
*/
inline double GetTimeWithFraction_s() const { return (double)(time_s_) + fraction_s_; }

// Operator
bool operator==(const EpochTime& target) const;
bool operator!=(const EpochTime& target) const { return !(*this == target); }
bool operator>(const EpochTime& right_side) const;
bool operator<=(const EpochTime& right_side) const { return !(*this > right_side); }
bool operator<(const EpochTime& right_side) const;
bool operator>=(const EpochTime& right_side) const { return !(*this < right_side); }
EpochTime operator+(const EpochTime& right_side) const;
EpochTime operator-(const EpochTime& right_side) const;

private:
uint64_t time_s_; //!< Number of seconds without leap seconds since 00:00:00 Jan 1 1970 UTC.
double fraction_s_; //!< Fraction of second under 1 sec [0, 1)
};

#endif // S2E_LIBRARY_TIME_SYSTEM_EPOCH_TIME_HPP_
25 changes: 25 additions & 0 deletions src/library/time_system/gps_time.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @file gps_time.cpp
* @brief A class to define GPS time expression
*/

#include "gps_time.hpp"

const DateTime GpsTime::kEpochOfGpsTimeInDateTime_ = DateTime("1980/1/6 00:00:00.0");
const EpochTime GpsTime::kEpochOfGpsTimeInEpochTime_ = EpochTime(kEpochOfGpsTimeInDateTime_);
const EpochTime GpsTime::kLeapSecondAheadFromUtc_ = EpochTime(18, 0); //!< Leap second ahead from UTC @ May 2023

void GpsTime::CalcGpsWeekTime() {
EpochTime time_diff = epoch_time_ - kEpochOfGpsTimeInEpochTime_;
week_ = (size_t)(time_diff.GetTime_s() / kSecondsInWeek_);
elapsed_time_from_week_s_ = (double)(time_diff.GetTime_s() - week_ * kSecondsInWeek_) + time_diff.GetFraction_s();
}

void GpsTime::CalcEpochTime() {
size_t integer_time_s = (size_t)elapsed_time_from_week_s_;
double fraction_s = elapsed_time_from_week_s_ - (double)(integer_time_s);
time_t time_s = week_ * kSecondsInWeek_ + integer_time_s;
EpochTime time_diff(time_s, fraction_s);

epoch_time_ = kEpochOfGpsTimeInEpochTime_ + time_diff;
}
60 changes: 58 additions & 2 deletions src/library/time_system/gps_time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

#include <cstddef>

#include "date_time_format.hpp"
#include "epoch_time.hpp"

/**
* @class GpsTime
* @brief A class to define GPS time expression
Expand All @@ -19,8 +22,25 @@ class GpsTime {
* @fn GpsTime
* @brief Constructor initialized with week and second
*/
GpsTime(const size_t week = 0, const double elapsed_time_from_week_s = 0.0) : week_(week), elapsed_time_from_week_s_(elapsed_time_from_week_s) {}
GpsTime(const size_t week = 0, const double elapsed_time_from_week_s = 0.0) : week_(week), elapsed_time_from_week_s_(elapsed_time_from_week_s) {
CalcEpochTime();
date_time_ = DateTime(epoch_time_);
}
/**
* @fn GpsTime
* @brief Constructor initialized with epoch time expression
*/
GpsTime(const EpochTime epoch_time) : date_time_(DateTime(epoch_time)), epoch_time_(epoch_time) { CalcGpsWeekTime(); }
/**
* @fn GpsTime
* @brief Constructor initialized with calender expression
*/
GpsTime(const DateTime date_time) : date_time_(date_time), epoch_time_(EpochTime(date_time_)) { CalcGpsWeekTime(); }

/**
* @fn ~GpsTime
* @brief Destructor
*/
~GpsTime() {}

/**
Expand All @@ -30,13 +50,49 @@ class GpsTime {
inline size_t GetWeek() const { return week_; }
/**
* @fn GetTime
* @return GPS time
* @return GPS time from week
*/
inline double GetElapsedTimeFromWeek_s() const { return elapsed_time_from_week_s_; }
/**
* @fn GetEpochTime
* @return GPS time in epoch time expression
*/
inline EpochTime GetEpochTime() const { return epoch_time_; }
/**
* @fn GetDateTime
* @return GPS time in date time expression
*/
inline DateTime GetDateTime() const { return date_time_; }
/**
* @fn GetDateTimeAsUtc
* @return DateTime as UTC including leap seconds
*/
inline DateTime GetDateTimeAsUtc() const { return epoch_time_ - kLeapSecondAheadFromUtc_; }

private:
size_t week_; //!< GPS week (week = 0 at 6th Jan. 1980)
double elapsed_time_from_week_s_; //!< Elapsed time from the GPS week [s] [0,0, 604800.0)
// Expressions
DateTime date_time_; //!< GPS time in date time expression
EpochTime epoch_time_; //!< GPS time in epoch time expression
// Epoch of GPS time
static const DateTime kEpochOfGpsTimeInDateTime_; //!< GPS time epoch in date time expression
static const EpochTime kEpochOfGpsTimeInEpochTime_; //!< GPS time epoch in epoch time expression
// Constants
static const size_t kSecondsInWeek_ = 86400 * 7; //!< Seconds in Week
static const EpochTime kLeapSecondAheadFromUtc_; //!< leap second ahead from UTC

// Functions
/**
* @fn CalcGpsWeekTime
* @brief Calculate GPS Time from epoch time
*/
void CalcGpsWeekTime();
/**
* @fn CalcEpochTime
* @brief Calculate Epoch time from GPS time
*/
void CalcEpochTime();
};

#endif // S2E_LIBRARY_TIME_SYSTEM_GPS_TIME_HPP_
16 changes: 16 additions & 0 deletions src/library/time_system/test_date_time_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ TEST(DateTime, ConstructorWithString) {
EXPECT_DOUBLE_EQ(59.4, date_time.GetSecond());
}

/**
* @brief Test Constructor with epoch time
*/
TEST(DateTime, ConstructorWithEpochTime) {
// Reference for correctness check: https://www.epochconverter.com/
EpochTime epoch_time(1686145305, 0.3);
DateTime date_time(epoch_time);

EXPECT_EQ(2023, date_time.GetYear());
EXPECT_EQ(6, date_time.GetMonth());
EXPECT_EQ(7, date_time.GetDay());
EXPECT_EQ(13, date_time.GetHour());
EXPECT_EQ(41, date_time.GetMinute());
EXPECT_DOUBLE_EQ(45.3, date_time.GetSecond());
}

/**
* @brief Test get as string
*/
Expand Down
Loading
Loading