From f618fb64cbfb02ef7bcb08bc41b49c00d3115060 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Thu, 9 Oct 2025 17:43:40 +0300 Subject: [PATCH] ... --- mcc/mcc_defaults.h | 346 ++++++++++++++++++++++++++++++++++++++ mcc/mcc_generics.h | 49 ++++++ mcc/mcc_utils.h | 1 + mcc/tests/netmsg_test.cpp | 48 +++++- 4 files changed, 443 insertions(+), 1 deletion(-) diff --git a/mcc/mcc_defaults.h b/mcc/mcc_defaults.h index 4106716..dccb662 100644 --- a/mcc/mcc_defaults.h +++ b/mcc/mcc_defaults.h @@ -64,6 +64,352 @@ protected: }; +/* DEFAULT CELESTIAL COORDINATES EPOCH CLASS */ + +class MccCelestialCoordEpoch : public mcc_coord_epoch_interface_t +{ + inline static const std::regex dateTimeISO8601Rx{ + " *[0-9]{4}-[0,1][0-9]-[0-2][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](\\.[0-9]+)? *"}; + + inline static const std::regex dateTimeJEpochRx{" *J[0-9]{4}(\\.[0-9]{1,})+ *"}; + inline static const std::regex dateTimeBEpochRx{" *B[0-9]{4}(\\.[0-9]{1,})+ *"}; + inline static const std::regex dateTimeMJDRx{" *([0-9]*[.])?[0-9]+([eE][-+]?[0-9]+)? *"}; + + + typedef std::chrono::duration> year_fp_t; + typedef std::chrono::duration> day_fp_t; + +public: + static constexpr auto J2000_UTC = + std::chrono::sys_days( + std::chrono::year_month_day(std::chrono::January / std::chrono::day(1) / std::chrono::year(2000))) + + std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816); + + static constexpr double J2000_MJD = 51544.5; + + + static MccCelestialCoordEpoch now() + { + MccCelestialCoordEpoch ep; + ep.fromTimePoint(std::chrono::system_clock::now()); + + return ep; + } + + MccCelestialCoordEpoch() : _MJD(J2000_MJD), _UTC(J2000_UTC), _JEpoch(2000.0) {} + + template + bool fromCharRange(IR&& str) + { + if constexpr (std::is_pointer_v>) { + return fromCharRange(std::string_view{str}); + } + + bool ret = false; + + std::string_view sv = utils::trimSpaces(std::forward(str)); + std::istringstream ist{std::string(sv)}; + + // try ISO8601 date ... + std::chrono::from_stream(ist, "%FT%T", _UTC); + if (ist.fail()) { // not ISO8601 date + // try MJD (floating-point number) ... + std::optional mjd = utils::numFromStr(sv); + if (mjd) { + _MJD = mjd.value(); + ret = fromMJD(); + } else { // not MJD + // try epoch (e.g. J2010.32) + if (sv[0] == 'J') { + auto jep = utils::numFromStr(sv.substr(1)); + if (jep) { + _JEpoch = jep.value(); + ret = fromJEpoch(); + } else { // ERROR!!! + ret = false; + } + } else { // ERROR!!! + ret = false; + } + } + } else { + ret = fromUTC(); + } + + return ret; + } + + template + bool fromTimePoint(std::chrono::time_point&& tp) + { + if constexpr (std::same_as) { + _UTC = std::chrono::time_point_cast(std::forward(tp)); + } else if constexpr (std::same_as) { + auto stp = std::chrono::utc_clock::to_sys(std::forward(tp)); + _UTC = std::chrono::time_point_cast(std::forward(stp)); + } else if constexpr (std::same_as) { + return fromTimePoint(ClockT::to_utc(std::forward(tp))); + } else if constexpr (std::same_as) { + return fromTimePoint(ClockT::to_utc(std::forward(tp))); + } else { + static_assert(false, "UNSUPPORTED CLOCK!!!"); + } + + + return fromUTC(); + } + + template + bool fromMJD(VT&& mjd) + requires std::is_arithmetic_v + { + _MJD = static_cast(std::forward(mjd)); + + return fromMJD(); + } + + template + MccCelestialCoordEpoch& operator+=(DT&& dt) + { + _UTC += std::chrono::duration_cast(std::forward
(dt)); + + _MJD += std::chrono::duration_cast(std::forward
(dt)).count(); + + _JEpoch += std::chrono::duration_cast(std::forward
(dt)).count(); + + return *this; + } + + template + MccCelestialCoordEpoch& operator-=(DT&& dt) + { + _UTC -= std::chrono::duration_cast(std::forward
(dt)); + + _MJD -= std::chrono::duration_cast(std::forward
(dt)).count(); + + _JEpoch -= std::chrono::duration_cast(std::forward
(dt)).count(); + + return *this; + } + + + template + friend MccCelestialCoordEpoch operator+(const MccCelestialCoordEpoch& lhs, const DT& dt) + { + MccCelestialCoordEpoch ep; + ep += dt; + + return ep; + } + template + friend MccCelestialCoordEpoch operator+(const DT& dt, const MccCelestialCoordEpoch& rhs) + { + return rhs + dt; + } + + template + friend MccCelestialCoordEpoch operator-(const MccCelestialCoordEpoch& lhs, const DT& dt) + { + MccCelestialCoordEpoch ep; + ep -= dt; + + return ep; + } + + friend auto operator-(const MccCelestialCoordEpoch& lhs, const MccCelestialCoordEpoch& rhs) + { + return lhs._UTC - rhs._UTC; + } + + + template + requires std::is_arithmetic_v + VT MJD() const + { + return _MJD; + } + + double MJD() const + { + return _MJD; + } + + template + std::chrono::sys_time
UTC() const + { + return std::chrono::time_point_cast
(_UTC); + } + + std::chrono::system_clock::time_point UTC() const + { + return _UTC; + } + + + template + R JEpoch(uint8_t prec = 0) const + { + std::string prec_str{"J{:"}; + if (prec > 0) { + prec_str += "."; + prec_str += std::to_string(prec); + } + prec_str += "f}"; + + std::string res = std::vformat(std::string_view{prec_str}, std::make_format_args(_JEpoch)); + + if constexpr (std::same_as) { + return res; + } + + R r; + std::ranges::copy(res, std::back_inserter(r)); + + return r; + } + + std::string JEpoch(uint8_t prec = 0) const + { + return JEpoch(prec); + } + + auto operator<=>(const MccCelestialCoordEpoch& rhs) const + { + return _UTC <=> rhs._UTC; + } + + auto operator==(const MccCelestialCoordEpoch& rhs) const + { + return _UTC == rhs._UTC; + } + +protected: + std::chrono::system_clock::time_point _UTC; + double _MJD; + double _JEpoch; + + bool fromUTC() + { + // modified Julian date (based on ERFA eraCal2jd) + auto dd = std::chrono::floor(_UTC); + std::chrono::year_month_day ymd{dd}; + + static constexpr std::chrono::year MIN_YEAR{-4799}; + + if (ymd.year() < MIN_YEAR) { + return false; + } + if (!ymd.month().ok()) { + return false; + } + + int64_t im = (unsigned)ymd.month(); + int64_t id = (unsigned)ymd.day(); + int64_t iy = (int)ymd.year(); + + int64_t my = (im - 14LL) / 12LL; + int64_t iypmy = iy + my; + + // integer part of result MJD + int64_t mjd_int = (1461LL * (iypmy + 4800LL)) / 4LL + (367LL * (im - 2LL - 12LL * my)) / 12LL - + (3LL * ((iypmy + 4900LL) / 100LL)) / 4LL + id - 2432076LL; + + _MJD = static_cast(mjd_int) + std::chrono::duration_cast(_UTC - dd).count(); + + _JEpoch = std::chrono::duration_cast(_UTC - J2000_UTC).count() + 2000.0; + + return true; + } + + bool fromMJD(bool only_time_point = false) + { + // Gregorian date from modified Julian date (based on ERFS eraJd2cal) + double f1 = -0.5; + long jd = std::round(_MJD); + double f2 = _MJD - jd; + jd += 2400001.0; + + double s = 0.5, cs = 0.0; + double v[] = {f1, f2}; + + auto lmd = [&](double v) { + // double x = v; + // double t = s + x; + // cs += std::fabs(s) >= std::fabs(x) ? (s - t) + x : (x - t) + s; + double t = s + v; + cs += std::fabs(s) >= std::fabs(v) ? (s - t) + v : (v - t) + s; + s = t; + if (s >= 1.0) { + jd++; + s -= 1.0; + } + }; + + lmd(f1); + lmd(f2); + + double f = s + cs; + cs = f - s; + + if (f < 0.0) { + f = s + 1.0; + cs += (1.0 - f) + s; + s = f; + f = s + cs; + cs = f - s; + jd--; + } + + + if ((f - 1.0) >= -std::numeric_limits::epsilon() / 4.0) { + /* Compensated summation: assume that |s| <= 1.0. */ + double t = s - 1.0; + cs += (s - t) - 1.0; + s = t; + f = s + cs; + if (-std::numeric_limits::epsilon() / 2.0 < f) { + jd++; + f = std::max(f, 0.0); + } + } + + long l = jd + 68569L; + long n = (4L * l) / 146097L; + l -= (146097L * n + 3L) / 4L; + long i = (4000L * (l + 1L)) / 1461001L; + l -= (1461L * i) / 4L - 31L; + long k = (80L * l) / 2447L; + + auto day = std::chrono::day(l - (2447L * k) / 80L); + l = k / 11L; + auto month = std::chrono::month(k + 2L - 12L * l); + auto year = std::chrono::year(100L * (n - 49L) + i + l); + + auto day_frac = day_fp_t(f); + + // _UTC = years + month + days + day_frac; + _UTC = std::chrono::sys_days(std::chrono::year_month_day(month / day / year)); + _UTC += std::chrono::duration_cast(day_frac); + + if (!only_time_point) { + _JEpoch = 2000.0 + (_MJD - J2000_MJD) / 365.25; + } + + return true; + } + + bool fromJEpoch() + { + _MJD = J2000_MJD + (_JEpoch - 2000.0) * 365.25; + + return fromMJD(true); + } +}; + + +static_assert(mcc_coord_epoch_c, "!!!"); + + /* DEFAULT CELESTIAL POINT CLASS */ diff --git a/mcc/mcc_generics.h b/mcc/mcc_generics.h index 8012439..df19c61 100644 --- a/mcc/mcc_generics.h +++ b/mcc/mcc_generics.h @@ -150,6 +150,55 @@ concept mcc_julday_c = mcc_fp_type_like_c && requires(const T v) { }; +/* CELESTIAL COORDINATES EPOCH CLASS CONCEPT */ + +struct mcc_coord_epoch_interface_t { + static constexpr double MJD0 = 2400000.5; + + template SelfT, traits::mcc_input_char_range IR> + bool fromCharRange(this SelfT&& self, IR&& str) + { + return std::forward(self).fromCharRange(std::forward(str)); + } + + template SelfT, typename ClockT, typename DurT> + bool fromTimePoint(this SelfT&& self, std::chrono::time_point&& tp) + { + return std::forward(self).fromTimePoint(std::forward(tp)); + } + + template SelfT, typename VT> + bool fromMJD(this SelfT&& self, VT&& mjd) + requires std::is_arithmetic_v + { + return std::forward(self).fromMJD(std::forward(mjd)); + } + + template SelfT, traits::mcc_time_duration_c DT> + SelfT& operator+=(this SelfT& self, DT&& dt) + { + return std::forward(self).operator+=(std::forward
(dt)); + } + + template SelfT, traits::mcc_time_duration_c DT> + SelfT& operator-=(this SelfT& self, DT&& dt) + { + return std::forward(self).operator-=(std::forward
(dt)); + } +}; + +template +concept mcc_coord_epoch_c = std::derived_from && requires(T t1, T t2, const T t_const) { + { t_const.MJD() } -> std::convertible_to; + { t_const.UTC() } -> traits::mcc_systime_c; + { t_const.JEpoch() } -> traits::mcc_output_char_range; + + { t1 <=> t2 }; + + { T::now() } -> std::same_as; +}; + + /* ERROR CLASS CONCEPT */ template diff --git a/mcc/mcc_utils.h b/mcc/mcc_utils.h index a86844a..76f63fa 100644 --- a/mcc/mcc_utils.h +++ b/mcc/mcc_utils.h @@ -16,6 +16,7 @@ namespace mcc::utils static const std::regex decimalNumberRx{" *[-+]?([0-9]*[.])?[0-9]+([eE][-+]?[0-9]+)? *"}; static const std::regex sexagesimalReprRx{" *[-+]?[0-9]{1,2}:[0-9]{1,2}:([0-9]{0,2}[.])?[0-9]+ *"}; + constexpr static bool isEqual(std::floating_point auto const& v1, std::floating_point auto const& v2) { constexpr auto eps = std::numeric_limits>::epsilon(); diff --git a/mcc/tests/netmsg_test.cpp b/mcc/tests/netmsg_test.cpp index 9ca2fce..e62df6b 100644 --- a/mcc/tests/netmsg_test.cpp +++ b/mcc/tests/netmsg_test.cpp @@ -2,9 +2,9 @@ #include +#include "../mcc_defaults.h" #include "../mcc_netserver_proto.h" - int main() { using msg1_t = mcc::network::MccNetMessage; @@ -50,6 +50,52 @@ int main() std::cout << ">\n"; std::cout << msg2_t::celestialPointToString(mcc::MccCelestialPoint{}) << "\n"; + std::cout << msg2_t::celestialPointToString( + mcc::MccCelestialPoint{.pair_kind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, + .time_point = std::chrono::system_clock::now(), + .X = 1.3284237648726384768273, + .Y = std::numbers::pi / 2.234}, + true) + << "\n"; + + + std::cout << "\n\n\n"; + + mcc::MccCelestialCoordEpoch cep; + + std::cout << "UTC: " << cep.UTC() << "\n"; + std::cout << "MJD: " << cep.MJD() << "\n"; + std::cout << "Epoch: " << cep.JEpoch() << "\n"; + + cep.fromTimePoint(std::chrono::system_clock::now()); + + std::cout << "\n"; + std::cout << "UTC: " << cep.UTC() << "\n"; + std::cout << "MJD: " << cep.MJD() << "\n"; + std::cout << "Epoch: " << cep.JEpoch() << "\n"; + + cep.fromCharRange("60958.90342"); + std::cout << "\n"; + std::cout << "UTC: " << cep.UTC() << "\n"; + std::cout << "MJD: " << cep.MJD() << "\n"; + std::cout << "Epoch: " << cep.JEpoch(2) << "\n"; + + cep -= std::chrono::days(10); + std::cout << "\n"; + std::cout << "UTC: " << cep.UTC() << "\n"; + std::cout << "MJD: " << cep.MJD() << "\n"; + std::cout << "Epoch: " << cep.JEpoch() << "\n"; + + std::cout << "\n" << (cep = mcc::MccCelestialCoordEpoch::now()).UTC() << "\n"; + std::cout << "MJD: " << cep.MJD() << "\n"; + std::cout << "Epoch: " << cep.JEpoch() << "\n"; + + auto ep1 = mcc::MccCelestialCoordEpoch::now(); + auto ep2 = mcc::MccCelestialCoordEpoch::now(); + + std::cout << "\n" << std::boolalpha << (ep1 < ep2) << "\n"; + std::cout << "\n" + << std::boolalpha << (mcc::MccCelestialCoordEpoch::now() != mcc::MccCelestialCoordEpoch::now()) << "\n"; return 0; }