mountcontrol/cxx/mount.h
Timur A. Fatkhullin 88d4b30a58 ...
2025-05-19 14:21:04 +03:00

451 lines
14 KiB
C++

#pragma once
/* MOUNT CONTROL COMPONENTS LIBRARY */
#include <atomic>
#include <chrono>
#include <concepts>
#include <cstdint>
#include <functional>
#include <string_view>
#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 <typename T>
concept mcc_mount_state_c = requires(T t, const T t_const) {
{ t_const.ident() } -> std::same_as<std::string_view>;
// requires mcc_is_callable<typename T::error_callback_t>;
// requires mcc_is_callable<typename T::enter_callback_t>;
// requires mcc_is_callable<typename T::exit_callback_t>;
{ t.enter() } -> std::same_as<void>;
{ t.exit() } -> std::same_as<void>;
{ t.stop() } -> std::same_as<void>;
};
} // 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 (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 <MccMountType TYPE>
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 <MccMountType MOUNT_TYPE, std::derived_from<MccMountConfig> CONFIG_TYPE = MccMountConfig>
class MccMount : public utils::MccSpdlogLogger
{
public:
static constexpr MccMountType mountType = MOUNT_TYPE;
static constexpr std::string_view mountTypeStr = MccMountTypeStr<MOUNT_TYPE>;
typedef CONFIG_TYPE mount_config_t;
enum IersDatabaseType { IERS_DB_LEAPSECS, IERS_DB_EARTH_ORIENT };
template <traits::mcc_time_duration_c DT = std::chrono::system_clock::duration>
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<spdlog::logger> 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 <traits::mcc_mount_state_c StateT>
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 <traits::mcc_prohibited_zone_c ZT>
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<typename ZT::pzcontext_t*>(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<decltype(utc)>::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<decltype(utc)>::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<MccProhibitedZoneContext> auto const& context,
traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now())
{
return std::forward<decltype(self)>(self).pzCheckImpl(xcoord, ycoord, context, utc);
}
template <std::ranges::range OR, std::ranges::input_range R1, std::ranges::input_range R2>
auto pzCheckRange(const R1& xcoord,
const R2& ycoord,
std::derived_from<MccProhibitedZoneContext> auto const& context)
requires(std::ranges::output_range<OR, pzcheck_result_t<>> &&
std::derived_from<std::ranges::range_value_t<R1>, MccAngle> &&
std::derived_from<std::ranges::range_value_t<R2>, 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 <std::ranges::input_range R1, std::ranges::input_range R2>
auto pzCheckRange(const R1& xcoord,
const R2& ycoord,
std::derived_from<MccProhibitedZoneContext> auto const& context)
requires(std::derived_from<std::ranges::range_value_t<R1>, MccAngle> &&
std::derived_from<std::ranges::range_value_t<R2>, MccAngle>)
{
return pzCheckRange<std::vector<pzcheck_result_t<>>>(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<spdlog::logger> _mountLogger;
std::function<void()> _exitCurrentState;
std::function<std::string_view()> _currentStateName;
// time scales related databases
astrom::MccLeapSeconds _leapSecondsDB;
astrom::MccIersBulletinA _earthOrientDB;
std::atomic<MccMountPosition> _currentMountOrient;
std::atomic<MccMountSiteInfo> _geoLocation;
std::atomic<MccMountMeteo> _currentMeteo;
std::vector<std::function<bool(const MccAngle&, const MccAngle&, void*)>> _pzInZoneFunc;
// default implementation
auto pzCheckImpl(const MccAngle& xcoord,
const MccAngle& ycoord,
std::derived_from<MccProhibitedZoneContext> auto const& context,
traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now())
{
using d_t = std::remove_cvref_t<decltype(utc)>::duration;
return pzcheck_result_t<d_t>{.inzone = false, .time_to = d_t::max(), .time_from = d_t{0}};
}
};
} // namespace mcc