From bb4171064593383a9a41aa83eb03b51f5dfa8228 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Sun, 14 Sep 2025 23:14:09 +0300 Subject: [PATCH] ... --- mcc/mcc_generics_new.h | 1046 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1046 insertions(+) create mode 100644 mcc/mcc_generics_new.h diff --git a/mcc/mcc_generics_new.h b/mcc/mcc_generics_new.h new file mode 100644 index 0000000..dd6eb7e --- /dev/null +++ b/mcc/mcc_generics_new.h @@ -0,0 +1,1046 @@ +#pragma once + +/* MOUNT CONTROL COMPONENTS LIBRARY */ + + + +/* SOME LIBRARY-WIDE DECLARATIONS */ + + +#include +#include + + +// #include "mcc_traits.h" +#include "mcc_angle.h" +#include "mcc_finite_state_machine.h" + + +namespace mcc +{ + +static constexpr double mcc_sideral_to_UT1_ratio = 1.002737909350795; // sideral/UT1 + + +// 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 class MccProhibitedZonePolicy : int { + PZ_POLICY_STOP, // stop mount near the zone + PZ_POLICY_FLIP // flip mount, e.g., near the meridian, near HA-axis encoder limit switch +}; + + +/* GENERIC LOGGER CLASS CONCEPT */ + +template +concept mcc_logger_c = requires(T t, const T t_const) { + { t.logError(std::declval()) }; + { t.logDebug(std::declval()) }; + { t.logWarn(std::declval()) }; + { t.logInfo(std::declval()) }; +}; + + +struct MccNullLogger { + void logError(const std::string&) {} + void logDebug(const std::string&) {} + void logWarn(const std::string&) {} + void logInfo(const std::string&) {} +}; + + + +/* FLOATING-POINT LIKE CLASS CONCEPT */ + +template +concept mcc_fp_type_like_c = + std::floating_point || + (std::convertible_to && std::constructible_from && std::default_initializable); + + +/* ANGLE REPRESENTATION CLASS CONCEPT */ + +template +concept mcc_angle_c = mcc_fp_type_like_c && requires(T v, double vd) { + // mandatory arithmetic operations + { v + v } -> std::same_as; + { v - v } -> std::same_as; + { v += v } -> std::same_as; + { v -= v } -> std::same_as; + + { vd + v } -> std::same_as; + { vd - v } -> std::same_as; + { v + vd } -> std::same_as; + { v - vd } -> std::same_as; + { v += vd } -> std::same_as; + { v -= vd } -> std::same_as; + + { v * vd } -> std::same_as; + { v / vd } -> std::same_as; +}; + + +/* TIME POINT CLASS CONCEPT */ + +/* + * USE OF STL std::chrono::time_point + */ +template +concept mcc_time_point_c = traits::mcc_systime_c; +// concept mcc_time_point_c = requires(T t) { [](std::chrono::time_point) {}(t); }; + + +typedef std::chrono::system_clock::time_point MccTimePoint; + +template +static constexpr void mcc_tp2tp(const T1& from_tp1, T2& to_tp) +{ + to_tp = std::chrono::time_point_cast(from_tp1); +} + + +/* JULIAN DAY CLASS CONCEPT */ + +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; +}; + + +/* DEFAULT JULIAN DAY CLASS */ + +struct MccJulianDay { + static constexpr double MJD0 = 2400000.5; + + MccJulianDay() = default; + + MccJulianDay(const MccJulianDay&) = default; + MccJulianDay(MccJulianDay&&) = default; + + MccJulianDay& operator=(const MccJulianDay&) = default; + MccJulianDay& operator=(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 +}; + + +/* ERROR CLASS CONCEPT */ + +template +concept mcc_error_c = std::default_initializable && (std::convertible_to || requires(const T t) { + { t.operator bool() }; + (bool)T() == false; // default constucted value must be a "non-error"! + }); + + +template +static constexpr ErrT mcc_deduce_error(const DErrT& err, const ErrT& default_err) +{ + if constexpr (std::same_as) { + return err; + } else { + return default_err; + } +} + + + +/* ATMOSPHERIC REFRACTION MODEL CLASS CONCEPT */ + +template +concept mcc_refract_model_c = requires(const T t_const) { + { t_const.name() } -> std::formattable; +}; + + +/* CELESTIAL POINT WITH A PAIR OF COORDINATES CLASS CONCEPT */ + +template +concept mcc_celestial_point_c = requires(T t) { + requires std::same_as, + MccCoordPairKind>; // type of given coordinate pair (it may be even static constexpr value) + + requires mcc_time_point_c; // time point for given coordinates + + requires mcc_angle_c; // co-longitude (X-axis) + requires mcc_angle_c; // co-latitude (Y-axis) +}; + + +/* 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 + + coord_t X{}, Y{}; +}; + + +typedef MccGenericCelestialPoint MccCelestialPoint; + + +static constexpr void mcc_copy_celestial_point(mcc_celestial_point_c auto const& from_pt, + mcc_celestial_point_c auto* to_pt) +{ + if (to_pt == nullptr) { + return; + } + + using from_pt_t = std::remove_cvref_t; + using to_pt_t = std::remove_cvref_t; + + if constexpr (std::derived_from && std::copyable) { + *to_pt = from_pt; + return; + } + + to_pt->pair_kind = from_pt.pair_kind; + to_pt->time_point = + std::chrono::time_point_casttime_point)::duration>(from_pt.time_point); + to_pt->X = (double)from_pt.X; + to_pt->Y = (double)from_pt.Y; +} + + +/* CELESTIAL POINT WITH APPARENT EQUATORIAL AND HORIZONTAL COORDINATES CLASS CONCEPT */ + +template +concept mcc_eqt_hrz_coord_c = mcc_celestial_point_c && requires(T t) { + requires mcc_angle_c; // right ascension + requires mcc_angle_c; // declination + requires mcc_angle_c; // hour angle + requires mcc_angle_c; // azimuth (NOTE: ASSUMING THE AZINUTH IS COUNTED FROM THE SOUTH THROUGH THE + // WEST!!!) + requires mcc_angle_c; // zenithal distance + requires mcc_angle_c; // altitude +}; + + +/* DEFAULT CELESTIAL POINT WITH APPARENT EQUATORIAL AND HORIZONTAL COORDINATES CLASS */ + +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; + +static constexpr void mcc_copy_eqt_hrz_coord(mcc_eqt_hrz_coord_c auto const& from_pt, mcc_eqt_hrz_coord_c auto* to_pt) +{ + if (to_pt == nullptr) { + return; + } + + using from_pt_t = std::remove_cvref_t; + using to_pt_t = std::remove_cvref_t; + + if constexpr (std::derived_from && std::copyable) { + *to_pt = from_pt; + return; + } + + to_pt->pair_kind = from_pt.pair_kind; + to_pt->time_point = + std::chrono::time_point_casttime_point)::duration>(from_pt.time_point); + to_pt->X = (double)from_pt.X; + to_pt->Y = (double)from_pt.Y; + + to_pt->RA_APP = (double)from_pt.RA_APP; + to_pt->DEC_APP = (double)from_pt.DEC_APP; + + to_pt->HA = (double)from_pt.HA; + to_pt->AZ = (double)from_pt.AZ; + to_pt->ZD = (double)from_pt.ZD; + to_pt->ALT = (double)from_pt.ALT; +} + + +/* CELESTIAL COORDINATES TRANSFORMATION ENGINE CONCEPT */ + +template +concept mcc_ccte_c = requires(const T t_const, T t) { + { t_const.nameCCTE() } -> std::formattable; + + requires mcc_refract_model_c; + + { t.refractionModel(std::declval()) } -> mcc_error_c; + + + [](T tt) -> mcc_error_c auto { + MccTimePoint tp{}; + double jd, st, pa, dZ; + + MccJulianDay jd_class; + + MccAngle st_class, pa_class, dZ_class; + + MccCelestialPoint cp; + MccGenericCelestialPoint cp_class; + + MccEqtHrzCoords eqhr; + MccGenericEqtHrzCoords eqhr_class; + + + auto ret = tt.timepointToJulday(tp, &jd); + ret = tt.timepointToJulday(tp, &jd_class); + + + ret = tt.timepointToAppSideral(tp, &st, true); + ret = tt.timepointToAppSideral(tp, &st_class, true); + + ret = tt.timepointToAppSideral(jd, &st, true); + ret = tt.timepointToAppSideral(jd, &st_class, true); + ret = tt.timepointToAppSideral(jd_class, &st, true); + ret = tt.timepointToAppSideral(jd_class, &st_class, true); + + // NOTE: ASSUMING THE AZIMUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!! + + ret = tt.transformCoordinates(cp, &cp); + ret = tt.transformCoordinates(cp, &cp_class); + ret = tt.transformCoordinates(cp_class, &cp); + ret = tt.transformCoordinates(cp_class, &cp_class); + + ret = tt.transformCoordinates(cp, &eqhr); + ret = tt.transformCoordinates(cp, &eqhr_class); + ret = tt.transformCoordinates(cp_class, &eqhr); + ret = tt.transformCoordinates(cp_class, &eqhr_class); + + ret = tt.parallacticAngle(cp, &pa); + ret = tt.parallacticAngle(cp, &pa_class); + ret = tt.parallacticAngle(cp_class, &pa); + ret = tt.parallacticAngle(cp_class, &pa_class); + + ret = tt.refractionCorrection(cp, &dZ); + ret = tt.refractionCorrection(cp, &dZ_class); + ret = tt.refractionCorrection(cp_class, &dZ); + ret = tt.refractionCorrection(cp_class, &dZ_class); + + return ret; + }(t); +}; + + +/* POINTING CORRECTION MODEL CLASS CONCEPT */ + +template +concept mcc_PCM_result_c = requires(T t) { + requires mcc_angle_c; + requires mcc_angle_c; +}; + +template +struct MccGenericPCMResult { + CoordT pcmX, pcmY; +}; + + +typedef MccGenericPCMResult MccPCMResult; + + +template +concept mcc_PCM_c = requires(T t) { + // the 'T' class must contain static constexpr member of 'MccMountType' type + requires std::same_as; + []() { + static constexpr MccMountType val = T::mountType; + return val; + }(); // to ensure 'mountType' can be used in compile-time context + + [](T tt) -> mcc_error_c auto { + MccPCMResult res; + MccGenericPCMResult res_class; + + MccCelestialPoint cp; + MccGenericCelestialPoint cp_class; + + MccEqtHrzCoords eqhr; + MccGenericEqtHrzCoords eqhr_class; + + // computePCM(mcc_celestial_point_c auto pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto* app_pt) + + auto ret = tt.computePCM(cp, &res, &cp); + ret = tt.computePCM(cp, &res_class, &cp); + ret = tt.computePCM(cp_class, &res, &cp); + ret = tt.computePCM(cp_class, &res_class, &cp); + + ret = tt.computePCM(cp, &res, &cp_class); + ret = tt.computePCM(cp, &res_class, &cp_class); + ret = tt.computePCM(cp_class, &res, &cp_class); + ret = tt.computePCM(cp_class, &res_class, &cp_class); + + // computePCM(mcc_celestial_point_c auto pt, mcc_PCM_result_c auto* result, mcc_eqt_hrz_coord_c auto* app_pt) + // + // for equatorial mounts the method must compute: + // app_pt->HA = pt.X + result->pcmX + // app_pt->DEC_APP = pt.Y + result->pcmY + // for alt-azimuthal: + // app_pt->AZ = pt.X + result->pcmX + // app_pt->ZD = pt.Y + result->pcmY ret = tt.computePCM(cp, &res, &eqhr); + ret = tt.computePCM(cp, &res_class, &eqhr); + ret = tt.computePCM(cp_class, &res, &eqhr); + ret = tt.computePCM(cp_class, &res_class, &eqhr); + + ret = tt.computePCM(cp, &res, &eqhr_class); + ret = tt.computePCM(cp, &res_class, &eqhr_class); + ret = tt.computePCM(cp_class, &res, &eqhr_class); + ret = tt.computePCM(cp_class, &res_class, &eqhr_class); + + // computeInversePCM(mcc_celestial_point_c auto pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto* + // app_pt) + + ret = tt.computeInversePCM(cp, &res, &cp); + ret = tt.computeInversePCM(cp, &res_class, &cp); + ret = tt.computeInversePCM(cp_class, &res, &cp); + ret = tt.computeInversePCM(cp_class, &res_class, &cp); + + ret = tt.computeInversePCM(cp, &res, &cp_class); + ret = tt.computeInversePCM(cp, &res_class, &cp_class); + ret = tt.computeInversePCM(cp_class, &res, &cp_class); + ret = tt.computeInversePCM(cp_class, &res_class, &cp_class); + + // computeInversePCM(mcc_eqt_hrz_coord_c auto pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto* + // app_pt) + // + // NOTE: for computation of the corrections the method must use of app_pt.X and app_pt.Y + // + // for equatorial mounts the method must compute: + // hw_pt->X = app_pt.HA + inv_result.pcmX + // hw_pt->Y = app_pt.DEC_APP + inv_result.pcmY + // and inputs for the corrections computing are app_pt.HA and app_pt.DEC_APP + // for alt-azimuthal: + // hw_pt->X = app_pt.AZ + inv_result.pcmX + // hw_pt->Y = app_pt.ZD + inv_result.pcmY + // and inputs for the corrections computing are app_pt.ZA and app_pt.ZD ret = + // tt.computeInversePCM(eqhr, &res, &cp); + ret = tt.computeInversePCM(eqhr, &res_class, &cp); + ret = tt.computeInversePCM(eqhr_class, &res, &cp); + ret = tt.computeInversePCM(eqhr_class, &res_class, &cp); + + ret = tt.computeInversePCM(eqhr, &res, &cp_class); + ret = tt.computeInversePCM(eqhr, &res_class, &cp_class); + ret = tt.computeInversePCM(eqhr_class, &res, &cp_class); + ret = tt.computeInversePCM(eqhr_class, &res_class, &cp_class); + + return ret; + }(t); +}; + +/* MOUNT HARDWARE ABSTRACTION CLASS CONCEPT */ + +template +concept mcc_hardware_c = requires(T t, const T t_const) { + requires mcc_error_c; + + { t_const.hardwareName() } -> std::formattable; + + // the 'T' class must contain static constexpr member of 'MccMountType' type + // requires std::same_as; + // []() { + // static constexpr MccMountType val = T::mountType; + // return val; + // }(); // to ensure 'mountType' can be used in compile-time context + + + // a type that defines at least HW_MOVE_ERROR, HW_MOVE_STOPPED, HW_MOVE_SLEWING, HW_MOVE_ADJUSTING, HW_MOVE_TRACKING + // and HW_MOVE_GUIDING compile-time constants. The main purpose of this type is a + // possible tunning of hardware hardwareSetState-related commands and detect stop-state + // + // e.g. an implementations can be as follows: + // enum class hardware_moving_state_t: int {HW_MOVE_ERROR = -1, HW_MOVE_STOPPED = 0, HW_MOVE_SLEWING, + // HW_MOVE_ADJUSTING, HW_MOVE_TRACKING, HW_MOVE_GUIDING} + // + // struct hardware_moving_state_t { + // uint16_t HW_MOVE_STOPPED = 0; + // uint16_t HW_MOVE_SLEWING = 111; + // uint16_t HW_MOVE_ADJUSTING = 222; + // uint16_t HW_MOVE_TRACKING = 333; + // uint16_t HW_MOVE_GUIDING = 444; + // uint16_t HW_MOVE_ERROR = 555; + // } + requires requires(typename T::hardware_moving_state_t type) { + []() { + // mount axes were stopped + static constexpr auto v0 = T::hardware_moving_state_t::HW_MOVE_STOPPED; + + // hardware was asked for slewing (move to given celestial point) + static constexpr auto v1 = T::hardware_moving_state_t::HW_MOVE_SLEWING; + + // hardware was asked for adjusting after slewing + // (adjusting actual mount position to align with target celestial point at the end of slewing process) + static constexpr auto v2 = T::hardware_moving_state_t::HW_MOVE_ADJUSTING; + + // hardware was asked for tracking (track target celestial point) + static constexpr auto v3 = T::hardware_moving_state_t::HW_MOVE_TRACKING; + + // hardware was asked for guiding + // (small corrections to align actual mount position with target celestial point) + static constexpr auto v4 = T::hardware_moving_state_t::HW_MOVE_GUIDING; + + // to detect possible hardware error + static constexpr auto v5 = T::hardware_moving_state_t::HW_MOVE_ERROR; + }(); + }; + + + + // a class that contains at least time point of measurement, coordinates for x,y axes, + // its moving rates and moving type + requires mcc_celestial_point_c && requires(typename T::hardware_state_t state) { + // 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 + + requires mcc_angle_c; // moving speed along co-longitude coordinate + requires mcc_angle_c; // moving speed along co-latitude coordinate + + requires std::same_as; + }; + + // set hardware state: + // i.g. set positions (angles) of mount axes with given speeds + // NOTE: exact interpretation (or even ignoring) of the given moving speeds is subject of a hardware-class + // implementation, e.g. it can be maximal speeds at slewing ramp + { t.hardwareSetState(std::declval()) } -> std::same_as; + + // get current state + { t.hardwareGetState(std::declval()) } -> std::same_as; + + { t.hardwareStop() } -> std::same_as; // stop any moving + { t.hardwareInit() } -> std::same_as; // initialize hardware +}; + + + +/* MOUNT TELEMETRY DATA CLASS CONCEPT */ + +template +concept mcc_pointing_target_coord_c = mcc_eqt_hrz_coord_c && requires(T t) { + requires mcc_angle_c; // ICRS right ascention + requires mcc_angle_c; // ICRS declination +}; + + +static constexpr void mcc_copy_pointing_target_coord(mcc_pointing_target_coord_c auto const& from_pt, + mcc_pointing_target_coord_c auto* to_pt) +{ + if (to_pt == nullptr) { + return; + } + + using from_pt_t = std::remove_cvref_t; + using to_pt_t = std::remove_cvref_t; + + if constexpr (std::derived_from && std::copyable) { + *to_pt = from_pt; + return; + } + + to_pt->pair_kind = from_pt.pair_kind; + to_pt->time_point = + std::chrono::time_point_casttime_point)::duration>(from_pt.time_point); + to_pt->X = (double)from_pt.X; + to_pt->Y = (double)from_pt.Y; + + to_pt->RA_ICRS = (double)from_pt.RA_ICRS; + to_pt->DEC_ICRS = (double)from_pt.DEC_ICRS; + + to_pt->RA_APP = (double)from_pt.RA_APP; + to_pt->DEC_APP = (double)from_pt.DEC_APP; + + to_pt->HA = (double)from_pt.HA; + to_pt->AZ = (double)from_pt.AZ; + to_pt->ZD = (double)from_pt.ZD; + to_pt->ALT = (double)from_pt.ALT; +} + + + +template +concept mcc_telemetry_data_c = mcc_eqt_hrz_coord_c && std::default_initializable && requires(T t) { + // target target coordinates + requires mcc_pointing_target_coord_c; + + // t.X and t.Y (from mcc_celestial_point_c) are encoder coordinates + // t.* from mcc_eqt_hrz_coord_c are apparent mount pointing coordinates + requires mcc_angle_c; // speed along X from hardware encoder + requires mcc_angle_c; // speed along Y from hardware encoder + + // corrections to transform hardware encoder coordinates to apparent celestial ones + requires mcc_angle_c; // PCM correction along X-axis + requires mcc_angle_c; // PCM correction along Y-axis + + // atmospheric refraction correction for current zenithal distance + requires mcc_angle_c; // for current .ZD +}; + + +static constexpr void mcc_copy_telemetry_data(mcc_telemetry_data_c auto const& from_pt, + mcc_telemetry_data_c auto* to_pt) +{ + if (to_pt == nullptr) { + return; + } + + using from_pt_t = std::remove_cvref_t; + using to_pt_t = std::remove_cvref_t; + + if constexpr (std::derived_from && std::copyable) { + *to_pt = from_pt; + return; + } + + to_pt->pair_kind = from_pt.pair_kind; + to_pt->time_point = + std::chrono::time_point_casttime_point)::duration>(from_pt.time_point); + + to_pt->X = (double)from_pt.X; + to_pt->Y = (double)from_pt.Y; + to_pt->speedX = (double)from_pt.speedX; + to_pt->speedY = (double)from_pt.speedY; + + to_pt->RA_APP = (double)from_pt.RA_APP; + to_pt->DEC_APP = (double)from_pt.DEC_APP; + + to_pt->HA = (double)from_pt.HA; + to_pt->AZ = (double)from_pt.AZ; + to_pt->ZD = (double)from_pt.ZD; + to_pt->ALT = (double)from_pt.ALT; + + to_pt->pcmX = (double)from_pt.pcmX; + to_pt->pcmY = (double)from_pt.pcmY; + + to_pt->refCorr = (double)from_pt.refCorr; + + mcc_copy_pointing_target_coord(from_pt.target, &to_pt->target); +} + + + +/* MOUNT TELEMETRY MANAGER CLASS CONCEPT */ + +template +struct mcc_telemetry_interface_t { + virtual ~mcc_telemetry_interface_t() = default; + + // update telemetry data right now + // an implementation is expected to update the data within the given 'timeout' time interval + // if not the method must return an appropriate timeout-error + template SelfT> + RetT updateTelemetryData(this SelfT&& self, traits::mcc_time_duration_c auto const& timeout) + { + return std::forward(self).updateTelemetryData(timeout); + } + + // get current data + // an implementation is expected to return current, possibly already expired, telemetry data + // one should call consistently both 'updateTelemetryData' + 'telemetryData' methods + // to ensure that the data is up-to-date + template SelfT> + RetT telemetryData(this SelfT&& self, mcc_telemetry_data_c auto* data) + { + return std::forward(self).telemetryData(data); + } + + // waiting for updated data + // an implementation is expected to block the current thread waiting for + // telemetry data to be updated (internal synchronization) + // if a timeout occured the method must return an appropriate timeout-error + template SelfT> + RetT waitForTelemetryData(this SelfT&& self, + mcc_telemetry_data_c auto* data, + traits::mcc_time_duration_c auto const& timeout) + { + return std::forward(self).waitForTelemetryData(data, timeout); + } + + + // set target coordinates + template SelfT> + RetT setPointingTarget(this SelfT&& self, mcc_celestial_point_c auto pt) + { + return std::forward(self).telemetryData(std::move(pt)); + } + + + // compute difference in coordinates: + // dx = targetX - mountX + // dy = targetY - mountY + // where X and Y is in according to 'pair_kind' input parameter + template SelfT> + RetT targetToMountDiff(this SelfT&& self, MccCoordPairKind pair_kind, mcc_angle_c auto* dx, mcc_angle_c auto* dy) + { + std::forward(self).targetToMountDiff(pair_kind, dx, dy); + } + + // compute distance between target and actual mount celestial points + template SelfT> + RetT targetToMountDist(this SelfT&& self, mcc_angle_c auto* dist) + { + std::forward(self).targetToMountDist(dist); + } + +protected: + mcc_telemetry_interface_t() = default; +}; + +template +concept mcc_telemetry_c = std::derived_from>; + + + +/* PROHIBITED ZONE CLASS CONCEPT */ + +template +struct mcc_pzone_interface_t { + virtual ~mcc_pzone_interface_t() = default; + + template SelfT, typename InputT> + RetT inPZone(this SelfT&& self, InputT coords, bool* result) + requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && + requires { self.inPZone(coords, result); } + { + return std::forward(self).InPZone(std::move(coords), result); + } + + + template SelfT, typename InputT> + RetT timeToPZone(this SelfT&& self, InputT coords, traits::mcc_time_duration_c auto* res_time) + requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && + requires { self.timeToPZone(coords, res_time); } + { + return std::forward(self).timeToPZone(std::move(coords), res_time); + } + + template SelfT, typename InputT> + RetT timeFromPZone(this SelfT&& self, InputT coords, traits::mcc_time_duration_c auto* res_time) + requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && + requires { self.timeFromPZone(coords, res_time); } + { + return std::forward(self).timeFromPZone(std::move(coords), res_time); + } + + // + // NOTE: the method must return: + // point = mcc_celestial_point_c{.pair_kind = MccCoordPairKind::COORDS_KIND_GENERIC, .X = NaN, .Y = NaN} + // if there is no intersection with the zone for given coordinates! + template SelfT, typename InputT, typename ResultT> + RetT intersectPZone(this SelfT&& self, InputT coords, ResultT* point) + requires((mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && + (mcc_eqt_hrz_coord_c || mcc_celestial_point_c)) && + requires { self.intersectPZone(coords, point); } + { + return std::forward(self).intersectPZone(std::move(coords), point); + } + // template SelfT, typename InputT> + // RetT intersectPZone(this SelfT&& self, InputT coords, mcc_celestial_point_c auto* point) + // requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && + // requires { self.intersectPZone(coords, point); } + // { + // return std::forward(self).intersectPZone(std::move(coords), point); + // } + +protected: + mcc_pzone_interface_t() = default; +}; + + +template +concept mcc_prohibited_zone_c = + std::derived_from> && requires(const T t_const) { + { t_const.name() } -> std::formattable; + + // the 'T' class must contain static constexpr member of 'MccMountType' type + requires std::same_as; + []() { + static constexpr MccProhibitedZonePolicy val = T::pzPolicy; + return val; + }(); // to ensure 'pzPolicy' can be used in compile-time context + }; + + +/* PROHIBITED ZONES CONTAINER CLASS CONCEPT */ + + +template +struct mcc_pzone_container_interface_t { + virtual ~mcc_pzone_container_interface_t() = default; + + + template SelfT> + size_t addPZone(this SelfT&& self, mcc_prohibited_zone_c auto zone) + { + return std::forward(self).addPZone(std::move(zone)); + } + + + template SelfT> + void clearPZones(this SelfT&& self) + { + return std::forward(self).clearPZones(); + } + + + template SelfT> + size_t sizePZones(this SelfT&& self) + { + return std::forward(self).sizePZones(); + } + + + template SelfT, typename InputT> + RetT inPZone(this SelfT&& self, InputT coords, bool* common_result, std::ranges::output_range auto* result) + requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) + { + return std::forward(self).InPZone(std::move(coords), common_result, result); + } + + + template SelfT, typename InputT, traits::mcc_time_duration_c DT> + RetT timeToPZone(this SelfT&& self, InputT coords, std::ranges::output_range
auto* res_time) + requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) + { + return std::forward(self).timeToPZone(std::move(coords), res_time); + } + + template SelfT, typename InputT, traits::mcc_time_duration_c DT> + RetT timeFromPZone(this SelfT&& self, InputT coords, std::ranges::output_range
auto* res_time) + requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) + { + return std::forward(self).timeFromPZone(std::move(coords), res_time); + } + + + // template SelfT, typename InputT, mcc_celestial_point_c CPT> + // RetT intersectPZone(this SelfT&& self, InputT coords, std::ranges::output_range auto* result) + // requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) + // { + // return std::forward(self).intersectPZone(std::move(coords), result); + // } + + template SelfT, typename InputT, typename ResultT> + RetT intersectPZone(this SelfT&& self, InputT coords, std::ranges::output_range auto* result) + requires((mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && + (mcc_eqt_hrz_coord_c || mcc_celestial_point_c)) + { + return std::forward(self).intersectPZone(std::move(coords), result); + } + +protected: + mcc_pzone_container_interface_t() = default; +}; + + +template +concept mcc_pzone_container_c = std::derived_from>; + + +/* MOUNT MOVING MODEL CONCEPT */ + +template +concept mcc_slewing_model_c = requires(T t) { + // requires mcc_error_c; + + // a class of slewing process parameters + requires requires(typename T::slewing_params_t pars) { + // slew mount to target and stop + requires std::convertible_to; + }; + + // { t.slewToTarget() } -> std::same_as; + // { t.stopSlewing() } -> std::same_as; + { t.slewToTarget() } -> mcc_error_c; + { t.stopSlewing() } -> mcc_error_c; + + { t.setSlewingParams(std::declval()) } -> mcc_error_c; + // { t.setSlewingParams(std::declval()) } -> std::same_as; + { t.getSlewingParams() } -> std::same_as; +}; + + +template +concept mcc_tracking_model_c = requires(T t) { + // requires mcc_error_c; + + // a class of tracking process parameters + requires requires(typename T::tracking_params_t pars) { + requires mcc_angle_c; + requires mcc_angle_c; + }; + + // { t.trackTarget() } -> std::same_as; + // { t.stopTracking() } -> std::same_as; + { t.trackTarget() } -> mcc_error_c; + { t.stopTracking() } -> mcc_error_c; + + { t.setTrackingParams(std::declval()) } -> mcc_error_c; + // { t.setTrackingParams(std::declval()) } -> std::same_as; + { t.getTrackingParams() } -> std::same_as; +}; + +// template +// concept mcc_guiding_model_c = requires(T t) { +// requires mcc_error_c; + +// // a class of guiding process parameters +// requires requires(typename T::guiding_params_t pars) { +// // guide along both mount axis +// requires std::convertible_to; +// }; + +// { t.startGuidingTarget() } -> std::same_as; +// { t.stopGuidingTarget() } -> std::same_as; + +// { t.setGuidingParams(std::declval()) } -> std::same_as; +// { t.getGuidingParams() } -> std::same_as; +// }; + + +/* GENERIC MOUNT CLASS CONCEPT */ + + +// a concept of class that consist of the full set of coordinate transformation mount control components +// (celestial coordinate transformation engine, mount hardware encoders readings and pointing correction model) +// the set of methods of this class is enough to transform coordinates from ICRS to hardware and back +template +concept mcc_position_controls_c = mcc_ccte_c && mcc_hardware_c && mcc_PCM_c; + +// a class containing full set of mount controls +template +concept mcc_all_controls_c = mcc_position_controls_c && mcc_telemetry_c && mcc_pzone_container_c; + + +// generic mount: +// 1) telemetry-related methods +// 2) prohibited zones related methods +// 3) slewing and tracking, stop and init mount methods +template +concept mcc_generic_mount_c = mcc_telemetry_c && mcc_pzone_container_c && requires(T t) { + // requires mcc_error_c; + + // slew mount to target (target coordinates were defined in telemetry data) + { t.slewToTarget() }; + // { t.slewToTarget() } -> std::same_as; + + // track target, i.e., the mount moves with celestial speed + { t.trackTarget() }; + // { t.trackTarget() } -> std::same_as; + + + // { t.startGuidingTarget() } -> std::same_as; + // { t.stopGuidingTarget() } -> std::same_as; + + // stop any movement + { t.stopMount() }; + // { t.stopMount() } -> std::same_as; + + // init mount + { t.initMount() }; +}; + + +// with logging methods +template +concept mcc_generic_log_mount_c = mcc_generic_mount_c && mcc_logger_c; + + +// Finite-state-machine +template +concept mcc_generic_fsm_mount_c = mcc_generic_mount_c && std::derived_from; + +template +concept mcc_generic_fsm_log_mount_c = + mcc_generic_mount_c && mcc_logger_c && std::derived_from; + + +} // namespace mcc