mountcontrol/cxx/mount.h
2025-03-18 17:48:07 +03:00

269 lines
8.2 KiB
C++

#pragma once
/* MOUNT CONTROL COMPONENTS LIBRARY */
#include <atomic>
#include <chrono>
#include <concepts>
#include <cstdint>
#include <fstream>
#include <functional>
#include <string_view>
#include "spdlog/sinks/null_sink.h"
#include "mcc_spdlog.h"
#include "mount_astrom.h"
#include "mount_astrom_default.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>;
{ 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;
// encoder-measured current mount coordinates (in radians)
mnt_coord_t mntRA, mntDEC;
mnt_coord_t mntHA;
mnt_coord_t mntAZ, mntZD;
// encoder-measured current mount moving speed (in radians/s)
mnt_speed_t mntSpeedX,
mntSpeedY; // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ZD for horizontal-type one
// current refraction coefficient
mnt_coord_t currRefr;
// PCS (pointing correction system) corrections
mnt_coord_t pcsX, pcsY; // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ZD for horizontal-type one
};
/* MOUNT BASE TEMPLATED CLASS WITH BASIC FUNCTIONALITY */
enum class MccMountType : uint8_t { GERMAN_TYPE, FORK_TYPE, CROSSAXIS_TYPE, ALTAZ_TYPE };
// implements a Finite State Machine Pattern
template <MccMountType MOUNT_TYPE>
class MccMount : public utils::MccSpdlogLogger
{
typedef double mnt_coord_t;
typedef double mnt_speed_t;
typedef double time_point_t;
public:
static constexpr MccMountType mountType = MOUNT_TYPE;
/* mount main-cycle variable quantities (mount orientation) */
struct mount_orient_t {
// time-related
std::chrono::system_clock::time_point utc;
time_point_t mjd; // modified Julian date
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;
// encoder-measured current mount coordinates (in radians)
mnt_coord_t mntRA, mntDEC;
mnt_coord_t mntHA;
mnt_coord_t mntAZ, mntZD;
// encoder-measured current mount moving speed (in radians/s)
mnt_speed_t mntSpeedX,
mntSpeedY; // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ZD for horizontal-type one
// current refraction coefficient
mnt_coord_t currRefr;
// PCS (pointing correction system) corrections
mnt_coord_t pcsX, pcsY; // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ZD for horizontal-type one
// mount current state
};
struct mount_config_t {
std::string leap_seconds_filename{}; // empty to use hardcoded default value!
std::string earth_orient_filename{}; // empty to use hardcoded default value!
};
/* 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 = {}", std::this_thread::get_id());
// init time scales related databases to default (pre-defined) state
logInfo("initializing leap seconds database to default state ...");
strst.str(defaults::MCC_DEFAULT_LEAP_SECONDS_FILE);
_leapSecondsDB = astro::mcc_parse_leapsecs(strst);
logInfo("leap seconds default database expired date: {}", _leapSecondsDB.expireDate);
logInfo("initializing Earth orientation (pole coordinates, UT1-UTC) database to default state ...");
strst.clear();
strst.str(defaults::MCC_DEFAULT_IERS_BULLETIN_A_FILE);
_earthOrientDB = astro::mcc_parse_bulletinA(strst);
logInfo("Earth orientation default database (Bulletin A) date: {}", _earthOrientDB.bulletinDate);
// load time scales relates databases from files
std::ifstream fst;
logInfo("Load leap seconds and Earth orientation databases ...");
auto time_db_loader = [&fst, 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;
}
fst.open(filename);
if (!fst.is_open()) {
logError("CANNOT open {} file '{}'!", type, filename);
logWarn("Keep {} database in default state!", type);
return;
}
if constexpr (std::same_as<astro::leapsecond_db_t, std::decay_t<decltype(db)>>) {
db = astro::mcc_parse_leapsecs(fst);
} else if constexpr (std::same_as<astro::earth_orient_db_t, std::decay_t<decltype(db)>>) {
db = astro::mcc_parse_bulletinA(fst);
} else {
static_assert(false, "INVALID DATABASE TYPE!!!");
}
if (db.state != astro::IERS_DB_STATE_OK) {
logError("CANNOT parse {} file '{}'!", type, filename);
logWarn("Keep {} database in default state!", type);
} else {
logInfo("{} database was successfully loaded from '{}' file", type, filename);
}
fst.close();
};
astro::leapsecond_db_t ldb;
astro::earth_orient_db_t edb;
time_db_loader(_mountCurrentConfig.leap_seconds_filename, "leap seconds", ldb);
if (ldb.state == astro::IERS_DB_STATE_OK) {
_leapSecondsDB = std::move(ldb);
logInfo("leap seconds default database expired date: {}", _leapSecondsDB.expireDate);
}
time_db_loader(_mountCurrentConfig.earth_orient_filename, "Earth orientation", edb);
if (edb.state == astro::IERS_DB_STATE_OK) {
_earthOrientDB = std::move(edb);
logInfo("Earth orientation default database (Bulletin A) date: {}", _earthOrientDB.bulletinDate);
}
}
virtual ~MccMount()
{
logDebug("Delete MccMount class instance: thread = {}", std::this_thread::get_id());
}
/* Public methods */
template <traits::mcc_mount_state_c StateT>
void setMountState(StateT& state)
{
_exitCurrentState(); // exit from current state
_exitCurrentState = [&state]() { state.exit(); };
state.enter();
}
mount_orient_t getMountData() const noexcept
{
return _currentMountOrient.load();
}
void setMeteo(std::derived_from<MccMountMeteo> auto const& meteo)
{
_currentMeteo.store(meteo);
}
protected:
mount_config_t _mountCurrentConfig;
// std::shared_ptr<spdlog::logger> _mountLogger;
std::function<void()> _exitCurrentState;
// time scales related databases
astro::leapsecond_db_t _leapSecondsDB;
astro::earth_orient_db_t _earthOrientDB;
std::atomic<mount_orient_t> _currentMountOrient;
std::atomic<MccMountMeteo> _currentMeteo;
};
} // namespace mcc