From 2c7d56399417e90e7a75dd432b13fad96639a2de Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Fri, 19 Dec 2025 12:01:36 +0300 Subject: [PATCH] ... --- mcc/mcc_generics.h | 27 ++++- mcc/mcc_telemetry.h | 287 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+), 2 deletions(-) diff --git a/mcc/mcc_generics.h b/mcc/mcc_generics.h index 1b0528c..a082ab4 100644 --- a/mcc/mcc_generics.h +++ b/mcc/mcc_generics.h @@ -517,7 +517,7 @@ concept mcc_hardware_c = requires(T t, const T t_const) { // 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 + // possible tunning of hardware hardwareSetState-related commands and detect stop end error states from hardware // // 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, @@ -598,7 +598,7 @@ concept mcc_telemetry_data_c = mcc_eqt_hrz_coord_c && std::default_initializa requires mcc_eqt_hrz_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 + // t.* from mcc_eqt_hrz_coord_c are current mount coordinates requires mcc_angle_c; // speed along X from hardware encoder requires mcc_angle_c; // speed along Y from hardware encoder @@ -613,6 +613,29 @@ concept mcc_telemetry_data_c = mcc_eqt_hrz_coord_c && std::default_initializa requires mcc_angle_c; }; +template +concept mcc_tlm_data_c = mcc_eqt_hrz_coord_c && std::default_initializable && requires(T t) { + // user entered target coordinates + requires mcc_celestial_point_c; + + // target target coordinates + requires mcc_eqt_hrz_coord_c; + + // t.X and t.Y (from mcc_celestial_point_c) are encoder coordinates + // t.* from mcc_eqt_hrz_coord_c are current mount coordinates + + requires mcc_PCM_result_c; // PCM correction + + // atmospheric refraction correction for current zenithal distance + requires mcc_angle_c; // for current .ZD + + // equation of the origins (ERA-GST) + requires mcc_angle_c; + + // local sideral time + requires mcc_angle_c; +}; + static constexpr void mcc_copy_telemetry_data(mcc_telemetry_data_c auto const& from_pt, mcc_telemetry_data_c auto* to_pt) diff --git a/mcc/mcc_telemetry.h b/mcc/mcc_telemetry.h index b06710b..88a850c 100644 --- a/mcc/mcc_telemetry.h +++ b/mcc/mcc_telemetry.h @@ -98,6 +98,293 @@ inline std::error_code make_error_code(MccTelemetryErrorCode ec) +template +class MccMountTelemetry : public mcc_telemetry_interface_t +{ +public: + static constexpr std::chrono::milliseconds defaultDataUpdatingTimeout{100}; + + typedef std::error_code error_t; + + struct telemetry_data_t : MccEqtHrzCoords { + MccCelestialPoint entered_target{}; + MccEqtHrzCoords target; + HARDWARE_T::hardware_state_t hardware; + + double JD; + double LST; + double EO; + double refCorr; + }; + + + template + MccMountTelemetry(HARDWARE_T* hardware, PCM_T* pcm, CCTE_T* ccte) + { + _data.entered_target.pair_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS; + + if constexpr (mccIsEquatorialMount(PCM_T::mountType)) { + _data.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; + _data.target.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; + } else if constexpr (mccIsAltAzMount(PCM_T::mountType)) { + _data.pair_kind = MccCoordPairKind::COORDS_KIND_AZZD; + _data.target.pair_kind = MccCoordPairKind::COORDS_KIND_AZZD; + } else { + static_assert(false, "UNKNOWN MOUNT TYPE!"); + } + + // target coordinates updater + _updateTargetFunc = [hardware, pcm, ccte, this](std::stop_token stop_token) -> error_t { + MccPCMResult pcm_res; + + mcc_tp2tp(_data.time_point, _data.target.time_point); + mcc_tp2tp(_data.time_point, _data.entered_target.time_point); + + if (_data.entered_target.pair_kind == + MccCoordPairKind::COORDS_KIND_XY) { // compute corresponded observed coordinates + auto pcm_err = pcm->computePCM(_data.entered_target, &pcm_res, &_data.target); + if (pcm_err) { + return mcc_deduce_error_code(pcm_err, MccTelemetryErrorCode::ERROR_PCM_COMP); + } + + _data.target.X = _data.entered_target.X; + _data.target.Y = _data.entered_target.Y; + + if (stop_token.stop_requested()) { + return MccTelemetryErrorCode::ERROR_UPDATE_STOPPED; + } + + if constexpr (mccIsEquatorialMount(PCM_T::mountType)) { + _data.target.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; + } else if constexpr (mccIsAltAzMount(PCM_T::mountType)) { + _data.target.pair_kind = MccCoordPairKind::COORDS_KIND_AZZD; + } else { + static_assert(false, "UNKNOWN MOUNT TYPE!!!"); + } + + auto ccte_err = ccte->transformCoordinates(_data.target, &_data.target); + if (ccte_err) { + return mcc_deduce_error_code(ccte_err, MccTelemetryErrorCode::ERROR_COORD_TRANSFORM); + } + } else { + _data.target.pair_kind = _data.entered_target.pair_kind; + + auto ccte_err = ccte->transformCoordinates(_data.entered_target, &_data.target); + if (ccte_err) { + return mcc_deduce_error_code(ccte_err, MccTelemetryErrorCode::ERROR_COORD_TRANSFORM); + } + + auto pcm_err = pcm->computeReversePCM(_data.target, &pcm_res, &_data.target); + if (pcm_err) { + return mcc_deduce_error_code(pcm_err, MccTelemetryErrorCode::ERROR_PCM_COMP); + } + } + + return MccTelemetryErrorCode::ERROR_OK; + }; + + // mount current coordinates updater + _updateFunc = [hardware, pcm, ccte, this](std::stop_token stop_token) -> std::error_code { + // std::lock_guard lock{*_updateMutex}; + + // first, update mount quantities + auto hw_err = hardware->hardwareGetState(&_data.hardware); + if (hw_err) { + return mcc_deduce_error_code(hw_err, MccTelemetryErrorCode::ERROR_HARDWARE_GETPOS); + } + + + // if (stop_token.stop_requested()) { + // return MccTelemetryErrorCode::ERROR_UPDATE_STOPPED; + // } + + double eo; + + mcc_tp2tp(_data.hardware.time_point, _data.time_point); + _data.X = _data.hardware.X; + _data.Y = _data.hardware.Y; + + auto ccte_err = _data->timepointToJulday(_data.time_point, &_data.JD); + if (!ccte_err) { + if (stop_token.stop_requested()) { + return MccTelemetryErrorCode::ERROR_UPDATE_STOPPED; + } + + ccte_err = ccte_err->juldayToAppSideral(_data.JD, &_data.LST, true); + if (!ccte_err) { + ccte_err = ccte_err->equationOrigins(_data.JD, &eo); + _data.EO = eo; + } + } + + if (ccte_err) { + return mcc_deduce_error_code(ccte_err, MccTelemetryErrorCode::ERROR_COORD_TRANSFORM); + } + + // if (stop_token.stop_requested()) { + // return MccTelemetryErrorCode::ERROR_UPDATE_STOPPED; + // } + + + MccCelestialPoint pt; + mcc_tp2tp(_data.time_point, pt.time_point); + + auto pcm_err = pcm->computePCM(_data, &_data, &pt); + if (pcm_err) { + return mcc_deduce_error_code(pcm_err, MccTelemetryErrorCode::ERROR_PCM_COMP); + } + + if constexpr (mccIsEquatorialMount(PCM_T::mountType)) { + pt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; + _data.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; + } else if constexpr (mccIsAltAzMount(PCM_T::mountType)) { + pt.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT; + _data.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT; + } else { + static_assert(false, "UNKNOWN MOUNT TYPE!"); + } + + ccte_err = ccte->transformCoordinates(pt, &_data); + if (!ccte_err) { + ccte_err = ccte->refractionCorrection(_data, &_data.refCorr); + if (!ccte_err) { + return _updateTargetFunc(stop_token); + } else { + return mcc_deduce_error_code(ccte_err, MccTelemetryErrorCode::ERROR_COORD_TRANSFORM); + } + } else { + return mcc_deduce_error_code(ccte_err, MccTelemetryErrorCode::ERROR_COORD_TRANSFORM); + } + + return MccTelemetryErrorCode::ERROR_OK; + }; + + + _internalUpdatingStopSource = std::stop_source{}; + + _dataUpdatingRequested->clear(); + _dataUpdatingStart->clear(); + + // start thread for data updating + _updatingFuture = std::async( + std::launch::async, + [this](std::stop_token stoken) { + while (!stoken.stop_requested()) { + _dataUpdatingRequested->wait(false); + + if (!stoken.stop_requested()) { + std::lock_guard lock{*_timeoutMutex}; + + _dataUpdatingStart->test_and_set(); + _dataUpdatingStart->notify_all(); + + _lastUpdateError = _updateFunc(stoken); + + _dataUpdatingStart->clear(); + _dataUpdatingRequested->clear(); + } + } + }, + _internalUpdatingStopSource.get_token()); + } + + + virtual ~MccMountTelemetry() = default; + + + void setDataUpdatingTimeout(traits::mcc_time_duration_c auto const& timeout) + { + _dataUpdatingTimeout = std::chrono::duration_cast(timeout); + } + + template + DT getDataUpdatingTimeout() const + { + return std::chrono::duration_cast
(_dataUpdatingTimeout); + } + + std::chrono::milliseconds getDataUpdatingTimeout() const + { + return getDataUpdatingTimeout(); + } + + + error_t telemetryData(telemetry_data_t* tdata) + { + if (tdata == nullptr) { + return MccTelemetryErrorCode::ERROR_NULLPTR; + } + + // trigger updating + _dataUpdatingRequested->test_and_set(); + _dataUpdatingRequested->notify_one(); + + // wait for updating start + _dataUpdatingStart->wait(false); + if (_timeoutMutex->try_lock_for(_dataUpdatingTimeout)) { + _timeoutMutex->unlock(); + } else { + _lastUpdateError = MccTelemetryErrorCode::ERROR_DATA_TIMEOUT; + } + + if (!_lastUpdateError) { + std::lock_guard lock(*_updateMutex); + *tdata = _data; + } + + return _lastUpdateError; + } + + error_t setPointingTarget(mcc_celestial_point_c auto const& cp) + { + std::lock_guard lock(*_updateMutex); + + mcc_copy_celestial_point(cp, &_data.entered_target); + + return MccTelemetryErrorCode::ERROR_OK; + } + +protected: + telemetry_data_t _data; + + std ::function _updateTargetFunc{}; + std::function _updateFunc{}; + + std::unique_ptr _updateMutex; + std::unique_ptr _updateCondVar; + + std::stop_source _internalUpdatingStopSource{}; + + std::future _updatingFuture{}; + std::unique_ptr _dataUpdatingRequested{new std::atomic_flag{}}; + std::unique_ptr _dataUpdatingStart{new std::atomic_flag{}}; + std::unique_ptr _timeoutMutex{new std::timed_mutex()}; + + std::chrono::nanoseconds _dataUpdatingTimeout{defaultDataUpdatingTimeout}; + + error_t _lastUpdateError{MccTelemetryErrorCode::ERROR_OK}; + + void updateLoop(std::stop_token stoken) + { + while (!stoken.stop_requested()) { + _dataUpdatingRequested->wait(false); + + if (!stoken.stop_requested()) { + std::lock_guard lock{*_timeoutMutex}; + + _dataUpdatingStart->test_and_set(); + _dataUpdatingStart->notify_all(); + + _lastUpdateError = _updateFunc(stoken); + + _dataUpdatingStart->clear(); + _dataUpdatingRequested->clear(); + } + } + } +}; + + class MccTelemetry : public mcc_telemetry_interface_t { protected: