mountcontrol/cxx/mount.h
2025-04-11 18:19:06 +03:00

318 lines
9.1 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_spdlog.h"
#include "mcc_traits.h"
#include "mount_astrom.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>;
};
} // 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{0.0}; // 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"}};
};
/* 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 };
/* 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([]() {})
{
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(); };
state.enter();
}
MccMountPosition getMountData() const noexcept
{
return _currentMountOrient.load();
}
// geo location setters/getters
void setGeoLocation(const MccMountSiteInfo& geoloc)
{
_geoLocation.store(geoloc);
}
void setSiteLatitude(auto const& lat)
{
auto v = utils::parsAngleString(lat);
if (v.has_value()) {
auto st = _geoLocation.load();
st.latitude = v.value() * utils::deg2radCoeff; // to radians
logInfo("Set current site latitude to {} radians", st.latitude);
_geoLocation.store(st);
} else {
logError("Invalid user latitude value! Do not change the current value!");
}
}
void setSiteLongitude(auto const& lon)
{
auto v = utils::parsAngleString(lon);
if (v.has_value()) {
auto st = _geoLocation.load();
st.longitude = v.value() * utils::deg2radCoeff; // to radians
logInfo("Set current site longitude to {} radians", st.longitude);
_geoLocation.store(st);
} else {
logError("Invalid user longitude value! Do not change the current value!");
}
}
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
bool pzCheck(traits::mcc_real_or_char_range auto const& xcoord, traits::mcc_real_or_char_range auto const& ycoord)
{
return true;
}
auto pzPredict(traits::mcc_systime_c auto const& time_point,
traits::mcc_real_or_char_range auto const& xcoord,
traits::mcc_real_or_char_range auto const& ycoord)
{
using d_t = std::remove_cvref_t<decltype(time_point)>::duration;
return d_t::max();
}
// 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;
// time scales related databases
astrom::MccLeapSeconds _leapSecondsDB;
astrom::MccIersBulletinA _earthOrientDB;
std::atomic<MccMountPosition> _currentMountOrient;
std::atomic<MccMountSiteInfo> _geoLocation;
std::atomic<MccMountMeteo> _currentMeteo;
};
} // namespace mcc