#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* SOME LIBRARY-WIDE DECLARATIONS */ #include #include // #include "mcc_traits.h" #include "mcc_angle.h" namespace mcc { // mount construction type (only the most common ones) enum class MccMountType : uint8_t { GERMAN_TYPE, FORK_TYPE, CROSSAXIS_TYPE, ALTAZ_TYPE }; template 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 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 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 MccCoordPairKind : size_t { // COORDS_KIND_GENERIC, // COORDS_KIND_RADEC_ICRS, // COORDS_KIND_RADEC_APP, // COORDS_KIND_HADEC_APP, // COORDS_KIND_AZZD, // COORDS_KIND_AZALT, // COORDS_KIND_XY, // COORDS_KIND_LATLON // }; /* FLOATING-POINT LIKE CLASS CONCEPT */ template concept mcc_fp_type_like_c = std::floating_point || (std::convertible_to && std::constructible_from && std::default_initializable); /* ANGLE REPRESENTATION CLASS CONCEPT */ template concept mcc_angle_c = mcc_fp_type_like_c && requires(T v, double vd) { // mandatory arithmetic operations { v + v } -> std::same_as; { v - v } -> std::same_as; { v += v } -> std::same_as; { v -= v } -> std::same_as; { vd + v } -> std::same_as; { vd - v } -> std::same_as; { v + vd } -> std::same_as; { v - vd } -> std::same_as; { v += vd } -> std::same_as; { v -= vd } -> std::same_as; { v * vd } -> std::same_as; { v / vd } -> std::same_as; }; /* TIME POINT CLASS CONCEPT */ /* * USE OF STL std::chrono::time_point */ template concept mcc_time_point_c = traits::mcc_systime_c; // concept mcc_time_point_c = requires(T t) { [](std::chrono::time_point) {}(t); }; /* JULIAN DAY CLASS CONCEPT */ template concept mcc_julday_c = mcc_fp_type_like_c && requires(const T v) { // modified Julian Day { v.MJD() } -> std::convertible_to; // comparison operators v <=> v; }; /* ERROR CLASS CONCEPT */ template concept mcc_error_c = std::convertible_to || requires(const T t) { { t.operator bool() }; }; template static constexpr ErrT mcc_deduce_error(const DErrT& err, const ErrT& default_err) { if constexpr (std::same_as) { return err; } else { return default_err; } } /* ATMOSPHERIC REFRACTION MODEL CLASS CONCEPT */ template concept mcc_refract_model_c = requires(const T t_const) { { t_const.name() } -> std::formattable; }; /* CELESTIAL POINT WITH A PAIR OF COORDINATES CLASS CONCEPT */ template concept mcc_celestial_point_c = requires(T t) { requires std::same_as; // type of given coordinate pair requires mcc_time_point_c; // time point for given coordinates requires mcc_angle_c; // co-longitude (X-axis) requires mcc_angle_c; // 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; using to_pt_t = std::remove_cvref_t; if constexpr (std::derived_from && std::copyable) { *to_pt = from_pt; return; } to_pt->pair_kind = from_pt.pair_kind; to_pt->time_point = std::chrono::time_point_casttime_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 concept mcc_eqt_hrz_coord_c = mcc_celestial_point_c && requires(T t) { requires mcc_angle_c; // right ascension requires mcc_angle_c; // declination requires mcc_angle_c; // hour angle requires mcc_angle_c; // azimuth (NOTE: ASSUMING THE AZINUTH IS COUNTED FROM THE SOUTH THROUGH THE // WEST!!!) requires mcc_angle_c; // zenithal distance requires mcc_angle_c; // 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; using to_pt_t = std::remove_cvref_t; if constexpr (std::derived_from && std::copyable) { *to_pt = from_pt; return; } to_pt->pair_kind = from_pt.pair_kind; to_pt->time_point = std::chrono::time_point_casttime_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; } /* CELESTIAL COORDINATES TRANSFORMATION ENGINE */ template struct mcc_CCTE_interface_t { virtual ~mcc_CCTE_interface_t() = default; template SelfT> RetT timepointToJulday(this SelfT&& self, mcc_time_point_c auto tp, mcc_julday_c auto* julday) { return std::forward(self).timepointToJulday(std::move(tp), julday); } // APPARENT SIDERAL TIME template SelfT> RetT timepointToAppSideral(this SelfT&& self, mcc_time_point_c auto tp, mcc_angle_c auto* st, bool islocal = false) { return std::forward(self).timepointToAppSideral(std::move(tp), st, islocal); } template SelfT> RetT juldayToAppSideral(this SelfT&& self, mcc_julday_c auto jd, mcc_angle_c auto* st, bool islocal = false) { return std::forward(self).timepointToAppSideral(std::move(jd), st, islocal); } // NOTE: ASSUMING THE AZINUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!! template SelfT> RetT transformCoordinates(this SelfT&& self, mcc_celestial_point_c auto from_pt, mcc_celestial_point_c auto* to_pt) { return std::forward(self).transformCoordinates(std::move(from_pt), to_pt); } // NOTE: ASSUMING THE AZIMUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!! template SelfT> RetT transformCoordinates(this SelfT&& self, mcc_celestial_point_c auto from_pt, mcc_eqt_hrz_coord_c auto* to_pt) { return std::forward(self).transformCoordinates(std::move(from_pt), to_pt); } template SelfT> RetT parallacticAngle(this SelfT&& self, mcc_celestial_point_c auto pt, mcc_angle_c auto* pa) { return std::forward(self).parallacticAngle(std::move(pt), pa); } template SelfT> RetT refractionCorrection(this SelfT&& self, mcc_celestial_point_c auto pt, mcc_angle_c auto* dZ) { return std::forward(self).refractionCoeff(std::move(pt), dZ); } protected: mcc_CCTE_interface_t() = default; }; template concept mcc_ccte_c = std::derived_from> && requires(const T t_const, T t) { { t_const.name() } -> std::formattable; requires mcc_refract_model_c; { t.refractionModel(std::declval()) } -> std::same_as; }; /* MOUNT TELEMETRY DATA CLASS CONCEPT */ template concept mcc_pointing_target_coord_c = mcc_eqt_hrz_coord_c && requires(T t) { requires mcc_angle_c; // ICRS right ascention requires mcc_angle_c; // 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; using to_pt_t = std::remove_cvref_t; if constexpr (std::derived_from && std::copyable) { *to_pt = from_pt; return; } to_pt->pair_kind = from_pt.pair_kind; to_pt->time_point = std::chrono::time_point_casttime_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 concept mcc_telemetry_data_c = mcc_eqt_hrz_coord_c && std::default_initializable && requires(T t) { // target target coordinates requires mcc_pointing_target_coord_c; // 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; // speed along X from hardware encoder requires mcc_angle_c; // speed along Y from hardware encoder // corrections to transform hardware encoder coordinates to apparent celestial ones requires mcc_angle_c; // PCM correction along X-axis requires mcc_angle_c; // PCM correction along Y-axis // atmospheric refraction correction for current zenithal distance requires mcc_angle_c; // 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; using to_pt_t = std::remove_cvref_t; if constexpr (std::derived_from && std::copyable) { *to_pt = from_pt; return; } to_pt->pair_kind = from_pt.pair_kind; to_pt->time_point = std::chrono::time_point_casttime_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 struct mcc_telemetry_interface_t { virtual ~mcc_telemetry_interface_t() = default; template SelfT> RetT telemetryData(this SelfT&& self, mcc_telemetry_data_c auto* data) { return std::forward(self).telemetryData(data); } template SelfT> RetT setPointingTarget(this SelfT&& self, mcc_celestial_point_c auto pt) { return std::forward(self).telemetryData(std::move(pt)); } protected: mcc_telemetry_interface_t() = default; }; template concept mcc_telemetry_c = std::derived_from>; /* POINTING CORRECTION MODEL CLASS CONCEPT */ template concept mcc_PCM_result_c = requires(T t) { requires mcc_angle_c; requires mcc_angle_c; }; template struct mcc_PCM_interface_t { template SelfT> RetT computePCM(this SelfT&& self, mcc_celestial_point_c auto pt, ResT* result) { return std::forward(self).computePCM(std::move(pt), result); } }; template concept mcc_PCM_c = std::derived_from> && requires { // the 'T' class must contain static constexpr member of 'MccMountType' type requires std::same_as; []() { static constexpr MccMountType val = T::mountType; return val; }(); // to ensure 'mountType' can be used in compile-time context }; /* MOUNT HARDWARE ABSTRACTION CLASS CONCEPT */ template concept mcc_hardware_c = requires(T t, const T t_const) { typename T::error_t; { t_const.name() } -> std::formattable; // a class that contains at least time point of measurement, coordinates for x,y axes and its moving rates requires requires(typename T::axes_pos_t pos) { requires mcc_time_point_c; // time point requires mcc_angle_c; // target or current co-longitude coordinate requires mcc_angle_c; // target or current co-latitude coordinate requires mcc_angle_c; requires mcc_angle_c; }; // 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()) } -> std::same_as; // get current positions and speeds (angles) of mount axes { t.getPos(std::declval()) } -> std::same_as; { t.stop() } -> std::same_as; // stop any moving { t.init() } -> std::same_as; // initialize hardware }; /* PROHIBITED ZONE CLASS CONCEPT */ template struct mcc_pzone_interface_t { virtual ~mcc_pzone_interface_t() = default; template SelfT, typename InputT> RetT inPZone(this SelfT&& self, InputT coords, bool* result) requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && requires { self.inPZone(coords, result); } { return std::forward(self).InPZone(std::move(coords), result); } template SelfT, typename InputT> RetT timeToPZone(this SelfT&& self, InputT coords, traits::mcc_time_duration_c auto* res_time) requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && requires { self.timeToPZone(coords, res_time); } { return std::forward(self).timeToPZone(std::move(coords), res_time); } template SelfT, typename InputT> RetT timeFromPZone(this SelfT&& self, InputT coords, traits::mcc_time_duration_c auto* res_time) requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && requires { self.timeFromPZone(coords, res_time); } { return std::forward(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 SelfT, typename InputT> RetT intersectPZone(this SelfT&& self, InputT coords, mcc_celestial_point_c auto* point) requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) && requires { self.intersectPZone(coords, point); } { return std::forward(self).intersectPZone(std::move(coords), point); } protected: mcc_pzone_interface_t() = default; }; template concept mcc_prohibited_zone_c = std::derived_from> && requires(const T t_const) { { t_const.name() } -> std::formattable; }; /* PROHIBITED ZONES CONTAINER CLASS CONCEPT */ template struct mcc_pzone_container_interface_t { virtual ~mcc_pzone_container_interface_t() = default; template SelfT> size_t addPZone(this SelfT&& self, mcc_prohibited_zone_c auto zone) { return std::forward(self).addPZone(std::move(zone)); } template SelfT> void clearPZones(this SelfT&& self) { return std::forward(self).clearPZones(); } template SelfT> size_t sizePZones(this SelfT&& self) { return std::forward(self).sizePZones(); } template SelfT, typename InputT> RetT inPZone(this SelfT&& self, InputT coords, bool* common_result, std::ranges::output_range auto* result) requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) { return std::forward(self).InPZone(std::move(coords), common_result, result); } template SelfT, typename InputT, traits::mcc_time_duration_c DT> RetT timeToPZone(this SelfT&& self, InputT coords, std::ranges::output_range
auto* res_time) requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) { return std::forward(self).timeToPZone(std::move(coords), res_time); } template SelfT, typename InputT, traits::mcc_time_duration_c DT> RetT timeFromPZone(this SelfT&& self, InputT coords, std::ranges::output_range
auto* res_time) requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) { return std::forward(self).timeFromPZone(std::move(coords), res_time); } template SelfT, typename InputT, mcc_celestial_point_c CPT> RetT intersectPZone(this SelfT&& self, InputT coords, std::ranges::output_range auto* result) requires(mcc_eqt_hrz_coord_c || mcc_celestial_point_c) { return std::forward(self).intersectPZone(std::move(coords), result); } protected: mcc_pzone_container_interface_t() = default; }; template concept mcc_pzone_container_c = std::derived_from>; } // namespace mcc