965 lines
37 KiB
C++
965 lines
37 KiB
C++
#pragma once
|
|
|
|
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
|
|
|
|
|
|
|
/* SOME LIBRARY-WIDE DECLARATIONS */
|
|
|
|
|
|
#include <chrono>
|
|
#include <concepts>
|
|
|
|
|
|
// #include "mcc_traits.h"
|
|
#include "mcc_angle.h"
|
|
#include "mcc_finite_state_machine.h"
|
|
|
|
|
|
namespace mcc
|
|
{
|
|
|
|
static constexpr double mcc_sideral_to_UT1_ratio = 1.002737909350795; // sideral/UT1
|
|
|
|
|
|
// 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;
|
|
};
|
|
|
|
|
|
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
|
|
};
|
|
|
|
|
|
/* GENERIC LOGGER CLASS CONCEPT */
|
|
|
|
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&>()) };
|
|
};
|
|
|
|
|
|
struct MccNullLogger {
|
|
void logError(const std::string&) {}
|
|
void logDebug(const std::string&) {}
|
|
void logWarn(const std::string&) {}
|
|
void logInfo(const std::string&) {}
|
|
};
|
|
|
|
|
|
|
|
/* 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>);
|
|
|
|
|
|
/* ANGLE REPRESENTATION CLASS CONCEPT */
|
|
|
|
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>;
|
|
};
|
|
|
|
|
|
/* TIME POINT CLASS CONCEPT */
|
|
|
|
/*
|
|
* USE OF STL std::chrono::time_point
|
|
*/
|
|
template <typename T>
|
|
concept mcc_time_point_c = traits::mcc_systime_c<T>;
|
|
// concept mcc_time_point_c = requires(T t) { []<typename CT, typename DT>(std::chrono::time_point<CT, DT>) {}(t); };
|
|
|
|
|
|
template <mcc_time_point_c T1, mcc_time_point_c T2>
|
|
static constexpr void mcc_tp2tp(const T1& from_tp1, T2& to_tp)
|
|
{
|
|
to_tp = std::chrono::time_point_cast<typename T2::duration>(from_tp1);
|
|
}
|
|
|
|
|
|
/* JULIAN DAY CLASS CONCEPT */
|
|
|
|
template <typename T>
|
|
concept mcc_julday_c = mcc_fp_type_like_c<T> && requires(const T v) {
|
|
// modified Julian Day
|
|
{ v.MJD() } -> std::convertible_to<double>;
|
|
// comparison operators
|
|
v <=> v;
|
|
};
|
|
|
|
|
|
/* ERROR CLASS CONCEPT */
|
|
|
|
template <typename T>
|
|
concept mcc_error_c = std::default_initializable<T> && (std::convertible_to<T, bool> || requires(const T t) {
|
|
{ t.operator bool() };
|
|
(bool)T() == false; // default constucted value must be a "non-error"!
|
|
});
|
|
|
|
|
|
template <mcc_error_c ErrT, typename DErrT>
|
|
static constexpr ErrT mcc_deduce_error(const DErrT& err, const ErrT& default_err)
|
|
{
|
|
if constexpr (std::same_as<ErrT, DErrT>) {
|
|
return err;
|
|
} else {
|
|
return default_err;
|
|
}
|
|
}
|
|
|
|
template <typename ErrT, typename DErrT>
|
|
static constexpr std::error_code mcc_deduce_error_code(const DErrT& err, const ErrT& default_err)
|
|
requires(std::is_error_code_enum_v<ErrT> || std::same_as<ErrT, std::error_code>)
|
|
{
|
|
if constexpr (std::is_error_code_enum_v<DErrT> || std::same_as<DErrT, std::error_code>) {
|
|
return err;
|
|
} else {
|
|
return default_err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* ATMOSPHERIC REFRACTION MODEL CLASS CONCEPT */
|
|
|
|
template <typename T>
|
|
concept mcc_refract_model_c = requires(const T t_const) {
|
|
{ t_const.name() } -> std::formattable<char>;
|
|
};
|
|
|
|
|
|
/* CELESTIAL POINT WITH A PAIR OF COORDINATES CLASS CONCEPT */
|
|
|
|
template <typename T>
|
|
concept mcc_celestial_point_c = requires(T t) {
|
|
requires std::same_as<std::remove_const_t<decltype(t.pair_kind)>,
|
|
MccCoordPairKind>; // type of given coordinate pair (it may be even static constexpr value)
|
|
|
|
requires mcc_time_point_c<decltype(t.time_point)>; // time point for given coordinates
|
|
|
|
requires mcc_angle_c<decltype(t.X)>; // co-longitude (X-axis)
|
|
requires mcc_angle_c<decltype(t.Y)>; // co-latitude (Y-axis)
|
|
};
|
|
|
|
|
|
static constexpr void mcc_copy_celestial_point(mcc_celestial_point_c auto const& from_pt,
|
|
mcc_celestial_point_c auto* to_pt)
|
|
{
|
|
if (to_pt == nullptr) {
|
|
return;
|
|
}
|
|
|
|
using from_pt_t = std::remove_cvref_t<decltype(from_pt)>;
|
|
using to_pt_t = std::remove_cvref_t<decltype(*to_pt)>;
|
|
|
|
if constexpr (std::derived_from<to_pt_t, from_pt_t> && std::copyable<to_pt_t>) {
|
|
*to_pt = from_pt;
|
|
return;
|
|
}
|
|
|
|
to_pt->pair_kind = from_pt.pair_kind;
|
|
to_pt->time_point =
|
|
std::chrono::time_point_cast<typename decltype(to_pt->time_point)::duration>(from_pt.time_point);
|
|
to_pt->X = (double)from_pt.X;
|
|
to_pt->Y = (double)from_pt.Y;
|
|
}
|
|
|
|
|
|
/* CELESTIAL POINT WITH APPARENT EQUATORIAL AND HORIZONTAL CLASS CONCEPT */
|
|
|
|
template <typename T>
|
|
concept mcc_eqt_hrz_coord_c = mcc_celestial_point_c<T> && requires(T t) {
|
|
requires mcc_angle_c<decltype(t.RA_APP)>; // right ascension
|
|
requires mcc_angle_c<decltype(t.DEC_APP)>; // declination
|
|
requires mcc_angle_c<decltype(t.HA)>; // hour angle
|
|
requires mcc_angle_c<decltype(t.AZ)>; // azimuth (NOTE: ASSUMING THE AZINUTH IS COUNTED FROM THE SOUTH THROUGH THE
|
|
// WEST!!!)
|
|
requires mcc_angle_c<decltype(t.ZD)>; // zenithal distance
|
|
requires mcc_angle_c<decltype(t.ALT)>; // altitude
|
|
};
|
|
|
|
|
|
static constexpr void mcc_copy_eqt_hrz_coord(mcc_eqt_hrz_coord_c auto const& from_pt, mcc_eqt_hrz_coord_c auto* to_pt)
|
|
{
|
|
if (to_pt == nullptr) {
|
|
return;
|
|
}
|
|
|
|
using from_pt_t = std::remove_cvref_t<decltype(from_pt)>;
|
|
using to_pt_t = std::remove_cvref_t<decltype(*to_pt)>;
|
|
|
|
if constexpr (std::derived_from<to_pt_t, from_pt_t> && std::copyable<to_pt_t>) {
|
|
*to_pt = from_pt;
|
|
return;
|
|
}
|
|
|
|
to_pt->pair_kind = from_pt.pair_kind;
|
|
to_pt->time_point =
|
|
std::chrono::time_point_cast<typename decltype(to_pt->time_point)::duration>(from_pt.time_point);
|
|
to_pt->X = (double)from_pt.X;
|
|
to_pt->Y = (double)from_pt.Y;
|
|
|
|
to_pt->RA_APP = (double)from_pt.RA_APP;
|
|
to_pt->DEC_APP = (double)from_pt.DEC_APP;
|
|
|
|
to_pt->HA = (double)from_pt.HA;
|
|
to_pt->AZ = (double)from_pt.AZ;
|
|
to_pt->ZD = (double)from_pt.ZD;
|
|
to_pt->ALT = (double)from_pt.ALT;
|
|
}
|
|
|
|
|
|
// nullptr_t or pointer to celestial/equatorial and horizontal coordinates class
|
|
template <typename T>
|
|
concept mcc_coord_pointer_or_nullptr =
|
|
std::is_null_pointer_v<T> ||
|
|
(std::is_pointer_v<std::decay_t<T>> && (mcc_celestial_point_c<std::remove_pointer_t<std::decay_t<T>>> ||
|
|
mcc_eqt_hrz_coord_c<std::remove_pointer_t<std::decay_t<T>>>));
|
|
|
|
|
|
/* CELESTIAL COORDINATES TRANSFORMATION ENGINE */
|
|
|
|
|
|
template <mcc_error_c RetT>
|
|
struct mcc_CCTE_interface_t {
|
|
virtual ~mcc_CCTE_interface_t() = default;
|
|
|
|
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
|
RetT timepointToJulday(this SelfT&& self, mcc_time_point_c auto tp, mcc_julday_c auto* julday)
|
|
{
|
|
return std::forward<SelfT>(self).timepointToJulday(std::move(tp), julday);
|
|
}
|
|
|
|
// APPARENT SIDERAL TIME
|
|
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
|
RetT timepointToAppSideral(this SelfT&& self, mcc_time_point_c auto tp, mcc_angle_c auto* st, bool islocal = false)
|
|
{
|
|
return std::forward<SelfT>(self).timepointToAppSideral(std::move(tp), st, islocal);
|
|
}
|
|
|
|
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
|
RetT juldayToAppSideral(this SelfT&& self, mcc_julday_c auto jd, mcc_angle_c auto* st, bool islocal = false)
|
|
{
|
|
return std::forward<SelfT>(self).timepointToAppSideral(std::move(jd), st, islocal);
|
|
}
|
|
|
|
// NOTE: ASSUMING THE AZINUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!!
|
|
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
|
RetT transformCoordinates(this SelfT&& self, mcc_celestial_point_c auto from_pt, mcc_celestial_point_c auto* to_pt)
|
|
{
|
|
return std::forward<SelfT>(self).transformCoordinates(std::move(from_pt), to_pt);
|
|
}
|
|
|
|
// NOTE: ASSUMING THE AZIMUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!!
|
|
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
|
RetT transformCoordinates(this SelfT&& self, mcc_celestial_point_c auto from_pt, mcc_eqt_hrz_coord_c auto* to_pt)
|
|
{
|
|
return std::forward<SelfT>(self).transformCoordinates(std::move(from_pt), to_pt);
|
|
}
|
|
|
|
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
|
RetT parallacticAngle(this SelfT&& self, mcc_celestial_point_c auto pt, mcc_angle_c auto* pa)
|
|
{
|
|
return std::forward<SelfT>(self).parallacticAngle(std::move(pt), pa);
|
|
}
|
|
|
|
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
|
RetT refractionCorrection(this SelfT&& self, mcc_celestial_point_c auto pt, mcc_angle_c auto* dZ)
|
|
{
|
|
return std::forward<SelfT>(self).refractionCoeff(std::move(pt), dZ);
|
|
}
|
|
|
|
protected:
|
|
mcc_CCTE_interface_t() = default;
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_ccte_c = std::derived_from<T, mcc_CCTE_interface_t<typename T::error_t>> && requires(const T t_const, T t) {
|
|
{ t_const.nameCCTE() } -> std::formattable<char>;
|
|
|
|
requires mcc_refract_model_c<typename T::refract_model_t>;
|
|
|
|
{ t.refractionModel(std::declval<typename T::refract_model_t*>()) } -> std::same_as<typename T::error_t>;
|
|
};
|
|
|
|
|
|
|
|
/* POINTING CORRECTION MODEL CLASS CONCEPT */
|
|
|
|
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 <mcc_error_c RetT>
|
|
struct mcc_PCM_interface_t {
|
|
virtual ~mcc_PCM_interface_t() = default;
|
|
|
|
// ignore app_pt->pair_kind and time points!!!
|
|
template <std::derived_from<mcc_PCM_interface_t> SelfT>
|
|
RetT computePCM(this SelfT&& self,
|
|
mcc_celestial_point_c auto pt,
|
|
mcc_PCM_result_c auto* result,
|
|
mcc_celestial_point_c auto* app_pt)
|
|
{
|
|
return std::forward<SelfT>(self).computePCM(std::move(pt), result, app_pt);
|
|
}
|
|
|
|
// for equatorial mounts the method must compute:
|
|
// app_pt->HA = pt.X + result->pcmX
|
|
// app_pt->DEC_APP = pt.Y + result->pcmY
|
|
// for alt-azimuthal:
|
|
// app_pt->AZ = pt.X + result->pcmX
|
|
// app_pt->ZD = pt.Y + result->pcmY
|
|
template <std::derived_from<mcc_PCM_interface_t> SelfT>
|
|
RetT computePCM(this SelfT&& self,
|
|
mcc_celestial_point_c auto pt,
|
|
mcc_PCM_result_c auto* result,
|
|
mcc_eqt_hrz_coord_c auto* app_pt)
|
|
{
|
|
return std::forward<SelfT>(self).computePCM(std::move(pt), result, app_pt);
|
|
}
|
|
|
|
template <std::derived_from<mcc_PCM_interface_t> SelfT>
|
|
RetT computeInversePCM(this SelfT&& self,
|
|
mcc_celestial_point_c auto app_pt,
|
|
mcc_PCM_result_c auto* inv_result,
|
|
mcc_celestial_point_c auto* hw_pt)
|
|
{
|
|
return std::forward<SelfT>(self).computePCM(std::move(app_pt), inv_result, hw_pt);
|
|
}
|
|
|
|
|
|
// NOTE: for computation of the corrections the method must use of app_pt.X and app_pt.Y
|
|
//
|
|
// for equatorial mounts the method must compute:
|
|
// hw_pt->X = app_pt.HA + inv_result.pcmX
|
|
// hw_pt->Y = app_pt.DEC_APP + inv_result.pcmY
|
|
// and inputs for the corrections computing are app_pt.HA and app_pt.DEC_APP
|
|
// for alt-azimuthal:
|
|
// hw_pt->X = app_pt.AZ + inv_result.pcmX
|
|
// hw_pt->Y = app_pt.ZD + inv_result.pcmY
|
|
// and inputs for the corrections computing are app_pt.ZA and app_pt.ZD
|
|
template <std::derived_from<mcc_PCM_interface_t> SelfT>
|
|
RetT computeInversePCM(this SelfT&& self,
|
|
mcc_eqt_hrz_coord_c auto app_pt,
|
|
mcc_PCM_result_c auto* inv_result,
|
|
mcc_celestial_point_c auto* hw_pt)
|
|
{
|
|
return std::forward<SelfT>(self).computePCM(std::move(app_pt), inv_result, hw_pt);
|
|
}
|
|
|
|
protected:
|
|
mcc_PCM_interface_t() = default;
|
|
};
|
|
|
|
template <typename T>
|
|
concept mcc_PCM_c = std::derived_from<T, mcc_PCM_interface_t<typename T::error_t>> && requires {
|
|
// the 'T' class must contain static constexpr member of 'MccMountType' type
|
|
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
|
|
};
|
|
|
|
|
|
|
|
/* MOUNT HARDWARE ABSTRACTION CLASS CONCEPT */
|
|
|
|
template <typename T>
|
|
concept mcc_hardware_c = requires(T t, const T t_const) {
|
|
requires mcc_error_c<typename T::error_t>;
|
|
|
|
{ t_const.hardwareName() } -> std::formattable<char>;
|
|
|
|
// the 'T' class must contain static constexpr member of 'MccMountType' type
|
|
// 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 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. The main purpose of this type is a
|
|
// possible tunning of hardware hardwareSetState-related commands and detect stop-state
|
|
//
|
|
// e.g. an implementations can be as follows:
|
|
// enum class hardware_moving_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_moving_state_t {
|
|
// uint16_t HW_MOVE_STOPPED = 0;
|
|
// uint16_t HW_MOVE_SLEWING = 111;
|
|
// uint16_t HW_MOVE_ADJUSTING = 222;
|
|
// uint16_t HW_MOVE_TRACKING = 333;
|
|
// uint16_t HW_MOVE_GUIDING = 444;
|
|
// uint16_t HW_MOVE_ERROR = 555;
|
|
// }
|
|
requires requires(typename T::hardware_moving_state_t type) {
|
|
[]() {
|
|
// mount axes were stopped
|
|
static constexpr auto v0 = T::hardware_moving_state_t::HW_MOVE_STOPPED;
|
|
|
|
// hardware was asked for slewing (move to given celestial point)
|
|
static constexpr auto v1 = T::hardware_moving_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_moving_state_t::HW_MOVE_ADJUSTING;
|
|
|
|
// hardware was asked for tracking (track target celestial point)
|
|
static constexpr auto v3 = T::hardware_moving_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_moving_state_t::HW_MOVE_GUIDING;
|
|
|
|
// to detect possible hardware error
|
|
static constexpr auto v5 = T::hardware_moving_state_t::HW_MOVE_ERROR;
|
|
}();
|
|
};
|
|
|
|
|
|
|
|
// a class that contains at least time point of measurement, coordinates for x,y axes,
|
|
// its moving rates and moving type
|
|
requires mcc_celestial_point_c<typename T::hardware_state_t> && requires(typename T::hardware_state_t state) {
|
|
// requires mcc_time_point_c<decltype(state.time_point)>; // time point
|
|
|
|
// requires mcc_angle_c<decltype(state.X)>; // target or current co-longitude coordinate
|
|
// requires mcc_angle_c<decltype(state.Y)>; // target or current co-latitude coordinate
|
|
|
|
requires mcc_angle_c<decltype(state.speedX)>; // moving speed along co-longitude coordinate
|
|
requires mcc_angle_c<decltype(state.speedY)>; // moving speed along co-latitude coordinate
|
|
|
|
requires std::same_as<typename T::hardware_moving_state_t, decltype(state.moving_state)>;
|
|
};
|
|
|
|
// set hardware state:
|
|
// i.g. 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.hardwareSetState(std::declval<typename T::hardware_state_t>()) } -> 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_pointing_target_coord_c = mcc_eqt_hrz_coord_c<T> && requires(T t) {
|
|
requires mcc_angle_c<decltype(t.RA_ICRS)>; // ICRS right ascention
|
|
requires mcc_angle_c<decltype(t.DEC_ICRS)>; // ICRS declination
|
|
};
|
|
|
|
|
|
static constexpr void mcc_copy_pointing_target_coord(mcc_pointing_target_coord_c auto const& from_pt,
|
|
mcc_pointing_target_coord_c auto* to_pt)
|
|
{
|
|
if (to_pt == nullptr) {
|
|
return;
|
|
}
|
|
|
|
using from_pt_t = std::remove_cvref_t<decltype(from_pt)>;
|
|
using to_pt_t = std::remove_cvref_t<decltype(*to_pt)>;
|
|
|
|
if constexpr (std::derived_from<to_pt_t, from_pt_t> && std::copyable<to_pt_t>) {
|
|
*to_pt = from_pt;
|
|
return;
|
|
}
|
|
|
|
to_pt->pair_kind = from_pt.pair_kind;
|
|
to_pt->time_point =
|
|
std::chrono::time_point_cast<typename decltype(to_pt->time_point)::duration>(from_pt.time_point);
|
|
to_pt->X = (double)from_pt.X;
|
|
to_pt->Y = (double)from_pt.Y;
|
|
|
|
to_pt->RA_ICRS = (double)from_pt.RA_ICRS;
|
|
to_pt->DEC_ICRS = (double)from_pt.DEC_ICRS;
|
|
|
|
to_pt->RA_APP = (double)from_pt.RA_APP;
|
|
to_pt->DEC_APP = (double)from_pt.DEC_APP;
|
|
|
|
to_pt->HA = (double)from_pt.HA;
|
|
to_pt->AZ = (double)from_pt.AZ;
|
|
to_pt->ZD = (double)from_pt.ZD;
|
|
to_pt->ALT = (double)from_pt.ALT;
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_telemetry_data_c = mcc_eqt_hrz_coord_c<T> && std::default_initializable<T> && requires(T t) {
|
|
// target target coordinates
|
|
requires mcc_pointing_target_coord_c<decltype(t.target)>;
|
|
|
|
// t.X and t.Y (from mcc_celestial_point_c) are encoder coordinates
|
|
// t.* from mcc_eqt_hrz_coord_c are apparent mount pointing coordinates
|
|
requires mcc_angle_c<decltype(t.speedX)>; // speed along X from hardware encoder
|
|
requires mcc_angle_c<decltype(t.speedY)>; // speed along Y from hardware encoder
|
|
|
|
// corrections to transform hardware encoder coordinates to apparent celestial ones
|
|
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)>; // for current .ZD
|
|
};
|
|
|
|
|
|
static constexpr void mcc_copy_telemetry_data(mcc_telemetry_data_c auto const& from_pt,
|
|
mcc_telemetry_data_c auto* to_pt)
|
|
{
|
|
if (to_pt == nullptr) {
|
|
return;
|
|
}
|
|
|
|
using from_pt_t = std::remove_cvref_t<decltype(from_pt)>;
|
|
using to_pt_t = std::remove_cvref_t<decltype(*to_pt)>;
|
|
|
|
if constexpr (std::derived_from<to_pt_t, from_pt_t> && std::copyable<to_pt_t>) {
|
|
*to_pt = from_pt;
|
|
return;
|
|
}
|
|
|
|
to_pt->pair_kind = from_pt.pair_kind;
|
|
to_pt->time_point =
|
|
std::chrono::time_point_cast<typename decltype(to_pt->time_point)::duration>(from_pt.time_point);
|
|
|
|
to_pt->X = (double)from_pt.X;
|
|
to_pt->Y = (double)from_pt.Y;
|
|
to_pt->speedX = (double)from_pt.speedX;
|
|
to_pt->speedY = (double)from_pt.speedY;
|
|
|
|
to_pt->RA_APP = (double)from_pt.RA_APP;
|
|
to_pt->DEC_APP = (double)from_pt.DEC_APP;
|
|
|
|
to_pt->HA = (double)from_pt.HA;
|
|
to_pt->AZ = (double)from_pt.AZ;
|
|
to_pt->ZD = (double)from_pt.ZD;
|
|
to_pt->ALT = (double)from_pt.ALT;
|
|
|
|
to_pt->pcmX = (double)from_pt.pcmX;
|
|
to_pt->pcmY = (double)from_pt.pcmY;
|
|
|
|
to_pt->refCorr = (double)from_pt.refCorr;
|
|
|
|
mcc_copy_pointing_target_coord(from_pt.target, &to_pt->target);
|
|
}
|
|
|
|
|
|
|
|
/* MOUNT TELEMETRY MANAGER CLASS CONCEPT */
|
|
|
|
template <mcc_error_c RetT>
|
|
struct mcc_telemetry_interface_t {
|
|
virtual ~mcc_telemetry_interface_t() = default;
|
|
|
|
// update telemetry data right now
|
|
// an implementation is expected to update the data within the given 'timeout' time interval
|
|
// if not the method must return an appropriate timeout-error
|
|
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
|
RetT updateTelemetryData(this SelfT&& self, traits::mcc_time_duration_c auto const& timeout)
|
|
{
|
|
return std::forward<SelfT>(self).updateTelemetryData(timeout);
|
|
}
|
|
|
|
// get current data
|
|
// an implementation is expected to return current, possibly already expired, telemetry data
|
|
// one should call consistently both 'updateTelemetryData' + 'telemetryData' methods
|
|
// to ensure that the data is up-to-date
|
|
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
|
RetT telemetryData(this SelfT&& self, mcc_telemetry_data_c auto* data)
|
|
{
|
|
return std::forward<SelfT>(self).telemetryData(data);
|
|
}
|
|
|
|
// waiting for updated data
|
|
// an implementation is expected to block the current thread waiting for
|
|
// telemetry data to be updated (internal synchronization)
|
|
// if a timeout occured the method must return an appropriate timeout-error
|
|
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
|
RetT waitForTelemetryData(this SelfT&& self,
|
|
mcc_telemetry_data_c auto* data,
|
|
traits::mcc_time_duration_c auto const& timeout)
|
|
{
|
|
return std::forward<SelfT>(self).waitForTelemetryData(data, timeout);
|
|
}
|
|
|
|
|
|
// set target coordinates
|
|
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
|
RetT setPointingTarget(this SelfT&& self, mcc_celestial_point_c auto pt)
|
|
{
|
|
return std::forward<SelfT>(self).telemetryData(std::move(pt));
|
|
}
|
|
|
|
|
|
// compute difference in coordinates:
|
|
// dx = targetX - mountX
|
|
// dy = targetY - mountY
|
|
// where X and Y is in according to 'pair_kind' input parameter
|
|
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
|
RetT targetToMountDiff(this SelfT&& self, MccCoordPairKind pair_kind, mcc_angle_c auto* dx, mcc_angle_c auto* dy)
|
|
{
|
|
std::forward<SelfT>(self).targetToMountDiff(pair_kind, dx, dy);
|
|
}
|
|
|
|
// compute distance between target and actual mount celestial points
|
|
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
|
RetT targetToMountDist(this SelfT&& self, mcc_angle_c auto* dist)
|
|
{
|
|
std::forward<SelfT>(self).targetToMountDist(dist);
|
|
}
|
|
|
|
protected:
|
|
mcc_telemetry_interface_t() = default;
|
|
};
|
|
|
|
template <typename T>
|
|
concept mcc_telemetry_c = std::derived_from<T, mcc_telemetry_interface_t<typename T::error_t>>;
|
|
|
|
|
|
|
|
/* PROHIBITED ZONE CLASS CONCEPT */
|
|
|
|
template <mcc_error_c RetT>
|
|
struct mcc_pzone_interface_t {
|
|
virtual ~mcc_pzone_interface_t() = default;
|
|
|
|
template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT>
|
|
RetT inPZone(this SelfT&& self, InputT coords, bool* result)
|
|
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>) &&
|
|
requires { self.inPZone(coords, result); }
|
|
{
|
|
return std::forward<SelfT>(self).InPZone(std::move(coords), result);
|
|
}
|
|
|
|
|
|
template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT>
|
|
RetT timeToPZone(this SelfT&& self, InputT coords, traits::mcc_time_duration_c auto* res_time)
|
|
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>) &&
|
|
requires { self.timeToPZone(coords, res_time); }
|
|
{
|
|
return std::forward<SelfT>(self).timeToPZone(std::move(coords), res_time);
|
|
}
|
|
|
|
template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT>
|
|
RetT timeFromPZone(this SelfT&& self, InputT coords, traits::mcc_time_duration_c auto* res_time)
|
|
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>) &&
|
|
requires { self.timeFromPZone(coords, res_time); }
|
|
{
|
|
return std::forward<SelfT>(self).timeFromPZone(std::move(coords), res_time);
|
|
}
|
|
|
|
//
|
|
// NOTE: the method must return:
|
|
// point = mcc_celestial_point_c{.pair_kind = MccCoordPairKind::COORDS_KIND_GENERIC, .X = NaN, .Y = NaN}
|
|
// if there is no intersection with the zone for given coordinates!
|
|
template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT, typename ResultT>
|
|
RetT intersectPZone(this SelfT&& self, InputT coords, ResultT* point)
|
|
requires((mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>) &&
|
|
(mcc_eqt_hrz_coord_c<ResultT> || mcc_celestial_point_c<ResultT>)) &&
|
|
requires { self.intersectPZone(coords, point); }
|
|
{
|
|
return std::forward<SelfT>(self).intersectPZone(std::move(coords), point);
|
|
}
|
|
// template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT>
|
|
// RetT intersectPZone(this SelfT&& self, InputT coords, mcc_celestial_point_c auto* point)
|
|
// requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>) &&
|
|
// requires { self.intersectPZone(coords, point); }
|
|
// {
|
|
// return std::forward<SelfT>(self).intersectPZone(std::move(coords), point);
|
|
// }
|
|
|
|
protected:
|
|
mcc_pzone_interface_t() = default;
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_prohibited_zone_c =
|
|
std::derived_from<T, mcc_pzone_interface_t<typename T::error_t>> && requires(const T t_const) {
|
|
{ t_const.name() } -> std::formattable<char>;
|
|
|
|
// the 'T' class must contain static constexpr member of 'MccMountType' type
|
|
requires std::same_as<decltype(T::pzPolicy), const MccProhibitedZonePolicy>;
|
|
[]() {
|
|
static constexpr MccProhibitedZonePolicy val = T::pzPolicy;
|
|
return val;
|
|
}(); // 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_prohibited_zone_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, typename InputT>
|
|
RetT inPZone(this SelfT&& self, InputT coords, bool* common_result, std::ranges::output_range<bool> auto* result)
|
|
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
|
{
|
|
return std::forward<SelfT>(self).InPZone(std::move(coords), common_result, result);
|
|
}
|
|
|
|
|
|
template <std::derived_from<mcc_pzone_container_interface_t> SelfT, typename InputT, traits::mcc_time_duration_c DT>
|
|
RetT timeToPZone(this SelfT&& self, InputT coords, std::ranges::output_range<DT> auto* res_time)
|
|
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
|
{
|
|
return std::forward<SelfT>(self).timeToPZone(std::move(coords), res_time);
|
|
}
|
|
|
|
template <std::derived_from<mcc_pzone_container_interface_t> SelfT, typename InputT, traits::mcc_time_duration_c DT>
|
|
RetT timeFromPZone(this SelfT&& self, InputT coords, std::ranges::output_range<DT> auto* res_time)
|
|
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
|
{
|
|
return std::forward<SelfT>(self).timeFromPZone(std::move(coords), res_time);
|
|
}
|
|
|
|
|
|
// template <std::derived_from<mcc_pzone_container_interface_t> SelfT, typename InputT, mcc_celestial_point_c CPT>
|
|
// RetT intersectPZone(this SelfT&& self, InputT coords, std::ranges::output_range<CPT> auto* result)
|
|
// requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
|
// {
|
|
// return std::forward<SelfT>(self).intersectPZone(std::move(coords), result);
|
|
// }
|
|
|
|
template <std::derived_from<mcc_pzone_container_interface_t> SelfT, typename InputT, typename ResultT>
|
|
RetT intersectPZone(this SelfT&& self, InputT coords, std::ranges::output_range<ResultT> auto* result)
|
|
requires((mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>) &&
|
|
(mcc_eqt_hrz_coord_c<ResultT> || mcc_celestial_point_c<ResultT>))
|
|
{
|
|
return std::forward<SelfT>(self).intersectPZone(std::move(coords), result);
|
|
}
|
|
|
|
protected:
|
|
mcc_pzone_container_interface_t() = default;
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_pzone_container_c = std::derived_from<T, mcc_pzone_container_interface_t<typename T::error_t>>;
|
|
|
|
|
|
/* MOUNT MOVING MODEL CONCEPT */
|
|
|
|
template <typename T>
|
|
concept mcc_slewing_model_c = requires(T t) {
|
|
// requires mcc_error_c<typename T::error_t>;
|
|
|
|
// a class of slewing process parameters
|
|
typename T::slewing_params_t;
|
|
// requires requires(typename T::slewing_params_t pars) {
|
|
// // slew mount to target and stop
|
|
// requires std::convertible_to<decltype(pars.slewAndStop), bool>;
|
|
// };
|
|
|
|
// { t.slewToTarget() } -> std::same_as<typename T::error_t>;
|
|
// { t.stopSlewing() } -> std::same_as<typename T::error_t>;
|
|
|
|
// the method signature:
|
|
// slewToTarget(bool slew_and_stop)
|
|
// slew_and_stop == true, slew mount and stop
|
|
// slew_and_stop == false, slew mount and track
|
|
{ t.slewToTarget(std::declval<bool>()) } -> mcc_error_c;
|
|
{ t.stopSlewing() } -> mcc_error_c;
|
|
|
|
{ t.setSlewingParams(std::declval<typename T::slewing_params_t>()) } -> mcc_error_c;
|
|
// { t.setSlewingParams(std::declval<typename T::slewing_params_t>()) } -> std::same_as<typename T::error_t>;
|
|
{ t.getSlewingParams() } -> std::same_as<typename T::slewing_params_t>;
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_tracking_model_c = requires(T t) {
|
|
// requires mcc_error_c<typename T::error_t>;
|
|
|
|
// a class of tracking process parameters
|
|
requires requires(typename T::tracking_params_t pars) {
|
|
requires mcc_angle_c<decltype(pars.trackSpeedX)>;
|
|
requires mcc_angle_c<decltype(pars.trackSpeedY)>;
|
|
};
|
|
|
|
// { t.trackTarget() } -> std::same_as<typename T::error_t>;
|
|
// { t.stopTracking() } -> std::same_as<typename T::error_t>;
|
|
{ t.trackTarget() } -> mcc_error_c;
|
|
{ t.stopTracking() } -> mcc_error_c;
|
|
|
|
{ t.setTrackingParams(std::declval<typename T::tracking_params_t>()) } -> mcc_error_c;
|
|
// { t.setTrackingParams(std::declval<typename T::tracking_params_t>()) } -> std::same_as<typename T::error_t>;
|
|
{ t.getTrackingParams() } -> std::same_as<typename T::tracking_params_t>;
|
|
};
|
|
|
|
// template <typename T>
|
|
// concept mcc_guiding_model_c = requires(T t) {
|
|
// requires mcc_error_c<typename T::error_t>;
|
|
|
|
// // a class of guiding process parameters
|
|
// requires requires(typename T::guiding_params_t pars) {
|
|
// // guide along both mount axis
|
|
// requires std::convertible_to<decltype(pars.dualAxisGuiding), bool>;
|
|
// };
|
|
|
|
// { t.startGuidingTarget() } -> std::same_as<typename T::error_t>;
|
|
// { t.stopGuidingTarget() } -> std::same_as<typename T::error_t>;
|
|
|
|
// { t.setGuidingParams(std::declval<typename T::guiding_params_t>()) } -> std::same_as<typename T::error_t>;
|
|
// { t.getGuidingParams() } -> std::same_as<typename T::guiding_params_t>;
|
|
// };
|
|
|
|
|
|
/* GENERIC MOUNT CLASS CONCEPT */
|
|
|
|
|
|
// a concept of class that consist of the full set of coordinate transformation mount control components
|
|
// (celestial coordinate transformation engine, mount hardware encoders readings and pointing correction model)
|
|
// the set of methods of this class is enough to transform coordinates from ICRS to hardware and back
|
|
template <typename T>
|
|
concept mcc_position_controls_c = mcc_ccte_c<T> && mcc_hardware_c<T> && mcc_PCM_c<T>;
|
|
|
|
// a class containing full set of mount controls
|
|
template <typename T>
|
|
concept mcc_all_controls_c = mcc_position_controls_c<T> && mcc_telemetry_c<T> && mcc_pzone_container_c<T>;
|
|
|
|
|
|
// generic mount:
|
|
// 1) telemetry-related methods
|
|
// 2) prohibited zones related methods
|
|
// 3) slewing and tracking, stop and init mount methods
|
|
template <typename T>
|
|
concept mcc_generic_mount_c = mcc_telemetry_c<T> && mcc_pzone_container_c<T> && requires(T t) {
|
|
// requires mcc_error_c<typename T::error_t>;
|
|
|
|
// slew mount to target (it is assumed that the target coordinates are determined in the telemetry data)
|
|
{ t.slewToTarget() };
|
|
// { t.slewToTarget() } -> std::same_as<typename T::error_t>;
|
|
|
|
// track target, i.e., the mount moves with celestial speed
|
|
{ t.trackTarget() };
|
|
// { t.trackTarget() } -> std::same_as<typename T::error_t>;
|
|
|
|
|
|
// { t.startGuidingTarget() } -> std::same_as<typename T::error_t>;
|
|
// { t.stopGuidingTarget() } -> std::same_as<typename T::error_t>;
|
|
|
|
// stop any movement
|
|
{ t.stopMount() };
|
|
// { t.stopMount() } -> std::same_as<typename T::error_t>;
|
|
|
|
// init mount
|
|
{ t.initMount() };
|
|
};
|
|
|
|
|
|
// with logging methods
|
|
template <typename T>
|
|
concept mcc_generic_log_mount_c = mcc_generic_mount_c<T> && mcc_logger_c<T>;
|
|
|
|
|
|
// Finite-state-machine
|
|
template <typename T>
|
|
concept mcc_generic_fsm_mount_c = mcc_generic_mount_c<T> && std::derived_from<T, fsm::MccFiniteStateMachine>;
|
|
|
|
template <typename T>
|
|
concept mcc_generic_fsm_log_mount_c =
|
|
mcc_generic_mount_c<T> && mcc_logger_c<T> && std::derived_from<T, fsm::MccFiniteStateMachine>;
|
|
|
|
|
|
} // namespace mcc
|