Files
mcc/mcc_concepts.h
Timur A. Fatkhullin 97b908838c ...
2026-02-06 00:09:15 +03:00

982 lines
37 KiB
C++

#pragma once
/****************************************************************************************
* *
* MOUNT CONTROL COMPONENTS LIBRARY *
* *
* *
* COMPONENT CLASSES CONCEPTS *
* *
****************************************************************************************/
#include <cstdint>
// #include <expected>
#include <string_view>
#include "mcc_angle.h"
#include "mcc_traits.h"
namespace mcc
{
/* LIBRARY-WIDE CONCEPT FOR LOGGER CLASS */
template <typename T>
concept mcc_logger_c = requires(T t, const T t_const) {
{ t.logError(std::declval<const std::string&>()) };
{ t.logDebug(std::declval<const std::string&>()) };
{ t.logWarn(std::declval<const std::string&>()) };
{ t.logInfo(std::declval<const std::string&>()) };
{ t.logTrace(std::declval<const std::string&>()) };
};
/* 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 <typename T>
concept mcc_error_c = std::default_initializable<T> && (std::convertible_to<T, bool> || requires(const T t) {
// std::formattable<T, char> && std::default_initializable<T> && (std::convertible_to<T, bool>
// || requires(const T t) {
(bool)T() == false; // default constucted value must be a "non-error"!
});
template <mcc_error_c ErrT, mcc_error_c DefErrT>
DefErrT mcc_deduced_err(ErrT const& err, DefErrT const& default_err)
{
if constexpr (std::same_as<ErrT, DefErrT>) {
return err;
} else {
return default_err;
}
}
// template <typename T, typename VT>
// concept mcc_retval_c = requires(T t) {
// //
// []<mcc_error_c ErrT>(std::expected<VT, ErrT>) {}(t);
// };
// // deduce an error from mcc_retval_c and default error value
// template <typename VT, mcc_retval_c<VT> 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<typename RetT::error_type, DefErrT>) {
// 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 <MccMountType TYPE>
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 <MccMountType TYPE>
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 <MccMountType TYPE>
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 <typename T>
concept mcc_fp_type_like_c =
std::floating_point<T> ||
(std::convertible_to<T, double> && std::constructible_from<T, double> && std::default_initializable<T>);
/* GEOMETRICAL ANGLE REPRESENTATION CLASS CONCEPT */
/* REQUIREMENT: in the MCC-library it is assumed that an arithmetic representation of angles are measured in the
radians!!! This means that possible conversion operator 'SOME_USER_ANGLE_CLASS::operator double()'
must return an angle in radians!
*/
template <typename T>
concept mcc_angle_c = mcc_fp_type_like_c<T> && requires(T v, double vd) {
// mandatory arithmetic operations
{ v + v } -> std::same_as<T>;
{ v - v } -> std::same_as<T>;
{ v += v } -> std::same_as<T&>;
{ v -= v } -> std::same_as<T&>;
{ vd + v } -> std::same_as<T>;
{ vd - v } -> std::same_as<T>;
{ v + vd } -> std::same_as<T>;
{ v - vd } -> std::same_as<T>;
{ v += vd } -> std::same_as<T&>;
{ v -= vd } -> std::same_as<T&>;
{ v * vd } -> std::same_as<T>;
{ v / vd } -> std::same_as<T>;
};
/* 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 <std::derived_from<mcc_coord_epoch_interface_t> SelfT, traits::mcc_input_char_range IR>
bool fromCharRange(this SelfT&& self, IR&& str)
{
return std::forward<decltype(self)>(self).fromCharRange(std::forward<IR>(str));
}
template <std::derived_from<mcc_coord_epoch_interface_t> SelfT, typename ClockT, typename DurT>
bool fromTimePoint(this SelfT&& self, std::chrono::time_point<ClockT, DurT>&& tp)
{
return std::forward<decltype(self)>(self).fromTimePoint(std::forward<decltype(tp)>(tp));
}
template <std::derived_from<mcc_coord_epoch_interface_t> SelfT, typename VT>
bool fromMJD(this SelfT&& self, VT&& mjd)
requires std::is_arithmetic_v<VT>
{
return std::forward<decltype(self)>(self).fromMJD(std::forward<VT>(mjd));
}
template <std::derived_from<mcc_coord_epoch_interface_t> SelfT, traits::mcc_time_duration_c DT>
SelfT& operator+=(this SelfT& self, DT&& dt)
{
return std::forward<decltype(self)>(self).operator+=(std::forward<DT>(dt));
}
template <std::derived_from<mcc_coord_epoch_interface_t> SelfT, traits::mcc_time_duration_c DT>
SelfT& operator-=(this SelfT& self, DT&& dt)
{
return std::forward<decltype(self)>(self).operator-=(std::forward<DT>(dt));
}
};
template <typename T>
concept mcc_coord_epoch_c = std::derived_from<T, mcc_coord_epoch_interface_t> && requires(T t1, T t2, const T t_const) {
{ t_const.MJD() } -> std::convertible_to<double>;
{ t_const.UTC() } -> traits::mcc_systime_c;
{ t_const.JEpoch() } -> traits::mcc_output_char_range;
{ t1 <=> t2 };
{ T::now() } -> std::same_as<T>;
};
/* CELESTIAL COORDINATE TRANSFORMATION ENGINE CLASS CONCEPT */
template <typename RetT>
struct mcc_ccte_engine_interface_t {
virtual ~mcc_ccte_engine_interface_t() = default;
// returns geographical site coordinates for underlying transformation calculations
template <std::derived_from<mcc_ccte_engine_interface_t> SelfT, mcc_angle_c LAT_T, mcc_angle_c LON_T>
void geoPosition(this SelfT&& self, std::pair<LAT_T, LON_T>* pos)
{
std::forward<SelfT>(self).geoPosition(pos);
}
template <std::derived_from<mcc_ccte_engine_interface_t> 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<SelfT>(self).apparentSideralTime(ep, st, isLocal);
}
// from ICRS to observed (taking into account atmospheric refraction) coordinate transformation
template <std::derived_from<mcc_ccte_engine_interface_t> 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<SelfT>(self).icrsToObs(ra_icrs, dec_icrs, ep, ra, dec, ha, az, zd);
}
// from ICRS to apparent (in vacuo) coordinate transformation
template <std::derived_from<mcc_ccte_engine_interface_t> 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<SelfT>(self).icrsToApp(ra_icrs, dec_icrs, ep, ra, dec, ha, az, zd);
}
// from observed (taking into account atmospheric refraction) to ICRS coordinate transformation
template <std::derived_from<mcc_ccte_engine_interface_t> 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<SelfT>(self).obsToICRS(epoch, obs_type, co_lon, co_lat, ra_icrs, dec_icrs);
}
// from apparent (in vacuo) to ICRS coordinate transformation
template <std::derived_from<mcc_ccte_engine_interface_t> 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<SelfT>(self).appToICRS(epoch, app_type, co_lon, co_lat, ra_icrs, dec_icrs);
}
// compute equation of origins EO = ERA-GAST
template <std::derived_from<mcc_ccte_engine_interface_t> 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<SelfT>(self).equationOrigins(epoch, eo);
}
// compute refraction correction from observed zenithal distance
// (Zapp = Zobs + dZ)
template <std::derived_from<mcc_ccte_engine_interface_t> 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<SelfT>(self).refractionCorrection(zd_obs, dZ);
}
// compute refraction correction from apparent (in vacuo) zenithal distance
// (Zobs = Zapp - dZ)
template <std::derived_from<mcc_ccte_engine_interface_t> 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<SelfT>(self).refractionInverseCorrection(zd_app, dZ);
}
};
template <typename T>
concept mcc_ccte_c = std::derived_from<T, mcc_ccte_engine_interface_t<typename T::error_t>> && requires {
// error type
requires mcc_error_c<typename T::error_t>;
// static const variable with name of CCTE
requires std::formattable<decltype(T::ccteName), char> && std::is_const_v<decltype(T::ccteName)>;
};
/* COORDINATES PAIR CLASS CONCEPT */
struct mcc_coord_pair_interface_t {
virtual ~mcc_coord_pair_interface_t() = default;
template <std::derived_from<mcc_coord_pair_interface_t> SelfT, mcc_coord_epoch_c EpT>
auto setEpoch(this SelfT&& self, EpT const& ep)
{
return std::forward<SelfT>(self).setEpoch(ep);
}
};
template <typename T>
concept mcc_coord_pair_c = std::derived_from<T, mcc_coord_pair_interface_t> && requires(T t, const T t_const) {
// 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<typename T::x_t>; // co-longitude coordinate type
requires mcc_angle_c<typename T::y_t>; // co-latitude coordinate type
// std::constructible_from<T, typename T::x_t const&, typename T::y_t const&>;
{ t_const.x() } -> std::same_as<typename T::x_t>;
{ t_const.y() } -> std::same_as<typename T::y_t>;
{ t_const.epoch() } -> mcc_coord_epoch_c;
{ t.setX(std::declval<typename T::x_t const&>()) };
{ t.setY(std::declval<typename T::y_t const&>()) };
};
/* SKY POINT CLASS CONCEPT */
struct mcc_skypoint_interface_t {
virtual ~mcc_skypoint_interface_t() = default;
template <std::derived_from<mcc_skypoint_interface_t> SelfT, mcc_coord_pair_c PT>
auto from(this SelfT&& self, PT&& cpair)
{
return std::forward<SelfT>(self).from(std::forward<PT>(cpair));
}
template <std::derived_from<mcc_skypoint_interface_t> SelfT, mcc_coord_pair_c PT>
auto operator=(this SelfT&& self, PT&& cpair)
{
return std::forward<SelfT>(self).operator=(std::forward<PT>(cpair));
}
template <std::derived_from<mcc_skypoint_interface_t> SelfT, mcc_coord_pair_c PT, mcc_coord_pair_c... PTs>
auto to(this SelfT&& self, PT& cpair, PTs&... cpairs)
{
return std::forward<SelfT>(self).to(cpair, cpairs...);
}
template <std::derived_from<mcc_skypoint_interface_t> SelfT, mcc_coord_pair_c PT>
operator PT(this SelfT&& self)
{
return std::forward<SelfT>(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 <std::derived_from<mcc_skypoint_interface_t> SelfT, mcc_coord_pair_c PT>
requires(PT::pairKind == impl::MccCoordPairKind::COORDS_KIND_LONLAT)
operator PT(this SelfT&& self)
{
return std::forward<SelfT>(self).operator PT();
}
// return coordinates pair at same epoch as 'this' sky point
template <std::derived_from<mcc_skypoint_interface_t> SelfT, mcc_coord_pair_c PT, mcc_coord_pair_c... PTs>
auto toAtSameEpoch(this SelfT&& self, PT& cpair, PTs&... cpairs)
{
return std::forward<SelfT>(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 <std::derived_from<mcc_skypoint_interface_t> SelfT>
auto refractCorrection(this SelfT&& self, mcc_angle_c auto* dZ)
{
return std::forward<SelfT>(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 <std::derived_from<mcc_skypoint_interface_t> SelfT>
auto refractInverseCorrection(this SelfT&& self, mcc_angle_c auto* dZ)
{
return std::forward<SelfT>(self).refractInverseCorrection(dZ);
}
// returns apparent sideral time (Greenwich or local) for the epoch of the celestial point
template <std::derived_from<mcc_skypoint_interface_t> SelfT>
auto appSideralTime(this SelfT&& self, mcc_angle_c auto* st, bool is_local)
{
return std::forward<SelfT>(self).appSideralTime(st, is_local);
}
// returns equation of origins for the epoch of the celestial point
template <std::derived_from<mcc_skypoint_interface_t> SelfT>
auto EO(this SelfT&& self, mcc_angle_c auto* eo)
{
return std::forward<SelfT>(self).EO(eo);
}
};
template <typename T>
concept mcc_skypoint_c = std::derived_from<T, mcc_skypoint_interface_t> && requires(const T t_const) {
{ t_const.epoch() } -> mcc_coord_epoch_c;
// currently stored coordinates pair
{ t_const.pairKind() } -> std::same_as<impl::MccCoordPairKind>;
};
/* POINTING CORRECTION MODEL CLASS CONCEPT */
// The result of PCM calculations must be at least corrections along both mount axes
template <typename T>
concept mcc_pcm_result_c = requires(T t) {
requires mcc_angle_c<decltype(t.pcmX)>;
requires mcc_angle_c<decltype(t.pcmY)>;
};
template <typename RetT>
struct mcc_pcm_interface_t {
virtual ~mcc_pcm_interface_t() = default;
template <std::derived_from<mcc_pcm_interface_t> 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<SelfT>(self).computePCM(hw_coord, result, obs_pt);
}
template <std::derived_from<mcc_pcm_interface_t> 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<SelfT>(self).computeInversePCM(obs_pt, inv_result, hw_coord);
}
};
template <typename T>
concept mcc_pcm_c = std::derived_from<T, mcc_pcm_interface_t<typename T::error_t>> && requires {
// error type
requires mcc_error_c<typename T::error_t>;
// the 'T' class must contain static constexpr member of 'MccMountType' type
requires std::same_as<decltype(T::mountType), const MccMountType>;
[]() {
[[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<decltype(T::pcmName), char> && std::is_const_v<decltype(T::pcmName)>;
};
/* 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 <typename T>
concept mcc_hardware_movement_state_c = std::formattable<T, char> && 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 <typename T>
concept mcc_hardware_state_c = requires(T state) {
// encoder co-longitude and co-latiitude positions, as well as its measurement time point
requires mcc_coord_pair_c<decltype(state.XY)> &&
(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)> &&
(decltype(state.speedXY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_GENERIC ||
decltype(state.speedXY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_XY);
requires mcc_hardware_movement_state_c<decltype(state.movementState)>;
};
template <typename T>
concept mcc_hardware_c = requires(T t) {
// error type
requires mcc_error_c<typename T::error_t>;
// static const variable with name of hardware
requires std::formattable<decltype(T::hardwareName), char> && std::is_const_v<decltype(T::hardwareName)>;
// 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<typename T::hardware_movement_state_t>;
// 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<typename T::hardware_state_t> && requires(typename T::hardware_state_t state) {
requires std::same_as<decltype(state.movementState), typename T::hardware_movement_state_t>;
};
// 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<decltype(state.XY)> &&
// ( // 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.speedXY)> &&
// (decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_GENERIC ||
// decltype(state.XY)::pairKind == impl::MccCoordPairKind::COORDS_KIND_XY);
// requires std::same_as<typename T::hardware_movement_state_t, decltype(state.movementState)>;
// };
// set hardware state:
{ t.hardwareSetState(std::declval<typename T::hardware_state_t const&>()) } -> std::same_as<typename T::error_t>;
// get current state
{ t.hardwareGetState(std::declval<typename T::hardware_state_t*>()) } -> std::same_as<typename T::error_t>;
// { t.hardwareStop() } -> std::same_as<typename T::error_t>; // stop any moving
{ t.hardwareInit() } -> std::same_as<typename T::error_t>; // initialize hardware
};
/* MOUNT TELEMETRY DATA CLASS CONCEPT */
template <typename T>
concept mcc_telemetry_data_c = requires(T t) {
// target celestial point (position on sky where mount must be slewed)
requires mcc_skypoint_c<decltype(t.targetPos)>;
// mount current celestial position
requires mcc_skypoint_c<decltype(t.mountPos)>;
// hardware state
requires mcc_hardware_state_c<decltype(t.hwState)>;
// corrections to transform hardware encoder coordinates to observed celestial ones
requires mcc_pcm_result_c<decltype(t.pcmCorrection)>;
// requires mcc_angle_c<decltype(t.pcmX)>; // PCM correction along X-axis
// requires mcc_angle_c<decltype(t.pcmY)>; // PCM correction along Y-axis
// // atmospheric refraction correction for current zenithal distance
// requires mcc_angle_c<decltype(t.refCorr)>;
// // current local apparent sideral time
// requires mcc_angle_c<decltype(t.LST)>;
// // equation of the origins (ERA-GST)
// requires mcc_angle_c<decltype(t.EO)>;
// // 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 <typename RetT>
struct mcc_telemetry_interface_t {
virtual ~mcc_telemetry_interface_t() = default;
// set target position
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
RetT setTarget(this SelfT&& self, mcc_skypoint_c auto const& pt)
{
return std::forward<SelfT>(self).setTarget(pt);
}
// get entered target position
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
RetT setTarget(this SelfT&& self, mcc_skypoint_c auto* pt)
{
return std::forward<SelfT>(self).getTarget(pt);
}
};
template <typename T>
concept mcc_telemetry_c = std::derived_from<T, mcc_telemetry_interface_t<typename T::error_t>> && requires(T t) {
// error type
requires mcc_error_c<typename T::error_t>;
// telemetry data type definition
requires mcc_telemetry_data_c<typename T::telemetry_data_t>;
// get telemetry data
{ t.telemetryData(std::declval<typename T::telemetry_data_t*>()) } -> std::same_as<typename T::error_t>;
};
/* 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 <typename RetT>
struct mcc_pzone_interface_t {
virtual ~mcc_pzone_interface_t() = default;
template <std::derived_from<mcc_pzone_interface_t> SelfT>
RetT inPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, bool* result)
{
return std::forward<SelfT>(self).inPZone(coords, result);
}
template <std::derived_from<mcc_pzone_interface_t> SelfT>
RetT timeToPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time)
{
return std::forward<SelfT>(self).timeToPZone(coords, res_time);
}
template <std::derived_from<mcc_pzone_interface_t> SelfT>
RetT timeFromPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time)
{
return std::forward<SelfT>(self).timeFromPZone(coords, res_time);
}
};
template <typename T>
concept mcc_pzone_c = std::derived_from<T, mcc_pzone_interface_t<typename T::error_t>> && requires(T t) {
// error type
requires mcc_error_c<typename T::error_t>;
// static constant member with prohibitted zone name
requires std::formattable<decltype(T::pzoneName), char> && std::is_const_v<decltype(T::pzoneName)>;
// the 'T' class must contain static constexpr member of 'MccProhibitedZonePolicy' type
requires std::same_as<decltype(T::pzPolicy), const MccProhibitedZonePolicy>;
[]() {
[[maybe_unused]] static constexpr MccProhibitedZonePolicy val = T::pzPolicy;
}(); // to ensure 'pzPolicy' can be used in compile-time context
};
/* PROHIBITED ZONES CONTAINER CLASS CONCEPT */
template <mcc_error_c RetT>
struct mcc_pzone_container_interface_t {
virtual ~mcc_pzone_container_interface_t() = default;
template <std::derived_from<mcc_pzone_container_interface_t> SelfT>
size_t addPZone(this SelfT&& self, mcc_pzone_c auto zone)
{
return std::forward<SelfT>(self).addPZone(std::move(zone));
}
template <std::derived_from<mcc_pzone_container_interface_t> SelfT>
void clearPZones(this SelfT&& self)
{
return std::forward<SelfT>(self).clearPZones();
}
template <std::derived_from<mcc_pzone_container_interface_t> SelfT>
size_t sizePZones(this SelfT&& self)
{
return std::forward<SelfT>(self).sizePZones();
}
template <std::derived_from<mcc_pzone_container_interface_t> SelfT>
RetT inPZone(this SelfT&& self,
mcc_skypoint_c auto const& coords,
bool* at_least_one,
std::ranges::output_range<bool> auto* result)
{
return std::forward<SelfT>(self).inPZone(coords, at_least_one, result);
}
template <std::derived_from<mcc_pzone_container_interface_t> SelfT, traits::mcc_time_duration_c DT>
RetT timeToPZone(this SelfT&& self, mcc_skypoint_c auto const& coords, std::ranges::output_range<DT> auto* res_time)
{
return std::forward<SelfT>(self).timeToPZone(coords, res_time);
}
template <std::derived_from<mcc_pzone_container_interface_t> SelfT, traits::mcc_time_duration_c DT>
RetT timeFromPZone(this SelfT&& self,
mcc_skypoint_c auto const& coords,
std::ranges::output_range<DT> auto* res_time)
{
return std::forward<SelfT>(self).timeFromPZone(coords, res_time);
}
};
template <typename T>
concept mcc_pzone_container_c = std::derived_from<T, mcc_pzone_container_interface_t<typename T::error_t>> && requires {
// error type
requires mcc_error_c<typename T::error_t>;
};
/* A CONCEPT FOR MOUNT MOVEMENT CONTROLS CLASS */
template <typename T>
concept mcc_movement_controls_c = requires(T t) {
// error type
requires mcc_error_c<typename T::error_t>;
// 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<bool>()) } -> std::same_as<typename T::error_t>;
{ t.trackTarget() } -> std::same_as<typename T::error_t>;
{ t.stopMount() } -> std::same_as<typename T::error_t>;
{ t.setMovementParams(std::declval<typename T::movement_params_t const&>()) } -> std::same_as<typename T::error_t>;
{ t.getMovementParams() } -> std::same_as<typename T::movement_params_t>;
};
/* GENERIC MOUNT CLASS CONCEPT */
// minimal set of the mount status constants
template <typename T>
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 <typename T>
concept mcc_generic_mount_c =
mcc_logger_c<T> && mcc_pzone_container_c<T> && mcc_telemetry_c<T> && mcc_movement_controls_c<T> && requires(T t) {
// error type
requires mcc_error_c<typename T::error_t>;
requires mcc_mount_status_c<typename T::mount_status_t>;
{ t.initMount() } -> std::same_as<typename T::error_t>;
{ t.mountStatus() } -> std::same_as<typename T::mount_status_t>;
};
} // namespace mcc