#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ #include #include #include #include #include #include #include "spdlog/sinks/null_sink.h" // #include "mcc_coord.h" #include "mcc_finite_state_machine.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 { /* SOME BASIC DATA STRUCTURES DEFINITIONS */ // coordinate system related constants enum class MccCoordKind : uint8_t { COORDS_KIND_RA_IRCS, // IRCS right ascension COORDS_KIND_RA_APP, // apparent right ascension COORDS_KIND_HA, // hour angle COORDS_KIND_AZ, // azimuth COORDS_KIND_X, // co-longitude coordinate COORDS_KIND_DEC_IRCS, // IRCS declination COORDS_KIND_DEC_APP, // apparent declination COORDS_KIND_ALT, // altitude COORDS_KIND_ZD, // zenithal distance COORDS_KIND_Y // co-latitude coordinate }; enum class MccCoordPairKind : uint16_t { // catalog's COORDS_KIND_RADEC_IRCS = (int)MccCoordKind::COORDS_KIND_RA_IRCS + ((int)MccCoordKind::COORDS_KIND_DEC_IRCS << 8), // apparent RA and DEC COORDS_KIND_RADEC_APP = (int)MccCoordKind::COORDS_KIND_RA_APP + ((int)MccCoordKind::COORDS_KIND_DEC_APP << 8), // apparent HA (hour angle) and DEC COORDS_KIND_HADEC_APP = (int)MccCoordKind::COORDS_KIND_HA + ((int)MccCoordKind::COORDS_KIND_DEC_APP << 8), // Azimuth and Altitude COORDS_KIND_AZALT = (int)MccCoordKind::COORDS_KIND_AZ + ((int)MccCoordKind::COORDS_KIND_ALT << 8), // Azimuth and Zenithal distance COORDS_KIND_AZZD = (int)MccCoordKind::COORDS_KIND_AZ + ((int)MccCoordKind::COORDS_KIND_ZD << 8), // co-longitude and co-latitude (e.g. mount hardware drive/axis encoders) COORDS_KIND_XY = (int)MccCoordKind::COORDS_KIND_X + ((int)MccCoordKind::COORDS_KIND_Y << 8), }; template static constexpr std::string_view MccCoordPairKindStr = KIND == MccCoordPairKind::COORDS_KIND_RADEC_IRCS ? "RADEC-IRCS" : KIND == MccCoordPairKind::COORDS_KIND_RADEC_APP ? "RADEC-APP" : KIND == MccCoordPairKind::COORDS_KIND_HADEC_APP ? "HADEC-APP" : KIND == MccCoordPairKind::COORDS_KIND_AZALT ? "Azimuth-Altitude" : KIND == MccCoordPairKind::COORDS_KIND_AZZD ? "AzimuthZendist" : KIND == MccCoordPairKind::COORDS_KIND_XY ? "XY" : "UNKNOWN"; // 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 (non-corrected for PCS) current mount position and moving speed (in radians, radians/s) // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ZD for horizontal-type one mnt_coord_t mntPosX, mntPosY; 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 fsm::MccFiniteStateMachine, 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 */ template MccMount(InitStateT, traits::mcc_input_char_range auto const& logger_mark = "[MOUNT]", std::shared_ptr logger = spdlog::null_logger_mt("NULL")) : fsm::MccFiniteStateMachine(InitStateT{}), utils::MccSpdlogLogger(logger) { 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 */ 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; // 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