add mcc directory
This commit is contained in:
parent
61e41b1a1d
commit
6c10c6b6ff
@ -13,3 +13,4 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||
# ******* C++ PART OF THE PROJECT *******
|
||||
|
||||
add_subdirectory(cxx)
|
||||
add_subdirectory(mcc)
|
||||
|
||||
@ -58,7 +58,10 @@ namespace mcc::astrom::erfa
|
||||
struct MccMountAstromEngineERFACategory : public std::error_category {
|
||||
MccMountAstromEngineERFACategory() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept { return "ADC_GENERIC_DEVICE"; }
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "ADC_GENERIC_DEVICE";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
@ -254,7 +257,10 @@ public:
|
||||
|
||||
/* time-related methods */
|
||||
|
||||
static time_point_t timePointNow() { return time_point_t::clock::now(); }
|
||||
static time_point_t timePointNow()
|
||||
{
|
||||
return time_point_t::clock::now();
|
||||
}
|
||||
|
||||
// templated generic version
|
||||
template <mcc::traits::mcc_systime_c TpT>
|
||||
@ -587,10 +593,10 @@ public:
|
||||
|
||||
// special case: to ICRS from apparent
|
||||
if (coord_to_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
|
||||
if (coord_to_kind == MccCoordPairKind::COORDS_KIND_AZALT ||
|
||||
coord_to_kind == MccCoordPairKind::COORDS_KIND_AZZD ||
|
||||
coord_to_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP ||
|
||||
coord_to_kind == MccCoordPairKind::COORDS_KIND_HADEC_APP) {
|
||||
if (coord_from_kind == MccCoordPairKind::COORDS_KIND_AZALT ||
|
||||
coord_from_kind == MccCoordPairKind::COORDS_KIND_AZZD ||
|
||||
coord_from_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP ||
|
||||
coord_from_kind == MccCoordPairKind::COORDS_KIND_HADEC_APP) {
|
||||
//
|
||||
ret = greg2jul(time_point_from, jd);
|
||||
if (!ret) {
|
||||
@ -718,14 +724,26 @@ public:
|
||||
|
||||
/* helper mathods */
|
||||
|
||||
auto leapSecondsExpireDate() const { return _currentState._leapSeconds.expireDate(); }
|
||||
auto leapSecondsExpireDate() const
|
||||
{
|
||||
return _currentState._leapSeconds.expireDate();
|
||||
}
|
||||
|
||||
auto leapSecondsExpireMJD() const { return _currentState._leapSeconds.expireMJD(); }
|
||||
auto leapSecondsExpireMJD() const
|
||||
{
|
||||
return _currentState._leapSeconds.expireMJD();
|
||||
}
|
||||
|
||||
|
||||
auto bulletinADateRange() const { return _currentState._bulletinA.dateRange(); }
|
||||
auto bulletinADateRange() const
|
||||
{
|
||||
return _currentState._bulletinA.dateRange();
|
||||
}
|
||||
|
||||
auto bulletinADateRangeMJD() const { return _currentState._bulletinA.dateRangeMJD(); }
|
||||
auto bulletinADateRangeMJD() const
|
||||
{
|
||||
return _currentState._bulletinA.dateRangeMJD();
|
||||
}
|
||||
|
||||
protected:
|
||||
engine_state_t _currentState{};
|
||||
|
||||
73
mcc/CMakeLists.txt
Normal file
73
mcc/CMakeLists.txt
Normal file
@ -0,0 +1,73 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
# set(CMAKE_BUILD_TYPE Release)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
|
||||
# ******* SPDLOG LIBRARY *******
|
||||
|
||||
include(FetchContent)
|
||||
include(ExternalProject)
|
||||
|
||||
set(SPDLOG_USE_STD_FORMAT ON CACHE INTERNAL "Use of C++20 std::format")
|
||||
set(SPDLOG_FMT_EXTERNAL OFF CACHE INTERNAL "Turn off external fmt library")
|
||||
|
||||
FetchContent_Declare(spdlog
|
||||
# ExternalProject_Add(spdlog
|
||||
# SOURCE_DIR ${CMAKE_BINARY_DIR}/spdlog_lib
|
||||
# BINARY_DIR ${CMAKE_BINARY_DIR}/spdlog_lib/build
|
||||
GIT_REPOSITORY "https://github.com/gabime/spdlog.git"
|
||||
GIT_TAG "v1.15.1"
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_SUBMODULES ""
|
||||
GIT_PROGRESS TRUE
|
||||
CMAKE_ARGS "-DSPDLOG_USE_STD_FORMAT=ON -DSPDLOG_FMT_EXTERNAL=OFF"
|
||||
# CONFIGURE_COMMAND ""
|
||||
# BUILD_COMMAND ""
|
||||
# INSTALL_COMMAND ""
|
||||
# UPDATE_COMMAND ""
|
||||
# SOURCE_SUBDIR cmake # turn off building
|
||||
OVERRIDE_FIND_PACKAGE
|
||||
)
|
||||
find_package(spdlog CONFIG)
|
||||
|
||||
|
||||
|
||||
# ******* ERFA LIBRARY *******
|
||||
|
||||
ExternalProject_Add(erfalib1
|
||||
PREFIX ${CMAKE_BINARY_DIR}/erfa_lib1
|
||||
GIT_REPOSITORY "https://github.com/liberfa/erfa.git"
|
||||
GIT_TAG "v2.0.1"
|
||||
UPDATE_COMMAND ""
|
||||
PATCH_COMMAND ""
|
||||
# BINARY_DIR erfa_build
|
||||
# SOURCE_DIR erfa
|
||||
# INSTALL_DIR
|
||||
LOG_CONFIGURE 1
|
||||
CONFIGURE_COMMAND meson setup --reconfigure -Ddefault_library=static -Dbuildtype=release
|
||||
-Dprefix=${CMAKE_BINARY_DIR}/erfa_lib -Dlibdir= -Dincludedir= -Ddatadir= <SOURCE_DIR>
|
||||
# CONFIGURE_COMMAND meson setup --reconfigure -Ddefault_library=static -Dbuildtype=release -Dc_args='-march=native' -Doptimization=3
|
||||
# -Dprefix=${CMAKE_BINARY_DIR}/erfa_lib -Dlibdir= -Dincludedir= -Ddatadir= <SOURCE_DIR>
|
||||
BUILD_COMMAND ninja -C <BINARY_DIR>
|
||||
INSTALL_COMMAND meson install -C <BINARY_DIR>
|
||||
BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/erfa_lib1/liberfa.a
|
||||
)
|
||||
add_library(ERFA_LIB STATIC IMPORTED)
|
||||
set_target_properties(ERFA_LIB PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/erfa_lib1/liberfa.a)
|
||||
add_dependencies(ERFA_LIB erfalib)
|
||||
set(ERFA_INCLUDE_DIR ${CMAKE_BINARY_DIR}/erfa_lib1)
|
||||
include_directories(${ERFA_INCLUDE_DIR})
|
||||
|
||||
|
||||
|
||||
set(MCC_LIBRARY_SRC1 mcc_generics.h mcc_defaults.h mcc_traits.h mcc_utils.h mcc_ccte_iers.h mcc_ccte_iers_default.h)
|
||||
set(MCC_LIBRARY1 mcc1)
|
||||
add_library(${MCC_LIBRARY1} INTERFACE ${MCC_LIBRARY_SRC1})
|
||||
target_compile_features(${MCC_LIBRARY1} INTERFACE cxx_std_23)
|
||||
target_include_directories(${MCC_LIBRARY1} INTERFACE ${ERFA_INCLUDE_DIR})
|
||||
|
||||
541
mcc/mcc_ccte_erfa.h
Normal file
541
mcc/mcc_ccte_erfa.h
Normal file
@ -0,0 +1,541 @@
|
||||
#pragma once
|
||||
|
||||
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
||||
|
||||
|
||||
/* CELESTIAL COORDINATES TRANSFORMATION ENGINE IMPLEMENTATION BASED ON ERFA LIBRARY */
|
||||
|
||||
#include <erfa.h>
|
||||
#include <erfam.h>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
#include "mcc_ccte_iers.h"
|
||||
#include "mcc_defaults.h"
|
||||
|
||||
namespace mcc::ccte::erfa
|
||||
{
|
||||
|
||||
|
||||
enum class MccCCTE_ERFAErrorCode : int {
|
||||
ERROR_OK = 0,
|
||||
ERROR_INVALID_INPUT_ARG,
|
||||
ERROR_julday_INVALID_YEAR,
|
||||
ERROR_julday_INVALID_MONTH,
|
||||
ERROR_julday_INVALID_DAY,
|
||||
ERROR_UNSUPPORTED_COORD_PAIR,
|
||||
ERROR_BULLETINA_OUT_OF_RANGE,
|
||||
ERROR_LEAPSECONDS_OUT_OF_RANGE,
|
||||
ERROR_DUBIOUS_YEAR,
|
||||
ERROR_UNACCEPTABLE_DATE,
|
||||
ERROR_UPDATE_LEAPSECONDS,
|
||||
ERROR_UPDATE_BULLETINA,
|
||||
ERROR_UNEXPECTED
|
||||
};
|
||||
|
||||
} // namespace mcc::ccte::erfa
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <>
|
||||
class is_error_code_enum<mcc::ccte::erfa::MccCCTE_ERFAErrorCode> : public true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
|
||||
|
||||
namespace mcc::ccte::erfa
|
||||
{
|
||||
|
||||
|
||||
/* error category definition */
|
||||
|
||||
// error category
|
||||
struct MccCCTE_ERFACategory : public std::error_category {
|
||||
MccCCTE_ERFACategory() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "CCTE-ERFA";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
MccCCTE_ERFAErrorCode err = static_cast<MccCCTE_ERFAErrorCode>(ec);
|
||||
|
||||
switch (err) {
|
||||
case MccCCTE_ERFAErrorCode::ERROR_OK:
|
||||
return "OK";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_INVALID_INPUT_ARG:
|
||||
return "invalid argument";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_julday_INVALID_YEAR:
|
||||
return "invalid year number";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_julday_INVALID_MONTH:
|
||||
return "invalid month number";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_julday_INVALID_DAY:
|
||||
return "invalid day number";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_UNSUPPORTED_COORD_PAIR:
|
||||
return "unsupported coordinate pair";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE:
|
||||
return "time point is out of range";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_LEAPSECONDS_OUT_OF_RANGE:
|
||||
return "time point is out of range";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_DUBIOUS_YEAR:
|
||||
return "dubious year";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_UNACCEPTABLE_DATE:
|
||||
return "unacceptable date";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_UPDATE_LEAPSECONDS:
|
||||
return "leap seconds update error";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_UPDATE_BULLETINA:
|
||||
return "bulletin A update error";
|
||||
case MccCCTE_ERFAErrorCode::ERROR_UNEXPECTED:
|
||||
return "unexpected error value";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const MccCCTE_ERFACategory& get()
|
||||
{
|
||||
static const MccCCTE_ERFACategory constInst;
|
||||
return constInst;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline std::error_code make_error_code(MccCCTE_ERFAErrorCode ec)
|
||||
{
|
||||
return std::error_code(static_cast<int>(ec), MccCCTE_ERFACategory::get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MccCCTE_ERFA
|
||||
{
|
||||
public:
|
||||
static constexpr double DEFAULT_WAVELENGTH = 0.55; // default observed wavelength in mkm
|
||||
|
||||
typedef std::error_code error_t;
|
||||
|
||||
struct refract_model_t {
|
||||
static constexpr std::string_view name()
|
||||
{
|
||||
return "ERFA";
|
||||
}
|
||||
|
||||
double refa, refb;
|
||||
};
|
||||
|
||||
/* use of the same type for representation of celestial and geodetic coordinates, celestial angles (e.g. P.A.),
|
||||
* and sideral time */
|
||||
typedef MccCelestialPoint::coord_t coord_t;
|
||||
|
||||
// meteo parameters (to compute refraction)
|
||||
struct meteo_t {
|
||||
typedef double temp_t;
|
||||
typedef double humid_t;
|
||||
typedef double press_t;
|
||||
|
||||
temp_t temperature; // Temperature in C
|
||||
humid_t humidity; // humidity in % ([0.0, 1.0])
|
||||
press_t pressure; // atmospheric presure in hPa=mB
|
||||
};
|
||||
|
||||
struct engine_state_t {
|
||||
meteo_t meteo{.temperature = 0.0, .humidity = 0.5, .pressure = 1010.0};
|
||||
|
||||
double wavelength = DEFAULT_WAVELENGTH; // observed wavelength in mkm
|
||||
|
||||
coord_t lat = 0.0; // site latitude
|
||||
coord_t lon = 0.0; // site longitude
|
||||
double elev = 0.0; // site elevation (in meters)
|
||||
|
||||
mcc::ccte::iers::MccLeapSeconds _leapSeconds{};
|
||||
mcc::ccte::iers::MccIersBulletinA _bulletinA{};
|
||||
};
|
||||
|
||||
MccCCTE_ERFA() : _stateMutex(new std::mutex) {}
|
||||
|
||||
virtual ~MccCCTE_ERFA() = default;
|
||||
|
||||
// engine state related methods
|
||||
|
||||
void setState(engine_state_t state)
|
||||
{
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
_currentState = std::move(state);
|
||||
}
|
||||
|
||||
engine_state_t getState() const
|
||||
{
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
return _currentState;
|
||||
}
|
||||
|
||||
void updateMeteo(meteo_t meteo)
|
||||
{
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
_currentState.meteo = std::move(meteo);
|
||||
}
|
||||
|
||||
error_t updateLeapSeconds(std::derived_from<std::basic_istream<char>> auto& stream, char comment_sym = '#')
|
||||
{
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
if (!_currentState._leapSeconds.load(stream, comment_sym)) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_UPDATE_LEAPSECONDS;
|
||||
}
|
||||
|
||||
return MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
error_t updateLeapSeconds(traits::mcc_input_char_range auto const& filename, char comment_sym = '#')
|
||||
{
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
if (!_currentState._leapSeconds.load(filename, comment_sym)) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_UPDATE_LEAPSECONDS;
|
||||
}
|
||||
|
||||
return MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
error_t updateBulletinA(std::derived_from<std::basic_istream<char>> auto& stream, char comment_sym = '*')
|
||||
{
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
if (!_currentState._bulletinA.load(stream, comment_sym)) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_UPDATE_BULLETINA;
|
||||
}
|
||||
|
||||
return MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
error_t updateBulletinA(traits::mcc_input_char_range auto const& filename, char comment_sym = '*')
|
||||
{
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
if (!_currentState._bulletinA.load(filename, comment_sym)) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_UPDATE_BULLETINA;
|
||||
}
|
||||
|
||||
return MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
// time-related methods
|
||||
|
||||
error_t timepointToJulday(mcc_time_point_c auto tp, mcc_julday_c auto* julday)
|
||||
{
|
||||
auto ret = MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
|
||||
if (julday == nullptr) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto days = std::chrono::floor<std::chrono::days>(tp);
|
||||
std::chrono::year_month_day ymd{days};
|
||||
|
||||
double mjd0;
|
||||
|
||||
int err = eraCal2jd(ymd.year(), (unsigned)ymd.month(), (unsigned)ymd.day(), &mjd0, &julday->mjd);
|
||||
|
||||
if (err != 0) {
|
||||
ret = err == -1 ? MccCCTE_ERFAErrorCode::ERROR_julday_INVALID_YEAR
|
||||
: err == -2 ? MccCCTE_ERFAErrorCode::ERROR_julday_INVALID_MONTH
|
||||
: err == -3 ? MccCCTE_ERFAErrorCode::ERROR_julday_INVALID_DAY
|
||||
: MccCCTE_ERFAErrorCode::ERROR_UNEXPECTED;
|
||||
} else { // partial part of day
|
||||
julday->mjd +=
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::ratio<86400>>>(tp - days).count();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
error_t timepointToAppSideral(mcc_time_point_c auto tp, mcc_angle_c auto* st, bool islocal = false)
|
||||
{
|
||||
auto ret = MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
|
||||
if (st == nullptr) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
using real_days_t = std::chrono::duration<double, std::ratio<86400>>;
|
||||
|
||||
MccJulianDay julday;
|
||||
|
||||
ret = timepointToJulday(tp, &julday);
|
||||
if (ret != MccCCTE_ERFAErrorCode::ERROR_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
double ut1 = julday.mjd;
|
||||
double tt = julday.mjd;
|
||||
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
auto dut1 = _currentState._bulletinA.DUT1(julday.mjd);
|
||||
|
||||
if (dut1.has_value()) {
|
||||
ut1 += std::chrono::duration_cast<real_days_t>(dut1.value()).count();
|
||||
} else { // out of range
|
||||
return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
auto tai_utc = _currentState._leapSeconds[julday.mjd];
|
||||
if (tai_utc.has_value()) {
|
||||
tt += std::chrono::duration_cast<real_days_t>(tai_utc.value()).count();
|
||||
} else {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_LEAPSECONDS_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
|
||||
auto tt_tai = _currentState._bulletinA.TT_TAI();
|
||||
tt += std::chrono::duration_cast<real_days_t>(tt_tai).count();
|
||||
|
||||
*st = eraGst06a(julday.MJD0, ut1, julday.MJD0, tt);
|
||||
|
||||
if (islocal) {
|
||||
*st += _currentState.lon;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// coordinates transformations
|
||||
|
||||
error_t transformCoordinates(mcc_celestial_point_c auto from_pt, mcc_celestial_point_c auto* to_pt)
|
||||
{
|
||||
error_t ret = MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
|
||||
MccJulianDay jd;
|
||||
|
||||
if (to_pt == nullptr) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// no transformations
|
||||
if (from_pt.time_point == to_pt->time_point && from_pt.pair_kind == to_pt->pair_kind) {
|
||||
to_pt->X = from_pt.X;
|
||||
to_pt->Y = from_pt.Y;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// special case: to ICRS from apparent
|
||||
if (to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
|
||||
if (from_pt.pair_kind == MccCoordPairKind::COORDS_KIND_AZALT ||
|
||||
from_pt.pair_kind == MccCoordPairKind::COORDS_KIND_AZZD ||
|
||||
from_pt.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP ||
|
||||
from_pt.pair_kind == MccCoordPairKind::COORDS_KIND_HADEC_APP) {
|
||||
//
|
||||
ret = timepointToJulday(from_pt.time_point, &jd);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
auto dut1 = _currentState._bulletinA.DUT1(jd.mjd);
|
||||
|
||||
if (!dut1.has_value()) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
auto pol_pos = _currentState._bulletinA.polarCoords(jd.mjd);
|
||||
if (!pol_pos.has_value()) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE;
|
||||
}
|
||||
const auto arcsec2rad = std::numbers::pi / 180 / 3600;
|
||||
pol_pos->x *= arcsec2rad;
|
||||
pol_pos->y *= arcsec2rad;
|
||||
|
||||
std::string type;
|
||||
switch (from_pt.pair_kind) {
|
||||
case mcc::MccCoordPairKind::COORDS_KIND_AZZD:
|
||||
type = "A";
|
||||
break;
|
||||
case mcc::MccCoordPairKind::COORDS_KIND_AZALT:
|
||||
from_pt.Y = std::numbers::pi / 2.0 - from_pt.Y; // altitude to zenithal distance
|
||||
type = "A";
|
||||
break;
|
||||
case mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP:
|
||||
type = "H";
|
||||
break;
|
||||
case mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP:
|
||||
type = "R";
|
||||
break;
|
||||
default:
|
||||
return MccCCTE_ERFAErrorCode::ERROR_UNSUPPORTED_COORD_PAIR;
|
||||
}
|
||||
|
||||
int err = eraAtoc13(type.c_str(), from_pt.X, from_pt.Y, jd.MJD0, jd.mjd, dut1->count(),
|
||||
_currentState.lon, _currentState.lat, _currentState.elev, pol_pos->x, pol_pos->y,
|
||||
_currentState.meteo.pressure, _currentState.meteo.temperature,
|
||||
_currentState.meteo.humidity, _currentState.wavelength, &to_pt->X, &to_pt->Y);
|
||||
|
||||
if (err == 1) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_DUBIOUS_YEAR;
|
||||
} else if (err == -1) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_UNACCEPTABLE_DATE;
|
||||
}
|
||||
} else {
|
||||
ret = MccCCTE_ERFAErrorCode::ERROR_UNSUPPORTED_COORD_PAIR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// special case: from ICRS to apparent
|
||||
if (from_pt.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
|
||||
if (to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_AZALT ||
|
||||
to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_AZZD ||
|
||||
to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP ||
|
||||
to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_HADEC_APP) {
|
||||
//
|
||||
ret = timepointToJulday(to_pt->time_point, &jd);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
auto dut1 = _currentState._bulletinA.DUT1(jd.mjd);
|
||||
|
||||
if (!dut1.has_value()) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
auto pol_pos = _currentState._bulletinA.polarCoords(jd.mjd);
|
||||
if (!pol_pos.has_value()) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_BULLETINA_OUT_OF_RANGE;
|
||||
}
|
||||
const auto arcsec2rad = std::numbers::pi / 180 / 3600;
|
||||
pol_pos->x *= arcsec2rad;
|
||||
pol_pos->y *= arcsec2rad;
|
||||
|
||||
double oaz, ozd, oha, odec, ora, eo_;
|
||||
|
||||
int err = eraAtco13(from_pt.X, from_pt.Y, 0.0, 0.0, 0.0, 0.0, jd.MJD0, jd.mjd, dut1->count(),
|
||||
_currentState.lon, _currentState.lat, _currentState.elev, pol_pos->x, pol_pos->y,
|
||||
_currentState.meteo.pressure, _currentState.meteo.temperature,
|
||||
_currentState.meteo.humidity, _currentState.wavelength, &oaz, &ozd, &oha, &odec,
|
||||
&ora, &eo_);
|
||||
|
||||
if (err == 1) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_DUBIOUS_YEAR;
|
||||
} else if (err == -1) {
|
||||
return MccCCTE_ERFAErrorCode::ERROR_UNACCEPTABLE_DATE;
|
||||
}
|
||||
|
||||
if (to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_AZALT) {
|
||||
to_pt->X = oaz;
|
||||
to_pt->Y = std::numbers::pi / 2.0 - ozd;
|
||||
} else if (to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_AZZD) {
|
||||
to_pt->X = oaz;
|
||||
to_pt->Y = ozd;
|
||||
} else if (to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP) {
|
||||
to_pt->X = ora;
|
||||
to_pt->Y = odec;
|
||||
} else if (to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_HADEC_APP) {
|
||||
to_pt->X = oha;
|
||||
to_pt->Y = odec;
|
||||
}
|
||||
} else {
|
||||
ret = MccCCTE_ERFAErrorCode::ERROR_UNSUPPORTED_COORD_PAIR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// refraction
|
||||
|
||||
error_t refractionModel(refract_model_t* model)
|
||||
{
|
||||
std::lock_guard lock{*_stateMutex};
|
||||
|
||||
eraRefco(_currentState.meteo.pressure, _currentState.meteo.temperature, _currentState.meteo.humidity,
|
||||
_currentState.wavelength, &model->refa, &model->refb);
|
||||
|
||||
return MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
}
|
||||
|
||||
error_t refractionCorrection(mcc_celestial_point_c auto pt, mcc_angle_c auto* dZ)
|
||||
{
|
||||
error_t ret = MccCCTE_ERFAErrorCode::ERROR_OK;
|
||||
|
||||
if (dZ == nullptr) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
refract_model_t rmodel;
|
||||
|
||||
refractionModel(&rmodel);
|
||||
|
||||
if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_AZZD) {
|
||||
if (pt.Y >= std::numbers::pi / 2.0) {
|
||||
*dZ = 35.4 / 60.0 * std::numbers::pi / 180.0; // 35.4 arcminutes
|
||||
} else {
|
||||
auto tanZ = tan(pt.Y);
|
||||
*dZ = rmodel.refa * tanZ + rmodel.refb * tanZ * tanZ * tanZ;
|
||||
}
|
||||
} else {
|
||||
MccCelestialPoint tr_pt{.pair_kind = MccCoordPairKind::COORDS_KIND_AZZD, .time_point = pt.time_point};
|
||||
ret = transformCoordinates(std::move(pt), &tr_pt);
|
||||
if (!ret) {
|
||||
ret = refractionCorrection(std::move(tr_pt), dZ);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* helper mathods */
|
||||
|
||||
auto leapSecondsExpireDate() const
|
||||
{
|
||||
return _currentState._leapSeconds.expireDate();
|
||||
}
|
||||
|
||||
auto leapSecondsExpireMJD() const
|
||||
{
|
||||
return _currentState._leapSeconds.expireMJD();
|
||||
}
|
||||
|
||||
|
||||
auto bulletinADateRange() const
|
||||
{
|
||||
return _currentState._bulletinA.dateRange();
|
||||
}
|
||||
|
||||
auto bulletinADateRangeMJD() const
|
||||
{
|
||||
return _currentState._bulletinA.dateRangeMJD();
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
engine_state_t _currentState{};
|
||||
|
||||
std::unique_ptr<std::mutex> _stateMutex;
|
||||
};
|
||||
|
||||
} // namespace mcc::ccte::erfa
|
||||
491
mcc/mcc_ccte_iers.h
Normal file
491
mcc/mcc_ccte_iers.h
Normal file
@ -0,0 +1,491 @@
|
||||
#pragma once
|
||||
|
||||
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
||||
|
||||
|
||||
/* Classes to represent IERS bulletins
|
||||
*
|
||||
* BULLETIN A: https://datacenter.iers.org/data/latestVersion/bulletinA.txt
|
||||
* leapseconds: https://hpiers.obspm.fr/iers/bul/bulc/Leap_Second.dat
|
||||
*
|
||||
*/
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
|
||||
#include "mcc_ccte_iers_default.h"
|
||||
#include "mcc_traits.h"
|
||||
#include "mcc_utils.h"
|
||||
|
||||
namespace mcc::ccte::iers
|
||||
{
|
||||
|
||||
class MccLeapSeconds final
|
||||
{
|
||||
public:
|
||||
typedef std::chrono::system_clock::time_point time_point_t;
|
||||
typedef std::chrono::duration<double> real_secs_t; // seconds duration in double
|
||||
|
||||
MccLeapSeconds()
|
||||
{
|
||||
// create default values
|
||||
std::istringstream ist(defaults::MCC_DEFAULT_LEAP_SECONDS_FILE);
|
||||
|
||||
load(ist);
|
||||
}
|
||||
|
||||
~MccLeapSeconds() = default;
|
||||
|
||||
|
||||
time_point_t expireDate() const
|
||||
{
|
||||
return _expireDate;
|
||||
}
|
||||
|
||||
auto expireMJD() const
|
||||
{
|
||||
return _expireMJD;
|
||||
}
|
||||
|
||||
// load from stream
|
||||
bool load(std::derived_from<std::basic_istream<char>> auto& stream, char comment_sym = '#')
|
||||
{
|
||||
std::istringstream is;
|
||||
double mjd;
|
||||
unsigned day, month;
|
||||
int year;
|
||||
double tai_utc;
|
||||
|
||||
decltype(_expireDate) edate;
|
||||
std::vector<leapsecond_db_elem_t> db;
|
||||
|
||||
for (std::string line; std::getline(stream, line);) {
|
||||
auto sv = utils::trimSpaces(line, utils::TrimType::TRIM_LEFT);
|
||||
|
||||
if (sv.size()) {
|
||||
if (sv[0] == comment_sym) { // comment string
|
||||
if (std::regex_match(line, expr_date_rx)) {
|
||||
auto pos = line.find("on");
|
||||
sv = utils::trimSpaces(std::string_view{line.begin() + pos + 2, line.end()},
|
||||
utils::TrimType::TRIM_LEFT);
|
||||
is.str({sv.begin(), sv.end()});
|
||||
is >> std::chrono::parse("%d %B %Y", edate);
|
||||
is.clear();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::regex_match(line, data_rx)) {
|
||||
is.str(line);
|
||||
is >> mjd >> day >> month >> year >> tai_utc;
|
||||
db.emplace_back(mjd, std::chrono::year_month_day{std::chrono::year{year} / month / day}, tai_utc);
|
||||
// db.emplace_back(mjd,
|
||||
// std::chrono::year_month_day{std::chrono::year{year}, std::chrono::month{month},
|
||||
// std::chrono::day{day}},
|
||||
// tai_utc);
|
||||
is.clear();
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (db.empty()) { // keep previous data
|
||||
return false;
|
||||
}
|
||||
|
||||
_expireDate = std::move(edate);
|
||||
|
||||
// compute expire Julian Day
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
std::chrono::year_month_day ymd{std::chrono::floor<std::chrono::days>(_expireDate)};
|
||||
|
||||
static constexpr std::chrono::year MIN_YEAR = -4799y;
|
||||
|
||||
if (ymd.year() < MIN_YEAR) {
|
||||
return -1;
|
||||
}
|
||||
if (!ymd.month().ok()) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
int64_t im = (unsigned)ymd.month();
|
||||
int64_t id = (unsigned)ymd.day();
|
||||
int64_t iy = (int)ymd.year();
|
||||
|
||||
int64_t my = (im - 14LL) / 12LL;
|
||||
int64_t iypmy = iy + my;
|
||||
|
||||
// integer part of result MJD
|
||||
int64_t mjd_int = (1461LL * (iypmy + 4800LL)) / 4LL + (367LL * (im - 2LL - 12LL * my)) / 12LL -
|
||||
(3LL * ((iypmy + 4900LL) / 100LL)) / 4LL + id - 2432076LL;
|
||||
|
||||
_expireMJD = static_cast<double>(mjd_int);
|
||||
|
||||
|
||||
_db = std::move(db);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool load(traits::mcc_input_char_range auto const& filename, char comment_sym = '#')
|
||||
{
|
||||
std::ifstream fst(filename);
|
||||
|
||||
bool ok = fst.is_open();
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = load(fst, comment_sym);
|
||||
|
||||
fst.close();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
// std::optional<double> operator[](const time_point_t& tp) const
|
||||
std::optional<real_secs_t> operator[](const time_point_t& tp) const
|
||||
{
|
||||
if (tp > _expireDate) { // ???????!!!!!!!!!!!
|
||||
return std::nullopt;
|
||||
// return _db.back().tai_utc;
|
||||
}
|
||||
|
||||
std::chrono::year_month_day ymd{std::chrono::floor<std::chrono::days>(tp)};
|
||||
|
||||
for (auto const& el : _db | std::views::reverse) {
|
||||
if (ymd >= el.ymd) {
|
||||
// return el.tai_utc;
|
||||
return real_secs_t{el.tai_utc};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// std::optional<double> operator[](const double& mjd) const
|
||||
std::optional<real_secs_t> operator[](const double& mjd) const
|
||||
{
|
||||
double e_mjd;
|
||||
|
||||
if (mjd > _expireMJD) { // ???????!!!!!!!!!!!
|
||||
return std::nullopt;
|
||||
// return _db.back().tai_utc;
|
||||
}
|
||||
|
||||
for (auto const& el : _db | std::views::reverse) {
|
||||
if (mjd >= el.mjd) {
|
||||
return real_secs_t{el.tai_utc};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
void dump(std::derived_from<std::basic_ostream<char>> auto& stream) const
|
||||
{
|
||||
stream << std::format("Leap seconds database expire date: {}", _expireDate) << '\n';
|
||||
for (auto const& el : _db) {
|
||||
stream << std::format("{} {} {}", el.mjd, el.ymd, el.tai_utc) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
inline static const std::regex expr_date_rx{
|
||||
"^ *# *File +expires +on +[0-8]{1,2} "
|
||||
"+(January|February|March|April|May|June|July|August|September|October|November|December) +[0-9]{4} *$"};
|
||||
|
||||
inline static const std::regex data_rx{"^ *[0-9]{5,}(\\.?[0-9]+) +[0-9]{1,2} +[0-9]{1,2} +[0-9]{4} +[0-9]{1,} *$"};
|
||||
|
||||
time_point_t _expireDate{};
|
||||
double _expireMJD{};
|
||||
|
||||
struct leapsecond_db_elem_t {
|
||||
double mjd;
|
||||
std::chrono::year_month_day ymd;
|
||||
double tai_utc; // TAI-UTC in seconds
|
||||
};
|
||||
|
||||
std::vector<leapsecond_db_elem_t> _db{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
class MccIersBulletinA final
|
||||
{
|
||||
public:
|
||||
typedef std::chrono::system_clock::time_point time_point_t;
|
||||
typedef std::chrono::duration<double> real_secs_t; // seconds duration in double
|
||||
|
||||
struct pole_pos_t {
|
||||
double x, y;
|
||||
};
|
||||
|
||||
struct date_range_t {
|
||||
std::chrono::year_month_day begin;
|
||||
std::chrono::year_month_day end;
|
||||
};
|
||||
|
||||
struct date_range_mjd_t {
|
||||
double begin;
|
||||
double end;
|
||||
};
|
||||
|
||||
MccIersBulletinA()
|
||||
{
|
||||
// create pre-defined (default-state) database
|
||||
std::istringstream ist(defaults::MCC_DEFAULT_IERS_BULLETIN_A_FILE);
|
||||
|
||||
load(ist);
|
||||
}
|
||||
|
||||
~MccIersBulletinA() = default;
|
||||
|
||||
std::chrono::system_clock::time_point bulletinDate() const
|
||||
{
|
||||
return _date;
|
||||
}
|
||||
|
||||
|
||||
date_range_t dateRange() const
|
||||
{
|
||||
return {_db.front().ymd, _db.back().ymd};
|
||||
}
|
||||
|
||||
date_range_mjd_t dateRangeMJD() const
|
||||
{
|
||||
return {_db.front().mjd, _db.back().mjd};
|
||||
}
|
||||
|
||||
|
||||
// double TT_TAI() const
|
||||
real_secs_t TT_TAI() const
|
||||
{
|
||||
return real_secs_t{_tt_tai};
|
||||
}
|
||||
|
||||
// DUT1 = UT1 - UTC
|
||||
// std::optional<double> DUT1(const time_point_t& tp) const
|
||||
std::optional<real_secs_t> DUT1(const time_point_t& tp) const
|
||||
{
|
||||
// use of the closest date
|
||||
std::chrono::year_month_day ymd{std::chrono::round<std::chrono::days>(tp)};
|
||||
|
||||
if (ymd < _db.front().ymd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (ymd > _db.back().ymd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (auto const& el : _db) {
|
||||
if (ymd <= el.ymd) {
|
||||
return real_secs_t{el.dut1};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// std::optional<double> DUT1(double mjd) const
|
||||
std::optional<real_secs_t> DUT1(double mjd) const
|
||||
{
|
||||
mjd = std::round(mjd); // round to closest integer MJD
|
||||
|
||||
if (mjd < _db.front().mjd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (mjd > _db.back().mjd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (auto const& el : _db) {
|
||||
if (mjd <= el.mjd) {
|
||||
return real_secs_t{el.dut1};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<pole_pos_t> polarCoords(const time_point_t& tp) const
|
||||
{
|
||||
std::chrono::year_month_day ymd{std::chrono::round<std::chrono::days>(tp)};
|
||||
|
||||
if (ymd < _db.front().ymd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (ymd > _db.back().ymd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (auto const& el : _db) {
|
||||
if (ymd <= el.ymd) {
|
||||
return pole_pos_t{el.x, el.y};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<pole_pos_t> polarCoords(double mjd) const
|
||||
{
|
||||
mjd = std::round(mjd); // round to closest integer MJD
|
||||
|
||||
if (mjd < _db.front().mjd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (mjd > _db.back().mjd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (auto const& el : _db) {
|
||||
if (mjd <= el.mjd) {
|
||||
return pole_pos_t{el.x, el.y};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool load(std::derived_from<std::basic_istream<char>> auto& stream, char comment_sym = '*')
|
||||
{
|
||||
std::vector<earth_orient_db_elem_t> db;
|
||||
enum { TAB_STATE_SEEK, TAB_STATE_START };
|
||||
int tab_state = TAB_STATE_SEEK;
|
||||
|
||||
int year;
|
||||
unsigned month, day;
|
||||
double mjd, x, y, dut1;
|
||||
std::istringstream is;
|
||||
decltype(_date) bdate;
|
||||
double tt_tai;
|
||||
|
||||
for (std::string line; std::getline(stream, line);) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sv = utils::trimSpaces(line, utils::TrimType::TRIM_LEFT);
|
||||
|
||||
if (sv.size()) {
|
||||
if (sv[0] == comment_sym) { // comment string
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tab_state == TAB_STATE_START) {
|
||||
if (std::regex_match(sv.begin(), sv.end(), bull_tab_vals_rx)) {
|
||||
// is.str({sv.begin(), sv.end()});
|
||||
is.str(line);
|
||||
is >> year >> month >> day >> mjd >> x >> y >> dut1;
|
||||
db.emplace_back(mjd, std::chrono::year_month_day{std::chrono::year{year} / month / day}, x, y,
|
||||
dut1);
|
||||
is.clear();
|
||||
} else { // end of the table - just stop parsing
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::regex_match(sv.begin(), sv.end(), bull_date_rx)) {
|
||||
is.str({sv.begin(), sv.end()});
|
||||
is >> std::chrono::parse("%d %B %Y", bdate);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::regex_match(sv.begin(), sv.end(), bull_tt_tai_rx)) {
|
||||
is.str({sv.begin(), sv.end()});
|
||||
std::string dummy;
|
||||
is >> dummy >> dummy >> dummy >> dummy >> tt_tai;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::regex_match(sv.begin(), sv.end(), bull_tab_title_rx)) {
|
||||
tab_state = TAB_STATE_START;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else { // empty string (only spaces)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (db.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_date = std::move(bdate);
|
||||
_tt_tai = tt_tai;
|
||||
_db = std::move(db);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load(traits::mcc_input_char_range auto const& filename, char comment_sym = '*')
|
||||
{
|
||||
std::ifstream fst(filename);
|
||||
|
||||
bool ok = fst.is_open();
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = load(fst, comment_sym);
|
||||
|
||||
fst.close();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
void dump(std::derived_from<std::basic_ostream<char>> auto& stream) const
|
||||
{
|
||||
stream << std::format("Bulletin A issue date: {}", _date) << '\n';
|
||||
stream << std::format("TT-TAI: {}", _tt_tai) << '\n';
|
||||
for (auto const& el : _db) {
|
||||
stream << std::format("{} {} {:6.4f} {:6.4f} {:7.5f}", el.mjd, el.ymd, el.x, el.y, el.dut1) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
inline static const std::regex bull_date_rx{
|
||||
"^ *[0-9]{1,2} +(January|February|March|April|May|June|July|August|September|October|November|December) "
|
||||
"+[0-9]{4,} +Vol\\. +[XMLCDVI]+ +No\\. +[0-9]+ *$"};
|
||||
|
||||
inline static const std::regex bull_tt_tai_rx{"^ *TT += +TAI +\\+ +[0-9]+\\.[0-9]+ +seconds *$"};
|
||||
|
||||
inline static const std::regex bull_tab_title_rx{"^ *MJD +x\\(arcsec\\) +y\\(arcsec\\) +UT1-UTC\\(sec\\) *$"};
|
||||
|
||||
inline static const std::regex bull_tab_vals_rx{
|
||||
"^ *[0-9]{4,} +[0-9]{1,2} +[0-9]{1,2} +[0-9]{5,} +[0-9]+\\.[0-9]+ +[0-9]+\\.[0-9]+ +[0-9]+\\.[0-9]+ *$"};
|
||||
|
||||
|
||||
time_point_t _date;
|
||||
double _tt_tai;
|
||||
|
||||
struct earth_orient_db_elem_t {
|
||||
double mjd;
|
||||
std::chrono::year_month_day ymd;
|
||||
double x, y; // Polar coordinates in arcsecs
|
||||
double dut1; // UT1-UTC in seconds
|
||||
};
|
||||
|
||||
std::vector<earth_orient_db_elem_t> _db;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace mcc::ccte::iers
|
||||
658
mcc/mcc_ccte_iers_default.h
Normal file
658
mcc/mcc_ccte_iers_default.h
Normal file
@ -0,0 +1,658 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace mcc::ccte::iers::defaults
|
||||
|
||||
{
|
||||
|
||||
// https://hpiers.obspm.fr/iers/bul/bulc/Leap_Second.dat
|
||||
|
||||
static std::string MCC_DEFAULT_LEAP_SECONDS_FILE = R"--(
|
||||
|
||||
# Value of TAI-UTC in second valid beetween the initial value until
|
||||
# the epoch given on the next line. The last line reads that NO
|
||||
# leap second was introduced since the corresponding date
|
||||
# Updated through IERS Bulletin 70 issued in July 2025
|
||||
#
|
||||
#
|
||||
# File expires on 28 June 2026
|
||||
#
|
||||
#
|
||||
# MJD Date TAI-UTC (s)
|
||||
# day month year
|
||||
# --- -------------- ------
|
||||
#
|
||||
41317.0 1 1 1972 10
|
||||
41499.0 1 7 1972 11
|
||||
41683.0 1 1 1973 12
|
||||
42048.0 1 1 1974 13
|
||||
42413.0 1 1 1975 14
|
||||
42778.0 1 1 1976 15
|
||||
43144.0 1 1 1977 16
|
||||
43509.0 1 1 1978 17
|
||||
43874.0 1 1 1979 18
|
||||
44239.0 1 1 1980 19
|
||||
44786.0 1 7 1981 20
|
||||
45151.0 1 7 1982 21
|
||||
45516.0 1 7 1983 22
|
||||
46247.0 1 7 1985 23
|
||||
47161.0 1 1 1988 24
|
||||
47892.0 1 1 1990 25
|
||||
48257.0 1 1 1991 26
|
||||
48804.0 1 7 1992 27
|
||||
49169.0 1 7 1993 28
|
||||
49534.0 1 7 1994 29
|
||||
50083.0 1 1 1996 30
|
||||
50630.0 1 7 1997 31
|
||||
51179.0 1 1 1999 32
|
||||
53736.0 1 1 2006 33
|
||||
54832.0 1 1 2009 34
|
||||
56109.0 1 7 2012 35
|
||||
57204.0 1 7 2015 36
|
||||
57754.0 1 1 2017 37
|
||||
)--";
|
||||
|
||||
|
||||
// https://datacenter.iers.org/data/latestVersion/bulletinA.txt
|
||||
|
||||
static std::string MCC_DEFAULT_IERS_BULLETIN_A_FILE = R"--(
|
||||
|
||||
|
||||
**********************************************************************
|
||||
* *
|
||||
* I E R S B U L L E T I N - A *
|
||||
* *
|
||||
* Rapid Service/Prediction of Earth Orientation *
|
||||
**********************************************************************
|
||||
7 August 2025 Vol. XXXVIII No. 032
|
||||
______________________________________________________________________
|
||||
GENERAL INFORMATION:
|
||||
MJD = Julian Date - 2 400 000.5 days
|
||||
UT2-UT1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T)
|
||||
- 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
|
||||
where pi = 3.14159265... and T is the date in Besselian years.
|
||||
TT = TAI + 32.184 seconds
|
||||
DUT1= (UT1-UTC) transmitted with time signals
|
||||
= +0.1 seconds beginning 10 July 2025 at 0000 UTC
|
||||
Beginning 1 January 2017:
|
||||
TAI-UTC = 37.000 000 seconds
|
||||
***********************************************************************
|
||||
* ANNOUNCEMENTS: *
|
||||
* *
|
||||
* There will NOT be a leap second introduced in UTC *
|
||||
* at the end of December 2025. *
|
||||
* *
|
||||
* The primary source for IERS Rapid Service/Prediction Center (RS/PC) *
|
||||
* data products is the official IERS RS/PC website: *
|
||||
* https://maia.usno.navy.mil *
|
||||
* *
|
||||
* IERS RS/PC products are also available from: *
|
||||
* NASA CDDIS: https://cddis.nasa.gov/archive/products/iers/ *
|
||||
* NASA CDDIS: ftps://gdc.cddis.eosdis.nasa.gov/products/iers/ *
|
||||
* IERS Central Bureau: https://datacenter.iers.org/eop.php *
|
||||
* *
|
||||
* Questions about IERS RS/PC products can be emailed to: *
|
||||
* eopcp@us.navy.mil *
|
||||
* *
|
||||
* Distribution statement A: *
|
||||
* Approved for public release: distribution unlimited. *
|
||||
* *
|
||||
***********************************************************************
|
||||
________________________________________________________________________
|
||||
The contributed observations used in the preparation of this Bulletin
|
||||
are available at <http://www.usno.navy.mil/USNO/earth-orientation/
|
||||
eo-info/general/input-data>. The contributed analysis results are based
|
||||
on data from Very Long Baseline Interferometry (VLBI), Satellite Laser
|
||||
Ranging (SLR), the Global Positioning System (GPS) satellites, Lunar
|
||||
Laser Ranging (LLR), and meteorological predictions of variations in
|
||||
Atmospheric Angular Momentum (AAM).
|
||||
________________________________________________________________________
|
||||
|
||||
COMBINED EARTH ORIENTATION PARAMETERS:
|
||||
|
||||
IERS Rapid Service
|
||||
MJD x error y error UT1-UTC error
|
||||
" " " " s s
|
||||
25 8 1 60888 0.21017 .00009 0.42717 .00009 0.062125 0.000021
|
||||
25 8 2 60889 0.21181 .00009 0.42627 .00009 0.062752 0.000019
|
||||
25 8 3 60890 0.21302 .00009 0.42504 .00009 0.063645 0.000016
|
||||
25 8 4 60891 0.21368 .00009 0.42383 .00009 0.064781 0.000010
|
||||
25 8 5 60892 0.21398 .00009 0.42271 .00009 0.066047 0.000009
|
||||
25 8 6 60893 0.21437 .00009 0.42152 .00009 0.067348 0.000008
|
||||
25 8 7 60894 0.21485 .00009 0.42038 .00009 0.068615 0.000055
|
||||
|
||||
IERS Final Values
|
||||
MJD x y UT1-UTC
|
||||
" " s
|
||||
25 6 2 60828 0.1141 0.4380 0.02903
|
||||
25 6 3 60829 0.1154 0.4384 0.02896
|
||||
25 6 4 60830 0.1172 0.4390 0.02885
|
||||
25 6 5 60831 0.1187 0.4399 0.02874
|
||||
25 6 6 60832 0.1202 0.4403 0.02868
|
||||
25 6 7 60833 0.1217 0.4408 0.02871
|
||||
25 6 8 60834 0.1232 0.4410 0.02891
|
||||
25 6 9 60835 0.1248 0.4415 0.02936
|
||||
25 6 10 60836 0.1262 0.4420 0.03004
|
||||
25 6 11 60837 0.1276 0.4425 0.03086
|
||||
25 6 12 60838 0.1291 0.4428 0.03178
|
||||
25 6 13 60839 0.1307 0.4428 0.03273
|
||||
25 6 14 60840 0.1325 0.4426 0.03360
|
||||
25 6 15 60841 0.1347 0.4424 0.03430
|
||||
25 6 16 60842 0.1370 0.4426 0.03479
|
||||
25 6 17 60843 0.1389 0.4427 0.03506
|
||||
25 6 18 60844 0.1406 0.4429 0.03512
|
||||
25 6 19 60845 0.1420 0.4431 0.03504
|
||||
25 6 20 60846 0.1436 0.4427 0.03492
|
||||
25 6 21 60847 0.1452 0.4426 0.03489
|
||||
25 6 22 60848 0.1468 0.4423 0.03515
|
||||
25 6 23 60849 0.1486 0.4420 0.03583
|
||||
25 6 24 60850 0.1502 0.4416 0.03682
|
||||
25 6 25 60851 0.1518 0.4411 0.03797
|
||||
25 6 26 60852 0.1533 0.4407 0.03919
|
||||
25 6 27 60853 0.1548 0.4404 0.04037
|
||||
25 6 28 60854 0.1564 0.4401 0.04139
|
||||
25 6 29 60855 0.1585 0.4400 0.04222
|
||||
25 6 30 60856 0.1603 0.4401 0.04287
|
||||
25 7 1 60857 0.1621 0.4398 0.04342
|
||||
|
||||
_______________________________________________________________________
|
||||
|
||||
PREDICTIONS:
|
||||
The following formulas will not reproduce the predictions given below,
|
||||
but may be used to extend the predictions beyond the end of this table.
|
||||
|
||||
x = 0.1420 + 0.1046 cos A + 0.1043 sin A - 0.0336 cos C - 0.0648 sin C
|
||||
y = 0.3838 + 0.1044 cos A - 0.0915 sin A - 0.0648 cos C + 0.0336 sin C
|
||||
UT1-UTC = 0.0474 + 0.00010 (MJD - 60902) - (UT2-UT1)
|
||||
|
||||
where A = 2*pi*(MJD-60894)/365.25 and C = 2*pi*(MJD-60894)/435.
|
||||
|
||||
TAI-UTC(MJD 60895) = 37.0
|
||||
The accuracy may be estimated from the expressions:
|
||||
S x,y = 0.00068 (MJD-60894)**0.80 S t = 0.00025 (MJD-60894)**0.75
|
||||
Estimated accuracies are: Predictions 10 d 20 d 30 d 40 d
|
||||
Polar coord's 0.004 0.007 0.010 0.013
|
||||
UT1-UTC 0.0014 0.0024 0.0032 0.0040
|
||||
|
||||
MJD x(arcsec) y(arcsec) UT1-UTC(sec)
|
||||
2025 8 8 60895 0.2155 0.4191 0.06979
|
||||
2025 8 9 60896 0.2161 0.4179 0.07073
|
||||
2025 8 10 60897 0.2169 0.4167 0.07137
|
||||
2025 8 11 60898 0.2176 0.4154 0.07173
|
||||
2025 8 12 60899 0.2184 0.4142 0.07190
|
||||
2025 8 13 60900 0.2191 0.4130 0.07200
|
||||
2025 8 14 60901 0.2198 0.4117 0.07218
|
||||
2025 8 15 60902 0.2204 0.4105 0.07254
|
||||
2025 8 16 60903 0.2210 0.4094 0.07316
|
||||
2025 8 17 60904 0.2216 0.4082 0.07403
|
||||
2025 8 18 60905 0.2222 0.4070 0.07507
|
||||
2025 8 19 60906 0.2227 0.4058 0.07619
|
||||
2025 8 20 60907 0.2232 0.4046 0.07725
|
||||
2025 8 21 60908 0.2237 0.4033 0.07814
|
||||
2025 8 22 60909 0.2242 0.4021 0.07878
|
||||
2025 8 23 60910 0.2247 0.4008 0.07912
|
||||
2025 8 24 60911 0.2251 0.3995 0.07921
|
||||
2025 8 25 60912 0.2255 0.3983 0.07914
|
||||
2025 8 26 60913 0.2259 0.3970 0.07899
|
||||
2025 8 27 60914 0.2263 0.3957 0.07887
|
||||
2025 8 28 60915 0.2266 0.3944 0.07884
|
||||
2025 8 29 60916 0.2269 0.3931 0.07898
|
||||
2025 8 30 60917 0.2272 0.3918 0.07930
|
||||
2025 8 31 60918 0.2275 0.3905 0.07979
|
||||
2025 9 1 60919 0.2278 0.3892 0.08042
|
||||
2025 9 2 60920 0.2280 0.3879 0.08112
|
||||
2025 9 3 60921 0.2282 0.3865 0.08182
|
||||
2025 9 4 60922 0.2283 0.3852 0.08242
|
||||
2025 9 5 60923 0.2285 0.3839 0.08283
|
||||
2025 9 6 60924 0.2286 0.3825 0.08295
|
||||
2025 9 7 60925 0.2287 0.3812 0.08277
|
||||
2025 9 8 60926 0.2287 0.3799 0.08232
|
||||
2025 9 9 60927 0.2287 0.3785 0.08173
|
||||
2025 9 10 60928 0.2288 0.3772 0.08114
|
||||
2025 9 11 60929 0.2287 0.3758 0.08072
|
||||
2025 9 12 60930 0.2287 0.3745 0.08056
|
||||
2025 9 13 60931 0.2286 0.3731 0.08070
|
||||
2025 9 14 60932 0.2285 0.3718 0.08108
|
||||
2025 9 15 60933 0.2284 0.3705 0.08159
|
||||
2025 9 16 60934 0.2282 0.3691 0.08209
|
||||
2025 9 17 60935 0.2281 0.3678 0.08247
|
||||
2025 9 18 60936 0.2279 0.3664 0.08265
|
||||
2025 9 19 60937 0.2276 0.3651 0.08260
|
||||
2025 9 20 60938 0.2274 0.3638 0.08234
|
||||
2025 9 21 60939 0.2271 0.3624 0.08194
|
||||
2025 9 22 60940 0.2268 0.3611 0.08146
|
||||
2025 9 23 60941 0.2264 0.3598 0.08101
|
||||
2025 9 24 60942 0.2261 0.3585 0.08064
|
||||
2025 9 25 60943 0.2257 0.3571 0.08042
|
||||
2025 9 26 60944 0.2253 0.3558 0.08037
|
||||
2025 9 27 60945 0.2248 0.3545 0.08049
|
||||
2025 9 28 60946 0.2244 0.3532 0.08076
|
||||
2025 9 29 60947 0.2239 0.3520 0.08112
|
||||
2025 9 30 60948 0.2234 0.3507 0.08148
|
||||
2025 10 1 60949 0.2228 0.3494 0.08177
|
||||
2025 10 2 60950 0.2223 0.3481 0.08189
|
||||
2025 10 3 60951 0.2217 0.3469 0.08176
|
||||
2025 10 4 60952 0.2211 0.3456 0.08133
|
||||
2025 10 5 60953 0.2204 0.3444 0.08059
|
||||
2025 10 6 60954 0.2198 0.3431 0.07962
|
||||
2025 10 7 60955 0.2191 0.3419 0.07856
|
||||
2025 10 8 60956 0.2184 0.3407 0.07759
|
||||
2025 10 9 60957 0.2177 0.3395 0.07687
|
||||
2025 10 10 60958 0.2169 0.3383 0.07647
|
||||
2025 10 11 60959 0.2161 0.3371 0.07637
|
||||
2025 10 12 60960 0.2153 0.3360 0.07648
|
||||
2025 10 13 60961 0.2145 0.3348 0.07665
|
||||
2025 10 14 60962 0.2137 0.3337 0.07674
|
||||
2025 10 15 60963 0.2128 0.3325 0.07667
|
||||
2025 10 16 60964 0.2119 0.3314 0.07640
|
||||
2025 10 17 60965 0.2110 0.3303 0.07594
|
||||
2025 10 18 60966 0.2101 0.3292 0.07535
|
||||
2025 10 19 60967 0.2092 0.3281 0.07470
|
||||
2025 10 20 60968 0.2082 0.3271 0.07405
|
||||
2025 10 21 60969 0.2072 0.3260 0.07350
|
||||
2025 10 22 60970 0.2062 0.3250 0.07310
|
||||
2025 10 23 60971 0.2052 0.3240 0.07289
|
||||
2025 10 24 60972 0.2041 0.3230 0.07287
|
||||
2025 10 25 60973 0.2031 0.3220 0.07302
|
||||
2025 10 26 60974 0.2020 0.3210 0.07329
|
||||
2025 10 27 60975 0.2009 0.3200 0.07360
|
||||
2025 10 28 60976 0.1998 0.3191 0.07388
|
||||
2025 10 29 60977 0.1986 0.3182 0.07405
|
||||
2025 10 30 60978 0.1975 0.3173 0.07402
|
||||
2025 10 31 60979 0.1963 0.3164 0.07372
|
||||
2025 11 1 60980 0.1951 0.3155 0.07315
|
||||
2025 11 2 60981 0.1939 0.3147 0.07232
|
||||
2025 11 3 60982 0.1927 0.3139 0.07133
|
||||
2025 11 4 60983 0.1915 0.3130 0.07034
|
||||
2025 11 5 60984 0.1902 0.3122 0.06953
|
||||
2025 11 6 60985 0.1890 0.3115 0.06901
|
||||
2025 11 7 60986 0.1877 0.3107 0.06882
|
||||
2025 11 8 60987 0.1864 0.3100 0.06889
|
||||
2025 11 9 60988 0.1851 0.3093 0.06908
|
||||
2025 11 10 60989 0.1838 0.3086 0.06924
|
||||
2025 11 11 60990 0.1825 0.3079 0.06927
|
||||
2025 11 12 60991 0.1812 0.3072 0.06910
|
||||
2025 11 13 60992 0.1798 0.3066 0.06876
|
||||
2025 11 14 60993 0.1784 0.3060 0.06828
|
||||
2025 11 15 60994 0.1771 0.3054 0.06774
|
||||
2025 11 16 60995 0.1757 0.3048 0.06722
|
||||
2025 11 17 60996 0.1743 0.3043 0.06677
|
||||
2025 11 18 60997 0.1729 0.3038 0.06647
|
||||
2025 11 19 60998 0.1715 0.3033 0.06635
|
||||
2025 11 20 60999 0.1701 0.3028 0.06642
|
||||
2025 11 21 61000 0.1687 0.3023 0.06667
|
||||
2025 11 22 61001 0.1672 0.3019 0.06705
|
||||
2025 11 23 61002 0.1658 0.3015 0.06751
|
||||
2025 11 24 61003 0.1643 0.3011 0.06788
|
||||
2025 11 25 61004 0.1629 0.3007 0.06817
|
||||
2025 11 26 61005 0.1614 0.3003 0.06829
|
||||
2025 11 27 61006 0.1600 0.3000 0.06820
|
||||
2025 11 28 61007 0.1585 0.2997 0.06785
|
||||
2025 11 29 61008 0.1570 0.2994 0.06727
|
||||
2025 11 30 61009 0.1556 0.2992 0.06650
|
||||
2025 12 1 61010 0.1541 0.2989 0.06567
|
||||
2025 12 2 61011 0.1526 0.2987 0.06494
|
||||
2025 12 3 61012 0.1511 0.2985 0.06442
|
||||
2025 12 4 61013 0.1496 0.2984 0.06421
|
||||
2025 12 5 61014 0.1481 0.2982 0.06429
|
||||
2025 12 6 61015 0.1466 0.2981 0.06454
|
||||
2025 12 7 61016 0.1451 0.2980 0.06483
|
||||
2025 12 8 61017 0.1437 0.2980 0.06500
|
||||
2025 12 9 61018 0.1422 0.2979 0.06498
|
||||
2025 12 10 61019 0.1407 0.2979 0.06476
|
||||
2025 12 11 61020 0.1392 0.2979 0.06439
|
||||
2025 12 12 61021 0.1377 0.2979 0.06395
|
||||
2025 12 13 61022 0.1362 0.2980 0.06353
|
||||
2025 12 14 61023 0.1347 0.2980 0.06319
|
||||
2025 12 15 61024 0.1332 0.2981 0.06299
|
||||
2025 12 16 61025 0.1318 0.2982 0.06297
|
||||
2025 12 17 61026 0.1303 0.2984 0.06315
|
||||
2025 12 18 61027 0.1288 0.2985 0.06351
|
||||
2025 12 19 61028 0.1274 0.2987 0.06401
|
||||
2025 12 20 61029 0.1259 0.2989 0.06461
|
||||
2025 12 21 61030 0.1245 0.2992 0.06522
|
||||
2025 12 22 61031 0.1230 0.2994 0.06575
|
||||
2025 12 23 61032 0.1216 0.2997 0.06614
|
||||
2025 12 24 61033 0.1201 0.3000 0.06632
|
||||
2025 12 25 61034 0.1187 0.3003 0.06627
|
||||
2025 12 26 61035 0.1173 0.3007 0.06599
|
||||
2025 12 27 61036 0.1159 0.3011 0.06554
|
||||
2025 12 28 61037 0.1145 0.3015 0.06500
|
||||
2025 12 29 61038 0.1131 0.3019 0.06451
|
||||
2025 12 30 61039 0.1117 0.3023 0.06418
|
||||
2025 12 31 61040 0.1103 0.3028 0.06410
|
||||
2026 1 1 61041 0.1090 0.3033 0.06429
|
||||
2026 1 2 61042 0.1076 0.3038 0.06468
|
||||
2026 1 3 61043 0.1063 0.3043 0.06514
|
||||
2026 1 4 61044 0.1050 0.3048 0.06553
|
||||
2026 1 5 61045 0.1036 0.3054 0.06573
|
||||
2026 1 6 61046 0.1023 0.3060 0.06569
|
||||
2026 1 7 61047 0.1010 0.3066 0.06545
|
||||
2026 1 8 61048 0.0998 0.3072 0.06509
|
||||
2026 1 9 61049 0.0985 0.3079 0.06472
|
||||
2026 1 10 61050 0.0973 0.3086 0.06441
|
||||
2026 1 11 61051 0.0960 0.3093 0.06423
|
||||
2026 1 12 61052 0.0948 0.3100 0.06423
|
||||
2026 1 13 61053 0.0936 0.3107 0.06442
|
||||
2026 1 14 61054 0.0924 0.3115 0.06480
|
||||
2026 1 15 61055 0.0912 0.3122 0.06532
|
||||
2026 1 16 61056 0.0901 0.3130 0.06595
|
||||
2026 1 17 61057 0.0889 0.3138 0.06660
|
||||
2026 1 18 61058 0.0878 0.3146 0.06719
|
||||
2026 1 19 61059 0.0867 0.3155 0.06763
|
||||
2026 1 20 61060 0.0856 0.3164 0.06787
|
||||
2026 1 21 61061 0.0846 0.3172 0.06785
|
||||
2026 1 22 61062 0.0835 0.3181 0.06761
|
||||
2026 1 23 61063 0.0825 0.3191 0.06717
|
||||
2026 1 24 61064 0.0815 0.3200 0.06664
|
||||
2026 1 25 61065 0.0805 0.3209 0.06615
|
||||
2026 1 26 61066 0.0795 0.3219 0.06579
|
||||
2026 1 27 61067 0.0786 0.3229 0.06566
|
||||
2026 1 28 61068 0.0776 0.3239 0.06578
|
||||
2026 1 29 61069 0.0767 0.3249 0.06612
|
||||
2026 1 30 61070 0.0758 0.3259 0.06656
|
||||
2026 1 31 61071 0.0749 0.3270 0.06697
|
||||
2026 2 1 61072 0.0741 0.3280 0.06723
|
||||
2026 2 2 61073 0.0733 0.3291 0.06725
|
||||
2026 2 3 61074 0.0725 0.3302 0.06704
|
||||
2026 2 4 61075 0.0717 0.3313 0.06666
|
||||
2026 2 5 61076 0.0709 0.3324 0.06622
|
||||
2026 2 6 61077 0.0702 0.3335 0.06583
|
||||
2026 2 7 61078 0.0695 0.3346 0.06557
|
||||
2026 2 8 61079 0.0688 0.3358 0.06552
|
||||
2026 2 9 61080 0.0681 0.3369 0.06570
|
||||
2026 2 10 61081 0.0675 0.3381 0.06610
|
||||
2026 2 11 61082 0.0668 0.3393 0.06671
|
||||
2026 2 12 61083 0.0662 0.3405 0.06747
|
||||
2026 2 13 61084 0.0657 0.3417 0.06829
|
||||
2026 2 14 61085 0.0651 0.3429 0.06910
|
||||
2026 2 15 61086 0.0646 0.3441 0.06977
|
||||
2026 2 16 61087 0.0641 0.3453 0.07023
|
||||
2026 2 17 61088 0.0636 0.3466 0.07040
|
||||
2026 2 18 61089 0.0632 0.3478 0.07026
|
||||
2026 2 19 61090 0.0627 0.3491 0.06986
|
||||
2026 2 20 61091 0.0623 0.3503 0.06930
|
||||
2026 2 21 61092 0.0620 0.3516 0.06869
|
||||
2026 2 22 61093 0.0616 0.3529 0.06818
|
||||
2026 2 23 61094 0.0613 0.3541 0.06787
|
||||
2026 2 24 61095 0.0610 0.3554 0.06779
|
||||
2026 2 25 61096 0.0607 0.3567 0.06793
|
||||
2026 2 26 61097 0.0605 0.3580 0.06818
|
||||
2026 2 27 61098 0.0602 0.3593 0.06844
|
||||
2026 2 28 61099 0.0600 0.3606 0.06858
|
||||
2026 3 1 61100 0.0599 0.3619 0.06850
|
||||
2026 3 2 61101 0.0597 0.3632 0.06818
|
||||
2026 3 3 61102 0.0596 0.3646 0.06765
|
||||
2026 3 4 61103 0.0595 0.3659 0.06699
|
||||
2026 3 5 61104 0.0595 0.3672 0.06631
|
||||
2026 3 6 61105 0.0594 0.3685 0.06572
|
||||
2026 3 7 61106 0.0594 0.3698 0.06529
|
||||
2026 3 8 61107 0.0594 0.3712 0.06505
|
||||
2026 3 9 61108 0.0595 0.3725 0.06500
|
||||
2026 3 10 61109 0.0595 0.3738 0.06511
|
||||
2026 3 11 61110 0.0596 0.3751 0.06533
|
||||
2026 3 12 61111 0.0597 0.3765 0.06562
|
||||
2026 3 13 61112 0.0599 0.3778 0.06589
|
||||
2026 3 14 61113 0.0600 0.3791 0.06606
|
||||
2026 3 15 61114 0.0602 0.3804 0.06604
|
||||
2026 3 16 61115 0.0604 0.3818 0.06576
|
||||
2026 3 17 61116 0.0607 0.3831 0.06516
|
||||
2026 3 18 61117 0.0610 0.3844 0.06423
|
||||
2026 3 19 61118 0.0613 0.3857 0.06304
|
||||
2026 3 20 61119 0.0616 0.3870 0.06180
|
||||
2026 3 21 61120 0.0619 0.3883 0.06065
|
||||
2026 3 22 61121 0.0623 0.3896 0.05966
|
||||
2026 3 23 61122 0.0627 0.3909 0.05895
|
||||
2026 3 24 61123 0.0631 0.3922 0.05844
|
||||
2026 3 25 61124 0.0636 0.3934 0.05812
|
||||
2026 3 26 61125 0.0640 0.3947 0.05791
|
||||
2026 3 27 61126 0.0645 0.3960 0.05759
|
||||
2026 3 28 61127 0.0651 0.3972 0.05717
|
||||
2026 3 29 61128 0.0656 0.3985 0.05646
|
||||
2026 3 30 61129 0.0662 0.3997 0.05548
|
||||
2026 3 31 61130 0.0668 0.4010 0.05432
|
||||
2026 4 1 61131 0.0674 0.4022 0.05306
|
||||
2026 4 2 61132 0.0680 0.4034 0.05187
|
||||
2026 4 3 61133 0.0687 0.4046 0.05076
|
||||
2026 4 4 61134 0.0694 0.4058 0.04986
|
||||
2026 4 5 61135 0.0701 0.4070 0.04920
|
||||
2026 4 6 61136 0.0708 0.4082 0.04872
|
||||
2026 4 7 61137 0.0716 0.4093 0.04838
|
||||
2026 4 8 61138 0.0724 0.4105 0.04815
|
||||
2026 4 9 61139 0.0732 0.4116 0.04795
|
||||
2026 4 10 61140 0.0740 0.4128 0.04768
|
||||
2026 4 11 61141 0.0748 0.4139 0.04735
|
||||
2026 4 12 61142 0.0757 0.4150 0.04689
|
||||
2026 4 13 61143 0.0766 0.4161 0.04620
|
||||
2026 4 14 61144 0.0775 0.4172 0.04531
|
||||
2026 4 15 61145 0.0784 0.4182 0.04419
|
||||
2026 4 16 61146 0.0794 0.4193 0.04299
|
||||
2026 4 17 61147 0.0803 0.4203 0.04183
|
||||
2026 4 18 61148 0.0813 0.4213 0.04095
|
||||
2026 4 19 61149 0.0823 0.4223 0.04036
|
||||
2026 4 20 61150 0.0833 0.4233 0.04007
|
||||
2026 4 21 61151 0.0844 0.4243 0.04013
|
||||
2026 4 22 61152 0.0854 0.4253 0.04026
|
||||
2026 4 23 61153 0.0865 0.4262 0.04040
|
||||
2026 4 24 61154 0.0876 0.4271 0.04039
|
||||
2026 4 25 61155 0.0887 0.4280 0.04013
|
||||
2026 4 26 61156 0.0899 0.4289 0.03962
|
||||
2026 4 27 61157 0.0910 0.4298 0.03894
|
||||
2026 4 28 61158 0.0922 0.4307 0.03819
|
||||
2026 4 29 61159 0.0933 0.4315 0.03743
|
||||
2026 4 30 61160 0.0945 0.4323 0.03681
|
||||
2026 5 1 61161 0.0957 0.4331 0.03639
|
||||
2026 5 2 61162 0.0970 0.4339 0.03619
|
||||
2026 5 3 61163 0.0982 0.4347 0.03616
|
||||
2026 5 4 61164 0.0994 0.4354 0.03629
|
||||
2026 5 5 61165 0.1007 0.4362 0.03660
|
||||
2026 5 6 61166 0.1020 0.4369 0.03691
|
||||
2026 5 7 61167 0.1033 0.4376 0.03721
|
||||
2026 5 8 61168 0.1046 0.4382 0.03749
|
||||
2026 5 9 61169 0.1059 0.4389 0.03763
|
||||
2026 5 10 61170 0.1072 0.4395 0.03762
|
||||
2026 5 11 61171 0.1086 0.4401 0.03744
|
||||
2026 5 12 61172 0.1099 0.4407 0.03706
|
||||
2026 5 13 61173 0.1113 0.4413 0.03652
|
||||
2026 5 14 61174 0.1126 0.4418 0.03595
|
||||
2026 5 15 61175 0.1140 0.4423 0.03555
|
||||
2026 5 16 61176 0.1154 0.4428 0.03545
|
||||
2026 5 17 61177 0.1168 0.4433 0.03561
|
||||
2026 5 18 61178 0.1182 0.4438 0.03605
|
||||
2026 5 19 61179 0.1196 0.4442 0.03672
|
||||
2026 5 20 61180 0.1210 0.4446 0.03732
|
||||
2026 5 21 61181 0.1224 0.4450 0.03779
|
||||
2026 5 22 61182 0.1239 0.4454 0.03807
|
||||
2026 5 23 61183 0.1253 0.4457 0.03818
|
||||
2026 5 24 61184 0.1267 0.4461 0.03811
|
||||
2026 5 25 61185 0.1282 0.4464 0.03802
|
||||
2026 5 26 61186 0.1296 0.4466 0.03795
|
||||
2026 5 27 61187 0.1311 0.4469 0.03798
|
||||
2026 5 28 61188 0.1326 0.4471 0.03820
|
||||
2026 5 29 61189 0.1340 0.4473 0.03862
|
||||
2026 5 30 61190 0.1355 0.4475 0.03926
|
||||
2026 5 31 61191 0.1369 0.4477 0.04005
|
||||
2026 6 1 61192 0.1384 0.4478 0.04090
|
||||
2026 6 2 61193 0.1399 0.4480 0.04191
|
||||
2026 6 3 61194 0.1414 0.4481 0.04298
|
||||
2026 6 4 61195 0.1428 0.4481 0.04399
|
||||
2026 6 5 61196 0.1443 0.4482 0.04491
|
||||
2026 6 6 61197 0.1458 0.4482 0.04568
|
||||
2026 6 7 61198 0.1472 0.4482 0.04614
|
||||
2026 6 8 61199 0.1487 0.4482 0.04638
|
||||
2026 6 9 61200 0.1502 0.4481 0.04649
|
||||
2026 6 10 61201 0.1516 0.4481 0.04648
|
||||
2026 6 11 61202 0.1531 0.4480 0.04649
|
||||
2026 6 12 61203 0.1545 0.4479 0.04661
|
||||
2026 6 13 61204 0.1560 0.4477 0.04699
|
||||
2026 6 14 61205 0.1574 0.4476 0.04756
|
||||
2026 6 15 61206 0.1589 0.4474 0.04835
|
||||
2026 6 16 61207 0.1603 0.4472 0.04924
|
||||
2026 6 17 61208 0.1618 0.4470 0.05017
|
||||
2026 6 18 61209 0.1632 0.4467 0.05089
|
||||
2026 6 19 61210 0.1646 0.4464 0.05142
|
||||
2026 6 20 61211 0.1660 0.4461 0.05178
|
||||
2026 6 21 61212 0.1674 0.4458 0.05200
|
||||
2026 6 22 61213 0.1688 0.4455 0.05221
|
||||
2026 6 23 61214 0.1702 0.4451 0.05260
|
||||
2026 6 24 61215 0.1716 0.4447 0.05320
|
||||
2026 6 25 61216 0.1730 0.4443 0.05401
|
||||
2026 6 26 61217 0.1743 0.4439 0.05511
|
||||
2026 6 27 61218 0.1757 0.4434 0.05647
|
||||
2026 6 28 61219 0.1770 0.4430 0.05798
|
||||
2026 6 29 61220 0.1783 0.4425 0.05967
|
||||
2026 6 30 61221 0.1797 0.4420 0.06128
|
||||
2026 7 1 61222 0.1810 0.4414 0.06275
|
||||
2026 7 2 61223 0.1823 0.4409 0.06403
|
||||
2026 7 3 61224 0.1836 0.4403 0.06505
|
||||
2026 7 4 61225 0.1848 0.4397 0.06585
|
||||
2026 7 5 61226 0.1861 0.4391 0.06645
|
||||
2026 7 6 61227 0.1873 0.4384 0.06686
|
||||
2026 7 7 61228 0.1886 0.4378 0.06722
|
||||
2026 7 8 61229 0.1898 0.4371 0.06760
|
||||
2026 7 9 61230 0.1910 0.4364 0.06810
|
||||
2026 7 10 61231 0.1921 0.4357 0.06889
|
||||
2026 7 11 61232 0.1933 0.4349 0.06992
|
||||
2026 7 12 61233 0.1945 0.4342 0.07122
|
||||
2026 7 13 61234 0.1956 0.4334 0.07254
|
||||
2026 7 14 61235 0.1967 0.4326 0.07391
|
||||
2026 7 15 61236 0.1978 0.4318 0.07504
|
||||
2026 7 16 61237 0.1989 0.4310 0.07591
|
||||
2026 7 17 61238 0.2000 0.4301 0.07652
|
||||
2026 7 18 61239 0.2010 0.4293 0.07702
|
||||
2026 7 19 61240 0.2021 0.4284 0.07743
|
||||
2026 7 20 61241 0.2031 0.4275 0.07799
|
||||
2026 7 21 61242 0.2041 0.4266 0.07873
|
||||
2026 7 22 61243 0.2050 0.4256 0.07969
|
||||
2026 7 23 61244 0.2060 0.4247 0.08084
|
||||
2026 7 24 61245 0.2069 0.4237 0.08212
|
||||
2026 7 25 61246 0.2078 0.4227 0.08367
|
||||
2026 7 26 61247 0.2087 0.4218 0.08531
|
||||
2026 7 27 61248 0.2096 0.4207 0.08702
|
||||
2026 7 28 61249 0.2105 0.4197 0.08862
|
||||
2026 7 29 61250 0.2113 0.4187 0.09004
|
||||
2026 7 30 61251 0.2121 0.4176 0.09130
|
||||
2026 7 31 61252 0.2129 0.4166 0.09234
|
||||
2026 8 1 61253 0.2136 0.4155 0.09321
|
||||
2026 8 2 61254 0.2144 0.4144 0.09386
|
||||
2026 8 3 61255 0.2151 0.4133 0.09435
|
||||
2026 8 4 61256 0.2158 0.4122 0.09493
|
||||
2026 8 5 61257 0.2165 0.4111 0.09554
|
||||
2026 8 6 61258 0.2171 0.4099 0.09635
|
||||
2026 8 7 61259 0.2177 0.4088 0.09737
|
||||
These predictions are based on all announced leap seconds.
|
||||
|
||||
CELESTIAL POLE OFFSET SERIES:
|
||||
NEOS Celestial Pole Offset Series
|
||||
MJD dpsi error deps error
|
||||
(msec. of arc)
|
||||
60868 -116.99 0.86 -10.78 0.21
|
||||
60869 -117.02 0.86 -10.71 0.30
|
||||
60870 -117.16 0.86 -10.75 0.30
|
||||
60871 -117.35 0.86 -10.82 0.34
|
||||
60872 -117.56 0.83 -10.83 0.29
|
||||
60873 -117.79 0.83 -10.77 0.29
|
||||
60874 -118.05 0.83 -10.70 0.29
|
||||
60875 -118.31 0.78 -10.66 0.17
|
||||
60876 -118.49 0.83 -10.70 0.16
|
||||
60877 -118.55 0.83 -10.81 0.16
|
||||
60878 -118.54 1.09 -10.92 0.16
|
||||
60879 -118.54 0.94 -10.97 0.15
|
||||
60880 -118.53 0.94 -10.93 0.15
|
||||
60881 -118.44 0.94 -10.81 0.15
|
||||
|
||||
IERS Celestial Pole Offset Final Series
|
||||
MJD dpsi deps
|
||||
(msec. of arc)
|
||||
60828 -111.0 -11.3
|
||||
60829 -111.7 -10.9
|
||||
60830 -112.1 -10.8
|
||||
60831 -112.0 -10.8
|
||||
60832 -111.7 -10.8
|
||||
60833 -111.7 -10.8
|
||||
60834 -111.9 -10.9
|
||||
60835 -112.2 -11.2
|
||||
60836 -112.5 -11.5
|
||||
60837 -112.7 -11.7
|
||||
60838 -112.8 -11.7
|
||||
60839 -112.7 -11.5
|
||||
60840 -112.6 -11.3
|
||||
60841 -112.5 -11.3
|
||||
60842 -112.4 -11.3
|
||||
60843 -112.3 -11.3
|
||||
60844 -112.4 -11.2
|
||||
60845 -112.6 -11.1
|
||||
60846 -113.0 -10.9
|
||||
60847 -113.6 -10.7
|
||||
60848 -114.2 -10.7
|
||||
60849 -114.6 -10.9
|
||||
60850 -114.7 -11.2
|
||||
60851 -114.6 -11.3
|
||||
60852 -114.5 -11.4
|
||||
60853 -114.3 -11.3
|
||||
60854 -114.0 -11.2
|
||||
60855 -114.1 -11.0
|
||||
60856 -114.6 -10.6
|
||||
60857 -115.3 -10.4
|
||||
|
||||
IAU2000A Celestial Pole Offset Series
|
||||
MJD dX error dY error
|
||||
(msec. of arc)
|
||||
60868 0.366 0.342 -0.270 0.206
|
||||
60869 0.360 0.343 -0.284 0.298
|
||||
60870 0.356 0.343 -0.292 0.298
|
||||
60871 0.352 0.343 -0.292 0.342
|
||||
60872 0.347 0.330 -0.282 0.288
|
||||
60873 0.343 0.330 -0.265 0.288
|
||||
60874 0.341 0.330 -0.243 0.288
|
||||
60875 0.342 0.308 -0.222 0.166
|
||||
60876 0.347 0.331 -0.202 0.164
|
||||
60877 0.352 0.331 -0.183 0.164
|
||||
60878 0.357 0.433 -0.166 0.157
|
||||
60879 0.359 0.375 -0.150 0.149
|
||||
60880 0.359 0.375 -0.135 0.149
|
||||
60881 0.358 0.375 -0.120 0.149
|
||||
|
||||
|
||||
IAU2000A Celestial Pole Offset Final Series
|
||||
MJD dX dY
|
||||
(msec. of arc)
|
||||
60828 0.30 -0.23
|
||||
60829 0.28 -0.26
|
||||
60830 0.28 -0.28
|
||||
60831 0.35 -0.27
|
||||
60832 0.43 -0.24
|
||||
60833 0.45 -0.20
|
||||
60834 0.43 -0.18
|
||||
60835 0.39 -0.16
|
||||
60836 0.34 -0.15
|
||||
60837 0.34 -0.19
|
||||
60838 0.35 -0.25
|
||||
60839 0.37 -0.30
|
||||
60840 0.41 -0.34
|
||||
60841 0.45 -0.35
|
||||
60842 0.50 -0.35
|
||||
60843 0.53 -0.34
|
||||
60844 0.52 -0.32
|
||||
60845 0.49 -0.29
|
||||
60846 0.45 -0.27
|
||||
60847 0.42 -0.25
|
||||
60848 0.41 -0.25
|
||||
60849 0.40 -0.26
|
||||
60850 0.39 -0.27
|
||||
60851 0.39 -0.29
|
||||
60852 0.39 -0.32
|
||||
60853 0.39 -0.33
|
||||
60854 0.38 -0.28
|
||||
60855 0.36 -0.19
|
||||
60856 0.35 -0.10
|
||||
60857 0.34 -0.03
|
||||
|
||||
)--";
|
||||
|
||||
|
||||
|
||||
} // namespace mcc::ccte::iers::defaults
|
||||
111
mcc/mcc_defaults.h
Normal file
111
mcc/mcc_defaults.h
Normal file
@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
||||
|
||||
|
||||
/* MCC LIBRARY DEFAULT IMPLEMENTATION OF SOME CLASSES */
|
||||
|
||||
// #include <compare>
|
||||
|
||||
#include "mcc_generics.h"
|
||||
|
||||
namespace mcc
|
||||
{
|
||||
|
||||
|
||||
/* DEFAULT TIME POINT CLASS */
|
||||
|
||||
typedef std::chrono::system_clock::time_point MccTimePoint;
|
||||
|
||||
|
||||
/* DEFAULT JULIAN DAY CLASS */
|
||||
|
||||
struct MccJulianDay {
|
||||
static constexpr double MJD0 = 2400000.5;
|
||||
double mjd{51544.5}; // J2000.0
|
||||
|
||||
constexpr operator double() const
|
||||
{
|
||||
return MccJulianDay::MJD0 + mjd;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const MccJulianDay&) const = default;
|
||||
|
||||
constexpr auto operator<=>(double v) const
|
||||
{
|
||||
return v <=> (MccJulianDay::MJD0 + mjd);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* DEFAULT CELESTIAL POINT CLASS */
|
||||
|
||||
|
||||
template <mcc_angle_c CoordT>
|
||||
struct MccGenericCelestialPoint {
|
||||
typedef CoordT coord_t;
|
||||
|
||||
MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
|
||||
|
||||
MccTimePoint time_point{std::chrono::sys_days(std::chrono::year_month_day{std::chrono::January / 1 / 2000}) +
|
||||
std::chrono::hours(12)}; // J2000.0
|
||||
|
||||
coord_t X{}, Y{};
|
||||
};
|
||||
|
||||
|
||||
typedef MccGenericCelestialPoint<double> MccCelestialPoint;
|
||||
|
||||
|
||||
template <mcc_angle_c CoordT>
|
||||
struct MccGenericEqtHrzCoords : MccGenericCelestialPoint<CoordT> {
|
||||
using typename MccGenericCelestialPoint<CoordT>::coord_t;
|
||||
|
||||
coord_t RA_APP{}, DEC_APP{}, HA{}, AZ{}, ZD{}, ALT{};
|
||||
};
|
||||
|
||||
typedef MccGenericEqtHrzCoords<MccCelestialPoint::coord_t> MccEqtHrzCoords;
|
||||
|
||||
|
||||
template <mcc_angle_c CoordT>
|
||||
struct MccGenericPointingTarget : MccGenericEqtHrzCoords<CoordT> {
|
||||
using typename MccGenericEqtHrzCoords<CoordT>::coord_t;
|
||||
|
||||
coord_t RA_ICRS{}, DEC_ICRS{};
|
||||
};
|
||||
|
||||
|
||||
typedef MccGenericPointingTarget<MccCelestialPoint::coord_t> MccPointingTarget;
|
||||
|
||||
|
||||
/* DEFAULT TELEMETRY DATA CLASS */
|
||||
|
||||
template <mcc_angle_c CoordT>
|
||||
struct MccGenericTelemetryData : MccGenericEqtHrzCoords<CoordT> {
|
||||
using typename MccGenericEqtHrzCoords<CoordT>::coord_t;
|
||||
|
||||
MccJulianDay JD;
|
||||
coord_t LST; // local apparent sideral time
|
||||
|
||||
MccGenericPointingTarget<coord_t> target{};
|
||||
|
||||
coord_t speedX, speedY;
|
||||
|
||||
coord_t pcmX, pcmY;
|
||||
|
||||
coord_t refCorr;
|
||||
};
|
||||
|
||||
|
||||
typedef MccGenericTelemetryData<MccCelestialPoint::coord_t> MccTelemetryData;
|
||||
|
||||
|
||||
|
||||
/* JUST CHECK FOR CONCEPT CONSISTENCY */
|
||||
|
||||
static_assert(mcc_julday_c<MccJulianDay>, "");
|
||||
static_assert(mcc_celestial_point_c<MccCelestialPoint>, "");
|
||||
static_assert(mcc_telemetry_data_c<MccTelemetryData>, "");
|
||||
|
||||
|
||||
} // namespace mcc
|
||||
406
mcc/mcc_generics.h
Normal file
406
mcc/mcc_generics.h
Normal file
@ -0,0 +1,406 @@
|
||||
#pragma once
|
||||
|
||||
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
||||
|
||||
|
||||
|
||||
/* SOME LIBRARY-WIDE DECLARATIONS */
|
||||
|
||||
|
||||
#include <chrono>
|
||||
#include <concepts>
|
||||
|
||||
|
||||
#include "mcc_traits.h"
|
||||
|
||||
namespace mcc
|
||||
{
|
||||
|
||||
|
||||
// mount construction type (only the most common ones)
|
||||
enum class MccMountType : uint8_t { GERMAN_TYPE, FORK_TYPE, CROSSAXIS_TYPE, ALTAZ_TYPE };
|
||||
|
||||
enum MccCoordPairKind : size_t {
|
||||
COORDS_KIND_GENERIC,
|
||||
COORDS_KIND_RADEC_ICRS,
|
||||
COORDS_KIND_RADEC_APP,
|
||||
COORDS_KIND_HADEC_APP,
|
||||
COORDS_KIND_AZZD,
|
||||
COORDS_KIND_AZALT,
|
||||
COORDS_KIND_XY,
|
||||
COORDS_KIND_LATLON
|
||||
};
|
||||
|
||||
|
||||
/* FLOATING-POINT LIKE CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_fp_type_like_c =
|
||||
std::floating_point<T> ||
|
||||
(std::convertible_to<T, double> && std::constructible_from<T, double> && std::default_initializable<T>);
|
||||
|
||||
|
||||
/* ANGLE REPRESENTATION CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_angle_c = mcc_fp_type_like_c<T> && requires(T v, double vd) {
|
||||
{ v + v } -> std::same_as<T>;
|
||||
{ v - v } -> std::same_as<T>;
|
||||
{ v += v } -> std::same_as<T&>;
|
||||
{ v -= v } -> std::same_as<T&>;
|
||||
|
||||
{ v * vd } -> std::same_as<T>;
|
||||
{ v / vd } -> std::same_as<T>;
|
||||
};
|
||||
|
||||
|
||||
/* TIME POINT CLASS CONCEPT */
|
||||
|
||||
/*
|
||||
* USE OF STL std::chrono::time_point
|
||||
*/
|
||||
template <typename T>
|
||||
concept mcc_time_point_c = requires(T t) { []<typename CT, typename DT>(std::chrono::time_point<CT, DT>) {}(t); };
|
||||
|
||||
|
||||
/* JULIAN DAY CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_julday_c = mcc_fp_type_like_c<T> && requires(const T v) {
|
||||
// comparison operators
|
||||
v <=> v;
|
||||
};
|
||||
|
||||
|
||||
/* ERROR CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_error_c = std::convertible_to<T, bool> || requires(const T t) {
|
||||
{ t.operator bool() };
|
||||
};
|
||||
|
||||
|
||||
/* ATMOSPHERIC REFRACTION MODEL CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_refract_model_c = requires(const T t_const) {
|
||||
{ t_const.name() } -> std::formattable<char>;
|
||||
};
|
||||
|
||||
|
||||
/* CELESTIAL POINT WITH A PAIR OF COORDINATES CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_celestial_point_c = requires(T t) {
|
||||
requires std::same_as<decltype(t.pair_kind), MccCoordPairKind>; // type of given coordinate pair
|
||||
|
||||
requires mcc_time_point_c<decltype(t.time_point)>; // time point for given coordinates
|
||||
|
||||
requires mcc_angle_c<decltype(t.X)>; // co-longitude (X-axis)
|
||||
requires mcc_angle_c<decltype(t.Y)>; // co-latitude (Y-axis)
|
||||
};
|
||||
|
||||
|
||||
/* CELESTIAL POINT WITH APPARENT EQUATORIAL AND HORIZONTAL CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_eqt_hrz_coord_c = mcc_celestial_point_c<T> && requires(T t) {
|
||||
requires mcc_angle_c<decltype(t.RA_APP)>; // right ascension
|
||||
requires mcc_angle_c<decltype(t.DEC_APP)>; // declination
|
||||
requires mcc_angle_c<decltype(t.HA)>; // hour angle
|
||||
requires mcc_angle_c<decltype(t.AZ)>; // azimuth
|
||||
requires mcc_angle_c<decltype(t.ZD)>; // zenithal distance
|
||||
requires mcc_angle_c<decltype(t.ALT)>; // altitude
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* CELESTIAL COORDINATES TRANSFORMATION ENGINE */
|
||||
|
||||
|
||||
template <mcc_error_c RetT>
|
||||
struct mcc_CCTE_interface_t {
|
||||
virtual ~mcc_CCTE_interface_t() = default;
|
||||
|
||||
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
||||
RetT timepointToJulday(this SelfT&& self, mcc_time_point_c auto tp, mcc_julday_c auto* julday)
|
||||
{
|
||||
return std::forward<SelfT>(self).timepointToJulday(std::move(tp), julday);
|
||||
}
|
||||
|
||||
// APPARENT SIDERAL TIME
|
||||
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
||||
RetT timepointToAppSideral(this SelfT&& self, mcc_time_point_c auto tp, mcc_angle_c auto* st, bool islocal = false)
|
||||
{
|
||||
return std::forward<SelfT>(self).timepointToAppSideral(std::move(tp), st, islocal);
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
||||
RetT transformCoordinates(this SelfT&& self, mcc_celestial_point_c auto from_pt, mcc_celestial_point_c auto* to_pt)
|
||||
{
|
||||
return std::forward<SelfT>(self).transformCoordinates(std::move(from_pt), to_pt);
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
||||
RetT transformCoordinates(this SelfT&& self, mcc_celestial_point_c auto from_pt, mcc_eqt_hrz_coord_c auto* to_pt)
|
||||
{
|
||||
return std::forward<SelfT>(self).transformCoordinates(std::move(from_pt), to_pt);
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
||||
RetT parallacticAngle(this SelfT&& self, mcc_celestial_point_c auto pt, mcc_angle_c auto* pa)
|
||||
{
|
||||
return std::forward<SelfT>(self).parallacticAngle(std::move(pt), pa);
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_CCTE_interface_t> SelfT>
|
||||
RetT refractionCorrection(this SelfT&& self, mcc_celestial_point_c auto pt, mcc_angle_c auto* dZ)
|
||||
{
|
||||
return std::forward<SelfT>(self).refractionCoeff(std::move(pt), dZ);
|
||||
}
|
||||
|
||||
protected:
|
||||
mcc_CCTE_interface_t() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_ast_engine_c =
|
||||
std::derived_from<T, mcc_CCTE_interface_t<typename T::error_t>> && requires(const T t_const, T t) {
|
||||
{ t_const.name() } -> std::formattable<char>;
|
||||
|
||||
requires mcc_refract_model_c<typename T::refract_model_t>;
|
||||
|
||||
{ t.refractionModel(std::declval<typename T::refract_model_t*>()) } -> std::same_as<typename T::error_t>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* MOUNT TELEMETRY DATA CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_pointing_target_coord_c = mcc_eqt_hrz_coord_c<T> && requires(T t) {
|
||||
requires mcc_angle_c<decltype(t.RA_ICRS)>; // ICRS right ascention
|
||||
requires mcc_angle_c<decltype(t.DEC_ICRS)>; // ICRS declination
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept mcc_telemetry_data_c = mcc_eqt_hrz_coord_c<T> && requires(T t) {
|
||||
// target target coordinates
|
||||
requires mcc_pointing_target_coord_c<decltype(t.target)>;
|
||||
|
||||
// t.X and t.Y (from mcc_celestial_point_c) are encoder coordinates
|
||||
// t.* from mcc_eqt_hrz_coord_c are apparent mount pointing coordinates
|
||||
requires mcc_angle_c<decltype(t.speedX)>; // speed along X from hardware encoder
|
||||
requires mcc_angle_c<decltype(t.speedY)>; // speed along Y from hardware encoder
|
||||
|
||||
// corrections to transform hardware encoder coordinates to apparent celestial ones
|
||||
requires mcc_angle_c<decltype(t.pcmX)>; // PCM correction along X-axis
|
||||
requires mcc_angle_c<decltype(t.pcmY)>; // PCM correction along Y-axis
|
||||
|
||||
// atmospheric refraction correction for current zenithal distance
|
||||
requires mcc_angle_c<decltype(t.refCorr)>; // for current .ZD
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* MOUNT TELEMETRY MANAGER CLASS CONCEPT */
|
||||
|
||||
template <mcc_error_c RetT>
|
||||
struct mcc_telemetry_interface_t {
|
||||
virtual ~mcc_telemetry_interface_t() = default;
|
||||
|
||||
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
||||
RetT telemetryData(this SelfT&& self, mcc_telemetry_data_c auto* data)
|
||||
{
|
||||
return std::forward<SelfT>(self).telemetryData(data);
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
||||
RetT setPointingTarget(this SelfT&& self, mcc_celestial_point_c auto pt)
|
||||
{
|
||||
return std::forward<SelfT>(self).telemetryData(std::move(pt));
|
||||
}
|
||||
|
||||
protected:
|
||||
mcc_telemetry_interface_t() = default;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept mcc_telemetry_c = std::derived_from<T, mcc_telemetry_interface_t<typename T::error_t>>;
|
||||
|
||||
|
||||
|
||||
/* POINTING CORRECTION MODEL CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_PCM_result_c = requires(T t) {
|
||||
requires mcc_angle_c<decltype(t.dx)>;
|
||||
requires mcc_angle_c<decltype(t.dy)>;
|
||||
};
|
||||
|
||||
template <mcc_error_c RetT, mcc_PCM_result_c ResT>
|
||||
struct mcc_PCM_interface_t {
|
||||
template <std::derived_from<mcc_PCM_interface_t> SelfT>
|
||||
RetT computePCM(this SelfT&& self, mcc_celestial_point_c auto pt, ResT* result)
|
||||
{
|
||||
return std::forward<SelfT>(self).computePCM(std::move(pt), result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept mcc_PCM_c =
|
||||
std::derived_from<T, mcc_PCM_interface_t<typename T::error_t, typename T::pcm_result_t>> && requires {
|
||||
// the 'T' class must contain static constexpr member of 'MccMountType' type
|
||||
requires std::same_as<decltype(T::mountType), const MccMountType>;
|
||||
[]() {
|
||||
static constexpr MccMountType val = T::mountType;
|
||||
return val;
|
||||
}(); // to ensure 'mountType' can be used in compile-time context
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* MOUNT HARDWARE ABSTRACTION CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_hardware_c = requires(T t, const T t_const) {
|
||||
typename T::error_t;
|
||||
|
||||
{ t_const.name() } -> std::formattable<char>;
|
||||
|
||||
// a class that contains at least time point of measurement, coordinates for x,y axes and its moving rates
|
||||
requires requires(typename T::axes_pos_t pos) {
|
||||
requires mcc_time_point_c<typename T::time_point_t>; // time point
|
||||
|
||||
requires mcc_angle_c<decltype(pos.X)>; // target or current co-longitude coordinate
|
||||
requires mcc_angle_c<decltype(pos.Y)>; // target or current co-latitude coordinate
|
||||
|
||||
requires mcc_angle_c<decltype(pos.speedX)>;
|
||||
requires mcc_angle_c<decltype(pos.speedY)>;
|
||||
};
|
||||
|
||||
// set positions (angles) of mount axes with given speeds
|
||||
// NOTE: exact interpretation (or even ignoring) of the given moving speeds is subject of a hardware-class
|
||||
// implementation.
|
||||
// e.g. it can be maximal speeds at slewing ramp
|
||||
{ t.setPos(std::declval<typename T::axes_pos_t>()) } -> std::same_as<typename T::error_t>;
|
||||
|
||||
// get current positions and speeds (angles) of mount axes
|
||||
{ t.getPos(std::declval<typename T::axes_pos_t*>()) } -> std::same_as<typename T::error_t>;
|
||||
|
||||
{ t.stop() } -> std::same_as<typename T::error_t>; // stop any moving
|
||||
{ t.init() } -> std::same_as<typename T::error_t>; // initialize hardware
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* PROHIBITED ZONE CLASS CONCEPT */
|
||||
|
||||
template <mcc_error_c RetT>
|
||||
struct mcc_pzone_interface_t {
|
||||
virtual ~mcc_pzone_interface_t() = default;
|
||||
|
||||
template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT>
|
||||
RetT inPZone(this SelfT&& self, InputT coords, bool* result)
|
||||
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
||||
{
|
||||
return std::forward<SelfT>(self).InPZone(std::move(coords), result);
|
||||
}
|
||||
|
||||
|
||||
template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT>
|
||||
RetT timeToPZone(this SelfT&& self, InputT coords, traits::mcc_time_duration_c auto* res_time)
|
||||
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
||||
{
|
||||
return std::forward<SelfT>(self).timeToPZone(std::move(coords), res_time);
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT>
|
||||
RetT timeFromPZone(this SelfT&& self, InputT coords, traits::mcc_time_duration_c auto* res_time)
|
||||
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
||||
{
|
||||
return std::forward<SelfT>(self).timeFromPZone(std::move(coords), res_time);
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_pzone_interface_t> SelfT, typename InputT>
|
||||
RetT intersectPZone(this SelfT&& self, InputT coords, mcc_celestial_point_c auto* point)
|
||||
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>) &&
|
||||
requires { self.intersectPZone(coords, point); }
|
||||
{
|
||||
return std::forward<SelfT>(self).intersectPZone(std::move(coords), point);
|
||||
}
|
||||
|
||||
protected:
|
||||
mcc_pzone_interface_t() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_prohibited_zone_c =
|
||||
std::derived_from<T, mcc_pzone_interface_t<typename T::error_t>> && requires(const T t_const) {
|
||||
{ t_const.name() } -> std::formattable<char>;
|
||||
};
|
||||
|
||||
|
||||
/* PROHIBITED ZONES CONTAINER CLASS CONCEPT */
|
||||
|
||||
|
||||
template <mcc_error_c RetT>
|
||||
struct mcc_pzone_container_interface_t {
|
||||
virtual ~mcc_pzone_container_interface_t() = default;
|
||||
|
||||
|
||||
template <std::derived_from<mcc_pzone_container_interface_t> SelfT>
|
||||
size_t addPZone(this SelfT&& self, mcc_prohibited_zone_c auto zone)
|
||||
{
|
||||
return std::forward<SelfT>(self).addPZone(std::move(zone));
|
||||
}
|
||||
|
||||
|
||||
template <std::derived_from<mcc_pzone_container_interface_t> SelfT>
|
||||
void clearPZones(this SelfT&& self)
|
||||
{
|
||||
return std::forward<SelfT>(self).clearPZones();
|
||||
}
|
||||
|
||||
|
||||
template <std::derived_from<mcc_pzone_container_interface_t> SelfT>
|
||||
size_t sizePZones(this SelfT&& self)
|
||||
{
|
||||
return std::forward<SelfT>(self).sizePZones();
|
||||
}
|
||||
|
||||
|
||||
template <std::derived_from<mcc_pzone_container_interface_t> SelfT, typename InputT>
|
||||
RetT inPZone(this SelfT&& self, InputT coords, std::ranges::output_range<bool> auto* result)
|
||||
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
||||
{
|
||||
return std::forward<SelfT>(self).InPZone(std::move(coords), result);
|
||||
}
|
||||
|
||||
|
||||
template <std::derived_from<mcc_pzone_container_interface_t> SelfT, typename InputT, traits::mcc_time_duration_c DT>
|
||||
RetT timeToPZone(this SelfT&& self, InputT coords, std::ranges::output_range<DT> auto* res_time)
|
||||
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
||||
{
|
||||
return std::forward<SelfT>(self).timeToPZone(std::move(coords), res_time);
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_pzone_container_interface_t> SelfT, typename InputT, traits::mcc_time_duration_c DT>
|
||||
RetT timeFromPZone(this SelfT&& self, InputT coords, std::ranges::output_range<DT> auto* res_time)
|
||||
requires(mcc_eqt_hrz_coord_c<InputT> || mcc_celestial_point_c<InputT>)
|
||||
{
|
||||
return std::forward<SelfT>(self).timeFromPZone(std::move(coords), res_time);
|
||||
}
|
||||
|
||||
protected:
|
||||
mcc_pzone_container_interface_t() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_pzone_container_c = std::derived_from<T, mcc_pzone_container_interface_t<typename T::error_t>>;
|
||||
|
||||
|
||||
} // namespace mcc
|
||||
212
mcc/mcc_traits.h
Normal file
212
mcc/mcc_traits.h
Normal file
@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
#include <ranges>
|
||||
|
||||
namespace mcc::traits
|
||||
{
|
||||
|
||||
template <typename R>
|
||||
concept mcc_char_view = std::ranges::view<R> && std::same_as<std::ranges::range_value_t<R>, char>;
|
||||
|
||||
|
||||
// input range of char/const char
|
||||
template <typename R, typename CharT = char>
|
||||
concept mcc_input_char_range =
|
||||
std::ranges::input_range<R> && std::is_same_v<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
|
||||
|
||||
|
||||
// output range of char/const char
|
||||
template <typename R, typename CharT = char>
|
||||
concept mcc_output_char_range =
|
||||
std::ranges::output_range<R, CharT> && std::same_as<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
|
||||
|
||||
|
||||
template <typename R>
|
||||
concept mcc_view_or_output_char_range = mcc_char_view<R> || mcc_output_char_range<R>;
|
||||
|
||||
|
||||
template <typename R>
|
||||
concept mcc_range_of_input_char_range =
|
||||
std::ranges::range<R> && traits::mcc_input_char_range<std::ranges::range_value_t<R>>;
|
||||
|
||||
|
||||
template <typename R>
|
||||
concept mcc_range_of_output_char_range =
|
||||
std::ranges::range<R> && traits::mcc_output_char_range<std::ranges::range_value_t<R>>;
|
||||
|
||||
|
||||
|
||||
// https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
|
||||
template <typename T>
|
||||
concept mcc_formattable =
|
||||
requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
|
||||
|
||||
|
||||
// from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types
|
||||
template <typename T>
|
||||
concept mcc_time_duration_c = requires {
|
||||
[]<class Rep, class Period>(std::type_identity<std::chrono::duration<Rep, Period>>) {
|
||||
|
||||
}(std::type_identity<std::remove_cvref_t<T>>());
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_systime_c = requires {
|
||||
[]<class DT>(std::type_identity<std::chrono::sys_time<DT>>) {}(std::type_identity<std::remove_cvref_t<T>>());
|
||||
};
|
||||
|
||||
|
||||
/* a callable concept and its signature traits */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_is_callable = std::is_function_v<T> || (std::is_object_v<T> && requires(T) { &T::operator(); });
|
||||
|
||||
|
||||
// helper classes for callable signature deducing
|
||||
template <typename... Ts>
|
||||
struct mcc_func_traits_helper_t;
|
||||
|
||||
template <typename R>
|
||||
struct mcc_func_traits_helper_t<R> {
|
||||
using ret_t = R;
|
||||
using args_t = std::tuple<>;
|
||||
using arg1_t = void;
|
||||
static constexpr size_t arity = 0;
|
||||
};
|
||||
|
||||
template <typename R, typename Arg, typename... Args>
|
||||
struct mcc_func_traits_helper_t<R, Arg, Args...> {
|
||||
using ret_t = R;
|
||||
using args_t = std::tuple<Arg, Args...>;
|
||||
using arg1_t = Arg;
|
||||
static constexpr size_t arity = sizeof...(Args) + 1;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct mcc_func_traits {
|
||||
// use of an empty struct here to match std::invoke_result behaivior (at least of GCC)
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct mcc_func_traits<R (*)(Args...)> : mcc_func_traits_helper_t<R, Args...> {
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct mcc_func_traits<R(Args...)> : mcc_func_traits_helper_t<R, Args...> {
|
||||
};
|
||||
|
||||
template <typename C, typename R, typename... Args>
|
||||
struct mcc_func_traits<R (C::*)(Args...)> : mcc_func_traits_helper_t<R, Args...> {
|
||||
};
|
||||
|
||||
template <typename C, typename R, typename... Args>
|
||||
struct mcc_func_traits<R (C::*)(Args...) const> : mcc_func_traits_helper_t<R, Args...> {
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
requires mcc_is_callable<F>
|
||||
struct mcc_func_traits<F> : mcc_func_traits<decltype(&F::operator())> {
|
||||
};
|
||||
|
||||
// reference/const ref and rvalue helpers
|
||||
template <typename F>
|
||||
struct mcc_func_traits<F&> : mcc_func_traits<F> {
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct mcc_func_traits<const F&> : mcc_func_traits<F> {
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct mcc_func_traits<F&&> : mcc_func_traits<F> {
|
||||
};
|
||||
|
||||
// type of the returned value
|
||||
template <typename T>
|
||||
using mcc_retval_t = typename mcc_func_traits<T>::ret_t;
|
||||
|
||||
// type of the first argument of callable
|
||||
template <typename T>
|
||||
using mcc_func_arg1_t = typename mcc_func_traits<T>::arg1_t;
|
||||
|
||||
// type of the N-th argument of callable
|
||||
// NOTE: starts from 1 not from 0!!!
|
||||
template <typename T, size_t N = 1>
|
||||
using mcc_func_argN_t = std::conditional_t<N >= mcc_func_traits<T>::arity,
|
||||
std::tuple_element_t<N - 1, typename mcc_func_traits<T>::args_t>,
|
||||
void>;
|
||||
|
||||
|
||||
// non-const lvalue reference, constructible from CtorArgTs (an output argument of function)
|
||||
template <typename T, typename... CtorArgTs>
|
||||
concept mcc_output_arg_c = !std::is_const_v<std::remove_reference_t<T>> && std::is_lvalue_reference_v<T> &&
|
||||
std::constructible_from<std::remove_reference_t<T>, CtorArgTs...>;
|
||||
|
||||
|
||||
|
||||
// std::tuple or std::pair
|
||||
template <typename T>
|
||||
concept mcc_tuple_c = requires {
|
||||
requires requires {
|
||||
[]<typename... Ts>(std::type_identity<std::tuple<Ts...>>) {}(std::type_identity<std::remove_cvref_t<T>>());
|
||||
} || requires {
|
||||
[]<typename T1, typename T2>(std::type_identity<std::pair<T1, T2>>) {
|
||||
}(std::type_identity<std::remove_cvref_t<T>>());
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_nonconst_lvref = std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>;
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_error_c = std::convertible_to<T, bool> || requires(const T t) {
|
||||
{ t.operator bool() };
|
||||
};
|
||||
|
||||
namespace details
|
||||
{
|
||||
|
||||
// compile-time hash for type
|
||||
// (from https://stackoverflow.com/questions/56292104/hashing-types-at-compile-time-in-c17-c2a)
|
||||
// WARNING: it does not work for unnamed struct!!!
|
||||
template <typename T>
|
||||
static consteval size_t Hash()
|
||||
{
|
||||
size_t result{};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
for (const auto& c : __FUNCSIG__)
|
||||
#else // GCC and clang
|
||||
for (const auto& c : __PRETTY_FUNCTION__)
|
||||
#endif
|
||||
(result ^= c) <<= 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename T>
|
||||
static constexpr size_t mcc_type_hash = details::Hash<T>();
|
||||
|
||||
static constexpr size_t mcc_hash_combine(size_t lhs, size_t rhs)
|
||||
{
|
||||
constexpr size_t v_const = sizeof(size_t) >= 8 ? 0x517cc1b727220a95 : 0x9e3779b9;
|
||||
|
||||
lhs ^= rhs + v_const + (lhs << 6) + (lhs >> 2);
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
static constexpr size_t mcc_type_pair_hash()
|
||||
{
|
||||
return mcc_hash_combine(mcc_type_hash<T1>, mcc_type_hash<T2>);
|
||||
}
|
||||
|
||||
} // namespace mcc::traits
|
||||
311
mcc/mcc_utils.h
Normal file
311
mcc/mcc_utils.h
Normal file
@ -0,0 +1,311 @@
|
||||
#pragma once
|
||||
|
||||
#include <charconv>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <numbers>
|
||||
#include <ranges>
|
||||
#include <regex>
|
||||
|
||||
namespace mcc::utils
|
||||
{
|
||||
|
||||
static const std::regex decimalNumberRx{" *[-+]?([0-9]*[.])?[0-9]+([eE][-+]?[0-9]+)? *"};
|
||||
static const std::regex sexagesimalReprRx{" *[-+]?[0-9]{1,2}:[0-9]{1,2}:([0-9]{0,2}[.])?[0-9]+ *"};
|
||||
|
||||
static bool isEqual(std::floating_point auto const& v1, std::floating_point auto const& v2)
|
||||
{
|
||||
constexpr auto eps = std::numeric_limits<std::common_type_t<decltype(v1), decltype(v2)>>::epsilon();
|
||||
|
||||
return std::fabs(v1 - v2) <= eps * std::fmax(std::fabs(v1), std::fabs(v2));
|
||||
}
|
||||
|
||||
enum class TrimType { TRIM_LEFT, TRIM_RIGHT, TRIM_BOTH };
|
||||
|
||||
template <std::ranges::contiguous_range R>
|
||||
static std::string_view trimSpaces(R&& r, TrimType type = TrimType::TRIM_BOTH)
|
||||
requires std::same_as<char, std::remove_cvref_t<std::ranges::range_value_t<R>>>
|
||||
{
|
||||
auto is_space = [](const auto& ch) { return ch == ' '; };
|
||||
|
||||
auto end = std::forward<R>(r).end();
|
||||
|
||||
auto f1 = std::forward<R>(r).begin();
|
||||
|
||||
if (type != TrimType::TRIM_RIGHT) {
|
||||
// look for the first non-space symbol
|
||||
f1 = std::ranges::find_if_not(std::forward<R>(r), is_space);
|
||||
if (f1 == end) { // all are spaces!
|
||||
return std::string_view();
|
||||
}
|
||||
}
|
||||
|
||||
auto f2 = end;
|
||||
|
||||
if (type != TrimType::TRIM_LEFT) {
|
||||
auto f3 = f1;
|
||||
|
||||
do {
|
||||
f2 = std::ranges::find_if(++f3, end, is_space);
|
||||
if (f2 == end)
|
||||
break;
|
||||
f3 = std::ranges::find_if_not(f2 + 1, end, is_space);
|
||||
} while (f3 != end);
|
||||
}
|
||||
|
||||
return std::string_view(f1, f2);
|
||||
}
|
||||
|
||||
static std::string_view trimSpaces(const char* r, TrimType type = TrimType::TRIM_BOTH)
|
||||
{
|
||||
return trimSpaces(std::string_view(r), type);
|
||||
}
|
||||
|
||||
template <typename T, std::ranges::contiguous_range R>
|
||||
std::optional<T> numFromStr(R&& r)
|
||||
requires((std::integral<T> || std::floating_point<T>) &&
|
||||
std::same_as<char, std::remove_cvref_t<std::ranges::range_value_t<R>>>)
|
||||
{
|
||||
T val;
|
||||
const char* end_ptr = &*r.end();
|
||||
|
||||
if constexpr (std::integral<T>) {
|
||||
auto cvt_res = std::from_chars(&*r.begin(), &*r.end(), val);
|
||||
if (cvt_res.ec != std::errc()) {
|
||||
return std::nullopt;
|
||||
} else if (cvt_res.ptr != end_ptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars
|
||||
std::string s{str.begin(), str.end()};
|
||||
size_t pos;
|
||||
|
||||
try {
|
||||
if constexpr (std::same_as<T, float>) {
|
||||
val = std::stof(s, &pos);
|
||||
} else if constexpr (std::same_as<T, double>) {
|
||||
val = std::stod(s, &pos);
|
||||
} else {
|
||||
val = std::stold(s, &pos);
|
||||
}
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (pos != s.size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
#else
|
||||
auto cvt_res = std::from_chars(&*r.begin(), &*r.end(), val);
|
||||
|
||||
if (cvt_res.ec != std::errc()) {
|
||||
return std::nullopt;
|
||||
} else if (cvt_res.ptr != end_ptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
template <std::ranges::contiguous_range R>
|
||||
static std::optional<double> parsAngleString(R&& r, bool hms = false)
|
||||
requires std::same_as<char, std::remove_cvref_t<std::ranges::range_value_t<R>>>
|
||||
{
|
||||
auto str = trimSpaces(std::forward<R>(r));
|
||||
|
||||
bool ok = std::regex_match(str.begin(), str.end(), decimalNumberRx);
|
||||
|
||||
if (ok) {
|
||||
return numFromStr<double>(str);
|
||||
}
|
||||
|
||||
ok = std::regex_match(str.begin(), str.end(), sexagesimalReprRx);
|
||||
|
||||
if (ok) {
|
||||
auto str = trimSpaces(std::forward<R>(r));
|
||||
auto parts = std::views::split(str, std::string_view(":"));
|
||||
|
||||
double val;
|
||||
|
||||
double p1 = numFromStr<double>(*parts.begin()).value();
|
||||
val = std::abs(p1);
|
||||
double dv = 60.0;
|
||||
for (auto const& v : parts | std::views::drop(1)) {
|
||||
val += numFromStr<double>(v).value() / dv;
|
||||
dv *= 60.0;
|
||||
}
|
||||
// val += numFromStr<double>(parts[1]) / 60.0;
|
||||
// val += numFromStr<double>(parts[2]) / 3600.0;
|
||||
if (p1 < 0) {
|
||||
val = -val;
|
||||
}
|
||||
|
||||
if (hms) {
|
||||
val *= 15.0;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static std::optional<double> parsAngleString(const char* s, bool hms = false)
|
||||
{
|
||||
return parsAngleString(std::span{s, std::strlen(s)}, hms);
|
||||
}
|
||||
|
||||
|
||||
static constexpr auto deg2radCoeff = std::numbers::pi / 180.0;
|
||||
|
||||
// radians to degrees
|
||||
template <bool NORM = false>
|
||||
static double rad2deg(double ang)
|
||||
{
|
||||
auto r = ang / deg2radCoeff;
|
||||
|
||||
if constexpr (NORM) {
|
||||
return std::fmod(r, 360.0);
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <std::ranges::output_range<char> R, bool NORM = false>
|
||||
static R rad2deg_str(double ang, int prec = 6)
|
||||
{
|
||||
R res;
|
||||
|
||||
std::string fmt = "{:0." + std::to_string(prec) + "f}";
|
||||
|
||||
auto degs = rad2deg<NORM>(ang);
|
||||
|
||||
std::vformat_to(std::back_inserter(res), {fmt.begin(), fmt.end()}, std::make_format_args(degs));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <bool NORM = false>
|
||||
static std::string rad2deg_str(double ang, int prec = 6)
|
||||
{
|
||||
return rad2deg_str<std::string, NORM>(ang, prec);
|
||||
}
|
||||
|
||||
|
||||
template <std::ranges::output_range<char> R, bool NORM = false>
|
||||
static R rad2deg_str(double ang1, double ang2, std::string_view delim = ",", int prec1 = 6, int prec2 = 6)
|
||||
{
|
||||
R res = rad2deg_str<R, NORM>(ang1, prec1);
|
||||
|
||||
R r = rad2deg_str<R, NORM>(ang2, prec2);
|
||||
|
||||
std::ranges::copy(delim, std::back_inserter(res));
|
||||
std::ranges::copy(r, std::back_inserter(res));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <bool NORM = false>
|
||||
static std::string rad2deg_str(double ang1, double ang2, std::string_view delim = ",", int prec1 = 6, int prec2 = 6)
|
||||
{
|
||||
return rad2deg_str<std::string, NORM>(ang1, ang2, delim, prec1, prec2);
|
||||
}
|
||||
|
||||
// radians to sexagesimal
|
||||
template <std::ranges::output_range<char> R, bool NORM = false>
|
||||
static R rad2sxg(double ang, bool hms = false, int prec = 2)
|
||||
{
|
||||
R res;
|
||||
|
||||
std::string fmt = "{:02.0f}:{:02.0f}:{:0" + std::to_string(prec + 3) + "." + std::to_string(prec) + "f}";
|
||||
// std::string fmt = "{:02.0f}:{:02.0f}:{:02." + std::to_string(prec) + "f}";
|
||||
|
||||
auto degs = rad2deg<NORM>(std::abs(ang));
|
||||
if (hms) {
|
||||
degs /= 15.0;
|
||||
}
|
||||
|
||||
auto term = 10.0;
|
||||
for (int i = 1; i < prec; ++i) {
|
||||
term *= 10.0;
|
||||
}
|
||||
|
||||
auto d = std::trunc(degs);
|
||||
auto s = (degs - d) * 60.0;
|
||||
auto m = std::trunc(s);
|
||||
|
||||
s = (s - m) * 60.0;
|
||||
// round to given precision
|
||||
s = std::round(s * term) / term;
|
||||
if (isEqual(s, 60.0)) {
|
||||
m += 1.0;
|
||||
s = 0.0;
|
||||
if (isEqual(m, 60.0)) {
|
||||
d += 1.0;
|
||||
m = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ang < 0) {
|
||||
std::ranges::copy(std::string_view("-"), std::back_inserter(res));
|
||||
}
|
||||
|
||||
std::vformat_to(std::back_inserter(res), std::string_view{fmt.begin(), fmt.end()}, std::make_format_args(d, m, s));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <bool NORM = false>
|
||||
static std::string rad2sxg(double ang, bool hms = false, int prec = 2)
|
||||
{
|
||||
return rad2sxg<std::string, NORM>(ang, hms, prec);
|
||||
}
|
||||
|
||||
|
||||
template <std::ranges::output_range<char> R, bool NORM = false>
|
||||
static R RADEC_rad2sxg(double ra, double dec, std::string_view delim = ",", int ra_prec = 2, int dec_prec = 1)
|
||||
{
|
||||
R res = rad2sxg<R, NORM>(ra, true, ra_prec);
|
||||
|
||||
R r = rad2sxg(dec, false, dec_prec);
|
||||
|
||||
std::ranges::copy(delim, std::back_inserter(res));
|
||||
std::ranges::copy(r, std::back_inserter(res));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <bool NORM = false>
|
||||
static std::string RADEC_rad2sxg(double ra, double dec, std::string_view delim = ",", int ra_prec = 2, int dec_prec = 1)
|
||||
{
|
||||
return RADEC_rad2sxg<std::string, NORM>(ra, dec, delim, ra_prec, dec_prec);
|
||||
}
|
||||
|
||||
|
||||
template <std::ranges::output_range<char> R, bool NORM = false>
|
||||
static R AZZD_rad2sxg(double az, double zd, std::string_view delim = ",", int az_prec = 2, int zd_prec = 1)
|
||||
{
|
||||
R res = rad2sxg<R, NORM>(az, false, az_prec);
|
||||
|
||||
R r = rad2sxg(zd, false, zd_prec);
|
||||
|
||||
std::ranges::copy(delim, std::back_inserter(res));
|
||||
std::ranges::copy(r, std::back_inserter(res));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <bool NORM = false>
|
||||
static std::string AZZD_rad2sxg(double az, double zd, std::string_view delim = ",", int az_prec = 2, int zd_prec = 1)
|
||||
{
|
||||
return AZZD_rad2sxg<std::string, NORM>(az, zd, delim, az_prec, zd_prec);
|
||||
}
|
||||
|
||||
|
||||
} // namespace mcc::utils
|
||||
Loading…
x
Reference in New Issue
Block a user