#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* MCC LIBRARY DEFAULT IMPLEMENTATION OF SOME CLASSES */ // #include #include "mcc_generics.h" namespace mcc { /* DEFAULT TIME-RELATED CLASSES */ typedef std::chrono::system_clock::time_point MccTimePoint; typedef std::chrono::duration MccTimeDuration; // seconds as floating-point value template static constexpr DT mcc_infinite_duration_v = std::floating_point ? DT{std::numeric_limits::infinity()} : DT{std::numeric_limits::max()}; /* DEFAULT JULIAN DAY CLASS */ struct MccJulianDay { static constexpr double MJD0 = 2400000.5; MccJulianDay() = default; MccJulianDay(double jd) : mjd(jd - MJD0) {} constexpr operator double() const { return MccJulianDay::MJD0 + mjd; } MccJulianDay& operator=(double jd) { mjd = jd - MJD0; return *this; } double MJD() const { return mjd; } constexpr auto operator<=>(const MccJulianDay&) const = default; constexpr auto operator<=>(double v) const { return v <=> (MccJulianDay::MJD0 + mjd); }; protected: double mjd{51544.5}; // J2000.0 }; /* 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 */ template struct MccGenericCelestialPoint { typedef CoordT coord_t; MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; // MccTimePoint time_point{std::chrono::sys_days(std::chrono::year_month_day{std::chrono::January / 1 / 2000}) + // std::chrono::hours(12)}; // J2000.0 MccTimePoint time_point{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)}; // J2000.0 UTC coord_t X{}, Y{}; }; typedef MccGenericCelestialPoint MccCelestialPoint; template struct MccGenericEqtHrzCoords : MccGenericCelestialPoint { using typename MccGenericCelestialPoint::coord_t; using MccGenericCelestialPoint::time_point; coord_t RA_APP{}, DEC_APP{}, HA{}, AZ{}, ZD{}, ALT{}; }; typedef MccGenericEqtHrzCoords MccEqtHrzCoords; template struct MccGenericPointingTarget : MccGenericEqtHrzCoords { using typename MccGenericEqtHrzCoords::coord_t; coord_t RA_ICRS{}, DEC_ICRS{}; }; typedef MccGenericPointingTarget MccPointingTarget; template struct MccGenericPCMResult { CoordT pcmX, pcmY; }; typedef MccGenericPCMResult MccPCMResult; /* DEFAULT TELEMETRY DATA CLASS */ template struct MccGenericTelemetryData : MccGenericEqtHrzCoords { using typename MccGenericEqtHrzCoords::coord_t; MccJulianDay JD; coord_t LST; // local apparent sideral time MccGenericPointingTarget target{}; coord_t speedX, speedY; coord_t pcmX, pcmY; coord_t refCorr; }; typedef MccGenericTelemetryData MccTelemetryData; template struct MccPositionControls : CCTE_T, HARDWARE_T, PCM_T { virtual ~MccPositionControls() = default; }; /* JUST CHECK FOR CONCEPT CONSISTENCY */ static_assert(mcc_julday_c, ""); static_assert(mcc_celestial_point_c, ""); static_assert(mcc_telemetry_data_c, ""); } // namespace mcc