#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* MOUNT TELEMETRY OBJECT POSSIBLE GENERIC IMPLEMENTATION */ #include #include "mcc_mount_telemetry_astrom.h" namespace mcc { enum class MccMountTelemetryErrorCode : int { ERROR_OK, ERROR_HARDWARE, ERROR_DATA_TIMEOUT }; /* 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"; case MccMountTelemetryErrorCode::ERROR_DATA_TIMEOUT: return "an timeout occured while waiting for new data"; 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 ASTROM_ENGINE_T::refract_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; struct coord_diff_t { typename ASTROM_ENGINE_T::coord_t xdiff, ydiff, r2; }; MccMountTelemetry(astrom_engine_t* astrom_engine, pec_t* pec, hardware_t* hardware) : base_t(astrom_engine, pec), _hardware(hardware) { } MccMountTelemetry(MccMountTelemetry&& other) : base_t(std::move(other)), _data(std::move(other._data)), _hardware(other._hardware), _updateMutex(std::move(other._updateMutex)), _updateCondVar(std::move(other._updateCondVar)) { } MccMountTelemetry& operator=(MccMountTelemetry&& other) { if (this == &other) { return *this; } base_t::operator=(std::move(other)); _data = std::move(other._data); _hardware = other._hardware; _updateMutex = std::move(other._updateMutex); _updateCondVar = std::move(other._updateCondVar); return *this; } 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; } // target - mount coordinate difference auto targetToMountDiff() { std::lock_guard lk(*_updateMutex); coord_diff_t result; if constexpr (mccIsEquatorialMount(pec_t::mountType)) { result.xdiff = _data.tagRA - _data.mntHA; result.ydiff = _data.tagDEC - _data.mntDEC; } else if constexpr (mccIsAltAzMount(pec_t::mountType)) { result.xdiff = _data.tagAZ - _data.mntAZ; result.ydiff = _data.tagALT - _data.mntALT; } else { static_assert(false, "UNSUPPORTED MOUNT TYPE!"); } result.r2 = result.xdiff * result.xdiff + result.ydiff * result.ydiff; return result; } // update telemetry right now 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); // compute corrections for PEC and celestial apparent coordinates 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) { current_data.pecX = current_data.mntHA - current_data.mntPosX; current_data.pecY = current_data.mntDEC - current_data.mntPosY; 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) { current_data.pecX = current_data.mntAZ - current_data.mntPosX; current_data.pecY = current_data.mntALT - current_data.mntPosY; 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 astrom_engine_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); // notify all threads for new telemetry data _updateCondVar->notify_all(); return MccMountTelemetryErrorCode::ERROR_OK; } error_t data(mount_telemetry_data_t& data) { std::lock_guard lock{*_updateMutex}; data = _data; return MccMountTelemetryErrorCode::ERROR_OK; } // wait (block current thread) until data is updated or given timeout occurs template error_t waitForUpdatedData(mount_telemetry_data_t& data, const DT& timeout) { auto timeout_tp = std::chrono::steady_clock::now() + timeout; std::unique_lock lk(*_updateMutex); auto res = _updateCondVar->wait_until( lk, timeout_tp, [last_time_point = _data.time_point, this]() { return last_time_point < _data.timepoint; }); if (res == std::cv_status::timeout) { return MccMountTelemetryErrorCode::ERROR_DATA_TIMEOUT; } data = _data; return MccMountTelemetryErrorCode::ERROR_OK; } protected: mount_telemetry_data_t _data{}; hardware_t* _hardware; std::unique_ptr _updateMutex; std::unique_ptr _updateCondVar; }; namespace traits { template concept mcc_mount_default_telemetry_c = requires { mcc_astrom_engine_c; mcc_mount_pec_c; mcc_mount_hardware_c; mcc_mount_telemetry_data_c; requires std::derived_from>; }; } // namespace traits } // namespace mcc