#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* MOUNT TELEMETRY OBJECT POSSIBLE GENERIC IMPLEMENTATION */ #include #include #include "mcc_mount_telemetry_astrom.h" namespace mcc { enum class MccMountTelemetryErrorCode : int { ERROR_OK, ERROR_HARDWARE }; /* error category definition */ // error category struct MccMountTelemetryCategory : public std::error_category { MccMountTelemetryCategory() : std::error_category() {} const char* name() const noexcept { return "ADC_GENERIC_DEVICE"; } std::string message(int ec) const { MccMountTelemetryErrorCode err = static_cast(ec); switch (err) { case MccMountTelemetryErrorCode::ERROR_OK: return "OK"; case MccMountTelemetryErrorCode::ERROR_HARDWARE: return "hardware request failed"; default: return "UNKNOWN"; } } static const MccMountTelemetryAstromTransformCategory& get() { static const MccMountTelemetryAstromTransformCategory constInst; return constInst; } }; inline std::error_code make_error_code(MccMountTelemetryErrorCode ec) { return std::error_code(static_cast(ec), MccMountTelemetryCategory::get()); } } // namespace mcc namespace std { template <> class is_error_code_enum : public true_type { }; } // namespace std namespace mcc { /* DEFAULT TELEMETRY DATA CLASS */ template struct MccMountTelemetryData { typedef typename ASTROM_ENGINE_T::coord_t coord_t; typedef typename ASTROM_ENGINE_T::time_point_t time_point_t; typedef typename ASTROM_ENGINE_T::juldate_t juldate_t; typedef typename ASTROM_ENGINE_T::sideral_time_t sideral_time_t; typedef typename ASTROM_ENGINE_T::pa_t pa_t; // time-related time_point_t time_point; // time point of measurements, UTC juldate_t jd; // Julian date sideral_time_t siderTime; // local apperant sideral time // target sky point ICRS and current coordinates coord_t tagRA_ICRS, tagDEC_ICRS; coord_t tagRA, tagDEC; coord_t tagHA; coord_t tagAZ, tagALT; pa_t tagPA; // encoder-measured current mount coordinates coord_t mntRA, mntDEC; coord_t mntHA; coord_t mntAZ, mntALT; pa_t mntPA; // encoder-measured (non-corrected for PCS) current mount position and axes rates // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one coord_t mntPosX, mntPosY; coord_t mntRateX, mntRateY; // current refraction coefficients typename PEC_T::pec_result_t currRefrCoeffs; // current refraction correction (for mntALT) coord_t currRefr; // PEC (pointing error correction): // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one coord_t pecX, pecY; }; /* DEFAULT TELEMETRY MANAGER CLASS */ template > DATA_T = MccMountTelemetryData> class MccMountTelemetry : public MccMountTelemetryAstromTransform { typedef MccMountTelemetryAstromTransform base_t; struct point_t { MccCoordPairKind coordPairKind; typename ASTROM_ENGINE_T::coord_t x, y; }; public: typedef ASTROM_ENGINE_T astrom_engine_t; typedef PEC_T pec_t; typedef HARDWARE_T hardware_t; typedef DATA_T mount_telemetry_data_t; typedef std::error_code error_t; typedef std::function update_callback_func_t; typedef std::list update_callback_container_t; MccMountTelemetry(astrom_engine_t& astrom_engine, pec_t& pec, hardware_t& hardware) : base_t(astrom_engine, pec), _hardware(hardware) { } virtual ~MccMountTelemetry() = default; // set current target sky point coordinates (update all data) template error_t setTarget(PointT tag_point) { std::lock_guard lock{_updateMutex}; auto err = this->toICRS(tag_point, _data.utc, _data.tagRA_ICRS, _data.tagDEC_ICRS); if (!err) { err = update(); } return err; } error_t update() { mount_telemetry_data_t current_data; typename hardware_t::axes_pos_t ax_pos; auto err = _hardware.getPos(ax_pos); if (err) { if constexpr (std::same_as) { return err; } return MccMountTelemetryErrorCode::ERROR_HARDWARE; } error_t res_err; typename astrom_engine_t::error_t ast_err{}; // times current_data.utc = ax_pos.time_point; // Julian date ast_err = this->_astromEngine.greg2jul(current_data.time_point, current_data.jd); if (!ast_err) { // local apparent sideral time ast_err = this->_astromEngine.apparentSiderTime(current_data.jd, current_data.siderTime, true); } if (ast_err) { if constexpr (std::same_as) { return ast_err; } return MccMountTelemetryAstromTransformErrorCode::ERROR_ASTROMETRY_COMP; } // encoder coordinates current_data.mntPosX = static_cast(ax_pos.x); current_data.mntPosY = static_cast(ax_pos.y); current_data.mntRateX = static_cast(ax_pos.xrate); current_data.mntRateY = static_cast(ax_pos.yrate); // correction for PEC if constexpr (base_t::equatorialMount) { res_err = this->toApparent(point_t{.coordPairKind = MccCoordPairKind::COORDS_KIND_XY, .x = current_data.mntPosX, .y = current_data.mntPosY}, current_data.utc, current_data.mntHA, current_data.mntDEC); if (!res_err) { ast_err = this->_astromEngine.hadec2azalt(current_data.mntHA, current_data.mntDEC, current_data.mntAZ, current_data.mntALT); } } else if constexpr (base_t::altAzMount) { res_err = this->toApparent(point_t{.coordPairKind = MccCoordPairKind::COORDS_KIND_XY, .x = current_data.mntPosX, .y = current_data.mntPosY}, current_data.utc, current_data.mntAZ, current_data.mntALT); if (!res_err) { ast_err = this->_astromEngine.azalt2hadec(current_data.mntAZ, current_data.mntALT, current_data.mntHA, current_data.mntDEC); } } else { static_assert(false, "UNSUPPORTED MOUNT TYPE!"); } if (res_err) { return res_err; } // PA angle if (!ast_err) { ast_err = this->_astromEngine.hadec2pa(current_data.mntHA, current_data.mntDEC, current_data.mntPA); if (!ast_err) { // target coordinates (assuming its ICRS RA-DEC are already given or computed) typename mount_telemetry_data_t::eo_t eo; ast_err = this->_astromEngine.icrs2obs(current_data.tagRA_ICRS, current_data.tagDEC_ICRS, current_data.jd, current_data.tagRA, current_data.tagDEC, current_data.tagHA, current_data.tagAZ, current_data.tagALT, eo); if (!ast_err) { current_data.mntRA = current_data.siderTime - current_data.mntHA + eo; // CIO based mount RA ast_err = this->_astromEngine.hadec2pa(current_data.tagHA, current_data.tagDEC, current_data.tagPA); } } } if (ast_err) { if constexpr (std::same_as) { return ast_err; } return MccMountTelemetryAstromTransformErrorCode::ERROR_ASTROMETRY_COMP; } std::lock_guard lock{_updateMutex}; _data = std::move(current_data); std::lock_guard cl_lock{_callbackMutex}; // callbacks will be invoked with its own copy of telemetry data! for (auto& func : _callbackFuncs) { std::thread t([this, &func] { auto d = _data; func(std::move(d)); }); t.detach(); } return MccMountTelemetryErrorCode::ERROR_OK; } update_callback_container_t::iterator addCallbackFunc(update_callback_func_t func) { std::lock_guard lock{_callbackMutex}; return _callbackFuncs.emplace_back(std::move(func)); } void delCallbackFunc(update_callback_container_t::iterator iter) { std::lock_guard lock{_callbackMutex}; if (_callbackFuncs.size()) { _callbackFuncs.erase(iter); } } void clearCallbackFuncs() { std::lock_guard lock{_callbackMutex}; _callbackFuncs.clear(); } error_t data(mount_telemetry_data_t& data) { std::lock_guard lock{_updateMutex}; data = std::move(_data); return MccMountTelemetryErrorCode::ERROR_OK; } protected: mount_telemetry_data_t _data{}; hardware_t& _hardware; update_callback_container_t _callbackFuncs{}; std::mutex _updateMutex; std::mutex _callbackMutex; }; } // namespace mcc