#pragma once /**************************************************************************************** * * * MOUNT CONTROL COMPONENTS LIBRARY * * * * * * IMPLEMENTATION OF SOME SIMPLE PROHIBITED ZONES * * * ****************************************************************************************/ #include "mcc_concepts.h" #include "mcc_constants.h" #include "mcc_coordinate.h" namespace mcc::impl { enum class MccPZoneErrorCode : int { ERROR_OK, ERROR_NULLPTR, ERROR_COORD_TRANSFROM, ERROR_PCM_COMP }; } // namespace mcc::impl namespace std { template <> class is_error_code_enum : public true_type { }; } // namespace std namespace mcc::impl { // error category struct MccPZoneCategory : public std::error_category { MccPZoneCategory() : std::error_category() {} const char* name() const noexcept { return "ALTITUDE-LIMIT-PZ"; } std::string message(int ec) const { MccPZoneErrorCode err = static_cast(ec); switch (err) { case MccPZoneErrorCode::ERROR_OK: return "OK"; case MccPZoneErrorCode::ERROR_NULLPTR: return "input argument os nullptr"; case MccPZoneErrorCode::ERROR_COORD_TRANSFROM: return "coordinate transformation error"; case MccPZoneErrorCode::ERROR_PCM_COMP: return "PCM computation error"; default: return "UNKNOWN"; } } static const MccPZoneCategory& get() { static const MccPZoneCategory constInst; return constInst; } }; inline std::error_code make_error_code(MccPZoneErrorCode ec) { return std::error_code(static_cast(ec), MccPZoneCategory::get()); } enum class MccAltLimitKind { MIN_ALT_LIMIT, MAX_ALT_LIMIT }; template class MccAltLimitPZ : public mcc_pzone_interface_t { public: static constexpr MccProhibitedZonePolicy pzPolicy = MccProhibitedZonePolicy::PZ_POLICY_STOP; typedef std::error_code error_t; MccAltLimitPZ(mcc_angle_c auto const& alt_limit, mcc_angle_c auto const& latitude) : _altLimit(MccAngle(alt_limit).normalize()), _cosALim(cos(_altLimit)), _sinAlim(sin(_altLimit)), _cosLat(cos(latitude)), _sinLat(sin(latitude)), _absLat(abs(latitude)), _latLim(MCC_TWO_PI - _altLimit) { } MccAltLimitPZ(MccAltLimitPZ&&) = default; MccAltLimitPZ(const MccAltLimitPZ&) = default; static constexpr std::string_view pzoneName = KIND == MccAltLimitKind::MIN_ALT_LIMIT ? "MINALT-ZONE" : KIND == MccAltLimitKind::MAX_ALT_LIMIT ? "MAXALT-ZONE" : "ALTLIMIT-UNKNOWN"; error_t inPZone(mcc_skypoint_c auto const& coords, bool* result) { if (result == nullptr) { return MccPZoneErrorCode::ERROR_NULLPTR; } error_t ret = MccPZoneErrorCode::ERROR_OK; MccSkyAZALT azalt; auto ccte_err = coords.to(azalt); if (ccte_err) { return mcc_deduced_err(ccte_err, MccPZoneErrorCode::ERROR_COORD_TRANSFROM); } if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) { *result = azalt.y() <= _altLimit; } else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { *result = azalt.y() >= _altLimit; } return ret; } error_t timeToPZone(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time) { using res_t = std::remove_cvref_t; if (res_time == nullptr) { return MccPZoneErrorCode::ERROR_NULLPTR; } error_t ret = MccPZoneErrorCode::ERROR_OK; bool inzone; ret = inPZone(coords, &inzone); if (ret) { return ret; } if (inzone) { *res_time = res_t{0}; return ret; } MccSkyHADEC_OBS hadec; auto ccte_err = coords.to(hadec); if (ccte_err) { return mcc_deduced_err(ccte_err, MccPZoneErrorCode::ERROR_COORD_TRANSFROM); } if (!doesObjectReachZone(hadec.y())) { *res_time = MCC_INFINITE_DURATION_V; return ret; } if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one after upper culmination compute(hadec.x(), hadec.y(), false, res_time); } else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one before upper // culmination compute(hadec.x(), hadec.y(), true, res_time); } return ret; } error_t timeFromPZone(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time) { using res_t = std::remove_cvref_t; if (res_time == nullptr) { return MccPZoneErrorCode::ERROR_NULLPTR; } error_t ret = MccPZoneErrorCode::ERROR_OK; bool inzone; ret = inPZone(coords, &inzone); if (ret) { return ret; } if (!inzone) { *res_time = res_t{0}; return ret; } MccSkyHADEC_OBS hadec; auto ccte_err = coords.to(hadec); if (ccte_err) { return mcc_deduced_err(ccte_err, MccPZoneErrorCode::ERROR_COORD_TRANSFROM); } if (!doesObjectExitFromZone(hadec.y())) { *res_time = MCC_INFINITE_DURATION_V; return ret; } if (!doesObjectReachZone(hadec.y())) { *res_time = res_t{0}; return ret; } if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one before upper culmination compute(hadec.x(), hadec.y(), true, res_time); } else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one after upper // culmination compute(hadec.x(), hadec.y(), false, res_time); } return ret; } protected: double _altLimit, _cosALim, _sinAlim; double _cosLat, _sinLat, _absLat, _latLim; bool doesObjectReachZone(const double& dec_app) { // check for limit conditions auto dd = std::abs(dec_app); if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) { dd += _altLimit; if (dd > _latLim) { // never fall below altitude limit return false; } } else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { auto z = std::numbers::pi / 2.0 - _altLimit; if ((dd < (_absLat - z)) || (dd > (_absLat + z))) { // never rise above altitude limit return false; } // if ((dd < (_absLat - _altLimit)) || (dd > (_absLat + _altLimit))) { // never rise above altitude limit // return false; // } } else { static_assert(false, "UNKNOWN ALTITUDE LIMIT TYPE!"); } return true; } bool doesObjectExitFromZone(const double& dec_app) { // check for limit conditions auto dd = std::abs(dec_app); if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) { dd -= _altLimit; if (-dd <= -_latLim) { // always below altitude limit return false; } } else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { if ((dd >= (_absLat - _altLimit)) || (dd <= (_absLat + _altLimit))) { // always above altitude limit return false; } } else { static_assert(false, "UNKNOWN ALTITUDE LIMIT TYPE!"); } return true; } void compute(const double& ha_app, const double& dec_app, bool before_upper_culm, traits::mcc_time_duration_c auto* result) { using res_t = std::remove_cvref_t; using period_t = typename res_t::period; double cos_ha = (_sinAlim - std::sin(dec_app) * _sinLat) / std::cos(dec_app) / _cosLat; if (cos_ha > 1.0) { // should not be! *result = MCC_INFINITE_DURATION_V; return; } double ha; // WARNING: what about south hemisphere?!!! if (before_upper_culm) { ha = -std::acos(cos_ha); // HA before upper culmination } else { ha = std::acos(cos_ha); // HA after upper culmination!! } auto time_ang = ha - ha_app; // in sideral time scale if (time_ang < 0.0) { // next day time_ang += MCC_TWO_PI; } time_ang /= MCC_SIDERAL_TO_UT1_RATIO; // to UT1 time scale std::chrono::nanoseconds ns{ static_cast(time_ang * 43200.0 / std::numbers::pi * 1.0E9)}; period_t rat; *result = res_t{static_cast(time_ang * 43200.0 / std::numbers::pi * rat.den / rat.num)}; } }; /* co-longitude axis (HA or AZ) limit switch prohibited zone */ template class MccAxisLimitSwitchPZ : public mcc_pzone_interface_t { public: static_assert(AXIS_KIND == MccCoordKind::COORDS_KIND_AZ || AXIS_KIND == MccCoordKind::COORDS_KIND_HA_OBS, "UNSUPPORTED AXIS TYPE!"); typedef std::error_code error_t; static constexpr MccCoordKind axisKind = AXIS_KIND; static constexpr MccProhibitedZonePolicy pzPolicy = MccProhibitedZonePolicy::PZ_POLICY_FLIP; // // min_limit_val and max_limit_val are hardware encoder angles in radians! // MccAxisLimitSwitchPZ(mcc_angle_c auto const& min_limit_val, mcc_angle_c auto const& max_limit_val, mcc_pcm_c auto* pcm) : _minLimit(min_limit_val), _maxLimit(max_limit_val) { _correctForPCM = [pcm](MccSkyPoint const& skypt, MccGenXY* hw_coords) { struct pcm_res_t { double pcmX, pcmY; }; pcm_res_t res; auto err = pcm->computeInversePCM(skypt, &res, hw_coords); return mcc_deduced_err(err, MccPZoneErrorCode::ERROR_PCM_COMP); }; } static constexpr std::string_view pzoneName = axisKind == MccCoordKind::COORDS_KIND_AZ ? "AZ_AXIS-LIMITSWITCH_ZONE" : axisKind == MccCoordKind::COORDS_KIND_HA_OBS ? "HA_AXIS-LIMITSWITCH_ZONE" : "UKNOWN"; error_t inPZone(mcc_skypoint_c auto const& coords, bool* result) { if (result == nullptr) { return MccPZoneErrorCode::ERROR_NULLPTR; } MccGenXY xy; if (coords.pairKind() != MccCoordPairKind::COORDS_KIND_XY) { auto err = _correctForPCM(coords, &xy); if (err) { return err; } } else { // 'coords' is interpretated as hardware (encoder readout) coordinates coords.to(xy); } double x = xy.x(); *result = (x > _maxLimit) || (x < _minLimit); return MccPZoneErrorCode::ERROR_OK; } // time to reach maximal limit error_t timeToPZone(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time) { return _computeTime(coords, res_time, false); } // time to reach minimal limit error_t timeFromPZone(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time) { return _computeTime(coords, res_time, true); } protected: double _minLimit, _maxLimit; std::function _correctForPCM{}; error_t _computeTime(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time, bool from_time) { using res_t = std::remove_cvref_t; using period_t = typename res_t::period; double time_ang; if (res_time == nullptr) { return MccPZoneErrorCode::ERROR_NULLPTR; } MccGenXY xy; if (coords.pairKind() != MccCoordPairKind::COORDS_KIND_XY) { auto err = _correctForPCM(coords, &xy); if (err) { return err; } } else { // 'coords' is interpretated as hardware (encoder readout) coordinates coords.to(xy); } double x = xy.x(); if constexpr (AXIS_KIND == MccCoordKind::COORDS_KIND_HA_OBS) { if (from_time) { // timeFromPZone time_ang = (_minLimit - x) / MCC_SIDERAL_TO_UT1_RATIO; // to UT1 scale } else { // timeToPZone time_ang = (_maxLimit - x) / MCC_SIDERAL_TO_UT1_RATIO; // to UT1 scale } } else if constexpr (AXIS_KIND == MccCoordKind::COORDS_KIND_AZ) { } std::chrono::nanoseconds ns{ static_cast(time_ang * 43200.0 / std::numbers::pi * 1.0E9)}; period_t rat; *res_time = res_t{static_cast(time_ang * 43200.0 / std::numbers::pi * rat.den / rat.num)}; return MccPZoneErrorCode::ERROR_OK; } }; } // namespace mcc::impl