mountcontrol/cxx/mcc_mount_concepts.h
2025-07-18 19:05:41 +03:00

444 lines
18 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;
};
} // 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&>()) };
};
/* 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;
// { t.setState(std::declval<typename T::engine_state_t>()) };
// { t_const.getState() } -> std::same_as<typename T::engine_state_t>;
{ t_const.errorString(std::declval<typename T::error_t>()) } -> mcc_formattable;
/* 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>;
// 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>;
// 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 */
// 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 class that contains at least time of measurement and coordinates for x,y axes
requires requires(typename T::axes_pos_t pos) {
requires std::same_as<decltype(pos.time_point), typename T::time_point_t>;
requires std::same_as<decltype(pos.x), typename T::coord_t>;
requires std::same_as<decltype(pos.y), typename T::coord_t>;
};
{ t.setPos(std::declval<typename T::axes_pos_t>()) } -> std::same_as<typename T::error_t>;
{ 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>;
{ t.init() } -> std::same_as<typename T::error_t>;
};
/* POINTING-ERROR CORRECTION */
template <typename T>
concept mcc_mount_pec_c = requires(T t, const T t_const) {
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 = requires(T telemetry) {
typename T::coord_t;
// // target 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
};
template <typename T>
concept mcc_mount_telemetry_c = requires(T t, const T t_const) {
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_const.errorString(std::declval<typename T::error_t>()) } -> mcc_formattable;
{ 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, typename TelemetryT>
concept mcc_slew_model_c = mcc_mount_telemetry_c<TelemetryT> && requires(T t) {
typename T::error_t;
requires mcc_slew_params_c<typename T::slew_params_t>;
{
t.slew(std::declval<typename T::slew_params_t>(), std::declval<TelemetryT&>())
} -> std::same_as<typename T::error_t>;
};
template <typename T, typename TelemetryT>
concept mcc_guiding_model_c = mcc_mount_telemetry_c<TelemetryT> && requires(T t) {
typename T::error_t;
{ t.guiding(std::declval<TelemetryT&>()) } -> std::same_as<typename T::error_t>;
};
/* MOUNT PROHIBITED ZONE */
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 inifite duration
requires requires {
requires std::same_as<decltype(T::infiniteDuration), typename T::duration_t>;
[]() {
constexpr auto val = T::infiniteDuration;
return val;
};
};
// the type 'T' must define a static constexpr member of type MccCoordPairKind
// to declarate type of coordinate pair used to describe the zone.
// This coordinate pair must be used as input in the 'inZone' class method.
requires requires {
requires std::same_as<decltype(T::zoneCoordPairKind), const MccCoordPairKind>;
[]() {
constexpr MccCoordPairKind val = T::zoneCoordPairKind;
return val;
}(); // to ensure that 'zoneCoordPairKind' can be used at compile-time context
};
// return a name of the zone
{ t_const.name() } -> mcc_formattable;
// check if given coordinates are into the zone.
// input coordinates interpretation is in according to 'zoneCoordPairKind' static constexpr member
{
t.inZone(std::declval<typename T::coord_t>(), std::declval<typename T::coord_t>())
} -> std::convertible_to<bool>;
// for given coordinates and time the method computes a time to reach the zone.
// implementation of the method must assume that input coordinates are apparent RA and DEC at given time
// point, while the time point is one from which computation should be performed (e.g. current time moment)
{
t.timeTo(std::declval<typename T::coord_t>(), std::declval<typename T::coord_t>(),
std::declval<typename T::time_point_t>())
} -> mcc_time_duration_c;
// for given coordinates and time the method computes a time to exit from the zone
{
t.timeFrom(std::declval<typename T::coord_t>(), std::declval<typename T::coord_t>(),
std::declval<typename T::time_point_t>())
} -> mcc_time_duration_c;
// requires for the methods above with the first argument of type
// 'const mcc_mount_telemetry_data_c&' (const lvalue reference)
{ 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>;
};
/* MOUNT GENERIC CONTROLS */
template <typename 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), decltype(t.telemetry)>;
requires mcc_guiding_model_c<decltype(t.guidingModel), decltype(t.telemetry)>;
// a std::tuple of prohibited zones
[]<mcc_prohibited_zone_c<typename decltype(t.telemetry)::mount_telemetry_data_t>... Ts>(std::tuple<Ts...>) {
}(t.prohibitedZones);
};
/* 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, typename T::mount_telemetry_t>;
requires mcc_guiding_model_c<typename T::guiding_model_t, typename T::mount_telemetry_t>;
requires std::same_as<typename T::slew_params_t, typename T::slew_model_t::slew_params_t>;
// public method
{ t.mountTelemetryData() } -> std::same_as<typename T::mount_telemetry_data_t>;
};
// generic with 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