779 lines
31 KiB
C++
779 lines
31 KiB
C++
#pragma once
|
|
|
|
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
|
|
|
#include <concepts>
|
|
|
|
#include "mcc_finite_state_machine.h"
|
|
#include "mcc_mount_coord.h"
|
|
#include "mcc_traits.h"
|
|
|
|
/* SOME LIBRARY-WIDE DECLARATIONS */
|
|
|
|
namespace mcc
|
|
{
|
|
// 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;
|
|
};
|
|
|
|
|
|
/* NULL-LOGGER CLASS */
|
|
|
|
struct MccNullLogger {
|
|
typedef int loglevel_t;
|
|
|
|
void setLogLevel(loglevel_t){};
|
|
loglevel_t getLogLevel() const { return 0; };
|
|
|
|
void logMessage(loglevel_t, const std::string&) {};
|
|
void logError(const std::string&) {};
|
|
void logDebug(const std::string&) {};
|
|
void logWarn(const std::string&) {};
|
|
void logInfo(const std::string&) {};
|
|
};
|
|
|
|
|
|
|
|
} // namespace mcc
|
|
|
|
|
|
|
|
/* MOUNT COMPONENTS CONCEPTS */
|
|
|
|
namespace mcc::traits
|
|
{
|
|
|
|
|
|
/* GENERIC LOGGER */
|
|
|
|
template <typename T>
|
|
concept mcc_logger_c = requires(T t, const T t_const) {
|
|
typename T::loglevel_t;
|
|
{ t.setLogLevel(std::declval<typename T::loglevel_t>()) };
|
|
{ t_const.getLogLevel() } -> std::same_as<typename T::loglevel_t>;
|
|
|
|
{ t.logMessage(std::declval<typename T::loglevel_t>(), std::declval<const std::string&>()) };
|
|
|
|
{ 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&>()) };
|
|
};
|
|
|
|
|
|
/* A CONCEPT FOR COORDINATE REPRESENTATION */
|
|
|
|
// it is a fundametal floating-point type or
|
|
// a class that can be constructed from or converted to the double fundametal type
|
|
template <typename T>
|
|
concept mcc_coord_t = std::floating_point<T> || (std::convertible_to<T, double> && std::constructible_from<T, double>);
|
|
|
|
|
|
/* A CONCEPT FOR UTC TIME POINT REPRESENTATION */
|
|
|
|
// it is a std::chrono::sys_time<Duration> or
|
|
// fundamental arithmetic type that represent number of seconds after the 00:00:00 of 1 January 1970 (UNIX time)
|
|
template <typename T>
|
|
concept mcc_utc_time_point_c = mcc_systime_c<T> || std::is_arithmetic_v<T>;
|
|
|
|
/* A CONCEPT FOR CLASS TO REPRESENT CELESTIAL POINT */
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_celestial_point_c = requires(T t) {
|
|
// a type to represent UTC time point of coordinates
|
|
// it's clear that this makes sense for apparent coordinates
|
|
typename T::time_point_t;
|
|
|
|
// coordinates pair type (e.g. IRCS RA,DEC, Az,Alt and so on)
|
|
requires std::same_as<decltype(t.coordPairKind), MccCoordPairKind>;
|
|
|
|
typename T::coord_t;
|
|
|
|
// co-longitude (e.g. RA or Az)
|
|
requires std::same_as<decltype(t.x), typename T::coord_t>;
|
|
|
|
// co-latitude (e.g. DEC or ZD)
|
|
requires std::same_as<decltype(t.y), typename T::coord_t>;
|
|
};
|
|
|
|
|
|
/* ASTROMETRY-RELATED COMPUTATION ENGINE */
|
|
|
|
template <typename T>
|
|
concept mcc_astrom_engine_c = requires(T t, const T t_const) {
|
|
requires mcc_error_c<typename T::error_t>;
|
|
// typename T::engine_state_t;
|
|
// requires std::movable<typename T::engine_state_t>;
|
|
|
|
typename T::coord_t; // type for coordinates representation
|
|
typename T::time_point_t; // type to represent UTC time point
|
|
typename T::juldate_t; // type to represent Julian date
|
|
typename T::sideral_time_t; // type to represent sideral time
|
|
typename T::eo_t; // equation of origins
|
|
typename T::pa_t; // type to represent parallactic angle
|
|
|
|
typename T::refract_result_t;
|
|
|
|
|
|
/* coordinates conversional methods */
|
|
|
|
// ICRS RA and DEC to observed place: icrs2obs(ra, dec, jd, ra_app, dec_app, ha, az, alt, eo)
|
|
{
|
|
t.icrs2obs(std::declval<typename T::coord_t>(), std::declval<typename T::coord_t>(),
|
|
std::declval<typename T::juldate_t>(), std::declval<typename T::coord_t&>(),
|
|
std::declval<typename T::coord_t&>(), std::declval<typename T::coord_t&>(),
|
|
std::declval<typename T::coord_t&>(), std::declval<typename T::coord_t&>(),
|
|
std::declval<typename T::eo_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
// observed place to ICRS RA and DEC: obs2icrs(type, x, y, jd, ra_icrs, dec_icrs)
|
|
// (x, y) = (AZ, ZD) if type == MccCoordPairKind::COORDS_KIND_AZZD
|
|
// (x, y) = (AZ, ALT) if type == MccCoordPairKind::COORDS_KIND_AZALT
|
|
// (x, y) = (HA, DEC) if type == MccCoordPairKind::COORDS_KIND_HADEC_APP
|
|
// (x, y) = (RA, DEC) if type == MccCoordPairKind::COORDS_KIND_RADEC_APP
|
|
{
|
|
t.obs2icrs(std::declval<MccCoordPairKind>(), std::declval<typename T::coord_t>(),
|
|
std::declval<typename T::coord_t>(), std::declval<typename T::juldate_t>(),
|
|
std::declval<typename T::coord_t&>(), std::declval<typename T::coord_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
|
|
// compute hour angle and declination from azimuth and altitude: hadec2azalt(ha, dec, az, alt)
|
|
{
|
|
t.hadec2azalt(std::declval<typename T::coord_t>(), std::declval<typename T::coord_t>(),
|
|
std::declval<typename T::coord_t&>(), std::declval<typename T::coord_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
// compute azimuth and altitude from hour angle and declination: azalt2hadec(az, alt, ha, dec)
|
|
{
|
|
t.azalt2hadec(std::declval<typename T::coord_t>(), std::declval<typename T::coord_t>(),
|
|
std::declval<typename T::coord_t&>(), std::declval<typename T::coord_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
// compute parallactic angle: hadec2pa(ha, dec, pa)
|
|
{
|
|
t.hadec2pa(std::declval<typename T::coord_t>(), std::declval<typename T::coord_t>(),
|
|
std::declval<typename T::pa_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
|
|
// transform coordinates according to its pair types and time points (a high-level wrapper):
|
|
//
|
|
// coord2coord(coord_pair_kind_from, x_from, y_from, time_point_from, coord_pair_kind_to, x_to, y_to, time_point_to)
|
|
//
|
|
// (x_*, y_*) = (AZ, ZD) if coord_pair_kind_* == MccCoordPairKind::COORDS_KIND_AZZD
|
|
// (x_*, y_*) = (AZ, ALT) if coord_pair_kind_* == MccCoordPairKind::COORDS_KIND_AZALT
|
|
// (x_*, y_*) = (HA, DEC) if coord_pair_kind_* == MccCoordPairKind::COORDS_KIND_HADEC_APP (apparent)
|
|
// (x_*, y_*) = (RA, DEC) if coord_pair_kind_* == MccCoordPairKind::COORDS_KIND_RADEC_APP (apparent)
|
|
// (x_*, y_*) = (RA, DEC) if coord_pair_kind_* == MccCoordPairKind::COORDS_KIND_RADEC_ICRS (ICRS)
|
|
//
|
|
// if coord_pair_kind_* and time_point_* are equal then x_to = x_from, y_to = y_from
|
|
{
|
|
t.coord2coord(std::declval<MccCoordPairKind>(), std::declval<typename T::coord_t>(),
|
|
std::declval<typename T::coord_t>(), std::declval<typename T::time_point_t>(),
|
|
std::declval<MccCoordPairKind>(), std::declval<typename T::coord_t&>(),
|
|
std::declval<typename T::coord_t&>(), std::declval<typename T::time_point_t>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
|
|
// compute equation of origins
|
|
{
|
|
t.eqOrigins(std::declval<typename T::juldate_t>(), std::declval<typename T::eo_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
|
|
/* time-related methods */
|
|
|
|
// this static method must return a current time point
|
|
{ T::timePointNow() } -> std::same_as<typename T::time_point_t>;
|
|
|
|
// Gregorian Calendar time point to Julian Date: greg2jul(time_point, jd)
|
|
{
|
|
t.greg2jul(std::declval<typename T::time_point_t>(), std::declval<typename T::juldate_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
// apparent sideral time: apparentSiderTime(jd, st, islocal)
|
|
// if islocal == false then the method must return the Greenwich apparent sideral time, otherwise - local one
|
|
{
|
|
t.apparentSiderTime(std::declval<typename T::juldate_t>(), std::declval<typename T::sideral_time_t&>(),
|
|
std::declval<bool>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
|
|
|
|
/* atmospheric refraction-related methods */
|
|
|
|
// compute refraction-related quantities: refraction(refr_params)
|
|
{ t.refraction(std::declval<typename T::refract_result_t&>()) } -> std::same_as<typename T::error_t>;
|
|
|
|
// compute refraction correction for given altitude: refractCorrection(alt, refr_params, refr_corr)
|
|
{
|
|
t.refractCorrection(std::declval<typename T::coord_t>(), std::declval<typename T::refract_result_t>(),
|
|
std::declval<typename T::coord_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
};
|
|
|
|
|
|
/* A VERY GENERIC MOUNT HARDWARE CONCEPT */
|
|
|
|
template <typename T>
|
|
concept mcc_mount_hardware_c = !std::copyable<T> && std::movable<T> && requires(T t, const T t_const) {
|
|
requires mcc_error_c<typename T::error_t>;
|
|
|
|
typename T::time_point_t;
|
|
typename T::coord_t;
|
|
|
|
{ t_const.id() } -> mcc_formattable;
|
|
|
|
// a type that defines at least 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 setPos-related commands
|
|
//
|
|
// e.g. an implementations can be as follows:
|
|
// enum class hw_moving_type_t: int {HW_MOVE_SLEWING, HW_MOVE_ADJUSTING, HW_MOVE_TRACKING, HW_MOVE_GUIDING}
|
|
//
|
|
// struct hw_moving_type_t {
|
|
// uint16_t HW_MOVE_SLEWING = 111;
|
|
// uint16_t HW_MOVE_ADJUSTING = 222;
|
|
// uint16_t HW_MOVE_TRACKING = 333;
|
|
// uint16_t HW_MOVE_GUIDING = 444;
|
|
// }
|
|
requires requires(typename T::hw_moving_type_t state) {
|
|
[]() {
|
|
// hardware was asked for slewing (move to given celestial point)
|
|
static constexpr auto v1 = T::hw_moving_type_t::HW_MOVE_SLEWING;
|
|
|
|
// hardware was asked for adjusting after slewing ("seeking" given celestial point at the end of slewing
|
|
// process)
|
|
static constexpr auto v2 = T::hw_moving_type_t::HW_MOVE_ADJUSTING;
|
|
|
|
// hardware was asked for tracking (track given celestial point)
|
|
static constexpr auto v3 = T::hw_moving_type_t::HW_MOVE_TRACKING;
|
|
|
|
// hardware was asked for guiding (small corrections to track given celestial point)
|
|
static constexpr auto v4 = T::hw_moving_type_t::HW_MOVE_GUIDING;
|
|
}();
|
|
};
|
|
|
|
|
|
|
|
// a class that contains at least time of measurement, coordinates for x,y axes and its moving rates
|
|
requires requires(typename T::axes_pos_t pos) {
|
|
requires std::same_as<decltype(pos.time_point), typename T::time_point_t>; // time point
|
|
|
|
requires std::same_as<decltype(pos.x), typename T::coord_t>; // co-longitude coordinate
|
|
requires std::same_as<decltype(pos.y), typename T::coord_t>; // co-latitude coordinate
|
|
|
|
requires std::same_as<decltype(pos.xrate), typename T::coord_t>;
|
|
requires std::same_as<decltype(pos.yrate), typename T::coord_t>;
|
|
|
|
requires std::same_as<decltype(pos.moving_type), typename T::hw_moving_type_t>; // a 'hint' to hardware
|
|
};
|
|
|
|
// set positions (angles) of mount axes with given speeds
|
|
// NOTE: exact interpretation (or even ignoring) of the given moving speeds is subject of a hardware-class
|
|
// implementation.
|
|
// e.g. it can be maximal speeds at slewing ramp
|
|
{ t.setPos(std::declval<typename T::axes_pos_t>()) } -> std::same_as<typename T::error_t>;
|
|
|
|
// get current positions and speeds (angles) of mount axes
|
|
{ t.getPos(std::declval<typename T::axes_pos_t&>()) } -> std::same_as<typename T::error_t>;
|
|
|
|
{ t.stop() } -> std::same_as<typename T::error_t>; // stop any moving
|
|
{ t.init() } -> std::same_as<typename T::error_t>; // initialize hardware
|
|
};
|
|
|
|
|
|
/* POINTING-ERROR CORRECTION */
|
|
|
|
template <typename T>
|
|
concept mcc_mount_pec_c = requires(T t) {
|
|
requires mcc_error_c<typename T::error_t>;
|
|
typename T::coord_t;
|
|
|
|
// the 'T' class must contain static constexpr member of 'MccMountType' type
|
|
requires requires {
|
|
requires std::same_as<decltype(T::mountType), const MccMountType>;
|
|
[]() {
|
|
static constexpr MccMountType val = T::mountType;
|
|
return val;
|
|
}(); // to ensure 'mountType' can be used in compile-time context
|
|
};
|
|
|
|
// a class that contains at least .dx and .dy public fields
|
|
requires requires(typename T::pec_result_t res) {
|
|
requires std::same_as<decltype(res.dx), typename T::coord_t>;
|
|
requires std::same_as<decltype(res.dy), typename T::coord_t>;
|
|
};
|
|
|
|
{
|
|
t.compute(std::declval<const typename T::coord_t&>(), std::declval<const typename T::coord_t&>(),
|
|
std::declval<typename T::pec_result_t&>())
|
|
} -> std::same_as<typename T::error_t>;
|
|
};
|
|
|
|
|
|
/* MOUNT STATE TELEMETRY */
|
|
|
|
// a class that contains at least celestial (equatorial and horizontal) and harware coordinates
|
|
template <typename T>
|
|
concept mcc_mount_telemetry_data_c = std::movable<T> && requires(T telemetry) {
|
|
typename T::coord_t;
|
|
typename T::time_point_t;
|
|
|
|
// time point
|
|
requires std::same_as<decltype(telemetry.time_point), typename T::time_point_t>;
|
|
|
|
// target sky point ICRS and current coordinates
|
|
requires std::same_as<decltype(telemetry.tagRA), typename T::coord_t>; // apparent RA
|
|
requires std::same_as<decltype(telemetry.tagDEC), typename T::coord_t>; // apparent DEC
|
|
requires std::same_as<decltype(telemetry.tagHA), typename T::coord_t>; // hour angle
|
|
requires std::same_as<decltype(telemetry.tagAZ), typename T::coord_t>; // azimuth
|
|
requires std::same_as<decltype(telemetry.tagALT), typename T::coord_t>; // altitude
|
|
|
|
// mount current coordinates
|
|
requires std::same_as<decltype(telemetry.mntRA), typename T::coord_t>; // apparent RA
|
|
requires std::same_as<decltype(telemetry.mntDEC), typename T::coord_t>; // apparent DEC
|
|
requires std::same_as<decltype(telemetry.mntHA), typename T::coord_t>; // hour angle
|
|
requires std::same_as<decltype(telemetry.mntAZ), typename T::coord_t>; // azimuth
|
|
requires std::same_as<decltype(telemetry.mntALT), typename T::coord_t>; // altitude
|
|
|
|
requires std::same_as<decltype(telemetry.mntPosX), typename T::coord_t>; // hardware encoder X-axis position
|
|
requires std::same_as<decltype(telemetry.mntPosY), typename T::coord_t>; // hardware encoder Y-axis position
|
|
requires std::same_as<decltype(telemetry.mntRateX), typename T::coord_t>; // hardware encoder X-axis rate
|
|
requires std::same_as<decltype(telemetry.mntRateY), typename T::coord_t>; // hardware encoder Y-axis rate
|
|
|
|
// corrections to transform mount hardware coordinates to apparent
|
|
// (pointing error corrections)
|
|
requires std::same_as<decltype(telemetry.pecX), typename T::coord_t>;
|
|
requires std::same_as<decltype(telemetry.pecY), typename T::coord_t>;
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_mount_telemetry_c = requires(T t, const T t_const) {
|
|
requires mcc_error_c<typename T::error_t>;
|
|
|
|
// // a class that at least contains celestial (equatorial and horizontal) coordinates
|
|
// requires requires(typename T::mount_telemetry_data_t telemetry) {
|
|
// typename T::mount_telemetry_data_t::coord_t;
|
|
// requires std::same_as<decltype(telemetry.mntRA), typename T::mount_telemetry_data_t::coord_t>; //
|
|
// apparent RA requires std::same_as<decltype(telemetry.mntDEC), typename
|
|
// T::mount_telemetry_data_t::coord_t>; // apparent DEC requires std::same_as<decltype(telemetry.mntHA),
|
|
// typename T::mount_telemetry_data_t::coord_t>;
|
|
// // hour angle requires std::same_as<decltype(telemetry.mntAZ), typename
|
|
// T::mount_telemetry_data_t::coord_t>;
|
|
// // azimuth requires std::same_as<decltype(telemetry.mntALT), typename
|
|
// T::mount_telemetry_data_t::coord_t>; // altitude
|
|
// };
|
|
|
|
requires mcc_mount_telemetry_data_c<typename T::mount_telemetry_data_t>;
|
|
|
|
{ t.update() } -> std::same_as<typename T::error_t>;
|
|
|
|
{ t.data(std::declval<typename T::mount_telemetry_data_t&>()) } -> std::same_as<typename T::error_t>;
|
|
};
|
|
|
|
|
|
|
|
// /* SLEW PARAMETERS */
|
|
|
|
// template <typename T>
|
|
// concept mcc_slew_params_c = std::movable<T> && requires(T t) {
|
|
// // input coordinates pair type (e.g. IRCS RA,DEC, Az,Alt and so on)
|
|
// requires std::same_as<decltype(t.coordPairKind), MccCoordPairKind>;
|
|
|
|
// typename T::coord_t;
|
|
|
|
// // co-longitude (e.g. RA or Az)
|
|
// requires std::same_as<decltype(t.x), typename T::coord_t>;
|
|
|
|
// // co-latitude (e.g. DEC or ZD)
|
|
// requires std::same_as<decltype(t.y), typename T::coord_t>;
|
|
|
|
// // stop after slewing
|
|
// requires std::convertible_to<decltype(t.stop), bool>;
|
|
// };
|
|
|
|
|
|
/* GENERIC SLEW AND GUIDING MODEL */
|
|
|
|
template <typename T>
|
|
concept mcc_slew_model_c = requires(T t) {
|
|
requires mcc_error_c<typename T::error_t>;
|
|
// requires mcc_slew_params_c<typename T::slew_params_t>;
|
|
requires mcc_celestial_point_c<typename T::slew_point_t>;
|
|
|
|
// { t.slew(std::declval<typename T::slew_params_t>()) } -> std::same_as<typename T::error_t>;
|
|
{ t.slew(std::declval<typename T::slew_point_t>()) } -> std::same_as<typename T::error_t>;
|
|
{ t.stop() } -> std::same_as<typename T::error_t>;
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_guiding_model_c = requires(T t) {
|
|
requires mcc_error_c<typename T::error_t>;
|
|
requires mcc_celestial_point_c<typename T::guiding_point_t>;
|
|
|
|
// start process of guiding
|
|
{ t.guiding(std::declval<typename T::guiding_point_t>()) } -> std::same_as<typename T::error_t>;
|
|
{ t.stop() } -> std::same_as<typename T::error_t>;
|
|
};
|
|
|
|
|
|
/* MOUNT PROHIBITED ZONE */
|
|
|
|
|
|
struct MccPzoneAbstractInterface {
|
|
bool inZone(this auto&& self, mcc_mount_telemetry_data_c auto const& telemetry_data)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPzoneAbstractInterface>) {
|
|
static_assert(false, "Call an empty MccPzoneAbstractInterface::inZone method");
|
|
} else {
|
|
return std::forward<self_t>(self).inZone(telemetry_data);
|
|
}
|
|
}
|
|
|
|
bool inZone(this auto&& self, mcc_celestial_point_c auto const& sky_point)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPzoneAbstractInterface>) {
|
|
static_assert(false, "Call an empty MccPzoneAbstractInterface::inZone method");
|
|
} else {
|
|
return std::forward<self_t>(self).inZone(sky_point);
|
|
}
|
|
}
|
|
|
|
// returns a time to reach the zone
|
|
auto timeTo(this auto&& self, mcc_mount_telemetry_data_c auto const& telemetry_data)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPzoneAbstractInterface>) {
|
|
static_assert(false, "Call an empty MccPzoneAbstractInterface::timeTo method");
|
|
} else {
|
|
return std::forward<self_t>(self).timeTo(telemetry_data);
|
|
}
|
|
}
|
|
|
|
auto timeTo(this auto&& self, mcc_celestial_point_c auto const& sky_point)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPzoneAbstractInterface>) {
|
|
static_assert(false, "Call an empty MccPzoneAbstractInterface::timeTo method");
|
|
} else {
|
|
return std::forward<self_t>(self).timeTo(sky_point);
|
|
}
|
|
}
|
|
|
|
// returns a time to exit from the zone
|
|
auto timeFrom(this auto&& self, mcc_mount_telemetry_data_c auto const& telemetry_data)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPzoneAbstractInterface>) {
|
|
static_assert(false, "Call an empty MccPzoneAbstractInterface::timeFrom method");
|
|
} else {
|
|
return std::forward<self_t>(self).timeFrom(telemetry_data);
|
|
}
|
|
}
|
|
|
|
auto timeFrom(this auto&& self, mcc_celestial_point_c auto const& sky_point)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPzoneAbstractInterface>) {
|
|
static_assert(false, "Call an empty MccPzoneAbstractInterface::timeFrom method");
|
|
} else {
|
|
return std::forward<self_t>(self).timeFrom(sky_point);
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename T, typename TelemetryDataT>
|
|
concept mcc_prohibited_zone_c =
|
|
mcc_mount_telemetry_data_c<TelemetryDataT> && std::movable<T> && requires(T t, const T t_const) {
|
|
typename T::coord_t;
|
|
// typename T::time_point_t;
|
|
requires mcc_time_duration_c<typename T::duration_t>;
|
|
|
|
// static constexpr member to represent infinite duration
|
|
requires requires {
|
|
requires std::same_as<decltype(T::infiniteDuration), typename T::duration_t const>;
|
|
[]() {
|
|
constexpr auto val = T::infiniteDuration;
|
|
return val;
|
|
};
|
|
};
|
|
|
|
// static constexpr member to represent zero duration
|
|
requires requires {
|
|
requires std::same_as<decltype(T::zeroDuration), typename T::duration_t const>;
|
|
[]() {
|
|
constexpr auto val = T::zeroDuration;
|
|
return val;
|
|
};
|
|
};
|
|
|
|
|
|
// return a name of the zone
|
|
{ t_const.name() } -> mcc_formattable;
|
|
|
|
|
|
// check if given coordinates are into the zone.
|
|
{ t.inZone(std::declval<const TelemetryDataT&>()) } -> std::convertible_to<bool>;
|
|
|
|
// a time duration to reach the zone.
|
|
// special values the method must return:
|
|
// 'infiniteDuration' if the given sky point never reaches the zone
|
|
// 0 (zero duration) if the given sky point is already in the zone or it never exits from the zone
|
|
{ t.timeTo(std::declval<const TelemetryDataT&>()) } -> std::same_as<typename T::duration_t>;
|
|
|
|
// a time duration to exit from the zone.
|
|
// special values the method must return:
|
|
// 0 (zero duration) if the given sky point already exited from the zone or it never reaches the zone
|
|
{ t.timeFrom(std::declval<const TelemetryDataT&>()) } -> std::same_as<typename T::duration_t>;
|
|
};
|
|
|
|
// an input range of prohibited zones
|
|
template <typename T, typename TelemetryDataT>
|
|
concept mcc_irange_of_pzones_c = mcc_mount_telemetry_data_c<TelemetryDataT> && std::ranges::input_range<T> &&
|
|
mcc_prohibited_zone_c<std::ranges::range_value_t<T>, TelemetryDataT>;
|
|
|
|
|
|
// // a concept for a callable with the first argument of type satisfied to 'mcc_prohibited_zone_c'
|
|
// template <typename T, typename TelemetryDataT>
|
|
// concept mcc_pzone_foreach_func_c = mcc_is_callable<T> && mcc_mount_telemetry_data_c<TelemetryDataT> &&
|
|
// mcc_prohibited_zone_c<mcc_func_arg1_t<T>, TelemetryDataT>;
|
|
|
|
|
|
// There is no way to declare a concept of class with templated method so one needs to define
|
|
// a generic interface of prohibited zones holder/container explicitly
|
|
template <mcc_mount_telemetry_data_c TelemetryDataT>
|
|
struct MccPZoneAbstractContainer {
|
|
virtual ~MccPZoneAbstractContainer() = default;
|
|
|
|
// must return a size of the container after the addition of the given zone
|
|
template <mcc_prohibited_zone_c<TelemetryDataT> ZT>
|
|
size_t pzAddZone(this auto&& self, ZT zone)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPZoneAbstractContainer>) {
|
|
static_assert(false, "Call an empty MccPZoneAbstractContainer::pzAddZone method");
|
|
} else {
|
|
return std::forward<self_t>(self).pzAddZone(std::move(zone));
|
|
}
|
|
}
|
|
|
|
// clear the container
|
|
auto pzClearZones(this auto&& self)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPZoneAbstractContainer>) {
|
|
static_assert(false, "Call an empty MccPZoneAbstractContainer::pzClearZones method");
|
|
} else {
|
|
return std::forward<self_t>(self).pzClearZones();
|
|
}
|
|
}
|
|
|
|
// must return the size of the container (number of zones)
|
|
size_t pzSize(this auto&& self)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPZoneAbstractContainer>) {
|
|
static_assert(false, "Call an empty MccPZoneAbstractContainer::pzSize method");
|
|
} else {
|
|
return std::forward<self_t>(self).pzSize();
|
|
}
|
|
}
|
|
|
|
|
|
// must return true if the given telemetry coordinates are in any of zones in the containe and
|
|
// false otherwise
|
|
template <typename RT>
|
|
bool pzInZone(this auto&& self, const TelemetryDataT& tdata, RT& result)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPZoneAbstractContainer>) {
|
|
static_assert(false, "Call an empty MccPZoneAbstractContainer::pzInZone method");
|
|
} else {
|
|
return std::forward<self_t>(self).pzInZone(tdata, result);
|
|
}
|
|
}
|
|
|
|
template <typename RT>
|
|
auto pzTimeTo(this auto&& self, const TelemetryDataT& tdata, RT& result)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPZoneAbstractContainer>) {
|
|
static_assert(false, "Call an empty MccPZoneAbstractContainer::pzInZone method");
|
|
} else {
|
|
return std::forward<self_t>(self).pzTimeTo(tdata, result);
|
|
}
|
|
}
|
|
|
|
template <typename RT>
|
|
auto pzTimeFrom(this auto&& self, const TelemetryDataT& tdata, RT& result)
|
|
{
|
|
using self_t = decltype(self);
|
|
|
|
if constexpr (std::same_as<std::remove_cvref_t<self_t>, MccPZoneAbstractContainer>) {
|
|
static_assert(false, "Call an empty MccPZoneAbstractContainer::pzInZone method");
|
|
} else {
|
|
return std::forward<self_t>(self).pzTimeFrom(tdata, result);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
MccPZoneAbstractContainer() = default;
|
|
};
|
|
|
|
// a full concept for prohibited zones container
|
|
template <typename T, typename TelemetryDataT>
|
|
concept mcc_mount_pzones_container_c = std::derived_from<T, MccPZoneAbstractContainer<TelemetryDataT>> && requires {
|
|
// common time duration type for zones 'timeTo' and 'timeFrom' methods
|
|
requires mcc_time_duration_c<typename T::duration_t>;
|
|
|
|
// static constexpr member to represent infinite duration
|
|
requires requires {
|
|
requires std::same_as<decltype(T::infiniteDuration), typename T::duration_t const>;
|
|
[]() {
|
|
constexpr auto val = T::infiniteDuration;
|
|
return val;
|
|
};
|
|
};
|
|
|
|
// static constexpr member to represent zero duration
|
|
requires requires {
|
|
requires std::same_as<decltype(T::zeroDuration), typename T::duration_t const>;
|
|
[]() {
|
|
constexpr auto val = T::zeroDuration;
|
|
return val;
|
|
};
|
|
};
|
|
};
|
|
|
|
/* MOUNT GENERIC CONTROLS */
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_mount_controls_c = requires(T t) {
|
|
// concept mcc_mount_controls_c = std::move_constructible<T> && std::movable<T> && requires(T t) {
|
|
requires mcc_astrom_engine_c<decltype(t.astrometryEngine)>;
|
|
requires mcc_mount_pec_c<decltype(t.PEC)>;
|
|
requires mcc_mount_hardware_c<decltype(t.hardware)>;
|
|
requires mcc_mount_telemetry_c<decltype(t.telemetry)>;
|
|
|
|
requires mcc_slew_model_c<decltype(t.slewModel)>;
|
|
requires mcc_guiding_model_c<decltype(t.guidingModel)>;
|
|
|
|
// a std::tuple of prohibited zones
|
|
// []<mcc_prohibited_zone_c<typename decltype(t.telemetry)::mount_telemetry_data_t>... Ts>(std::tuple<Ts...>) {
|
|
// }(t.prohibitedZones);
|
|
|
|
// requires mcc_tuple_c<decltype(t.prohibitedZones)>;
|
|
requires mcc_irange_of_pzones_c<decltype(t.prohibitedZones),
|
|
typename decltype(t.telemetry)::mount_telemetry_data_t>;
|
|
};
|
|
|
|
|
|
/* GENERIC MOUNT CONCEPTS */
|
|
|
|
template <typename T>
|
|
concept mcc_mount_c = requires(T t) {
|
|
// the class must define typename 'mount_controls_t' and it must be its base class
|
|
requires mcc_mount_controls_c<typename T::mount_controls_t>;
|
|
requires std::derived_from<T, typename T::mount_controls_t>;
|
|
|
|
|
|
// deduced from 'mount_controls_t' typenames
|
|
requires mcc_mount_telemetry_c<typename T::mount_telemetry_t>;
|
|
requires std::same_as<typename T::mount_telemetry_data_t, typename T::mount_telemetry_t::mount_telemetry_data_t>;
|
|
requires mcc_astrom_engine_c<typename T::astrom_engine_t>;
|
|
requires mcc_mount_pec_c<typename T::pec_t>;
|
|
requires mcc_mount_hardware_c<typename T::hardware_t>;
|
|
requires mcc_slew_model_c<typename T::slew_model_t>;
|
|
requires mcc_guiding_model_c<typename T::guiding_model_t>;
|
|
|
|
// public methods
|
|
{
|
|
t.mountTelemetryData(std::declval<typename T::mount_telemetry_data_t&>())
|
|
} -> std::same_as<typename T::mount_telemetry_t::error_t>;
|
|
|
|
{
|
|
t.slewMount(std::declval<typename T::slew_model_t::slew_point_t>())
|
|
} -> std::same_as<typename T::slew_model_t::error_t>;
|
|
|
|
{
|
|
t.guidingTarget(std::declval<typename T::guiding_model_t::guiding_point_t>())
|
|
} -> std::same_as<typename T::guiding_model_t::error_t>;
|
|
};
|
|
|
|
// generic with public logging methods
|
|
template <typename T>
|
|
concept mcc_log_mount_c = mcc_mount_c<T> && mcc_logger_c<T>;
|
|
|
|
|
|
// a generic Finite State Machine mount with logging methods
|
|
template <typename T>
|
|
concept mcc_fsm_log_mount_c = std::derived_from<T, fsm::MccFiniteStateMachine> && mcc_log_mount_c<T>;
|
|
|
|
} // namespace mcc::traits
|
|
|
|
|
|
|
|
/* CHECK LIBRARY-WIDE CLASS DECLARATIONS FOR ITS CONCEPTS SATISFACTION */
|
|
|
|
namespace mcc
|
|
{
|
|
|
|
static_assert(traits::mcc_logger_c<MccNullLogger>, "MccNullLogger INVALID DECLARATION!");
|
|
|
|
} // namespace mcc
|