#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ #include #include #include #include #include #include #include "spdlog/sinks/null_sink.h" #include "mcc_coord.h" #include "mcc_spdlog.h" #include "mcc_traits.h" #include "mount_astrom.h" #include "mount_pz.h" // low-level functions // namespace lowlevel // { // #include "../LibSidServo/sidservo.h" // } // namespace lowlevel namespace mcc { namespace traits { // mount state type concept template concept mcc_mount_state_c = requires(T t, const T t_const) { { t_const.ident() } -> std::same_as; // requires mcc_is_callable; // requires mcc_is_callable; // requires mcc_is_callable; { t.enter() } -> std::same_as; { t.exit() } -> std::same_as; { t.stop() } -> std::same_as; }; } // namespace traits /* SOME BASIC DATA STRUCTURES DEFINITIONS */ // meteo parameters (e.g. to compute refraction) struct MccMountMeteo { typedef double temp_t; typedef double humid_t; typedef double press_t; temp_t temperature; // Temperature in C humid_t humidity; // humidity in % press_t pressure; // atmospheric presure in hPa=mB }; // mount current position and related quantities struct MccMountPosition { typedef double mnt_coord_t; typedef double mnt_speed_t; typedef double time_point_t; // time-related std::chrono::system_clock::time_point utc; time_point_t mjd; // modified Julian date time_point_t ut1; time_point_t tt; time_point_t siderTime; // sideral time (in radians) // apparent target (user-input) current coordinates (in radians) mnt_coord_t tagRA, tagDEC; mnt_coord_t tagHA; mnt_coord_t tagAZ, tagZD; mnt_coord_t tagPA; // paralactic angle // encoder-measured current mount coordinates (in radians) mnt_coord_t mntRA, mntDEC; mnt_coord_t mntHA; mnt_coord_t mntAZ, mntZD; mnt_coord_t mntPA; // encoder-measured current mount moving speed (in radians/s) // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ZD for horizontal-type one mnt_speed_t mntSpeedX, mntSpeedY; // current refraction coefficient (for tagZD) mnt_coord_t currRefr; // PCS (pointing correction system) corrections // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ZD for horizontal-type one mnt_coord_t pcsX, pcsY; }; // mount site geographical location struct MccMountSiteInfo { typedef double mnt_site_coord_t; typedef double mnt_site_elev_t; mnt_site_coord_t latitude{"00:00:00.0"_dms}; // in radians mnt_site_coord_t longitude{0.0}; // in radians (positive to the East) mnt_site_elev_t elevation{0.0}; // in meters std::string_view name{"ALL-ZERO"}; // just a human-readable name }; enum class MccMountType : uint8_t { GERMAN_TYPE, FORK_TYPE, CROSSAXIS_TYPE, ALTAZ_TYPE }; template static constexpr std::string_view MccMountTypeStr = TYPE == MccMountType::GERMAN_TYPE ? "GERMAN" : TYPE == MccMountType::FORK_TYPE ? "FORK" : TYPE == MccMountType::CROSSAXIS_TYPE ? "CROSSAXIS" : TYPE == MccMountType::ALTAZ_TYPE ? "ALTAZ" : "UNKNOWN"; /* MOUNT CONFIGURATION BASE CLASS */ struct MccMountConfig { std::string leap_seconds_filename{}; // empty to use hardcoded default value! std::string earth_orient_filename{}; // empty to use hardcoded default value! MccMountSiteInfo siteInfo{.latitude = 0.0, .longitude = 0.0, .elevation = 0.0, .name{"ALL-ZERO"}}; }; /* PROHIBITED ZONE CONTEXT BASE CLASS */ struct MccProhibitedZoneContext { // type of prohibited zone enum pzgeometry_t { PZ_MINALT, // trivial minimal altitude PZ_MAXALT, // trivial maximal altitude (e.g. near-zenith zone for alt-azimuthal types) PZ_ELLIPSE, // simple circular/elliptical zone PZ_PIER_MERIDIAN, // pier-side or cross-meridian PZ_CONVEXHULL // general convex hull polygonal zone }; // type of input cordinates enum pzcoords_t { PZCOORD_RADEC, // catalog (FK5, IRCS, etc...) PZCOORD_RADEC_APP, // apparent RA and DEC PZCOORD_HADEC_APP, // apparent HA and DEC PZCOORD_AZZD, // apparent azimuth and zenithal distance }; pzgeometry_t geometryType; pzcoords_t coordType; }; struct MccProhibitedZone1 { // type of prohibited zone enum pztype_t { PZ_MINALT, // trivial minimal altitude PZ_MAXALT, // trivial maximal altitude (e.g. near-zenith zone for alt-azimuthal types) PZ_ELLIPSE, // simple circular/elliptical zone PZ_PIER_MERIDIAN, // pier-side or cross-meridian PZ_CONVEXHULL // general convex hull polygonal zone }; pztype_t type; struct pzminalt_t { double minalt{0.0}; }; union { } geometry; }; /* MOUNT BASE TEMPLATED CLASS WITH BASIC FUNCTIONALITY */ // implements a Finite State Machine Pattern template CONFIG_TYPE = MccMountConfig> class MccMount : public utils::MccSpdlogLogger { public: static constexpr MccMountType mountType = MOUNT_TYPE; static constexpr std::string_view mountTypeStr = MccMountTypeStr; typedef CONFIG_TYPE mount_config_t; enum IersDatabaseType { IERS_DB_LEAPSECS, IERS_DB_EARTH_ORIENT }; template struct pzcheck_result_t { bool inzone{false}; DT time_to{DT::max()}; DT time_from{DT::min()}; }; /* Constructors and destructor */ MccMount(traits::mcc_input_char_range auto const& logger_mark = "[MOUNT]", std::shared_ptr logger = spdlog::null_logger_mt("NULL")) : utils::MccSpdlogLogger(logger), _exitCurrentState([]() {}), _currentStateName([]() {}) { std::istringstream strst; addMarkToPatternIdx(logger_mark); logDebug("Create MccMount class instance: thread = {}", getThreadId()); logInfo("Load leap seconds and Earth orientation databases ..."); updateIERSDatabase(IERS_DB_LEAPSECS); updateIERSDatabase(IERS_DB_EARTH_ORIENT); } virtual ~MccMount() { logDebug("Delete MccMount class instance: thread = {}", getThreadId()); } /* Public methods */ template void setMountState(StateT& state) { _exitCurrentState(); // exit from current state _exitCurrentState = [&state]() { state.exit(); }; _currentStateName = [&state]() { return state.name(); }; state.enter(); } std::string_view currenStateName() const { return _currentStateName(); } MccMountPosition getMountData() const noexcept { return _currentMountOrient.load(); } // geo location setters/getters void setGeoLocation(const MccMountSiteInfo& geoloc) { _geoLocation.store(geoloc); } void setSiteLatitude(const MccAngle& lat) { auto st = _geoLocation.load(); st.latitude = lat; logInfo("Set current site latitude to {} radians", st.latitude); _geoLocation.store(st); } void setSiteLongitude(const MccAngle& lon) { auto st = _geoLocation.load(); st.longitude = lon; logInfo("Set current site longitude to {} radians", st.longitude); _geoLocation.store(st); } void setSiteElevation(MccMountSiteInfo::mnt_site_elev_t elev) { auto st = _geoLocation.load(); st.elevation = elev; logInfo("Set current site elevation to {} meters", st.elevation); _geoLocation.store(st); } MccMountSiteInfo getGeoLocation() const { return _geoLocation.load(); } // current meteo setters/getters void setMeteo(const MccMountMeteo& meteo) { _currentMeteo.store(meteo); } MccMountMeteo getMeteo() const { return _currentMeteo.load(); } /* prohibited zone related methods */ template size_t pzAddZone(ZT zone) { _pzInZoneFunc.emplace_back([zone = std::move(zone)](const MccAngle& x, const MccAngle& y, void* ctx) mutable { auto context = *static_cast(ctx); return zone.inZone(x, y, context); }); return _pzInZoneFunc.size(); } // returns a time to reach the prohibited area: // 0 - already in the zone // std::chrono::duration<>::max() - never reach the zone // the kind of cordinates (e.g. IRCS or apparent, equatorial or horizontal) is a subject of implementation auto pzTimeTo(const MccAngle& xcoord, const MccAngle& ycoord, traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now()) { using d_t = std::remove_cvref_t::duration; return d_t::max(); } // returns a time to exit the prohibited area: // 0 - already out of the zone // std::chrono::duration<>::max() - the zone itself // the kind of cordinates (e.g. IRCS or apparent, equatorial or horizontal) is a subject of implementation auto pzTimeFrom(const MccAngle& xcoord, const MccAngle& ycoord, traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now()) { using d_t = std::remove_cvref_t::duration; return d_t{0}; } // returns a pzcheck_result_t with: // .inzone = true - coordinates in zone, false - otherwise // .time_to = time duration to reach the zone (0 - if already in the zone, chrono::duration<>::max() if never // reach the zone) // .time_from = time duration to exit the zone (0 - if already out of the zone, chrono::duration<>::max() if // never exit the zone) auto pzCheck(this auto&& self, const MccAngle& xcoord, const MccAngle& ycoord, std::derived_from auto const& context, traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now()) { return std::forward(self).pzCheckImpl(xcoord, ycoord, context, utc); } template auto pzCheckRange(const R1& xcoord, const R2& ycoord, std::derived_from auto const& context) requires(std::ranges::output_range> && std::derived_from, MccAngle> && std::derived_from, MccAngle>) { OR res; for (auto const& [c1, c2] : std::views::zip(xcoord, ycoord)) { auto r = pzCheck(c1, c2, context); std::back_inserter(res) = r; } return res; } template auto pzCheckRange(const R1& xcoord, const R2& ycoord, std::derived_from auto const& context) requires(std::derived_from, MccAngle> && std::derived_from, MccAngle>) { return pzCheckRange>>(xcoord, ycoord, context); } // IERS databases updater bool updateIERSDatabase(IersDatabaseType type) { auto time_db_loader = [this](const std::string& filename, std::string_view type, auto& db) { if (filename.empty()) { logWarn("An empty {} filename! Skip and keep default values!", type); return false; } bool ok = db.load(filename); if (!ok) { logError("CANNOT parse {} file '{}' or it is not accessible!", type, filename); logWarn("Keep {} database in default state!", type); } else { logInfo("{} database was successfully loaded from '{}' file", type, filename); } return ok; }; switch (type) { case IERS_DB_LEAPSECS: return time_db_loader(_mountCurrentConfig.leap_seconds_filename, "leap seconds", _leapSecondsDB); case IERS_DB_EARTH_ORIENT: return time_db_loader(_mountCurrentConfig.earth_orient_filename, "Earth orientation", _earthOrientDB); default: logError("Invalid type for IERS database!"); return false; } } protected: mount_config_t _mountCurrentConfig; // std::shared_ptr _mountLogger; std::function _exitCurrentState; std::function _currentStateName; // time scales related databases astrom::MccLeapSeconds _leapSecondsDB; astrom::MccIersBulletinA _earthOrientDB; std::atomic _currentMountOrient; std::atomic _geoLocation; std::atomic _currentMeteo; std::vector> _pzInZoneFunc; // default implementation auto pzCheckImpl(const MccAngle& xcoord, const MccAngle& ycoord, std::derived_from auto const& context, traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now()) { using d_t = std::remove_cvref_t::duration; return pzcheck_result_t{.inzone = false, .time_to = d_t::max(), .time_from = d_t{0}}; } }; } // namespace mcc