From 6315d5e18eabc0132e5c77c0d256c9d48991a02e Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Wed, 6 Aug 2025 02:06:20 +0300 Subject: [PATCH] ... --- cxx/CMakeLists.txt | 7 +- cxx/asibfm700_common.h | 9 +- cxx/asibfm700_hardware.cpp | 15 +- cxx/asibfm700_hardware.h | 2 +- cxx/mcc_fsm_mount.h | 105 +++++++++-- cxx/mcc_generic_mount.h | 258 ++++++++++++++++++++++++++++ cxx/mcc_guiding_model.h | 23 +++ cxx/mcc_mount_astro_erfa.h | 63 +++---- cxx/mcc_mount_concepts.h | 42 +++-- cxx/mcc_mount_pec.h | 111 ++++-------- cxx/mcc_mount_pz.h | 2 - cxx/mcc_mount_telemetry.h | 52 ++++-- cxx/mcc_mount_telemetry_astrom.h | 45 ++++- cxx/mcc_slew_guiding_model_common.h | 24 +++ cxx/mcc_slew_model.h | 24 ++- 15 files changed, 630 insertions(+), 152 deletions(-) create mode 100644 cxx/mcc_generic_mount.h diff --git a/cxx/CMakeLists.txt b/cxx/CMakeLists.txt index 3c61586..9672fe2 100644 --- a/cxx/CMakeLists.txt +++ b/cxx/CMakeLists.txt @@ -123,9 +123,12 @@ set(CNTR_PROTO_LIB comm_proto) add_library(${CNTR_PROTO_LIB} STATIC ${CNTR_PROTO_LIB_SRC}) -set(MCC_LIBRARY_SRC mcc_mount_concepts.h mcc_fsm_mount.h mcc_mount_coord.h mcc_mount_events_states.h mcc_finite_state_machine.h +include_directories(${FITPACK_INCLUDE_DIR}) + + +set(MCC_LIBRARY_SRC mcc_mount_concepts.h mcc_fsm_mount.h mcc_generic_mount.h mcc_mount_coord.h mcc_mount_events_states.h mcc_finite_state_machine.h mcc_mount_pec.h mcc_mount_pz.h mcc_traits.h mcc_mount_telemetry_astrom.h mcc_mount_telemetry.h mcc_mount_config.h mcc_mount_astro_erfa.h - mcc_astrom_iers.h mcc_astrom_iers_default.h mcc_slew_model.h mcc_guiding_model.h mcc_utils.h mcc_spdlog.h) + mcc_astrom_iers.h mcc_astrom_iers_default.h mcc_slew_model.h mcc_guiding_model.h mcc_slew_guiding_model_common.h mcc_utils.h mcc_spdlog.h) set(MCC_LIBRARY mcc) add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC}) target_compile_features(${MCC_LIBRARY} INTERFACE cxx_std_23) diff --git a/cxx/asibfm700_common.h b/cxx/asibfm700_common.h index 76872a7..9a14e5f 100644 --- a/cxx/asibfm700_common.h +++ b/cxx/asibfm700_common.h @@ -29,6 +29,9 @@ typedef mcc::MccMountTelemetry AsibFM700Telemetry; +static_assert(std::movable); +static_assert(std::movable); +static_assert(std::movable>); // typedef mcc::MccSimpleSlewModel<> AsibFM700SlewModel; // typedef mcc::MccSimpleGuidingModel<> AsibFM700GuidingModel; @@ -63,7 +66,7 @@ static_assert(mcc::traits::mcc_mount_controls_c> { + class InitState : public AsibFM700Mount::MccMountEventBase + { + }; + public: }; diff --git a/cxx/asibfm700_hardware.cpp b/cxx/asibfm700_hardware.cpp index 7f2b033..7affffb 100644 --- a/cxx/asibfm700_hardware.cpp +++ b/cxx/asibfm700_hardware.cpp @@ -100,19 +100,26 @@ AsibFM700Hardware::error_t AsibFM700Hardware::setPos(AsibFM700Hardware::axes_pos double tp = std::chrono::duration(pos.time_point.time_since_epoch()).count(); coordval_pair_t hw_posval{.X{.val = pos.x, .t = tp}, .Y{.val = pos.y, .t = tp}}; + // + // WARNING: The LibSidservo API was chagned! hw_pos is endpoint where mount must + // go "after" slewing + // + switch (pos.moving_type) { case hw_moving_type_t::HW_MOVE_SLEWING: // slew mount if (pos.moveAndStop) { - err = static_cast(Mount.moveTo(&hw_pos)); + // err = static_cast(Mount.moveTo(&hw_pos)); + err = static_cast(Mount.correctTo(&hw_posval, &hw_pos)); } else { - err = static_cast(Mount.slewTo(&hw_pos, pos.flags)); + // err = static_cast(Mount.slewTo(&hw_pos, pos.flags)); + err = static_cast(Mount.correctTo(&hw_posval, &hw_pos)); } break; case hw_moving_type_t::HW_MOVE_ADJUSTING: // corrections at the end of slewing - err = static_cast(Mount.correctTo(&hw_posval)); + err = static_cast(Mount.correctTo(&hw_posval, &hw_pos)); break; case hw_moving_type_t::HW_MOVE_GUIDING: // interpretate as guiding correction - err = static_cast(Mount.correctTo(&hw_posval)); + err = static_cast(Mount.correctTo(&hw_posval, &hw_pos)); break; default: break; diff --git a/cxx/asibfm700_hardware.h b/cxx/asibfm700_hardware.h index 2588a73..0f577cb 100644 --- a/cxx/asibfm700_hardware.h +++ b/cxx/asibfm700_hardware.h @@ -79,7 +79,7 @@ public: coord_t xrate, yrate; hw_moving_type_t moving_type{hw_moving_type_t::HW_MOVE_TRACKING}; - slewflags_t flags; + // slewflags_t flags; bool moveAndStop{false}; }; diff --git a/cxx/mcc_fsm_mount.h b/cxx/mcc_fsm_mount.h index 11197cb..8018ee2 100644 --- a/cxx/mcc_fsm_mount.h +++ b/cxx/mcc_fsm_mount.h @@ -23,12 +23,33 @@ namespace mcc { +// adaptor class for prohibited zones +template +struct MccPZoneWrapper { + // a type to which the result of calling prohibited zone class methods 'timeTo' and 'timeFrom' will be converted + typedef std::chrono::duration pz_duration_t; // seconds as floating-point number + + static constexpr pz_duration_t infiniteDuration{std::numeric_limits::infinity()}; + static constexpr pz_duration_t zeroDuration{0.0}; + + typedef std::function pz_inzone_func_t; + typedef std::function pz_timeto_func_t; + typedef std::function pz_timefrom_func_t; + + MccCoordPairKind coordPairKind; + const std::function name; + pz_inzone_func_t inZone; + pz_timeto_func_t timeTo; + pz_timefrom_func_t timeFrom; +}; + + template class MccMount : public fsm::MccFiniteStateMachine, public utils::MccSpdlogLogger, protected MOUNT_CONTROLS { // declare these classes as friends to allow them access protected members 'slewModel' and 'guidingModel' - friend class MccMountStateSlew>; - friend class MccMountStateGuiding>; + // friend class MccMountStateSlew>; + // friend class MccMountStateGuiding>; public: typedef MOUNT_CONTROLS mount_controls_t; @@ -43,6 +64,47 @@ public: // typedef typename slew_model_t::slew_params_t slew_params_t; + /* base classes for event and state */ + + class MccMountEventBase + { + protected: + MccMount& _mount; + + public: + MccMountEventBase(MccMount& mount) : _mount(mount) {} + virtual ~MccMountEventBase() = default; + + MccMount& mount() { return _mount; } + }; + + // The implementation of FSM (see mcc_finite_state_machine.h) requires + // state to be a default constructible class, so one needs to hold + // a pointer to mount class to guarantee access to its private or protected + // members and methods + class MccMountStateBase + { + protected: + // a pointer to mount class to allow access to private/protected + // members/methods of MccMount class from descendent state-classes. + // the memory address can be obtained from a call of + // MccMountEventBase::mount method (see above) + MccMount* _mount; + + public: + MccMountStateBase() = default; + + MccMountStateBase(const MccMountStateBase&) = delete; + MccMountStateBase& operator=(const MccMountStateBase&) = delete; + + // just as an example (ugly but I still cannot find a solution) + // template EvT> + // void enter(EvT& event) + // { + // _mount = &event.mount(); + // _mount->forEachPZone(...); + // } + }; /* constructors and destructor */ @@ -64,6 +126,8 @@ public: logDebug("{}", std::string(r.begin(), r.end())); } + MccMount(const MccMount&) = delete; + MccMount& operator=(const MccMount&) = delete; virtual ~MccMount() { logDebug("Delete MccMount class instance: thread = {}", getThreadId()); } @@ -103,15 +167,34 @@ public: { auto zone_ptr = std::make_shared(std::move(zone)); + using d_t = typename MccPZoneWrapper::pz_duration_t; + _pzFuncs.emplace_back( {.coordPairKind = ZT::zoneCoordPairKind, - .name = std::format("{}", zone_ptr->name()), - .inZoneFunc = [zone_ptr, - this](const mount_telemetry_data_t& tmry_data) { return zone_ptr->inZone(tmry_data); }, - .timeToFunc = [zone_ptr, - this](const mount_telemetry_data_t& tmry_data) { return zone_ptr->timeTo(tmry_data); }, - .timeFromFunc = - [zone_ptr, this](const mount_telemetry_data_t& tmry_data) { return zone_ptr->timeFrom(tmry_data); }}); + .name = [zone_ptr]() { return std::format("{}", zone_ptr->name()); }, + .inZone = [zone_ptr](const mount_telemetry_data_t& tmry_data) { return zone_ptr->inZone(tmry_data); }, + .timeTo = + [zone_ptr](const mount_telemetry_data_t& tmry_data) { + auto d = zone_ptr->timeTo(tmry_data); + + if (d == ZT::infiniteDuration) { + return MccPZoneWrapper::infiniteDuration; + } else if (d == ZT::zeroDuration) { + return MccPZoneWrapper::zeroDuration; + } + return std::chrono::duration_cast(d); + }, + .timeFrom = + [zone_ptr](const mount_telemetry_data_t& tmry_data) { + auto d = zone_ptr->timeFrom(tmry_data); + + if (d == ZT::infiniteDuration) { + return MccPZoneWrapper::infiniteDuration; + } else if (d == ZT::zeroDuration) { + return MccPZoneWrapper::zeroDuration; + } + return std::chrono::duration_cast(d); + }}); if constexpr (sizeof...(ZTs)) { @@ -144,8 +227,8 @@ protected: pz_timefrom_func_t timeFromFunc; }; - std::vector _pzFuncs{}; - + // std::vector _pzFuncs{}; + std::vector> _pzFuncs{}; template ... ZTs> auto forEachPZone(FuncT&& func, std::tuple& zones) diff --git a/cxx/mcc_generic_mount.h b/cxx/mcc_generic_mount.h new file mode 100644 index 0000000..0272eb8 --- /dev/null +++ b/cxx/mcc_generic_mount.h @@ -0,0 +1,258 @@ +#pragma once + + +/* MOUNT CONTROL COMPONENTS LIBRARY */ + + + +/* A GENERIC MOUNT CLASS IMPLEMENTATION */ + + +#include +#include "mcc_mount_concepts.h" + +namespace mcc +{ + +// adaptor class for prohibited zones +template +struct MccPZoneWrapper { + // a type to which the result of calling prohibited zone class methods 'timeTo' and 'timeFrom' will be converted + typedef std::chrono::duration duration_t; // seconds as floating-point number + + static constexpr duration_t infiniteDuration{std::numeric_limits::infinity()}; + static constexpr duration_t zeroDuration{0.0}; + + typedef std::function pz_inzone_func_t; + typedef std::function pz_timeto_func_t; + typedef std::function pz_timefrom_func_t; + + MccCoordPairKind coordPairKind; + const std::function name; + pz_inzone_func_t inZone; + pz_timeto_func_t timeTo; + pz_timefrom_func_t timeFrom; +}; + + +template +class MccGenericMount : public LOGGER_T +{ + typedef LOGGER_T logger_t; + + using logger_t::logDebug; + using logger_t::logError; + using logger_t::logInfo; + using logger_t::logWarn; + + typedef ASTROM_ENGINE_T astrom_engine_t; + typedef HARDWARE_T hardware_t; + typedef PEC_T pec_t; + typedef TELEMETRY_T telemetry_t; + typedef typename TELEMETRY_T::telemetry_data_t telemetry_data_t; + typedef SLEWMODEL_T slew_model_t; + typedef GUIDEMODEL_T guiding_model_t; + + typedef typename MccPZoneWrapper::duration_t pz_duration_t; + + static constexpr MccMountType mountType = pec_t::mountType; + + virtual ~MccGenericMount() = default; + + // get telemetry data + auto mountTelemetryData(telemetry_data_t& data) { return _telemetry.data(data); } + + + /* prohibited zone related public methods */ + + // add zones to mount control system + template ZT, + traits::mcc_prohibited_zone_c... ZTs> + size_t pzAddZone(ZT zone, ZTs... zones) + { + auto zone_ptr = std::make_shared(std::move(zone)); + + using d_t = typename MccPZoneWrapper::duration_t; + + _pzFuncs.emplace_back( + {.coordPairKind = ZT::zoneCoordPairKind, + .name = [zone_ptr]() { return std::format("{}", zone_ptr->name()); }, + .inZone = [zone_ptr](const telemetry_data_t& tmry_data) { return zone_ptr->inZone(tmry_data); }, + .timeTo = + [zone_ptr](const telemetry_data_t& tmry_data) { + auto d = zone_ptr->timeTo(tmry_data); + + if constexpr (std::same_as) { + return d; + } else { + if (d == ZT::infiniteDuration) { + return MccPZoneWrapper::infiniteDuration; + } else if (d == ZT::zeroDuration) { + return MccPZoneWrapper::zeroDuration; + } + return std::chrono::duration_cast(d); + } + }, + .timeFrom = + [zone_ptr](const telemetry_data_t& tmry_data) { + auto d = zone_ptr->timeFrom(tmry_data); + + if constexpr (std::same_as) { + return d; + } else { + if (d == ZT::infiniteDuration) { + return MccPZoneWrapper::infiniteDuration; + } else if (d == ZT::zeroDuration) { + return MccPZoneWrapper::zeroDuration; + } + return std::chrono::duration_cast(d); + } + }}); + + + if constexpr (sizeof...(ZTs)) { + pzAddZone(std::move(zones)...); + } + + return _pzFuncs.size(); + } + + // delete all zones from mount control system + void pzClearZone() + { + // stop mount here?!! + _pzFuncs.clear(); + } + + // prohibited zones timeTo from given time point + template RT, traits::mcc_celestial_point_c CT, traits::mcc_systime_c TPT> + RT pzTimeTo(CT coord, const TPT& time_point) + requires std::convertible_to + { + RT res; + + telemetry_data_t tdata; + typename astrom_engine_t::juldate_t jd; + + _astromEngine.greg2jul(time_point, jd); + + if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_XY) { // from encoder's + typename pec_t::pec_result_t pec_res; + auto pec_err = _pec.compute(coord.x, coord.y, pec_res); + if (!pec_err) { + if constexpr (mccIsEquatorialMount(mountType)) { + tdata.mntHA = coord.x + pec_res.dx; + tdata.mntDEC = coord.y + pec_res.dy; + _astromEngine.hadec2azalt(tdata.mntHA, tdata.mntDEC, tdata.mntAZ, tdata.mntALT); + } else if constexpr (mccIsAltAzMount(mountType)) { + tdata.mntAZ = coord.x + pec_res.dx; + tdata.mntALT = coord.y + pec_res.dy; + _astromEngine.azalt2hadec(tdata.mntAZ, tdata.mntALT, tdata.mntHA, tdata.mntDEC); + } + } else { + return res; + } + } else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_APP) { // from app RA-DEC + typename astrom_engine_t::eo_t eo; + typename astrom_engine_t::sideral_time_t lst; + + _astromEngine.apparentSiderTime(jd, lst, true); + _astromEngine.eqOrigins(jd, eo); + + } else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP) { // from app HA-DEC + tdata.mntHA = coord.x; + tdata.mntDEC = coord.y; + _astromEngine.hadec2azalt(tdata.mntHA, tdata.mntDEC, tdata.mntAZ, tdata.mntALT); + } else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // from app AZ-ALT + tdata.mntAZ = coord.x; + tdata.mntALT = coord.y; + _astromEngine.azalt2hadec(tdata.mntAZ, tdata.mntALT, tdata.mntHA, tdata.mntDEC); + } else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZZD) { // from app AZ-ZD + tdata.mntAZ = coord.x; + tdata.mntALT = std::numbers::pi / 2.0 - coord.y; + _astromEngine.azalt2hadec(tdata.mntAZ, tdata.mntALT, tdata.mntHA, tdata.mntDEC); + } else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // from ICRS RA-DEC + typename astrom_engine_t::coord_t az, alt; + typename astrom_engine_t::eo_t eo; + typename astrom_engine_t::juldate_t jd; + + _astromEngine.greg2jul(time_point, jd); + _astromEngine.icrs2obs(coord.x, coord.y, jd, tdata.mntRA, tdata.mntDEC, tdata.mntHA, tdata.mntAZ, + tdata.mntALT, eo); + } else { + return res; + } + } + + template + auto pzTimeTo(CT point, const TPT& time_point) + { + return pzTimeTo>(std::move(point), time_point); + } + + // prohibited zones timeTo from current mount position + template RT, traits::mcc_celestial_point_c CT> + RT pzTimeTo(CT point) + { + RT result; + + telemetry_data_t data; + auto err = _telemetry.data(data); + if (err) { + // log... + return result; + } + + std::ranges::for_each(_pzFuncs, + [&result, &data](auto& funcs) { std::back_inserter(result) = funcs.timeTo(data); }); + } + + template + auto pzTimeTo(CT point) + { + return pzTimeTo>(std::move(point)); + } + + // prohibited zone timeFrom + +protected: + ASTROM_ENGINE_T _astromEngine; + HARDWARE_T _hardware; + PEC_T _pec; + TELEMETRY_T _telemetry; + SLEWMODEL_T _slewModel; + GUIDEMODEL_T _gudingModel; + + std::vector> _pzFuncs{}; +}; + + +namespace traits +{ + +template +concept mcc_generic_mount_c = requires(T t) { + // derived from MccGenericMount + [](MccGenericMount*) {}(&t); + + { t.slewMount(std::declval()) }; + { t.startGuiding(std::declval()) }; + + { t.stop() }; +}; + + +template +concept mcc_generic_fsm_mount_c = mcc_generic_mount_c && std::derived_from; + + +} // namespace traits + + +} // namespace mcc diff --git a/cxx/mcc_guiding_model.h b/cxx/mcc_guiding_model.h index eeca9bc..6101e98 100644 --- a/cxx/mcc_guiding_model.h +++ b/cxx/mcc_guiding_model.h @@ -164,6 +164,27 @@ public: init(telemetry, hardware, prohibited_zone); } + + MccSimpleGuidingModel(MccSimpleGuidingModel&& other) + : _guidingFunc(std::move(other._guidingFunc)), + _doCorrection(other._doCorrection.load()), + _stopRequested(other._stopRequested.load()) + { + } + + MccSimpleGuidingModel& operator=(MccSimpleGuidingModel&& other) + { + if (this == &other) { + return *this; + } + + _guidingFunc = std::move(other._guidingFunc); + _doCorrection = other._doCorrection.load(); + _stopRequested = other._stopRequested.load(); + + return *this; + } + virtual ~MccSimpleGuidingModel() { logDebug(std::format("Delete 'MccSimpleGuidingModel' class instance ({})", (void*)this)); @@ -333,4 +354,6 @@ protected: } }; +// static_assert(std::movable>); + } // namespace mcc diff --git a/cxx/mcc_mount_astro_erfa.h b/cxx/mcc_mount_astro_erfa.h index b492b6b..3cb3a84 100644 --- a/cxx/mcc_mount_astro_erfa.h +++ b/cxx/mcc_mount_astro_erfa.h @@ -151,15 +151,9 @@ public: double mjd{51544.5}; // J2000.0 }; - // typedef MccAngle coord_t; - // typedef MccAngle sideral_time_t; - // typedef MccAngle pa_t; - // typedef MccAngle eo_t; - - - /* use of the same type fro representation of celestial and geodetic coordinates, celestial angles (e.g. P.A.), - * sideral time */ + /* use of the same type for representation of celestial and geodetic coordinates, celestial angles (e.g. P.A.), + * and sideral time */ typedef AngleT coord_t; typedef AngleT sideral_time_t; @@ -173,40 +167,51 @@ public: MccMountAstromEngineERFA() = default; - MccMountAstromEngineERFA(engine_state_t state) : _currentState(std::move(state)) {} - - MccMountAstromEngineERFA(MccMountAstromEngineERFA&&) = default; - MccMountAstromEngineERFA& operator=(MccMountAstromEngineERFA&&) = default; + MccMountAstromEngineERFA(engine_state_t state) : _currentState(std::move(state)), _stateMutex(new std::mutex) {} MccMountAstromEngineERFA(const MccMountAstromEngineERFA&) = delete; MccMountAstromEngineERFA& operator=(const MccMountAstromEngineERFA&) = delete; + MccMountAstromEngineERFA(MccMountAstromEngineERFA&& other) + : _currentState(std::move(other._currentState)), _stateMutex(std::move(other._stateMutex)) {}; + + MccMountAstromEngineERFA& operator=(MccMountAstromEngineERFA&& other) + { + if (this != &other) { + _currentState = std::move(other._currentState); + _stateMutex = std::move(other._stateMutex); + } + + return *this; + } + + virtual ~MccMountAstromEngineERFA() = default; void setState(engine_state_t state) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; _currentState = std::move(state); } engine_state_t getState() const { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; return _currentState; } void updateMeteo(meteo_t meteo) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; _currentState.meteo = std::move(meteo); } error_t updateLeapSeconds(std::derived_from> auto& stream, char comment_sym = '#') { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; if (!_currentState._leapSeconds.load(stream, comment_sym)) { return MccMountAstromEngineERFAErrorCode::ERROR_UPDATE_LEAPSECONDS; @@ -218,7 +223,7 @@ public: error_t updateLeapSeconds(traits::mcc_input_char_range auto const& filename, char comment_sym = '#') { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; if (!_currentState._leapSeconds.load(filename, comment_sym)) { return MccMountAstromEngineERFAErrorCode::ERROR_UPDATE_LEAPSECONDS; @@ -230,7 +235,7 @@ public: error_t updateBulletinA(std::derived_from> auto& stream, char comment_sym = '*') { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; if (!_currentState._bulletinA.load(stream, comment_sym)) { return MccMountAstromEngineERFAErrorCode::ERROR_UPDATE_BULLETINA; @@ -242,7 +247,7 @@ public: error_t updateBulletinA(traits::mcc_input_char_range auto const& filename, char comment_sym = '*') { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; if (!_currentState._bulletinA.load(filename, comment_sym)) { return MccMountAstromEngineERFAErrorCode::ERROR_UPDATE_BULLETINA; @@ -299,7 +304,7 @@ public: error_t terrestrialTime(juldate_t juldate, juldate_t& tt) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; using real_days_t = std::chrono::duration>; @@ -320,7 +325,7 @@ public: error_t apparentSiderTime(juldate_t juldate, sideral_time_t& gst, bool islocal = false) { - // std::lock_guard lock{_stateMutex}; + // std::lock_guard lock{*_stateMutex}; using real_days_t = std::chrono::duration>; @@ -328,7 +333,7 @@ public: // double tt = juldate.mjd; { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; auto dut1 = _currentState._bulletinA.DUT1(juldate.mjd); @@ -384,7 +389,7 @@ public: error_t refraction(refract_result_t& refr) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; eraRefco(_currentState.meteo.pressure, _currentState.meteo.temperature, _currentState.meteo.humidity, _currentState.wavelength, &refr.refa, &refr.refb); @@ -417,7 +422,7 @@ public: coord_t& alt, eo_t& eo) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; auto dut1 = _currentState._bulletinA.DUT1(juldate.mjd); @@ -459,7 +464,7 @@ public: error_t obs2icrs(MccCoordPairKind coord_kind, coord_t x, coord_t y, juldate_t juldate, coord_t ra, coord_t dec) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; auto dut1 = _currentState._bulletinA.DUT1(juldate.mjd); @@ -516,7 +521,7 @@ public: error_t hadec2azalt(coord_t ha, coord_t dec, coord_t& az, coord_t& alt) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; double r_az, r_alt; eraHd2ae(ha, dec, _currentState.lat, &r_az, &r_alt); @@ -529,7 +534,7 @@ public: error_t azalt2hadec(coord_t az, coord_t alt, coord_t& ha, coord_t& dec) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; double r_ha, r_dec; @@ -544,7 +549,7 @@ public: error_t hadec2pa(coord_t ha, coord_t dec, pa_t& pa) { - std::lock_guard lock{_stateMutex}; + std::lock_guard lock{*_stateMutex}; pa = eraHd2pa(ha, dec, _currentState.lat); @@ -566,7 +571,7 @@ public: protected: engine_state_t _currentState{}; - mutable std::mutex _stateMutex; + std::unique_ptr _stateMutex; }; } // namespace mcc::astrom::erfa diff --git a/cxx/mcc_mount_concepts.h b/cxx/mcc_mount_concepts.h index 268d97b..f36bee1 100644 --- a/cxx/mcc_mount_concepts.h +++ b/cxx/mcc_mount_concepts.h @@ -297,7 +297,7 @@ concept mcc_mount_pec_c = requires(T t) { // a class that contains at least celestial (equatorial and horizontal) and harware coordinates template -concept mcc_mount_telemetry_data_c = requires(T telemetry) { +concept mcc_mount_telemetry_data_c = std::movable && requires(T telemetry) { typename T::coord_t; typename T::time_point_t; @@ -423,8 +423,8 @@ concept mcc_guiding_model_c = requires(T t) { template concept mcc_prohibited_zone_c = mcc_mount_telemetry_data_c && std::movable && requires(T t, const T t_const) { - typename T::coord_t; - typename T::time_point_t; + // typename T::coord_t; + // typename T::time_point_t; requires mcc_time_duration_c; // static constexpr member to represent inifite duration @@ -436,6 +436,15 @@ concept mcc_prohibited_zone_c = }; }; + // static constexpr member to represent zero duration + requires requires { + requires std::same_as; + []() { + constexpr auto val = T::zeroDuration; + return val; + }; + }; + // the type 'T' must define a static constexpr member of type MccCoordPairKind // to declarate type of coordinate pair used to describe the zone. @@ -491,6 +500,11 @@ concept mcc_prohibited_zone_c = { t.timeFrom(std::declval()) } -> std::same_as; }; +// an input range of prohibited zones +template +concept mcc_irange_of_pzones_c = mcc_mount_telemetry_data_c && std::ranges::input_range && + mcc_prohibited_zone_c, TelemetryDataT>; + /* MOUNT GENERIC CONTROLS */ @@ -511,7 +525,9 @@ concept mcc_mount_controls_c = requires(T t) { // []... Ts>(std::tuple) { // }(t.prohibitedZones); - requires mcc_tuple_c; + // requires mcc_tuple_c; + requires mcc_irange_of_pzones_c; }; @@ -530,18 +546,24 @@ concept mcc_mount_c = requires(T t) { requires mcc_astrom_engine_c; requires mcc_mount_pec_c; requires mcc_mount_hardware_c; - // requires mcc_slew_model_c; requires mcc_slew_model_c; - // requires mcc_guiding_model_c; requires mcc_guiding_model_c; - // requires std::same_as; + // public methods + { + t.mountTelemetryData(std::declval()) + } -> std::same_as; - // public method - { t.mountTelemetryData() } -> std::same_as; + { + t.slewMount(std::declval()) + } -> std::same_as; + + { + t.guidingTarget(std::declval()) + } -> std::same_as; }; -// generic with logging methods +// generic with public logging methods template concept mcc_log_mount_c = mcc_mount_c && mcc_logger_c; diff --git a/cxx/mcc_mount_pec.h b/cxx/mcc_mount_pec.h index e92edad..beb6ea6 100644 --- a/cxx/mcc_mount_pec.h +++ b/cxx/mcc_mount_pec.h @@ -23,10 +23,7 @@ enum class MccMountDefaultPECErrorCode : int { ERROR_OK, ERROR_INVALID_INPUTS_BI struct MccMountDefaultPECCategory : public std::error_category { MccMountDefaultPECCategory() : std::error_category() {} - const char* name() const noexcept - { - return "ADC_GENERIC_DEVICE"; - } + const char* name() const noexcept { return "ADC_GENERIC_DEVICE"; } std::string message(int ec) const { @@ -140,14 +137,41 @@ public: : _pecData(std::move(pdata)), _phi(_pecData.siteLatitude), _geomCoeffs(_pecData.geomCoefficients), - _bsplCoeffs(_pecData.bsplineCoefficients) + _bsplCoeffs(_pecData.bsplineCoefficients), + _pecDataMutex(new std::mutex) { } + MccMountDefaultPEC(const MccMountDefaultPEC&) = delete; + MccMountDefaultPEC& operator=(const MccMountDefaultPEC&) = delete; + + MccMountDefaultPEC(MccMountDefaultPEC&& other) + : _pecData(std::move(other._pecData)), + _phi(_pecData.siteLatitude), + _geomCoeffs(_pecData.geomCoefficients), + _bsplCoeffs(_pecData.bsplineCoefficients), + _pecDataMutex(std::move(other._pecDataMutex)) + { + } + + MccMountDefaultPEC& operator=(MccMountDefaultPEC&& other) + { + if (this == &other) { + return *this; + } + + _pecData = std::move(other._pecData); + _phi = _pecData.siteLatitude; + _geomCoeffs = _pecData.geomCoefficients; + _bsplCoeffs = _pecData.bsplineCoefficients; + _pecDataMutex = std::move(other._pecDataMutex); + + return *this; + } void setData(pec_data_t pdata) { - std::lock_guard lock(_pecDataMutex); + std::lock_guard lock(*_pecDataMutex); _pecData = std::move(pdata); _phi = _pecData.siteLatitude; @@ -158,21 +182,21 @@ public: pec_data_t getData() const { - std::lock_guard lock(_pecDataMutex); + std::lock_guard lock(*_pecDataMutex); return _pecData; } void setType(MccMountDefaultPECType type) { - std::lock_guard lock(_pecDataMutex); + std::lock_guard lock(*_pecDataMutex); _pecData.type = type; } MccMountDefaultPECType getType() const { - std::lock_guard lock(_pecDataMutex); + std::lock_guard lock(*_pecDataMutex); return _pecData.type; } @@ -183,7 +207,7 @@ public: // so, input x and y are assumed to be mount axis encoder coordinates error_t compute(const coord_t& x, const coord_t& y, pec_result_t& res) { - std::lock_guard lock(_pecDataMutex); + std::lock_guard lock(*_pecDataMutex); if constexpr (mcc_is_equatorial_mount) { // equatorial if (_pecData.type == MccMountDefaultPECType::PEC_TYPE_GEOMETRY) { @@ -249,70 +273,6 @@ public: return MccMountDefaultPECErrorCode::ERROR_OK; } - // from celestial to encoder (use of iterative scheme) - error_t reverseCompute(const coord_t& x, const coord_t& y, pec_result_t& res, coord_t eps, size_t max_iter = 5) - { - coord_t e2 = eps * eps; - - coord_t xi = x, yi = y; - coord_t xe, ye; - size_t iter = 1; - - // the first iteration - auto err = compute(x, y, res); - - if (!err) { - // 'encoder' cocordinates - xe = x - res.dx; - ye = y - res.dy; - - err = compute(xe, ye, res); // to celestial - if (err) { - return MccMountDefaultPECErrorCode::ERROR_INVALID_INPUTS_BISPLEV; - } - - xi = xe + res.dx; // celestial - yi = ye + res.dy; - - auto rx = (x - xi); - auto ry = (y - yi); - auto d = rx * rx + ry * ry; - - bool ok = d <= e2; - if (ok) { - return MccMountDefaultPECErrorCode::ERROR_OK; - } - - while (iter < max_iter) { - err = compute(xi, yi, res); // to encoder - if (err) { - return MccMountDefaultPECErrorCode::ERROR_INVALID_INPUTS_BISPLEV; - } - xe = x - res.dx; - ye = y - res.dy; - - err = compute(xe, ye, res); // to celestial - if (err) { - return MccMountDefaultPECErrorCode::ERROR_INVALID_INPUTS_BISPLEV; - } - - xi = xe + res.dx; // celestial - yi = ye + res.dy; - - ok = ((x - xi) * (x - xi) + (y - yi) * (y - yi)) <= e2; - if (ok) { - return MccMountDefaultPECErrorCode::ERROR_OK; - } - - ++iter; - } - - err = MccMountDefaultPECErrorCode::ERROR_EXCEED_MAX_ITERS; - } - - return err; - } - private: pec_data_t _pecData; @@ -320,7 +280,7 @@ private: pec_geom_coeffs_t& _geomCoeffs; pec_bspline_coeffs_t& _bsplCoeffs; - mutable std::mutex _pecDataMutex; + std::unique_ptr _pecDataMutex; }; @@ -328,6 +288,7 @@ typedef MccMountDefaultPEC MccMountDefaultAltAzPec; typedef MccMountDefaultPEC MccMountDefaultForkPec; static_assert(traits::mcc_mount_pec_c, ""); +static_assert(std::movable); } // namespace mcc diff --git a/cxx/mcc_mount_pz.h b/cxx/mcc_mount_pz.h index ae8f351..a022225 100644 --- a/cxx/mcc_mount_pz.h +++ b/cxx/mcc_mount_pz.h @@ -45,8 +45,6 @@ public: static constexpr duration_t infiniteDuration{std::numeric_limits::infinity()}; static constexpr duration_t zeroDuration{0.0}; - // - // TODO: add context (e.g. TT-TAI, UT1-UTC, geo location and so on)!!! MccAltLimitPZ(const MccAngle& alt_limit, const MccAngle& lat) // : MccProhibitedZone(KIND == MccAltLimitKind::MIN_ALT_LIMIT ? "MINALT-ZONE" // : KIND == MccAltLimitKind::MAX_ALT_LIMIT ? "MAXALT-ZONE" diff --git a/cxx/mcc_mount_telemetry.h b/cxx/mcc_mount_telemetry.h index 67ef926..ef9a430 100644 --- a/cxx/mcc_mount_telemetry.h +++ b/cxx/mcc_mount_telemetry.h @@ -148,6 +148,32 @@ public: { } + 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; @@ -155,7 +181,7 @@ public: template error_t setTarget(PointT tag_point) { - std::lock_guard lock{_updateMutex}; + std::lock_guard lock{*_updateMutex}; auto err = this->toICRS(tag_point, _data.utc, _data.tagRA_ICRS, _data.tagDEC_ICRS); if (!err) { @@ -168,7 +194,7 @@ public: // target - mount coordinate difference auto targetToMountDiff() { - std::lock_guard lk(_updateMutex); + std::lock_guard lk(*_updateMutex); coord_diff_t result; @@ -232,13 +258,16 @@ public: - // correction for PEC + // 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); } @@ -248,6 +277,9 @@ public: .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); } @@ -284,12 +316,12 @@ public: return MccMountTelemetryAstromTransformErrorCode::ERROR_ASTROMETRY_COMP; } - std::lock_guard lock{_updateMutex}; + std::lock_guard lock{*_updateMutex}; _data = std::move(current_data); // notify all threads for new telemetry data - _updateCondVar.notify_all(); + _updateCondVar->notify_all(); return MccMountTelemetryErrorCode::ERROR_OK; } @@ -297,7 +329,7 @@ public: error_t data(mount_telemetry_data_t& data) { - std::lock_guard lock{_updateMutex}; + std::lock_guard lock{*_updateMutex}; data = _data; @@ -310,9 +342,9 @@ public: { auto timeout_tp = std::chrono::steady_clock::now() + timeout; - std::unique_lock lk(_updateMutex); + std::unique_lock lk(*_updateMutex); - auto res = _updateCondVar.wait_until( + 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; @@ -327,8 +359,8 @@ protected: mount_telemetry_data_t _data{}; hardware_t& _hardware; - std::mutex _updateMutex; - std::condition_variable _updateCondVar; + std::unique_ptr _updateMutex; + std::unique_ptr _updateCondVar; }; diff --git a/cxx/mcc_mount_telemetry_astrom.h b/cxx/mcc_mount_telemetry_astrom.h index 4c34d9b..a51f38e 100644 --- a/cxx/mcc_mount_telemetry_astrom.h +++ b/cxx/mcc_mount_telemetry_astrom.h @@ -109,6 +109,41 @@ public: { } + + MccMountTelemetryAstromTransform(MccMountTelemetryAstromTransform&& other) + : _pec(other._pec), _astromEngine(other._astromEngine) + { + } + + MccMountTelemetryAstromTransform& operator=(MccMountTelemetryAstromTransform&& other) + { + if (this == &other) { + return; + } + + _pec = other._pec; + _astromEngine = other._astromEngine; + + return *this; + } + + MccMountTelemetryAstromTransform(const MccMountTelemetryAstromTransform& other) + : _pec(other._pec), _astromEngine(other._astromEngine) + { + } + + MccMountTelemetryAstromTransform& operator=(const MccMountTelemetryAstromTransform& other) + { + if (this == &other) { + return; + } + + _pec = other._pec; + _astromEngine = other._astromEngine; + + return *this; + } + virtual ~MccMountTelemetryAstromTransform() = default; template @@ -189,9 +224,12 @@ public: } } else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // from app AZ-ALT if constexpr (equatorialMount) { - ast_err = azalt2hadec(coord.x, coord.y, X_app, Y_app); // compute HA-DEC - if (!ast_err) { // compute CIO RA (as XX_app) - ast_err = toApparent(X_app, Y_app, X_app, Y_app, XX_app); + ast_err = _astromEngine.azalt2hadec(coord.x, coord.y, X_app, Y_app); // compute HA-DEC + if (!ast_err) { // compute CIO RA (as XX_app) + coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP; + coord.x = X_app; + coord.y = Y_app; + ast_err = toApparent(std::move(coord), std::move(time_point), X_app, Y_app, XX_app); } } else if (altAzMount) { X_app = coord.x; @@ -465,4 +503,5 @@ protected: pec_t& _pec; }; + } // namespace mcc diff --git a/cxx/mcc_slew_guiding_model_common.h b/cxx/mcc_slew_guiding_model_common.h index aeb9b28..a0bea74 100644 --- a/cxx/mcc_slew_guiding_model_common.h +++ b/cxx/mcc_slew_guiding_model_common.h @@ -95,5 +95,29 @@ auto mccCheckInZonePZTuple(const TelemetryDataT& telemetry_data, }(std::make_index_sequence{}); } +template RT, + std::ranges::output_range ResT> +auto mccCheckInZonePZRange(const TelemetryDataT& telemetry_data, const RT& pzones, ResT& result) +{ + auto Npz = std::ranges::distance(pzones); + if (!Npz) { + return; + } + + auto res_sz = std::ranges::distance(result); + + size_t i = 1; + auto res_iter = result.begin(); + for (auto& el : pzones) { + if (i > res_sz) { + std::back_inserter(result) = el.inZone(telemetry_data); + } else { + std::ranges::advance(res_iter, 1); + *res_iter = el.inZone(telemetry_data); + } + ++i; + } +} } // namespace mcc diff --git a/cxx/mcc_slew_model.h b/cxx/mcc_slew_model.h index 2f672e4..55109a4 100644 --- a/cxx/mcc_slew_model.h +++ b/cxx/mcc_slew_model.h @@ -150,10 +150,25 @@ public: init(telemetry, hardware, prohibited_zone); } - MccSimpleSlewModel(MccSimpleSlewModel&&) = default; - MccSimpleSlewModel& operator=(MccSimpleSlewModel&&) = default; - MccSimpleSlewModel(const MccSimpleSlewModel&) = default; - MccSimpleSlewModel& operator=(const MccSimpleSlewModel&) = default; + MccSimpleSlewModel(MccSimpleSlewModel&& other) + : _stopRequested(other._stopRequested.load()), _slewFunc(std::move(other._slewFunc)) + { + } + + MccSimpleSlewModel& operator=(MccSimpleSlewModel&& other) + { + if (this == &other) { + return *this; + } + + _stopRequested = other._stopRequested.load(); + _slewFunc = std::move(_slewFunc); + + return *this; + }; + + MccSimpleSlewModel(const MccSimpleSlewModel&) = delete; + MccSimpleSlewModel& operator=(const MccSimpleSlewModel&) = delete; virtual ~MccSimpleSlewModel() { @@ -422,5 +437,6 @@ protected: } }; +static_assert(std::movable>); } // namespace mcc