From da46ab3e3be86c5b630252950d9c5718c0be938a Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Tue, 19 Aug 2025 00:23:31 +0300 Subject: [PATCH] ... --- mcc/CMakeLists.txt | 4 +- mcc/mcc_ccte_erfa.h | 102 +++++++++++++++++------- mcc/mcc_defaults.h | 20 ++++- mcc/mcc_generics.h | 46 ++++++++++- mcc/mcc_telemetry.h | 187 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+), 33 deletions(-) create mode 100644 mcc/mcc_telemetry.h diff --git a/mcc/CMakeLists.txt b/mcc/CMakeLists.txt index 7590203..1fc21d0 100644 --- a/mcc/CMakeLists.txt +++ b/mcc/CMakeLists.txt @@ -64,12 +64,14 @@ set(ERFA_INCLUDE_DIR ${CMAKE_BINARY_DIR}/erfa_lib) message(STATUS ${ERFA_INCLUDE_DIR}) -set(MCC_LIBRARY_SRC1 mcc_generics.h mcc_defaults.h mcc_traits.h mcc_utils.h mcc_ccte_iers.h mcc_ccte_iers_default.h mcc_ccte_erfa.h) +set(MCC_LIBRARY_SRC1 mcc_generics.h mcc_defaults.h mcc_traits.h mcc_utils.h mcc_ccte_iers.h mcc_ccte_iers_default.h mcc_ccte_erfa.h mcc_pzone.h mcc_telemetry.h) set(MCC_LIBRARY1 mcc1) add_library(${MCC_LIBRARY1} INTERFACE ${MCC_LIBRARY_SRC1}) target_compile_features(${MCC_LIBRARY1} INTERFACE cxx_std_23) target_include_directories(${MCC_LIBRARY1} INTERFACE ${ERFA_INCLUDE_DIR}) +# add_subdirectory(fitpack) +# message(STATUS "FITPACK: " ${FITPACK_INCLUDE_DIR}) option(WITH_TESTS "Build tests" ON) diff --git a/mcc/mcc_ccte_erfa.h b/mcc/mcc_ccte_erfa.h index 517de99..9c48d81 100644 --- a/mcc/mcc_ccte_erfa.h +++ b/mcc/mcc_ccte_erfa.h @@ -272,9 +272,9 @@ public: auto days = std::chrono::floor(tp); std::chrono::year_month_day ymd{days}; - double mjd0; + double mjd0, mjd; - int err = eraCal2jd((int)ymd.year(), (unsigned)ymd.month(), (unsigned)ymd.day(), &mjd0, &julday->mjd); + int err = eraCal2jd((int)ymd.year(), (unsigned)ymd.month(), (unsigned)ymd.day(), &mjd0, &mjd); if (err != 0) { ret = err == -1 ? MccCCTE_ERFAErrorCode::ERROR_julday_INVALID_YEAR @@ -282,15 +282,14 @@ public: : err == -3 ? MccCCTE_ERFAErrorCode::ERROR_julday_INVALID_DAY : MccCCTE_ERFAErrorCode::ERROR_UNEXPECTED; } else { // partial part of day - julday->mjd += - std::chrono::duration_cast>>(tp - days).count(); + mjd += std::chrono::duration_cast>>(tp - days).count(); + *julday = mjd + mjd0; } return ret; } - - error_t timepointToAppSideral(mcc_time_point_c auto tp, mcc_angle_c auto* st, bool islocal = false) + error_t juldayToAppSideral(mcc_julday_c auto jd, mcc_angle_c auto* st, bool islocal = false) { error_t ret = MccCCTE_ERFAErrorCode::ERROR_OK; @@ -300,19 +299,12 @@ public: using real_days_t = std::chrono::duration>; - MccJulianDay julday; - - ret = timepointToJulday(tp, &julday); - if (ret != MccCCTE_ERFAErrorCode::ERROR_OK) { - return ret; - } - - double ut1 = julday.mjd; - double tt = julday.mjd; + double ut1 = jd.MJD(); + double tt = jd.MJD(); std::lock_guard lock{*_stateMutex}; - auto dut1 = _currentState._bulletinA.DUT1(julday.mjd); + auto dut1 = _currentState._bulletinA.DUT1(jd.MJD()); if (dut1.has_value()) { ut1 += std::chrono::duration_cast(dut1.value()).count(); @@ -320,7 +312,7 @@ public: return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE; } - auto tai_utc = _currentState._leapSeconds[julday.mjd]; + auto tai_utc = _currentState._leapSeconds[jd.MJD()]; if (tai_utc.has_value()) { tt += std::chrono::duration_cast(tai_utc.value()).count(); } else { @@ -331,7 +323,7 @@ public: auto tt_tai = _currentState._bulletinA.TT_TAI(); tt += std::chrono::duration_cast(tt_tai).count(); - *st = eraGst06a(julday.MJD0, ut1, julday.MJD0, tt); + *st = eraGst06a(ERFA_DJM0, ut1, ERFA_DJM0, tt); if (islocal) { *st += _currentState.lon; @@ -341,6 +333,57 @@ public: return ret; } + error_t timepointToAppSideral(mcc_time_point_c auto tp, mcc_angle_c auto* st, bool islocal = false) + { + error_t ret = MccCCTE_ERFAErrorCode::ERROR_OK; + + if (st == nullptr) { + return ret; + } + + MccJulianDay julday; + + ret = timepointToJulday(tp, &julday); + if (ret != MccCCTE_ERFAErrorCode::ERROR_OK) { + return ret; + } + + return juldayToAppSideral(julday, st, islocal); + + // double ut1 = julday.MJD(); + // double tt = julday.MJD(); + + // std::lock_guard lock{*_stateMutex}; + + // auto dut1 = _currentState._bulletinA.DUT1(julday.MJD()); + + // if (dut1.has_value()) { + // ut1 += std::chrono::duration_cast(dut1.value()).count(); + // } else { // out of range + // return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE; + // } + + // auto tai_utc = _currentState._leapSeconds[julday.MJD()]; + // if (tai_utc.has_value()) { + // tt += std::chrono::duration_cast(tai_utc.value()).count(); + // } else { + // return MccCCTE_ERFAErrorCode::ERROR_LEAPSECONDS_OUT_OF_RANGE; + // } + + + // auto tt_tai = _currentState._bulletinA.TT_TAI(); + // tt += std::chrono::duration_cast(tt_tai).count(); + + // *st = eraGst06a(ERFA_DJM0, ut1, ERFA_DJM0, tt); + + // if (islocal) { + // *st += _currentState.lon; + // } + + + // return ret; + } + // coordinates transformations @@ -691,13 +734,13 @@ protected: std::lock_guard lock{*_stateMutex}; - auto dut1 = _currentState._bulletinA.DUT1(jd.mjd); + auto dut1 = _currentState._bulletinA.DUT1(jd.MJD()); if (!dut1.has_value()) { return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE; } - auto pol_pos = _currentState._bulletinA.polarCoords(jd.mjd); + auto pol_pos = _currentState._bulletinA.polarCoords(jd.MJD()); if (!pol_pos.has_value()) { return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE; } @@ -709,7 +752,7 @@ protected: double az, zd, ha, ra, dec, eo; - int err = eraAtco13(pt.X, pt.Y, 0.0, 0.0, 0.0, 0.0, jd.MJD0, jd.mjd, dut1->count(), _currentState.lon, + int err = eraAtco13(pt.X, pt.Y, 0.0, 0.0, 0.0, 0.0, jd.MJD0, jd.MJD(), dut1->count(), _currentState.lon, _currentState.lat, _currentState.elev, pol_pos->x, pol_pos->y, _currentState.meteo.pressure, _currentState.meteo.temperature, _currentState.meteo.humidity, _currentState.wavelength, &az, &zd, &ha, &dec, &ra, &eo); @@ -750,13 +793,13 @@ protected: std::lock_guard lock{*_stateMutex}; - auto dut1 = _currentState._bulletinA.DUT1(jd.mjd); + auto dut1 = _currentState._bulletinA.DUT1(jd.MJD()); if (!dut1.has_value()) { return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE; } - auto pol_pos = _currentState._bulletinA.polarCoords(jd.mjd); + auto pol_pos = _currentState._bulletinA.polarCoords(jd.MJD()); if (!pol_pos.has_value()) { return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE; } @@ -793,7 +836,7 @@ protected: return MccCCTE_ERFAErrorCode::ERROR_UNSUPPORTED_COORD_PAIR; } - int err = eraAtoc13(type.c_str(), from_pt.X, from_pt.Y, jd.MJD0, jd.mjd, dut1->count(), _currentState.lon, + int err = eraAtoc13(type.c_str(), from_pt.X, from_pt.Y, jd.MJD0, jd.MJD(), dut1->count(), _currentState.lon, _currentState.lat, _currentState.elev, pol_pos->x, pol_pos->y, _currentState.meteo.pressure, _currentState.meteo.temperature, _currentState.meteo.humidity, _currentState.wavelength, &to_pt->X, &to_pt->Y); @@ -842,7 +885,8 @@ protected: return MccCCTE_ERFAErrorCode::ERROR_OK; } - MccJulianDay jd, tt; + MccJulianDay jd; + double tt; auto ret = timepointToJulday(tp, &jd); if (ret) { return ret; @@ -852,14 +896,14 @@ protected: using real_days_t = std::chrono::duration>; - auto tai_utc = _currentState._leapSeconds[jd.mjd]; + auto tai_utc = _currentState._leapSeconds[jd.MJD()]; if (tai_utc.has_value()) { - tt.mjd = jd.mjd + std::chrono::duration_cast(tai_utc.value()).count(); + tt = jd.MJD() + std::chrono::duration_cast(tai_utc.value()).count(); auto tt_tai = _currentState._bulletinA.TT_TAI(); - tt.mjd = jd.mjd + std::chrono::duration_cast(tt_tai).count(); + tt += +std::chrono::duration_cast(tt_tai).count(); - *eo = eraEo06a(tt.MJD0, tt.mjd); + *eo = eraEo06a(jd.MJD0, tt); } else { ret = MccCCTE_ERFAErrorCode::ERROR_LEAPSECONDS_OUT_OF_RANGE; } diff --git a/mcc/mcc_defaults.h b/mcc/mcc_defaults.h index 9160d09..caeef69 100644 --- a/mcc/mcc_defaults.h +++ b/mcc/mcc_defaults.h @@ -22,19 +22,37 @@ typedef std::chrono::system_clock::time_point MccTimePoint; struct MccJulianDay { static constexpr double MJD0 = 2400000.5; - double mjd{51544.5}; // J2000.0 + + 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 }; diff --git a/mcc/mcc_generics.h b/mcc/mcc_generics.h index 7920373..77cb801 100644 --- a/mcc/mcc_generics.h +++ b/mcc/mcc_generics.h @@ -21,6 +21,46 @@ namespace mcc // mount construction type (only the most common ones) enum class MccMountType : uint8_t { GERMAN_TYPE, FORK_TYPE, CROSSAXIS_TYPE, ALTAZ_TYPE }; +template +static constexpr std::string_view MccMountTypeStr = TYPE == MccMountType::GERMAN_TYPE ? "GERMAN" + : TYPE == MccMountType::FORK_TYPE ? "FORK" + : TYPE == MccMountType::CROSSAXIS_TYPE ? "CROSSAXIS" + : TYPE == MccMountType::ALTAZ_TYPE ? "ALTAZ" + : "UNKNOWN"; + +template +static constexpr bool mcc_is_equatorial_mount = TYPE == MccMountType::GERMAN_TYPE ? true + : TYPE == MccMountType::FORK_TYPE ? true + : TYPE == MccMountType::CROSSAXIS_TYPE ? true + : TYPE == MccMountType::ALTAZ_TYPE ? false + : false; +template +static constexpr bool mcc_is_altaz_mount = TYPE == MccMountType::GERMAN_TYPE ? false + : TYPE == MccMountType::FORK_TYPE ? false + : TYPE == MccMountType::CROSSAXIS_TYPE ? false + : TYPE == MccMountType::ALTAZ_TYPE ? true + : false; + +static consteval bool mccIsEquatorialMount(const MccMountType type) +{ + return type == MccMountType::GERMAN_TYPE ? true + : type == MccMountType::FORK_TYPE ? true + : type == MccMountType::CROSSAXIS_TYPE ? true + : type == MccMountType::ALTAZ_TYPE ? false + : false; +}; + +static consteval bool mccIsAltAzMount(const MccMountType type) +{ + return type == MccMountType::GERMAN_TYPE ? false + : type == MccMountType::FORK_TYPE ? false + : type == MccMountType::CROSSAXIS_TYPE ? false + : type == MccMountType::ALTAZ_TYPE ? true + : false; +}; + + + // enum MccCoordPairKind : size_t { // COORDS_KIND_GENERIC, // COORDS_KIND_RADEC_ICRS, @@ -68,6 +108,8 @@ concept mcc_time_point_c = requires(T t) { [](std::chr template concept mcc_julday_c = mcc_fp_type_like_c && requires(const T v) { + // modified Julian Day + { v.MJD() } -> std::convertible_to; // comparison operators v <=> v; }; @@ -145,7 +187,7 @@ struct mcc_CCTE_interface_t { return std::forward(self).transformCoordinates(std::move(from_pt), to_pt); } - // NOTE: ASSUMING THE AZINUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!! + // NOTE: ASSUMING THE AZIMUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!! template SelfT> RetT transformCoordinates(this SelfT&& self, mcc_celestial_point_c auto from_pt, mcc_eqt_hrz_coord_c auto* to_pt) { @@ -275,7 +317,7 @@ concept mcc_hardware_c = requires(T t, const T t_const) { // a class that contains at least time point of measurement, coordinates for x,y axes and its moving rates requires requires(typename T::axes_pos_t pos) { - requires mcc_time_point_c; // time point + requires mcc_time_point_c; // time point requires mcc_angle_c; // target or current co-longitude coordinate requires mcc_angle_c; // target or current co-latitude coordinate diff --git a/mcc/mcc_telemetry.h b/mcc/mcc_telemetry.h new file mode 100644 index 0000000..850ed2c --- /dev/null +++ b/mcc/mcc_telemetry.h @@ -0,0 +1,187 @@ +#pragma once + +/* MOUNT CONTROL COMPONENTS LIBRARY */ + + +/* IMPLEMENTATION OF TELEMETRY CLASS */ + + +#include "mcc_defaults.h" + +namespace mcc +{ + +static constexpr double mcc_sideral_to_UT1_ratio = 1.002737909350795; // sideral/UT1 + + +enum MccTelemetryErrorCode : int { ERROR_OK, ERROR_COORD_TRANSFROM, ERROR_PCM_COMP }; + +} // namespace mcc + +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + + +namespace mcc +{ + +/* error category definition */ + +// error category +struct MccTelemetryCategory : public std::error_category { + MccTelemetryCategory() : std::error_category() {} + + const char* name() const noexcept + { + return "ALTITUDE-LIMIT-PZ"; + } + + std::string message(int ec) const + { + MccTelemetryErrorCode err = static_cast(ec); + + switch (err) { + case MccTelemetryErrorCode::ERROR_OK: + return "OK"; + case MccTelemetryErrorCode::ERROR_COORD_TRANSFROM: + return "coordinate transformation error"; + case MccTelemetryErrorCode::ERROR_PCM_COMP: + return "PCM computation error"; + default: + return "UNKNOWN"; + } + } + + static const MccTelemetryCategory& get() + { + static const MccTelemetryCategory constInst; + return constInst; + } +}; + + +inline std::error_code make_error_code(MccTelemetryErrorCode ec) +{ + return std::error_code(static_cast(ec), MccTelemetryCategory::get()); +} + + +/* TELEMETRY UPDATE POLICY */ + +enum class MccTelemetryUpdatePolicy : int { TEMETRY_UPDATE_INNER, TEMETRY_UPDATE_EXTERNAL }; + + +template +class MccTelemetry : public mcc_telemetry_interface_t +{ +public: + static constexpr MccTelemetryUpdatePolicy updatePolicy = UPDATE_POLICY; + + typedef std::error_code error_t; + + + MccTelemetry(mcc_ccte_c auto* ccte, mcc_PCM_c auto* pcm, mcc_hardware_c auto* hardware) + { + using ccte_t = std::remove_cvref_t; + using pcm_t = std::remove_cvref_t; + using hardware_t = std::remove_cvref_t; + + _updateFunc = [ccte, pcm, hardware, this](MccTelemetryData* data) { + typename hardware_t::axes_pos_t hw_pos; + auto hw_err = hardware->getPos(&hw_pos); + + if (!hw_err) { + data->time_point = + std::chrono::time_point_casttime_point)::duration>(hw_pos.time_point); + + auto ccte_err = ccte->timepointToJulday(data->time_point, &data->JD); + if (!ccte_err) { + ccte_err = ccte->timepointToAppSideral(data->time_point, &data->LST, true); + if (!ccte_err) { + data->speedX = (double)hw_pos.speedX; + data->speedY = (double)hw_pos.speedY; + + struct { + double dx, dy; + } pcm_res; + + auto pcm_err = pcm->computePCM(data, &pcm_res); + if (pcm_err) { + if constexpr (std::same_as) { + return pcm_err; + } else { + return MccTelemetryErrorCode::ERROR_PCM_COMP; + } + } + + MccCelestialPoint pt{.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT, + .time_point = data->time_point}; + + if constexpr (mccIsEquatorialMount(pcm_t::mountType)) { + data->HA = (double)hw_pos.X + pcm_res.dx; + data->DEC_APP = (double)hw_pos.Y + pcm_res.dy; + + data->X = data->HA; + data->Y = data->DEC_APP; + + data->pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; + + ccte_err = ccte->transformCoordinates(*data, &pt); + if (!ccte_err) { + data->AZ = pt.X; + data->ALT = pt.Y; + data->ZD = std::numbers::pi / 2.0 - data->ALT; + } + } else if constexpr (mccIsAltAzMount(pcm_t::mountType)) { + data->AZ = (double)hw_pos.X + pcm_res.dx; + data->ALT = (double)hw_pos.Y + pcm_res.dy; + data->ZD = std::numbers::pi / 2.0 - data->ALT; + + data->X = data->AZ; + data->Y = data->ALT; + + data->pair_kind = MccCoordPairKind::COORDS_KIND_AZALT; + + pt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; + ccte_err = ccte->transformCoordinates(*data, &pt); + if (!ccte) { + data->HA = pt.X; + data->DEC_APP = pt.Y; + } + + } else { + static_assert(false, "UNKNOWN MOUNT TYPE!"); + } + } + } + } + }; + } + + + error_t telemetryData(mcc_telemetry_data_c auto* data) + { + error_t ret = MccTelemetryErrorCode::ERROR_OK; + + // first, update mount coordinates + + + + return ret; + } + + + error_t setPointingTarget(mcc_celestial_point_c auto pt) {} + +protected: + std::function _updateFunc; +}; + +} // namespace mcc