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

259 lines
9.2 KiB
C++

#pragma once
/* MOUNT CONTROL COMPONENTS LIBRARY */
/* A GENERIC MOUNT CLASS IMPLEMENTATION */
#include <algorithm>
#include "mcc_mount_concepts.h"
namespace mcc
{
// adaptor class for prohibited zones
template <traits::mcc_mount_telemetry_data_c TelemetryDataT>
struct MccPZoneWrapper {
// a type to which the result of calling prohibited zone class methods 'timeTo' and 'timeFrom' will be converted
typedef std::chrono::duration<double> duration_t; // seconds as floating-point number
static constexpr duration_t infiniteDuration{std::numeric_limits<double>::infinity()};
static constexpr duration_t zeroDuration{0.0};
typedef std::function<bool(const TelemetryDataT&)> pz_inzone_func_t;
typedef std::function<duration_t(const TelemetryDataT&)> pz_timeto_func_t;
typedef std::function<duration_t(const TelemetryDataT&)> pz_timefrom_func_t;
MccCoordPairKind coordPairKind;
const std::function<std::string()> name;
pz_inzone_func_t inZone;
pz_timeto_func_t timeTo;
pz_timefrom_func_t timeFrom;
};
template <traits::mcc_astrom_engine_c ASTROM_ENGINE_T,
traits::mcc_mount_hardware_c HARDWARE_T,
traits::mcc_mount_pec_c PEC_T,
traits::mcc_mount_telemetry_c TELEMETRY_T,
traits::mcc_slew_model_c SLEWMODEL_T,
traits::mcc_guiding_model_c GUIDEMODEL_T,
traits::mcc_logger_c LOGGER_T>
class MccGenericMount : public LOGGER_T
{
typedef LOGGER_T logger_t;
using logger_t::logDebug;
using logger_t::logError;
using logger_t::logInfo;
using logger_t::logWarn;
typedef ASTROM_ENGINE_T astrom_engine_t;
typedef HARDWARE_T hardware_t;
typedef PEC_T pec_t;
typedef TELEMETRY_T telemetry_t;
typedef typename TELEMETRY_T::telemetry_data_t telemetry_data_t;
typedef SLEWMODEL_T slew_model_t;
typedef GUIDEMODEL_T guiding_model_t;
typedef typename MccPZoneWrapper<telemetry_data_t>::duration_t pz_duration_t;
static constexpr MccMountType mountType = pec_t::mountType;
virtual ~MccGenericMount() = default;
// get telemetry data
auto mountTelemetryData(telemetry_data_t& data) { return _telemetry.data(data); }
/* prohibited zone related public methods */
// add zones to mount control system
template <traits::mcc_prohibited_zone_c<telemetry_data_t> ZT,
traits::mcc_prohibited_zone_c<telemetry_data_t>... ZTs>
size_t pzAddZone(ZT zone, ZTs... zones)
{
auto zone_ptr = std::make_shared<ZT>(std::move(zone));
using d_t = typename MccPZoneWrapper<telemetry_data_t>::duration_t;
_pzFuncs.emplace_back(
{.coordPairKind = ZT::zoneCoordPairKind,
.name = [zone_ptr]() { return std::format("{}", zone_ptr->name()); },
.inZone = [zone_ptr](const telemetry_data_t& tmry_data) { return zone_ptr->inZone(tmry_data); },
.timeTo =
[zone_ptr](const telemetry_data_t& tmry_data) {
auto d = zone_ptr->timeTo(tmry_data);
if constexpr (std::same_as<typename ZT::duration_t, d_t>) {
return d;
} else {
if (d == ZT::infiniteDuration) {
return MccPZoneWrapper<telemetry_data_t>::infiniteDuration;
} else if (d == ZT::zeroDuration) {
return MccPZoneWrapper<telemetry_data_t>::zeroDuration;
}
return std::chrono::duration_cast<d_t>(d);
}
},
.timeFrom =
[zone_ptr](const telemetry_data_t& tmry_data) {
auto d = zone_ptr->timeFrom(tmry_data);
if constexpr (std::same_as<typename ZT::duration_t, d_t>) {
return d;
} else {
if (d == ZT::infiniteDuration) {
return MccPZoneWrapper<telemetry_data_t>::infiniteDuration;
} else if (d == ZT::zeroDuration) {
return MccPZoneWrapper<telemetry_data_t>::zeroDuration;
}
return std::chrono::duration_cast<d_t>(d);
}
}});
if constexpr (sizeof...(ZTs)) {
pzAddZone(std::move(zones)...);
}
return _pzFuncs.size();
}
// delete all zones from mount control system
void pzClearZone()
{
// stop mount here?!!
_pzFuncs.clear();
}
// prohibited zones timeTo from given time point
template <std::ranges::output_range<pz_duration_t> RT, traits::mcc_celestial_point_c CT, traits::mcc_systime_c TPT>
RT pzTimeTo(CT coord, const TPT& time_point)
requires std::convertible_to<TPT, typename astrom_engine_t::time_point_t>
{
RT res;
telemetry_data_t tdata;
typename astrom_engine_t::juldate_t jd;
_astromEngine.greg2jul(time_point, jd);
if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_XY) { // from encoder's
typename pec_t::pec_result_t pec_res;
auto pec_err = _pec.compute(coord.x, coord.y, pec_res);
if (!pec_err) {
if constexpr (mccIsEquatorialMount(mountType)) {
tdata.mntHA = coord.x + pec_res.dx;
tdata.mntDEC = coord.y + pec_res.dy;
_astromEngine.hadec2azalt(tdata.mntHA, tdata.mntDEC, tdata.mntAZ, tdata.mntALT);
} else if constexpr (mccIsAltAzMount(mountType)) {
tdata.mntAZ = coord.x + pec_res.dx;
tdata.mntALT = coord.y + pec_res.dy;
_astromEngine.azalt2hadec(tdata.mntAZ, tdata.mntALT, tdata.mntHA, tdata.mntDEC);
}
} else {
return res;
}
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_APP) { // from app RA-DEC
typename astrom_engine_t::eo_t eo;
typename astrom_engine_t::sideral_time_t lst;
_astromEngine.apparentSiderTime(jd, lst, true);
_astromEngine.eqOrigins(jd, eo);
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP) { // from app HA-DEC
tdata.mntHA = coord.x;
tdata.mntDEC = coord.y;
_astromEngine.hadec2azalt(tdata.mntHA, tdata.mntDEC, tdata.mntAZ, tdata.mntALT);
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // from app AZ-ALT
tdata.mntAZ = coord.x;
tdata.mntALT = coord.y;
_astromEngine.azalt2hadec(tdata.mntAZ, tdata.mntALT, tdata.mntHA, tdata.mntDEC);
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_AZZD) { // from app AZ-ZD
tdata.mntAZ = coord.x;
tdata.mntALT = std::numbers::pi / 2.0 - coord.y;
_astromEngine.azalt2hadec(tdata.mntAZ, tdata.mntALT, tdata.mntHA, tdata.mntDEC);
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // from ICRS RA-DEC
typename astrom_engine_t::coord_t az, alt;
typename astrom_engine_t::eo_t eo;
typename astrom_engine_t::juldate_t jd;
_astromEngine.greg2jul(time_point, jd);
_astromEngine.icrs2obs(coord.x, coord.y, jd, tdata.mntRA, tdata.mntDEC, tdata.mntHA, tdata.mntAZ,
tdata.mntALT, eo);
} else {
return res;
}
}
template <traits::mcc_celestial_point_c CT, traits::mcc_systime_c TPT>
auto pzTimeTo(CT point, const TPT& time_point)
{
return pzTimeTo<std::vector<pz_duration_t>>(std::move(point), time_point);
}
// prohibited zones timeTo from current mount position
template <std::ranges::output_range<pz_duration_t> RT, traits::mcc_celestial_point_c CT>
RT pzTimeTo(CT point)
{
RT result;
telemetry_data_t data;
auto err = _telemetry.data(data);
if (err) {
// log...
return result;
}
std::ranges::for_each(_pzFuncs,
[&result, &data](auto& funcs) { std::back_inserter(result) = funcs.timeTo(data); });
}
template <traits::mcc_celestial_point_c CT>
auto pzTimeTo(CT point)
{
return pzTimeTo<std::vector<pz_duration_t>>(std::move(point));
}
// prohibited zone timeFrom
protected:
ASTROM_ENGINE_T _astromEngine;
HARDWARE_T _hardware;
PEC_T _pec;
TELEMETRY_T _telemetry;
SLEWMODEL_T _slewModel;
GUIDEMODEL_T _gudingModel;
std::vector<MccPZoneWrapper<telemetry_data_t>> _pzFuncs{};
};
namespace traits
{
template <typename T>
concept mcc_generic_mount_c = requires(T t) {
// derived from MccGenericMount
[]<typename... Ts>(MccGenericMount<Ts...>*) {}(&t);
{ t.slewMount(std::declval<typename T::slew_model_t::slew_point_t>()) };
{ t.startGuiding(std::declval<typename T::guiding_model_t::guiding_point_t>()) };
{ t.stop() };
};
template <typename T>
concept mcc_generic_fsm_mount_c = mcc_generic_mount_c<T> && std::derived_from<T, fsm::MccFiniteStateMachine>;
} // namespace traits
} // namespace mcc