996 lines
38 KiB
C++
996 lines
38 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>
|
|
auto mcc_deduced_err(ErrT const& err, DefErrT const& default_err)
|
|
// DefErrT mcc_deduced_err(ErrT const& err, DefErrT const& default_err)
|
|
{
|
|
if constexpr (std::same_as<ErrT, DefErrT>) {
|
|
return err;
|
|
} else if constexpr (std::is_error_code_enum_v<DefErrT>) {
|
|
if constexpr (std::same_as<ErrT, std::error_code>) {
|
|
return err;
|
|
} else {
|
|
return default_err;
|
|
}
|
|
} else if constexpr (std::is_error_condition_enum_v<DefErrT>) {
|
|
if constexpr (std::same_as<ErrT, std::error_condition>) {
|
|
return err;
|
|
} else {
|
|
return default_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_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
|