#pragma once /**************************************************************************************** * * * MOUNT CONTROL COMPONENTS LIBRARY * * * * * * COMPONENT CLASSES CONCEPTS * * * ****************************************************************************************/ #include #include #include #include "mcc_angle.h" #include "mcc_traits.h" namespace mcc { /* LIBRARY-WIDE CONCEPT FOR LOGGER CLASS */ 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()) }; { t.logTrace(std::declval()) }; }; /* CLASS TO EMULATE NO LOGGING */ struct MccNullLogger { void logError(const std::string&) {} void logDebug(const std::string&) {} void logWarn(const std::string&) {} void logInfo(const std::string&) {} void logTrace(const std::string&) {} }; /* LIBRARY-WIDE TYPES DEFINITION OF OPERATIONAL ERROR */ /* AND CLASS METHODS RETURNED VALUE */ template concept mcc_error_c = std::formattable && std::default_initializable && (std::convertible_to || requires(const T t) { (bool)T() == false; // default constucted value must be a "non-error"! }); template DefErrT mcc_deduced_err(ErrT const& err, DefErrT const& default_err) { if constexpr (std::same_as) { return err; } else { return default_err; } } template concept mcc_retval_c = requires(T t) { // [](std::expected) {}(t); }; // deduce an error from mcc_retval_c and default error value template RetT, mcc_error_c DefErrT> DefErrT mcc_deduced_err(RetT const& ret, DefErrT const& default_err) { if (ret) { return DefErrT{}; // no error } if constexpr (std::same_as) { return ret.error(); } else { return default_err; } } /* MOUNT CONSTRUCTION-RELATED STUFF */ // 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; }; /* FLOATING-POINT LIKE CLASS CONCEPT */ template concept mcc_fp_type_like_c = std::floating_point || (std::convertible_to && std::constructible_from && std::default_initializable); /* GEOMETRICAL 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; }; /* CELESTIAL COORDINATES EPOCH CLASS CONCEPT */ struct mcc_coord_epoch_interface_t { virtual ~mcc_coord_epoch_interface_t() = default; 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; }; /* CELESTIAL COORDINATE TRANSFORMATION ENGINE CLASS CONCEPT */ template struct mcc_ccte_engine_interface_t { virtual ~mcc_ccte_engine_interface_t() = default; // returns geographical site coordinates for underlying transformation calculations template SelfT, mcc_angle_c LAT_T, mcc_angle_c LON_T> void geoPosition(this SelfT&& self, std::pair* pos) { std::forward(self).geoPosition(pos); } template SelfT, mcc_angle_c StT, mcc_coord_epoch_c EpT> auto apparentSideralTime(this SelfT&& self, EpT const& ep, StT* st, bool isLocal) { return std::forward(self).apparentSideralTime(ep, st, isLocal); } // from ICRS to observed (taking into account atmospheric refraction) coordinate transformation template SelfT, mcc_angle_c RA_ICRS_T, mcc_angle_c DEC_ICRS_T, mcc_coord_epoch_c EpT, mcc_angle_c RA_OBS_T, mcc_angle_c DEC_OBS_T, mcc_angle_c HA_OBS_T, mcc_angle_c AZ_T, mcc_angle_c ZD_T> RetT icrsToObs(this SelfT&& self, RA_ICRS_T const& ra_icrs, DEC_ICRS_T const& dec_icrs, EpT const& ep, RA_OBS_T* ra, DEC_OBS_T* dec, HA_OBS_T* ha, AZ_T* az, ZD_T* zd) { return std::forward(self).icrsToObs(ra_icrs, dec_icrs, ep, ra, dec, ha, az, zd); } // from ICRS to apparent (in vacuo) coordinate transformation template SelfT, mcc_angle_c RA_ICRS_T, mcc_angle_c DEC_ICRS_T, mcc_coord_epoch_c EpT, mcc_angle_c RA_APP_T, mcc_angle_c DEC_APP_T, mcc_angle_c HA_APP_T, mcc_angle_c AZ_T, mcc_angle_c ZD_T> RetT icrsToApp(this SelfT&& self, RA_ICRS_T const& ra_icrs, DEC_ICRS_T const& dec_icrs, EpT const& ep, RA_APP_T* ra, DEC_APP_T* dec, HA_APP_T* ha, AZ_T* az, ZD_T* zd) { return std::forward(self).icrsToApp(ra_icrs, dec_icrs, ep, ra, dec, ha, az, zd); } // from observed (taking into account atmospheric refraction) to ICRS coordinate transformation template SelfT, mcc_coord_epoch_c EpT, mcc_angle_c CO_LON_T, mcc_angle_c CO_LAT_T, mcc_angle_c RA_ICRS_T, mcc_angle_c DEC_ICRS_T> RetT obsToICRS(this SelfT&& self, impl::MccCoordPairKind obs_type, EpT const& epoch, CO_LON_T const& co_lon, CO_LAT_T const& co_lat, RA_ICRS_T* ra_icrs, DEC_ICRS_T* dec_icrs) { return std::forward(self).obsToICRS(epoch, obs_type, co_lon, co_lat, ra_icrs, dec_icrs); } // from apparent (in vacuo) to ICRS coordinate transformation template SelfT, mcc_coord_epoch_c EpT, mcc_angle_c CO_LON_T, mcc_angle_c CO_LAT_T, mcc_angle_c RA_ICRS_T, mcc_angle_c DEC_ICRS_T> RetT appToICRS(this SelfT&& self, impl::MccCoordPairKind app_type, EpT const& epoch, CO_LON_T const& co_lon, CO_LAT_T const& co_lat, RA_ICRS_T* ra_icrs, DEC_ICRS_T* dec_icrs) { return std::forward(self).appToICRS(epoch, app_type, co_lon, co_lat, ra_icrs, dec_icrs); } // compute equation of origins EO = ERA-GAST template SelfT, mcc_coord_epoch_c EpT, mcc_angle_c EO_T> RetT equationOrigins(this SelfT&& self, EpT const& epoch, EO_T* eo) { return std::forward(self).equationOrigins(epoch, eo); } // compute refraction correction from observed zenithal distance // (Zapp = Zobs + dZ) template SelfT, mcc_angle_c ZD_OBS_T, mcc_angle_c DZ_T> RetT refractionCorrection(this SelfT&& self, ZD_OBS_T const& zd_obs, DZ_T* dZ) { return std::forward(self).refractionCorrection(zd_obs, dZ); } // compute refraction correction from apparent (in vacuo) zenithal distance // (Zobs = Zapp - dZ) template SelfT, mcc_angle_c ZD_APP_T, mcc_angle_c DZ_T> RetT refractionInverseCorrection(this SelfT&& self, ZD_APP_T const& zd_app, DZ_T* dZ) { return std::forward(self).refractionInverseCorrection(zd_app, dZ); } }; template concept mcc_ccte_c = std::derived_from> && requires { // error type requires mcc_error_c; // static const variable with name of CCTE requires std::formattable && std::is_const_v; }; /* COORDINATES PAIR CLASS CONCEPT */ struct mcc_coord_pair_interface_t { virtual ~mcc_coord_pair_interface_t() = default; template SelfT, mcc_coord_epoch_c EpT> auto setEpoch(this SelfT&& self, EpT const& ep) { return std::forward(self).setEpoch(ep); } }; template concept mcc_coord_pair_c = std::derived_from && requires(T t) { // the 'T' class must contain static constexpr member of 'pairKind' of some type // (usually just a enum: see mcc_coordinate.h for an example of the implementation) []() { [[maybe_unused]] static constexpr auto val = T::pairKind; }(); // to ensure 'pairKind' can be used in compile-time context requires mcc_angle_c; // co-longitude coordinate type requires mcc_angle_c; // co-latitude coordinate type // std::constructible_from; { t.x() } -> std::same_as; { t.y() } -> std::same_as; { t.setX(std::declval()) }; { t.setY(std::declval()) }; }; /* SKY POINT CLASS CONCEPT */ struct mcc_skypoint_interface_t { virtual ~mcc_skypoint_interface_t() = default; template SelfT, mcc_coord_pair_c PT> auto from(this SelfT&& self, PT&& cpair) { return std::forward(self).from(std::forward(cpair)); } template SelfT, mcc_coord_pair_c PT> auto operator=(this SelfT&& self, PT&& cpair) { return std::forward(self).operator=(std::forward(cpair)); } template SelfT, mcc_coord_pair_c PT, mcc_coord_pair_c... PTs> auto to(this SelfT&& self, PT& cpair, PTs&... cpairs) { return std::forward(self).to(cpair, cpairs...); } template SelfT, mcc_coord_pair_c PT> operator PT(this SelfT&& self) { return std::forward(self).operator PT(); } // mandatory specialization conversional operator to // get geographic coordinates used in underlying transformation calculations // (it are geographic site coordinates mandatory used in celestial coordinate transformation engine // of any implementation of skypoint class) template SelfT, mcc_coord_pair_c PT> requires(PT::pairKind == impl::MccCoordPairKind::COORDS_KIND_LONLAT) operator PT(this SelfT&& self) { return std::forward(self).operator PT(); } // return coordinates pair at same epoch as 'this' sky point template SelfT, mcc_coord_pair_c PT, mcc_coord_pair_c... PTs> auto toAtSameEpoch(this SelfT&& self, PT& cpair, PTs&... cpairs) { return std::forward(self).to(cpair, cpairs...); } // Refraction correction for given celestial point. // It is assumed that for the apparent (in vacuo) and ICRS kinds of coordinates this correction is 0! // The returned refraction correction must be calculated as the correction applied to observed (affected by // refraction) zenithal distance to compute apparent (in vacuo) one, i.e., Z_app = Z_obs + refr_corr template SelfT> auto refractCorrection(this SelfT&& self, mcc_angle_c auto* dZ) { return std::forward(self).refractCorrection(dZ); } // As above but the returned correction must be calculated as the correction applied to apparent (in vacuo) // zenithal distance to compute observed (affected by refraction) one, i.e., Z_obs = Z_app - refr_corr. // It is assumed that for the observed and ICRS kinds of coordinates this correction is 0! template SelfT> auto refractInverseCorrection(this SelfT&& self, mcc_angle_c auto* dZ) { return std::forward(self).refractInverseCorrection(dZ); } // returns apparent sideral time (Greenwich) for the epoch of the celestial point template SelfT> auto appSideralTime(this SelfT&& self, mcc_angle_c auto* st) { return std::forward(self).appSideralTime(st); } // returns equation of origins for the epoch of the celestial point template SelfT> auto EO(this SelfT&& self, mcc_angle_c auto* eo) { return std::forward(self).EO(eo); } }; template concept mcc_skypoint_c = std::derived_from && requires(const T t_const) { { t_const.epoch() } -> mcc_coord_epoch_c; // currently stored coordinates pair { t_const.pairKind() } -> std::same_as; }; /* POINTING CORRECTION MODEL CLASS CONCEPT */ // The result of PCM calculations must be at least corrections along both mount axes template concept mcc_pcm_result_c = requires(T t) { requires mcc_angle_c; requires mcc_angle_c; }; template struct mcc_pcm_interface_t { virtual ~mcc_pcm_interface_t() = default; template SelfT, mcc_coord_pair_c HW_COORD_T> RetT computePCM(this SelfT&& self, HW_COORD_T const& hw_coord, mcc_pcm_result_c auto* result, mcc_skypoint_c auto* obs_pt) { return std::forward(self).computePCM(hw_coord, result, obs_pt); } template SelfT, mcc_coord_pair_c HW_COORD_T> RetT computeInversePCM(this SelfT&& self, mcc_skypoint_c auto const& obs_pt, mcc_pcm_result_c auto* inv_result, HW_COORD_T* hw_coord) { return std::forward(self).computeInversePCM(obs_pt, inv_result, hw_coord); } }; template concept mcc_pcm_c = std::derived_from> && requires { // error type requires mcc_error_c; // the 'T' class must contain static constexpr member of 'MccMountType' type requires std::same_as; []() { [[maybe_unused]] static constexpr MccMountType val = T::mountType; }(); // to ensure 'mountType' can be used in compile-time context // static const variable with name of PCM requires std::formattable && std::is_const_v; }; /* MOUNT HARDWARE ABSTRACTION CLASS CONCEPT */ // 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. // // e.g. an implementations can be as follows: // enum class hardware_movement_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_movement_state_t { // static constexpr uint16_t HW_MOVE_STOPPED = 0; // static constexpr uint16_t HW_MOVE_SLEWING = 111; // static constexpr uint16_t HW_MOVE_ADJUSTING = 222; // static constexpr uint16_t HW_MOVE_TRACKING = 333; // static constexpr uint16_t HW_MOVE_GUIDING = 444; // static constexpr uint16_t HW_MOVE_ERROR = 555; // } template concept mcc_hardware_movement_state_c = requires { []() { // // mount axes were stopped // [[maybe_unused]] static constexpr auto v0 = T::HW_MOVE_STOPPED; // // hardware was asked for slewing (move to given celestial point) // [[maybe_unused]] static constexpr auto v1 = 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) // [[maybe_unused]] static constexpr auto v2 = T::HW_MOVE_ADJUSTING; // // hardware was asked for tracking (track target celestial point) // [[maybe_unused]] static constexpr auto v3 = T::HW_MOVE_TRACKING; // // hardware was asked for guiding // // (small corrections to align actual mount position with target celestial point) // [[maybe_unused]] static constexpr auto v4 = T::HW_MOVE_GUIDING; // // to detect possible hardware error // [[maybe_unused]] static constexpr auto v5 = T::HW_MOVE_ERROR; [[maybe_unused]] static constexpr std::array arr{ // mount hardware was asked to stop T::HW_MOVE_STOPPING, // mount axes were stopped T::HW_MOVE_STOPPED, // move to given celestial point T::HW_MOVE_SLEWING, // adjusting after slewing T::HW_MOVE_ADJUSTING, // tracking (track target celestial point) T::HW_MOVE_TRACKING, // guiding (small corrections to align actual mount position with target celestial point) T::HW_MOVE_GUIDING, // to detect possible hardware error T::HW_MOVE_ERROR}; }(); }; template concept mcc_hardware_state_c = requires(T state) { // encoder co-longitude and co-latiitude positions, as well as its measurement time point // the given constrains on coordinate pair kind can be used to deduce mount type requires mcc_coord_pair_c && (decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_GENERIC || decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_XY); // co-longitude and co-latiitude axis angular speeds, as well as its measurement/computation time point requires mcc_coord_pair_c && (decltype(state.speedXY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_GENERIC || decltype(state.speedXY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_XY); requires mcc_hardware_movement_state_c; }; template concept mcc_hardware_c = requires(T t) { // error type requires mcc_error_c; // static const variable with name of hardware requires std::formattable && std::is_const_v; // a type that defines at least HW_MOVE_ERROR, HW_MOVE_STOPPING, 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 the stop and error states from // hardware // // e.g. an implementations can be as follows: // enum class hardware_movement_state_t: int {HW_MOVE_ERROR = -1, HW_MOVE_STOPPED = 0, HW_MOVE_STOPPING, // HW_MOVE_SLEWING, HW_MOVE_ADJUSTING, HW_MOVE_TRACKING, HW_MOVE_GUIDING} // // struct hardware_movement_state_t { // static constexpr uint16_t HW_MOVE_STOPPED = 0; // static constexpr uint16_t HW_MOVE_SLEWING = 111; // static constexpr uint16_t HW_MOVE_ADJUSTING = 222; // static constexpr uint16_t HW_MOVE_TRACKING = 333; // static constexpr uint16_t HW_MOVE_GUIDING = 444; // static constexpr uint16_t HW_MOVE_ERROR = 555; // static constexpr uint16_t HW_MOVE_STOPPING = 666; // } requires mcc_hardware_movement_state_c; // requires requires(typename T::hardware_movement_state_t type) { // []() { // // mount axes were stopped // static constexpr auto v0 = T::hardware_movement_state_t::HW_MOVE_STOPPED; // // hardware was asked for slewing (move to given celestial point) // static constexpr auto v1 = T::hardware_movement_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_movement_state_t::HW_MOVE_ADJUSTING; // // hardware was asked for tracking (track target celestial point) // static constexpr auto v3 = T::hardware_movement_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_movement_state_t::HW_MOVE_GUIDING; // // to detect possible hardware error // static constexpr auto v5 = T::hardware_movement_state_t::HW_MOVE_ERROR; // }(); // }; requires mcc_hardware_state_c && requires(typename T::hardware_state_t state) { requires std::same_as; }; // requires requires(typename T::hardware_state_t state) { // // encoder co-longitude and co-latiitude positions, as well as its measurement time point // // the given constrains on coordinate pair kind can be used to deduce mount type // requires mcc_coord_pair_c && // ( // for equathorial mount: // decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_HADEC_OBS || // // for alt-azimuthal mount: // decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_AZALT || // decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_AZZD); // // co-longitude and co-latiitude axis angular speeds, as well as its measurement/computation time point // requires mcc_coord_pair_c && // (decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_GENERIC || // decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_XY); // requires std::same_as; // }; // set hardware state: { 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_telemetry_data_c = requires(T t) { // target celestial point (position on sky where mount must be slewed) requires mcc_skypoint_c; // mount current celestial position requires mcc_skypoint_c; // hardware state requires mcc_hardware_state_c; // corrections to transform hardware encoder coordinates to observed celestial ones requires mcc_pcm_result_c; // 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; // // current local apparent sideral time // requires mcc_angle_c; // // equation of the origins (ERA-GST) // requires mcc_angle_c; // // target celestial point // { t.targetPos() } -> mcc_skypoint_c; // // mount current celestial position // { t.mountPos() } -> mcc_skypoint_c; // // hardware state // { t.hwState() } -> mcc_hardware_c; // // corrections to transform hardware encoder coordinates to observed celestial ones // { t.pcmData() } -> mcc_pcm_result_c; // // // atmospheric refraction correction for current mount zenithal distance // // { t.refractionCorr() } -> mcc_angle_c; // // current local apparent sideral time // { t.LST() } -> mcc_angle_c; // // equation of the origins (ERA-GST) // { t.EO() } -> mcc_angle_c; }; /* MOUNT TELEMETRY CLASS CONCEPT */ template struct mcc_telemetry_interface_t { virtual ~mcc_telemetry_interface_t() = default; // set target position template SelfT> RetT setTarget(this SelfT&& self, mcc_skypoint_c auto const& pt) { return std::forward(self).setTarget(pt); } }; template concept mcc_telemetry_c = std::derived_from> && requires(T t) { // error type requires mcc_error_c; // telemetry data type definition requires mcc_telemetry_data_c; // get telemetry data { t.telemetryData(std::declval()) } -> std::same_as; }; /* PROHIBITED ZONE CLASS CONCEPT */ 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 }; template struct mcc_pzone_interface_t { virtual ~mcc_pzone_interface_t() = default; template SelfT> RetT inPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, bool* result) { return std::forward(self).inPZone(coords, result); } template SelfT> RetT timeToPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time) { return std::forward(self).timeToPZone(coords, res_time); } template SelfT> RetT timeFromPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time) { return std::forward(self).timeFromPZone(coords, res_time); } }; template concept mcc_pzone_c = std::derived_from> && requires(T t) { // error type requires mcc_error_c; // static constant member with prohibitted zone name requires std::formattable && std::is_const_v; // the 'T' class must contain static constexpr member of 'MccProhibitedZonePolicy' type requires std::same_as; []() { [[maybe_unused]] static constexpr MccProhibitedZonePolicy val = T::pzPolicy; }(); // 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_pzone_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> RetT inPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, bool* at_least_one, std::ranges::output_range auto* result) { return std::forward(self).inPZone(coords, at_least_one, result); } template SelfT, traits::mcc_time_duration_c DT> RetT timeToPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, std::ranges::output_range
auto* res_time) { return std::forward(self).timeToPZone(coords, res_time); } template SelfT, traits::mcc_time_duration_c DT> RetT timeFromPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, std::ranges::output_range
auto* res_time) { return std::forward(self).timeFromPZone(coords, res_time); } }; template concept mcc_pzone_container_c = std::derived_from> && requires { // error type requires mcc_error_c; }; /* A CONCEPT FOR MOUNT MOVEMENT CONTROLS CLASS */ template concept mcc_movement_controls_c = requires(T t) { // error type requires mcc_error_c; // movement parameters holder type typename T::movement_params_t; // argument of the method: // true - slew and stop // false - slew and track { t.slewToTarget(std::declval()) } -> std::same_as; { t.trackTarget() } -> std::same_as; { t.stopMount() } -> std::same_as; { t.setMovementParams(std::declval()) } -> std::same_as; { t.getMovementParams() } -> std::same_as; }; /* GENERIC MOUNT CLASS CONCEPT */ // minimal set of the mount status constants template concept mcc_mount_status_c = requires { []() { [[maybe_unused]] static constexpr std::array arr = { T::MOUNT_STATUS_ERROR, T::MOUNT_STATUS_IDLE, T::MOUNT_STATUS_INITIALIZATION, T::MOUNT_STATUS_ERROR, T::MOUNT_STATUS_STOPPED, T::MOUNT_STATUS_SLEWING, T::MOUNT_STATUS_ADJUSTING, T::MOUNT_STATUS_GUIDING, T::MOUNT_STATUS_TRACKING}; }; // to ensure mount status is compile-time constants }; template concept mcc_generic_mount_c = mcc_logger_c && mcc_pzone_container_c && mcc_telemetry_c && mcc_movement_controls_c && requires(T t) { // error type requires mcc_error_c; requires mcc_mount_status_c; { t.initMount() } -> std::same_as; { t.mountStatus() } -> std::same_as; }; } // namespace mcc