mountcontrol/cxx/mcc_mount_telemetry_astrom.h
2025-08-06 02:06:59 +03:00

508 lines
18 KiB
C++

#pragma once
/* MOUNT CONTROL COMPONENTS LIBRARY */
/* VARIUOS ASTROMETRIC TRANSFORMATIONS FOR TELEMETRY */
#include "mcc_mount_concepts.h"
namespace mcc
{
enum class MccMountTelemetryAstromTransformErrorCode : int {
ERROR_OK = 0,
ERROR_COORD_PAIR_KIND,
ERROR_ASTROMETRY_COMP,
ERROR_PEC
};
/* error category definition */
// error category
struct MccMountTelemetryAstromTransformCategory : public std::error_category {
MccMountTelemetryAstromTransformCategory() : std::error_category() {}
const char* name() const noexcept { return "ADC_GENERIC_DEVICE"; }
std::string message(int ec) const
{
MccMountTelemetryAstromTransformErrorCode err = static_cast<MccMountTelemetryAstromTransformErrorCode>(ec);
switch (err) {
case MccMountTelemetryAstromTransformErrorCode::ERROR_OK:
return "OK";
case MccMountTelemetryAstromTransformErrorCode::ERROR_COORD_PAIR_KIND:
return "unsupported coordinate pair kind";
case MccMountTelemetryAstromTransformErrorCode::ERROR_ASTROMETRY_COMP:
return "astrometry engine error";
case MccMountTelemetryAstromTransformErrorCode::ERROR_PEC:
return "PEC computation error";
default:
return "UNKNOWN";
}
}
static const MccMountTelemetryAstromTransformCategory& get()
{
static const MccMountTelemetryAstromTransformCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(MccMountTelemetryAstromTransformErrorCode ec)
{
return std::error_code(static_cast<int>(ec), MccMountTelemetryAstromTransformCategory::get());
}
} // namespace mcc
namespace std
{
template <>
class is_error_code_enum<mcc::MccMountTelemetryAstromTransformErrorCode> : public true_type
{
};
} // namespace std
namespace mcc
{
template <traits::mcc_astrom_engine_c ASTROM_ENGINE_T, traits::mcc_mount_pec_c PEC_T>
class MccMountTelemetryAstromTransform
{
inline static typename ASTROM_ENGINE_T::coord_t dummyCoord{};
public:
typedef ASTROM_ENGINE_T astrom_engine_t;
typedef PEC_T pec_t;
// check for coordinate types consistency
static_assert(
requires(typename astrom_engine_t::coord_t ac, typename pec_t::coord_t pc) {
// to compute PEC-corrected values
{ ac += pc };
{ ac = ac + pc };
{ ac = ac - pc };
},
"ASTROMETRY ENGINE AND PEC COORDINATES TYPE MUST BE CONSISTENT!");
// deduce mount type
static constexpr bool equatorialMount = mccIsEquatorialMount(PEC_T::mountType);
static constexpr bool altAzMount = mccIsAltAzMount(PEC_T::mountType);
typedef typename astrom_engine_t::coord_t coord_t;
typedef std::error_code error_t;
MccMountTelemetryAstromTransform(astrom_engine_t& astrom_engine, pec_t& pec)
: _astromEngine(astrom_engine), _pec(pec)
{
}
MccMountTelemetryAstromTransform(MccMountTelemetryAstromTransform&& other)
: _pec(other._pec), _astromEngine(other._astromEngine)
{
}
MccMountTelemetryAstromTransform& operator=(MccMountTelemetryAstromTransform&& other)
{
if (this == &other) {
return;
}
_pec = other._pec;
_astromEngine = other._astromEngine;
return *this;
}
MccMountTelemetryAstromTransform(const MccMountTelemetryAstromTransform& other)
: _pec(other._pec), _astromEngine(other._astromEngine)
{
}
MccMountTelemetryAstromTransform& operator=(const MccMountTelemetryAstromTransform& other)
{
if (this == &other) {
return;
}
_pec = other._pec;
_astromEngine = other._astromEngine;
return *this;
}
virtual ~MccMountTelemetryAstromTransform() = default;
template <traits::mcc_celestial_point_c CT>
error_t toApparent(CT coord,
astrom_engine_t::time_point_t time_point,
coord_t& X_app,
coord_t& Y_app,
coord_t& XX_app = dummyCoord)
{
typedef typename astrom_engine_t::jd_t jd_t;
jd_t jd;
typedef typename astrom_engine_t::eo_t eo_t;
eo_t eo;
typedef typename astrom_engine_t::sideral_time_t sideral_time_t;
sideral_time_t lst;
typename astrom_engine_t::error_t ast_err;
typename pec_t::error_t pec_err;
auto get_jd_lst_eo = [&time_point, this](jd_t& jd, sideral_time_t& lst, eo_t& eo) {
auto ast_err = _astromEngine.greg2jul(time_point, jd);
if (!ast_err) {
ast_err = _astromEngine.apparentSiderTime(jd, lst, true);
if (!ast_err) {
ast_err = _astromEngine->eqOrigins(jd, eo);
}
}
return ast_err;
};
if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_XY) { // from encoder's
typename pec_t::pec_result_t pec_res;
pec_err = _pec.compute(coord.x, coord.y, pec_res);
if (!pec_err) {
X_app = coord.x + pec_res.dx;
Y_app = coord.y + pec_res.dy;
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_APP) { // from app RA-DEC
coord_t HA;
// logDebug("Input slew coordinates are apparent RA-DEC: convert it to apparent HA-DEC ...");
ast_err = get_jd_lst_eo(jd, lst, eo);
if (!ast_err) {
HA = lst - coord.x + eo; // HA = LST - RA_APP + EO
if constexpr (equatorialMount) { // compute HA (as XX_app)
X_app = coord.x;
Y_app = coord.y;
XX_app = HA;
} else if constexpr (altAzMount) {
ast_err = _astromEngine.hadec2azalt(HA, coord.y, X_app, Y_app);
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP) { // from app HA-DEC
ast_err = get_jd_lst_eo(jd, lst, eo);
if (!ast_err) {
if constexpr (equatorialMount) { // compute CIO RA (as XX_app)
X_app = coord.x;
Y_app = coord.y;
XX_app = lst - coord.x + eo;
} else if constexpr (altAzMount) {
ast_err = _astromEngine.hadec2azalt(coord.x, coord.y, X_app, Y_app);
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // from app AZ-ALT
if constexpr (equatorialMount) {
ast_err = _astromEngine.azalt2hadec(coord.x, coord.y, X_app, Y_app); // compute HA-DEC
if (!ast_err) { // compute CIO RA (as XX_app)
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP;
coord.x = X_app;
coord.y = Y_app;
ast_err = toApparent(std::move(coord), std::move(time_point), X_app, Y_app, XX_app);
}
} else if (altAzMount) {
X_app = coord.x;
Y_app = coord.y;
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZZD) { // from app AZ-ZD
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT;
coord.y = std::numbers::pi / 2.0 - coord.y;
ast_err = toApparent(std::move(coord), std::move(time_point), X_app, Y_app, XX_app);
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // from ICRS RA-DEC
coord_t az, alt;
eo_t eo;
// for equatorial mount:
// X_app = RA_app, Y_app = DEC_app, XX_app = HA_app
// for alt-azimuthal mount:
// X_app = AZ, Y_app = ALT
ast_err = _astromEngine.greg2jul(time_point, jd);
if (!ast_err) {
ast_err = _astromEngine.icrs2obs(coord.x, coord.y, jd, X_app, Y_app, XX_app, az, alt, eo);
if (!ast_err) {
if constexpr (equatorialMount) {
// nothing to do
} else if (altAzMount) {
X_app = az;
Y_app = alt;
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
}
}
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_COORD_PAIR_KIND;
}
if (pec_err) {
if constexpr (std::same_as<decltype(pec_err), error_t>) {
return pec_err;
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_PEC;
}
}
if (ast_err) {
if constexpr (std::same_as<decltype(ast_err), error_t>) {
return ast_err;
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_ASTROMETRY_COMP;
}
}
return MccMountTelemetryAstromTransformErrorCode::ERROR_OK;
}
template <traits::mcc_celestial_point_c CT>
error_t toICRS(CT coord, astrom_engine_t::time_point_t time_point, coord_t& RA, coord_t& DEC)
{
typename astrom_engine_t::error_t ast_err;
typename pec_t::error_t pec_err;
if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_XY) { // from encoder's
typename pec_t::pec_result_t pec_res;
pec_err = _pec.compute(coord.x, coord.y, pec_res);
if (!pec_err) {
coord.x += pec_res.dx;
coord.y += pec_res.dy;
if constexpr (equatorialMount) {
coord.coordPairKind = MccCoordPairKind::COORDS_KIND_HADEC_APP;
} else if constexpr (altAzMount) {
coord.coordPairKind = MccCoordPairKind::COORDS_KIND_AZALT;
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
ast_err = toICRS(std::move(coord), std::move(time_point), RA, DEC);
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_APP) { // from app RA-DEC
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP) { // from app HA-DEC
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // from app AZ-ALT
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZZD) { // from app AZ-ZD
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT;
coord.y = std::numbers::pi / 2.0 - coord.y;
ast_err = toICRS(std::move(coord), std::move(time_point), RA, DEC);
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // from ICRS RA-DEC
RA = coord.x;
DEC = coord.y;
return MccMountTelemetryAstromTransformErrorCode::ERROR_OK;
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_COORD_PAIR_KIND;
}
if (coord.coordPairKind != MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
typename astrom_engine_t::jd_t jd;
ast_err = _astromEngine.greg2jul(astrom_engine_t::timePointNow(), jd);
if (!ast_err) {
ast_err = _astromEngine.obs2icrs(coord.coordPairKind, coord.x, coord.y, jd, RA, DEC);
}
}
if (pec_err) {
if constexpr (std::same_as<decltype(pec_err), error_t>) {
return pec_err;
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_PEC;
}
}
if (ast_err) {
if constexpr (std::same_as<decltype(ast_err), error_t>) {
return ast_err;
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_ASTROMETRY_COMP;
}
}
return MccMountTelemetryAstromTransformErrorCode::ERROR_OK;
}
template <traits::mcc_celestial_point_c CT>
error_t toHardware(CT coord, astrom_engine_t::time_point_t time_point, coord_t& X, coord_t& Y)
{
typedef typename astrom_engine_t::jd_t jd_t;
jd_t jd;
typedef typename astrom_engine_t::eo_t eo_t;
eo_t eo;
typedef typename astrom_engine_t::sideral_time_t sideral_time_t;
sideral_time_t lst;
auto get_jd_lst_eo = [&time_point, this](jd_t& jd, sideral_time_t& lst, eo_t& eo) {
auto ast_err = _astromEngine.greg2jul(time_point, jd);
if (!ast_err) {
ast_err = _astromEngine.apparentSiderTime(jd, lst, true);
if (!ast_err) {
ast_err = _astromEngine->eqOrigins(jd, eo);
}
}
return ast_err;
};
typename astrom_engine_t::error_t ast_err;
typename pec_t::error_t pec_err;
if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_XY) { // from encoder's
X = coord.x;
Y = coord.y;
return MccMountTelemetryAstromTransformErrorCode::ERROR_OK;
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_APP) { // from app RA-DEC
ast_err = get_jd_lst_eo(jd, lst, eo);
if (!ast_err) {
coord.x = lst - coord.x + eo; // HA
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP;
ast_err = toHardware(std::move(coord), std::move(time_point), X, Y);
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP) { // from app HA-DEC
if constexpr (equatorialMount) {
typename pec_t::pec_result_t pec_res;
pec_err = _pec.compute(coord.x, coord.y, pec_res);
if (!pec_err) {
X = coord.x - pec_res.dx;
Y = coord.y - pec_res.dy;
return MccMountTelemetryAstromTransformErrorCode::ERROR_OK;
}
} else if constexpr (altAzMount) {
coord_t az, alt;
ast_err = _astromEngine.hadec2azalt(coord.x, coord.y, az, alt);
if (!ast_err) {
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT;
coord.x = az;
coord.y = alt;
ast_err = toHardware(std::move(coord), std::move(time_point), X, Y);
}
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // from app AZ-ALT
if constexpr (equatorialMount) {
coord_t ha, dec;
ast_err = _astromEngine.azalt2hadec(coord.x, coord.y, ha, dec);
if (!ast_err) {
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP;
coord.x = ha;
coord.y = dec;
ast_err = toHardware(std::move(coord), std::move(time_point), X, Y);
}
} else if constexpr (altAzMount) {
typename pec_t::pec_result_t pec_res;
pec_err = _pec.compute(coord.x, coord.y, pec_res);
if (!pec_err) {
X = coord.x - pec_res.dx;
Y = coord.y - pec_res.dy;
return {};
}
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZZD) { // from app AZ-ZD
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT;
coord.y = std::numbers::pi / 2.0 - coord.y;
ast_err = toICRS(std::move(coord), std::move(time_point), X, Y);
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // from ICRS RA-DEC
coord_t ra, dec, ha, az, alt;
eo_t eo;
ast_err = _astromEngine.greg2jul(time_point, jd);
if (!ast_err) {
ast_err = icrs2obs(coord.x, coord.y, jd, ra, dec, ha, az, alt, eo);
if (!ast_err) {
if constexpr (equatorialMount) {
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP;
coord.x = ha;
coord.y = dec;
} else if constexpr (altAzMount) {
coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT;
coord.x = az;
coord.y = alt;
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
ast_err = toHardware(std::move(coord), std::move(time_point), X, Y);
}
}
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_COORD_PAIR_KIND;
}
if (pec_err) {
if constexpr (std::same_as<decltype(pec_err), error_t>) {
return pec_err;
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_PEC;
}
}
if (ast_err) {
if constexpr (std::same_as<decltype(ast_err), error_t>) {
return ast_err;
} else {
return MccMountTelemetryAstromTransformErrorCode::ERROR_ASTROMETRY_COMP;
}
}
return MccMountTelemetryAstromTransformErrorCode::ERROR_OK;
}
protected:
astrom_engine_t& _astromEngine;
pec_t& _pec;
};
} // namespace mcc