From 76d61129c0ddeae7f22538d9204e868d543a9a85 Mon Sep 17 00:00:00 2001 From: xanthos Date: Tue, 17 Oct 2023 15:31:14 +0300 Subject: [PATCH] testing tpdates --- src/datetime_tops.hpp | 16 ++++++++ src/tpdate.hpp | 31 +++++++++++++- test/unit_tests/tpdates1.cpp | 78 ++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 test/unit_tests/tpdates1.cpp diff --git a/src/datetime_tops.hpp b/src/datetime_tops.hpp index 08c65bc..6ef70ab 100644 --- a/src/datetime_tops.hpp +++ b/src/datetime_tops.hpp @@ -376,6 +376,22 @@ constexpr typename S::underlying_type max_days_allowed() { S::max_in_day; } +/** Transform any second type S to fractional days + * + * @warning This function assumes that a day is made up of exactly 86400 sec + * and is thus not able to represent a fractional day when the day at hand is + * on a leap second insertion. + */ +#if __cplusplus >= 202002L +template +#else +template > +#endif +double fractional_days(S nsec) noexcept { + const double sec = static_cast(nsec.__member_ref__()); + return sec / static_cast(S::max_in_day); +} + /** Explicit cast of any second type to another second type. * * Cast an instance of any second type (aka any instance for which diff --git a/src/tpdate.hpp b/src/tpdate.hpp index c8974f1..50cba7f 100644 --- a/src/tpdate.hpp +++ b/src/tpdate.hpp @@ -1,3 +1,10 @@ +/** @file + * A utility class to hold datetime instances, in a continuous time-scale + * (e.g. TT, TAI, etc). In construst to datetime, this is not a template + * class and uses a storage method of two floating point numbers (one for + * MJD and one for time of day) to represent datetime epochs. + */ + #ifndef __DSO_DATETIME_TWOPARTDATES_HPP__ #define __DSO_DATETIME_TWOPARTDATES_HPP__ @@ -27,6 +34,13 @@ struct TwoPartJulianDate { /** Given an MJD, turn it to a JD and return it split in a convinient way * The way the (returned) JD is split is determined by the template parameter * \p S. + * + * @param[in] mjd The Modified Julian Day (integral part) + * @param[in] fday The time of day as fractional days + * @return An TwoPartJulianDate instance; the two parts of the instance if + * added, give the JD corresponding to the passed in MJD (i.e. \p mjd). + * The method in which the JD is plit, is driven by the template + * parameter S. */ template TwoPartJulianDate jd_split(double mjd, double fday) noexcept { @@ -41,6 +55,15 @@ TwoPartJulianDate jd_split(double mjd, double fday) noexcept { } } /* namespace core */ +/** A datetime class to represent epochs in any continuous system. + * + * A TwoPartDate instance conviniently splits a datetime into two numeric + * values: + * - the Modified Julian Day (i.e. anumeric value which only has an integral + * part), and + * - the time of day, which is stored in fractional days + * + */ class TwoPartDate { private: double _mjd; /** Mjd */ @@ -55,7 +78,8 @@ class TwoPartDate { #endif TwoPartDate(const datetime &d) noexcept : _mjd((double)d.imjd().as_underlying_type()), - _fday(d.sec().fractional_days()) { + //_fday(d.sec().fractional_days()) { + _fday(fractional_days(d.sec())) { this->normalize(); } @@ -83,7 +107,7 @@ class TwoPartDate { * day * * This is not an 'actual date' but rather a datetime interval, but can be - * represented a TwoPartDate instance. If the calling instance is prior to + * represented by a TwoPartDate instance. If the calling instance is prior to * the operand (i.e. d1-d2 with d2>d1) the interval is signed as 'negative'. * This means that the number of days can be negative, but the fractional * day will always be positive @@ -237,6 +261,9 @@ class TwoPartDate { _fday = std::modf(_fday, &extra); _mjd += extra; } +#ifdef DEBUG + assert(_fday >= 0e0 && _fday < 1e0); +#endif // all done return; } diff --git a/test/unit_tests/tpdates1.cpp b/test/unit_tests/tpdates1.cpp new file mode 100644 index 0000000..6ff3347 --- /dev/null +++ b/test/unit_tests/tpdates1.cpp @@ -0,0 +1,78 @@ +#include "dtcalendar.hpp" +#include "tpdate.hpp" +#include +#include + +using namespace dso; + +constexpr const long num_tests = 1'000'000; +using nsec = dso::nanoseconds; +typedef nsec::underlying_type SecIntType; +constexpr const SecIntType TOSEC = nsec::sec_factor(); +constexpr const double SECINDAY = nsec::max_in_day; + +int main() { + /* Generators for random numbers ... */ + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> ydstr(1972, 2050); /* range for years */ + std::uniform_int_distribution<> mdstr(1, 12); /* range for months */ + std::uniform_int_distribution<> ddstr(1, 31); /* range for day of month */ + std::uniform_int_distribution nsdstr( + 0, nsec::max_in_day); /* range for day of month */ + + int testnr = 0, ok; + datetime d1, d2, d3; + while (testnr < num_tests) { + /* do we have a valid date ? */ + try { + d1 = datetime{year(ydstr(gen)), month(mdstr(gen)), + day_of_month(ddstr(gen)), nsec(nsdstr(gen))}; + d2 = datetime{year(ydstr(gen)), month(mdstr(gen)), + day_of_month(ddstr(gen)), nsec(nsdstr(gen))}; + /* d3 on same day as d2 */ + d3 = datetime{d2.imjd(), nsec(nsdstr(gen))}; + ok = 1; + } catch (std::exception &) { + ok = 0; + } + if (ok) { + /* construct a TwoPartDate from a datetime */ + TwoPartDate tpd1(d1); + assert(tpd1.imjd() == d1.imjd().as_underlying_type()); + assert(tpd1.fday() == d1.sec().to_fractional_seconds()/SECINDAY); + /* constructor from MJD and fraction of day */ + tpd1 = TwoPartDate(d1.imjd().as_underlying_type(), + d1.sec().to_fractional_seconds() / SECINDAY); + assert(tpd1.imjd() == d1.imjd().as_underlying_type()); + assert(tpd1.fday() == d1.sec().to_fractional_seconds() / SECINDAY); + + TwoPartDate tpd2(d2); + TwoPartDate tpd3(d3); + + /* how its done using datetime_intervals */ + const auto interval = d2 - d1; + auto d = d1 + interval; + assert(d == d2); + /* same thing using TwoPartDate's */ + const auto i1 = tpd2 - tpd1; + auto tpd = tpd1 + i1; + assert(tpd == tpd2); + + /* using datetime_interval ... */ + const auto d32 = d3 - d2; + d = d2 + d32; + assert(d == d3); + /* same thing using TwoPartDate's */ + const auto tpd32 = tpd3 - tpd2; + tpd = tpd2 + tpd32; + assert(tpd == tpd3); + + ++testnr; + if (testnr % 10) + printf("%d/%ld\r", testnr, num_tests); + } + } + + return 0; +}