Compare commits

...

2 Commits

Author SHA1 Message Date
961c72f17f ... 2025-07-24 18:55:59 +03:00
7e1709727a ... 2025-07-24 15:57:04 +03:00
10 changed files with 664 additions and 223 deletions

View File

@ -124,8 +124,8 @@ add_library(${CNTR_PROTO_LIB} STATIC ${CNTR_PROTO_LIB_SRC})
set(MCC_LIBRARY_SRC mcc_mount_concepts.h mcc_fsm_mount.h mcc_mount_coord.h mcc_mount_events_states.h mcc_finite_state_machine.h
mcc_mount_pec.h mcc_mount_pz.h mcc_traits.h mcc_mount_telemetry.h mcc_mount_config.h mcc_mount_astro_erfa.h mcc_astrom_iers.h mcc_astrom_iers_default.h
mcc_slew_model.h mcc_guiding_model.h mcc_utils.h mcc_spdlog.h)
mcc_mount_pec.h mcc_mount_pz.h mcc_traits.h mcc_mount_telemetry_astrom.h mcc_mount_telemetry.h mcc_mount_config.h mcc_mount_astro_erfa.h
mcc_astrom_iers.h mcc_astrom_iers_default.h mcc_slew_model.h mcc_guiding_model.h mcc_utils.h mcc_spdlog.h)
set(MCC_LIBRARY mcc)
add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC})
target_compile_features(${MCC_LIBRARY} INTERFACE cxx_std_23)

View File

@ -62,12 +62,40 @@ const AsibFM700HardwareErrorCategory& AsibFM700HardwareErrorCategory::get()
/* constructors and destructor */
AsibFM700Hardware::AsibFM700Hardware(const hardware_config_t& conf) : _hardwareConfig(conf)
AsibFM700Hardware::AsibFM700Hardware(const hardware_config_t& conf)
: _hardwareConfig(conf), _sideralRate2(_hardwareConfig.hwConfig.eqrate)
{
_hardwareConfig.devConfig.MountDevPath = const_cast<char*>(_hardwareConfig.MountDevPath.c_str());
_hardwareConfig.devConfig.EncoderDevPath = const_cast<char*>(_hardwareConfig.EncoderDevPath.c_str());
_hardwareConfig.devConfig.EncoderXDevPath = const_cast<char*>(_hardwareConfig.EncoderXDevPath.c_str());
_hardwareConfig.devConfig.EncoderYDevPath = const_cast<char*>(_hardwareConfig.EncoderYDevPath.c_str());
_sideralRate2 *= _sideralRate2;
_sideralRateEps2 = 0.01; // 1%
// start state polling
_statePollingThread = std::jthread([this](std::stop_token stoken) {
mountdata_t data;
while (true) {
if (stoken.stop_requested()) {
return;
}
error_t err = static_cast<AsibFM700HardwareErrorCode>(Mount.getMountData(&data));
if (err == AsibFM700HardwareErrorCode::ERROR_OK) {
// are both motors stopped?
bool stop_motors =
(data.extradata.ExtraBits & XMOTOR_STOP_BIT) && (data.extradata.ExtraBits & YMOTOR_STOP_BIT);
if (stop_motors) {
_state = hw_state_t::HW_STATE_STOP;
}
}
}
});
}
// AsibFM700Hardware::AsibFM700Hardware(AsibFM700Hardware&& other)
@ -102,6 +130,15 @@ AsibFM700Hardware::error_t AsibFM700Hardware::getState(AsibFM700Hardware::hw_sta
state = hw_state_t::HW_STATE_STOP;
return AsibFM700HardwareErrorCode::ERROR_OK;
}
// compute current speed
auto rate2 = data.encXspeed.val * data.encXspeed.val + data.encYspeed.val * data.encYspeed.val;
auto ratio2 = rate2 / _sideralRate2;
if (ratio2 <= _sideralRateEps2) { // tracking
state = hw_state_t::HW_STATE_TRACK;
} else {
state = hw_state_t::HW_STATE_SLEW;
}
}
return err;
@ -110,16 +147,21 @@ AsibFM700Hardware::error_t AsibFM700Hardware::getState(AsibFM700Hardware::hw_sta
AsibFM700Hardware::error_t AsibFM700Hardware::setPos(AsibFM700Hardware::axes_pos_t pos)
{
// according to"SiTech protocol notes" X is DEC-axis and Y is HA-axis
double X = pos.y, Y = pos.x;
error_t err = static_cast<AsibFM700HardwareErrorCode>(Mount.moveTo(&X, &Y));
error_t err;
// according to"SiTech protocol notes" X is DEC-axis and Y is HA-axis
coordpair_t hw_pos{.X = pos.y, .Y = pos.x};
if (!pos.flags.slewNguide) {
return static_cast<AsibFM700HardwareErrorCode>(Mount.slewTo(&hw_pos, pos.flags));
}
switch (pos.state) {
case hw_state_t::HW_STATE_SLEW: // slew mount
err = static_cast<AsibFM700HardwareErrorCode>(Mount.slewTo(&hw_pos, pos.flags));
break;
case hw_state_t::HW_STATE_TRACK: // interpretate as guiding correction
err = static_cast<AsibFM700HardwareErrorCode>(Mount.correctBy(&hw_pos));
break;
case hw_state_t::HW_STATE_STOP:
break;

View File

@ -4,6 +4,8 @@
/* HARDWARE WRAPPER IMPLEMENTATION */
#include <thread>
#include "../LibSidServo/sidservo.h"
#include "mcc_mount_concepts.h"
@ -116,7 +118,11 @@ public:
private:
hardware_config_t _hardwareConfig;
// static void moveInst(AsibFM700Hardware* from, AsibFM700Hardware* to);
std::jthread _statePollingThread;
hw_state_t _state;
double _sideralRate2; // square of sideral rate
double _sideralRateEps2;
};
static_assert(mcc::traits::mcc_mount_hardware_c<AsibFM700Hardware>, "AsibFM700Hardware!!!");

View File

@ -7,6 +7,7 @@
#include "mcc_mount_concepts.h"
#include "mcc_slew_guiding_model_common.h"
namespace mcc
@ -143,15 +144,10 @@ public:
std::chrono::duration<double> predictedTrackResolution{0.1}; // 0.1 seconds
};
typedef MccCelestialPoint guiding_point_t;
// struct guiding_point_t {
// typedef double coord_t;
// mcc::MccCoordPairKind coordPairKind{mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
// coord_t x, y;
// };
struct guiding_point_t : MccCelestialPoint {
coord_t corrThresh{(double)MccAngle("00:00:00.2"_dms)}; // correction threshold
coord_t correctionRange[2]{(double)MccAngle(0.5_arcsecs), (double)MccAngle(5.0_arcsecs)};
};
template <traits::mcc_mount_controls_c MOUNT_CONTROLS_T, typename... LoggerCtorArgTs>
MccSimpleGuidingModel(MOUNT_CONTROLS_T& mount_controls, guiding_context_t context, LoggerCtorArgTs&&... ctor_args)
@ -192,7 +188,7 @@ public:
}
protected:
std::function<error_t()> _guidingFunc{};
std::function<error_t(guiding_point_t)> _guidingFunc{};
std::atomic_bool _doCorrection{true};
@ -358,9 +354,16 @@ protected:
}
}
guiding_point.x += pec_res.dx; // app HA
guiding_point.y += pec_res.dy; // app DEC
guiding_point.coordPairKind = MccCoordPairKind::COORDS_KIND_HADEC_APP;
if constexpr (mccIsEquatorialMount(pec_t::mountType)) { // use of HA and DEC
guiding_point.coordPairKind = MccCoordPairKind::COORDS_KIND_HADEC_APP;
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) { // use of Az and Alt
guiding_point.coordPairKind = MccCoordPairKind::COORDS_KIND_AZALT;
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
}
guiding_point.x += pec_res.dx; // app HA/Az
guiding_point.y += pec_res.dy; // app DEC/Alt
res_err = self(std::move(guiding_point));
if (res_err) {
@ -447,11 +450,11 @@ protected:
// compare t_data with computed coordinates ...
if (_doCorrection) {
if constexpr (mccIsEquatorialMount(pec_t::mountType)) {
xr = t_data.mntHA - ha;
yr = t_data.mntDEC - dec_app;
xr = ha - t_data.mntHA;
yr = dec_app - t_data.mntDEC;
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
xr = t_data.mntAZ - az;
yr = t_data.mntALT - alt;
xr = az - t_data.mntAZ;
yr = alt - t_data.mntALT;
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
@ -470,9 +473,13 @@ protected:
}
// do correction
ax_pos.state = hardware_t::hw_state_t::HW_STATE_TRACK;
ax_pos.x = t_data.mntPosX;
ax_pos.y = t_data.mntPosY;
ax_pos.state = hardware_t::hw_state_t::HW_STATE_TRACK; // indicates to hardware level
ax_pos.x = xr;
ax_pos.y = yr;
// ax_pos.x = t_data.mntPosX;
// ax_pos.y = t_data.mntPosY;
ax_pos.time_point = t_data.time_point;
// asynchronous operation!
auto err = hardware.setPos(std::move(ax_pos));

View File

@ -73,13 +73,6 @@ struct MccNullLogger {
};
struct MccCelestialPoint {
typedef double coord_t;
MccCoordPairKind coordPairKind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
coord_t x{0.0}, y{0.0};
};
} // namespace mcc
@ -257,8 +250,8 @@ concept mcc_mount_hardware_c = !std::copyable<T> && std::movable<T> && requires(
{ t_const.getState(std::declval<typename T::hw_state_t&>()) } -> std::same_as<typename T::error_t>;
{ t.stop() } -> std::same_as<typename T::error_t>;
{ t.init() } -> 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
};
@ -545,6 +538,5 @@ namespace mcc
{
static_assert(traits::mcc_logger_c<MccNullLogger>, "MccNullLogger INVALID DECLARATION!");
static_assert(traits::mcc_celestial_point_c<MccCelestialPoint>, "MccCelestialPoint INVALID DECLARATION!");
} // namespace mcc

View File

@ -13,6 +13,16 @@ constexpr double operator""_degs(long double val) // angle in degrees
return val * std::numbers::pi / 180.0;
}
constexpr double operator""_arcmins(long double val) // angle in arc minutes
{
return val * std::numbers::pi / 180.0 / 60.0;
}
constexpr double operator""_arcsecs(long double val) // angle in arc seconds
{
return val * std::numbers::pi / 180.0 / 3600.0;
}
constexpr double operator""_dms(const char* s, size_t size) // as a string "DEGREES:MINUTES:SECONDS"
{
auto res = mcc::utils::parsAngleString(std::span{s, size});

View File

@ -1,9 +1,9 @@
#pragma once
/* MOUNT CONTROL COMPONENTS LIBRARY */
/* MOUNT CONTROL COMPONENTS LIBRARY */
/* MOUNT TELEMETRY OBJECT POSSIBLE GENERIC IMPLEMENTATION */
/* MOUNT TELEMETRY OBJECT POSSIBLE GENERIC IMPLEMENTATION */
#include <mutex>

View File

@ -0,0 +1,296 @@
#pragma once
/* MOUNT CONTROL COMPONENTS LIBRARY */
/* VARIUOS ASTROMETRIC TRANSFORMATIONS FOR TELEMETRY */
#include "mcc_mount_concepts.h"
namespace mcc
{
template <traits::mcc_astrom_engine_c ASTROM_ENGINE_T, traits::mcc_mount_pec_c PEC_T>
class MccMountTelemetryAstromTransform
{
static typename ASTROM_ENGINE_T::coord_t dummyCoord{};
public:
// deduce mount type
static constexpr bool equatorialMount = mccIsEquatorialMount(PEC_T::mountType);
static constexpr bool altAzMount = mccIsAltAzMount(PEC_T::mountType);
typedef ASTROM_ENGINE_T astrom_engine_t;
typedef PEC_T pec_t;
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)
{
}
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 = azalt2hadec(coord.x, coord.y, X_app, Y_app); // compute HA-DEC
if (!ast_err) { // compute CIO RA (as XX_app)
ast_err = toApparent(X_app, Y_app, 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 std::make_error_code(std::errc::operation_canceled);
}
if (pec_err) {
if constexpr (std::same_as<decltype(pec_err), error_t>) {
return pec_err;
} else {
return std::make_error_code(std::errc::operation_canceled);
}
}
if (ast_err) {
if constexpr (std::same_as<decltype(ast_err), error_t>) {
return ast_err;
} else {
return std::make_error_code(std::errc::operation_canceled);
}
}
return {};
}
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 {};
} else {
return std::make_error_code(std::errc::operation_canceled);
}
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 std::make_error_code(std::errc::operation_canceled);
}
}
if (ast_err) {
if constexpr (std::same_as<decltype(ast_err), error_t>) {
return ast_err;
} else {
return std::make_error_code(std::errc::operation_canceled);
}
}
return {};
}
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)
{
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 {};
} 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), X, Y);
} else if (coord.coordPairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // from ICRS RA-DEC
} else {
return std::make_error_code(std::errc::operation_canceled);
}
if (pec_err) {
if constexpr (std::same_as<decltype(pec_err), error_t>) {
return pec_err;
} else {
return std::make_error_code(std::errc::operation_canceled);
}
}
if (ast_err) {
if constexpr (std::same_as<decltype(ast_err), error_t>) {
return ast_err;
} else {
return std::make_error_code(std::errc::operation_canceled);
}
}
return {};
}
protected:
astrom_engine_t& _astromEngine;
pec_t& _pec;
};
} // namespace mcc

View File

@ -0,0 +1,67 @@
#pragma once
/* MOUNT CONTROL COMPONENTS LIBRARY */
/* COMMON DECLARATION FOR SLEW AND GUIDING MODELS GENERIC IMPLEMENTATIONS */
#include "mcc_mount_concepts.h"
namespace mcc
{
/* DEFAULT CLASS TO REPRESENT CELESTIAL POINT */
struct MccCelestialPoint {
typedef double coord_t;
MccCoordPairKind coordPairKind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
coord_t x{0.0}, y{0.0};
};
static_assert(traits::mcc_celestial_point_c<MccCelestialPoint>, "MccCelestialPoint INVALID DECLARATION!");
/* CHECK FOR CURRENT MOUNT POSITION IN PROHIBITED ZONES */
/*
* WARNING: if an error occured during telemetry data request
* result 'in zone' flags cannot be interpretated correctly!
*/
template <traits::mcc_mount_telemetry_c TelemetryT,
traits::mcc_prohibited_zone_c<typename TelemetryT::mount_telemetry_data_t>... ZTs>
auto mccCheckInZonePZTuple(TelemetryT& telemetry,
std::tuple<ZTs...>& tuple_zones,
std::array<bool, sizeof...(ZTs)>& in_zone)
{
const auto p_telemetry = &telemetry;
const auto p_tuple_zones = &tuple_zones;
const auto p_in_zone = &in_zone;
return [p_telemetry, p_tuple_zones, p_in_zone]<size_t... Is>(std::index_sequence<Is...>) {
typename TelemetryT::error_t t_err;
(
[&t_err]() {
if constexpr (Is) {
if (t_err) {
(*p_in_zone)[Is] = false;
return;
}
}
typename TelemetryT::mount_telemetry_data_t tdata;
t_err = p_telemetry->data(tdata);
if (!t_err) {
(*p_in_zone)[Is] = std::get<Is>(p_tuple_zones).inZone(tdata);
}
}(),
...);
}(std::make_index_sequence<sizeof...(ZTs)>{});
}
} // namespace mcc

View File

@ -9,6 +9,7 @@
#include "mcc_mount_concepts.h"
#include "mcc_slew_guiding_model_common.h"
namespace mcc
{
@ -111,44 +112,35 @@ public:
typedef std::error_code error_t;
typedef MccCelestialPoint slew_point_t;
struct slew_point_t : MccCelestialPoint {
// target-mount coordinate difference to start adjusting slewing (in radians)
coord_t adjustCoordDiff{(double)MccAngle{10.0_degs}};
// struct slew_params_t {
// typedef mcc::MccAngle coord_t;
// coordinates difference to stop slewing (in radians)
coord_t slewPrecision{(double)MccAngle{5.0_arcsecs}};
// mcc::MccCoordPairKind coordPairKind{mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
// coord_t x{0.0};
// coord_t y{0.0};
// // if <= 0 then hardware must assume default rate
// coord_t xrate{-1};
// coord_t yrate{-1};
// bool stop{false};
// };
struct context_t {
// coordinates polling interval in seconds
std::chrono::duration<double> coordPollingInterval{0.1};
bool stopAfterSlew{false};
std::chrono::seconds timeout{300};
std::chrono::seconds timeout{3600};
};
template <traits::mcc_mount_controls_c MOUNT_CONTROLS_T, typename... LoggerCtorArgTs>
MccSimpleSlewModel(MOUNT_CONTROLS_T& mount_controls, context_t context, LoggerCtorArgTs&&... ctor_args)
MccSimpleSlewModel(MOUNT_CONTROLS_T& mount_controls, LoggerCtorArgTs&&... ctor_args)
requires(!std::same_as<LoggerT, MccNullLogger>)
: LoggerT(std::forward<LoggerCtorArgTs>(ctor_args)...)
{
logDebug(std::format("Create 'MccSimpleSlewModel' class instance ({})", (void*)this));
init(mount_controls, std::move(context));
init(mount_controls);
}
template <traits::mcc_mount_controls_c MOUNT_CONTROLS_T>
MccSimpleSlewModel(MOUNT_CONTROLS_T& mount_controls, context_t context)
MccSimpleSlewModel(MOUNT_CONTROLS_T& mount_controls)
requires(std::same_as<LoggerT, MccNullLogger>)
{
init(mount_controls, std::move(context));
init(mount_controls);
}
virtual ~MccSimpleSlewModel()
@ -166,7 +158,7 @@ public:
protected:
std::function<error_t(const slew_point_t&)> _slewFunc{};
void init(auto& mount_controls, context_t context)
void init(auto& mount_controls)
{
// deduce controls types
using astrom_engine_t = decltype(mount_controls.astrometryEngine);
@ -179,43 +171,8 @@ protected:
const auto p_mount_controls = &mount_controls;
// prohibited zones related lambdas
auto check_zones = [p_mount_controls]<size_t... Is>(std::index_sequence<Is...>) {
// std::array<std::error_code, sizeof...(Is)> result{};
error_t ret;
(
[&ret]() {
if constexpr (Is > 0) {
if (ret) {
return;
}
}
typename telemetry_t::mount_telemetry_data_t tdata;
auto tel_err = p_mount_controls->telemetry.data(tdata);
if (tel_err) {
if constexpr (std::same_as<decltype(tel_err), error_t>) {
ret = tel_err;
} else {
ret = MccSimpleSlewModelErrorCode::ERROR_TELEMETRY_DATA;
}
} else {
ret = std::get<Is>(p_mount_controls->prohibitedZones).inZone(tdata)
? MccSimpleSlewModelErrorCode::ERROR_IN_PROHIBITED_ZONE
: MccSimpleSlewModelErrorCode::ERROR_OK;
}
}(),
...);
return ret;
};
_slewFunc = [p_mount_controls, context = std::move(context), check_zones](this auto&& self,
slew_point_t slew_point) {
_slewFunc = [p_mount_controls](this auto&& self, slew_point_t slew_point) {
auto& astrom_engine = p_mount_controls->astrometryEngine;
auto& hardware = p_mount_controls->hardware;
auto& pec = p_mount_controls->PEC;
@ -233,137 +190,164 @@ protected:
typename telemetry_t::error_t t_err;
typename telemetry_t::mount_telemetry_data_t t_data;
coord_t ra_icrs, dec_icrs;
if (slew_point.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_XY) {
// trivial case (the pair is interpretated as raw encoder coordinates)
ax_pos.x = slew_point.x;
ax_pos.y = slew_point.y;
// ax_pos.xrate = slew_point.xrate;
// ax_pos.yrate = slew_point.yrate;
// the pair is interpretated as raw encoder coordinates
if (slew_point.stopAfterSlew) {
ax_pos.x = slew_point.x;
ax_pos.y = slew_point.y;
} else { // very strange but should be processed! forward to compute ICRS RA AND DEC
typename pec_t::pec_result_t pec_res;
pec_err = pec->compute(slew_point.x, slew_point.y, pec_res);
if (!pec_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
slew_point.x += pec_res.dx;
slew_point.y += pec_res.dy;
res_err = self(std::move(slew_point));
}
}
} else if (slew_point.coordPairKind ==
mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // catalog coordinates
jd_t jd;
coord_t ra_app, dec_app, ha, az, alt;
typename astrom_engine_t::eo_t eo;
if (slew_point.stopAfterSlew) {
jd_t jd;
coord_t ra_app, dec_app, ha, az, alt;
typename astrom_engine_t::eo_t eo;
logDebug("Input slew coordinates are ICRS RA-DEC: convert it to apparent ...");
logDebug("Input slew coordinates are ICRS RA-DEC: convert it to apparent ...");
ast_err = astrom_engine->greg2jul(astrom_engine_t::timePointNow(), jd);
if (!ast_err) {
ast_err = astrom_engine->icrs2obs(slew_point.x, slew_point.y, jd, ra_app, dec_app, ha, az, alt, eo);
ast_err = astrom_engine->greg2jul(astrom_engine_t::timePointNow(), jd);
if (!ast_err) {
if constexpr (mccIsEquatorialMount(pec_t::mountType)) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
slew_point.x = ha;
slew_point.y = dec_app;
ast_err =
astrom_engine->icrs2obs(slew_point.x, slew_point.y, jd, ra_app, dec_app, ha, az, alt, eo);
if (!ast_err) {
if constexpr (mccIsEquatorialMount(pec_t::mountType)) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
slew_point.x = ha;
slew_point.y = dec_app;
res_err = self(std::move(slew_point));
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT;
slew_point.x = az;
slew_point.y = alt;
res_err = self(std::move(slew_point));
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
}
}
}
} else { // OK, here one should stop with coordinates converting
ra_icrs = slew_point.x;
dec_icrs = slew_point.y;
}
} else if (slew_point.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP) { // apparent
if (slew_point.stopAfterSlew) {
jd_t jd;
typename astrom_engine_t::eo_t eo;
logDebug("Input slew coordinates are apparent RA-DEC: convert it to apparent HA-DEC ...");
ast_err = astrom_engine->greg2jul(astrom_engine_t::timePointNow(), jd);
if (!ast_err) {
typename astrom_engine_t::sideral_time_t lst;
ast_err = astrom_engine->apparentSiderTime(jd, lst, true);
if (!ast_err) {
ast_err = astrom_engine->eqOrigins(jd, eo);
if (!ast_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
slew_point.x = lst - slew_point.x + eo; // HA = LST - RA_APP + EO
res_err = self(std::move(slew_point));
}
}
}
}
} else if (slew_point.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP) { // apparent
if (slew_point.stopAfterSlew) {
if constexpr (mccIsEquatorialMount(pec_t::mountType)) { // compute encoder coordinates
logDebug("Input slew coordinates are apparent HA-DEC: convert it to hardware encoder ones ...");
coord_t eps = 1.0 / 3600.0 * std::numbers::pi / 180.0;
typename pec_t::pec_result_t pec_res;
// pec_err = pec->reverseCompute(slew_point.x, slew_point.y, pec_res, context.eps,
// context.maxIter);
pec_err = pec->compute(slew_point.x, slew_point.y, pec_res);
if (!pec_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
slew_point.x -= pec_res.dx;
slew_point.y -= pec_res.dy;
res_err = self(std::move(slew_point));
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
}
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
coord_t az, alt;
logDebug("Input slew coordinates are apparent HA-DEC: convert it to AZ-ALT ...");
ast_err = astrom_engine->hadec2azalt(slew_point.x, slew_point.y, az, alt);
if (!ast_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT;
slew_point.x = az;
slew_point.y = alt;
res_err = self(std::move(slew_point));
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
}
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
}
}
} else if (slew_point.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_AZALT) {
if (slew_point.stopAfterSlew) {
if constexpr (mccIsEquatorialMount(pec_t::mountType)) {
coord_t ha, dec;
} else if (slew_point.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP) { // apparent
jd_t jd;
typename astrom_engine_t::eo_t eo;
logDebug("Input slew coordinates are AZ-ALT: convert it to HA-DEC ...");
logDebug("Input slew coordinates are apparent RA-DEC: convert it to apparent HA-DEC ...");
ast_err = astrom_engine->azalt2hadec(slew_point.x, slew_point.y, ha, dec);
ast_err = astrom_engine->greg2jul(astrom_engine_t::timePointNow(), jd);
if (!ast_err) {
typename astrom_engine_t::sideral_time_t lst;
ast_err = astrom_engine->apparentSiderTime(jd, lst, true);
if (!ast_err) {
ast_err = astrom_engine->eqOrigins(jd, eo);
if (!ast_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
slew_point.x = lst - slew_point.x + eo; // HA = LST - RA_APP + EO
slew_point.x = ha;
slew_point.y = dec;
res_err = self(std::move(slew_point));
}
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) { // compute encoder coordinates
coord_t eps = 1.0 / 3600.0 * std::numbers::pi / 180.0;
logDebug("Input slew coordinates are AZ-ALT: convert it to hardware encoder ones ...");
typename pec_t::pec_result_t pec_res;
// pec_err = pec->reverseCompute(slew_point.x, slew_point.y, pec_res, context.eps,
// context.maxIter);
pec_err = pec->compute(slew_point.x, slew_point.y, pec_res);
if (!pec_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
slew_point.x -= pec_res.dx;
slew_point.y -= pec_res.dy;
res_err = self(std::move(slew_point));
}
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
}
}
} else if (slew_point.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP) { // apparent
if constexpr (mccIsEquatorialMount(pec_t::mountType)) { // compute encoder coordinates
logDebug("Input slew coordinates are apparent HA-DEC: convert it to hardware encoder ones ...");
coord_t eps = 1.0 / 3600.0 * std::numbers::pi / 180.0;
typename pec_t::pec_result_t pec_res;
// pec_err = pec->reverseCompute(slew_point.x, slew_point.y, pec_res, context.eps, context.maxIter);
pec_err = pec->compute(slew_point.x, slew_point.y, pec_res);
if (!pec_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
slew_point.x -= pec_res.dx;
slew_point.y -= pec_res.dy;
res_err = self(std::move(slew_point));
}
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
coord_t az, alt;
logDebug("Input slew coordinates are apparent HA-DEC: convert it to AZ-ALT ...");
ast_err = astrom_engine->hadec2azalt(slew_point.x, slew_point.y, az, alt);
if (!ast_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT;
slew_point.x = az;
slew_point.y = alt;
res_err = self(std::move(slew_point));
}
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
}
} else if (slew_point.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_AZALT) {
if constexpr (mccIsEquatorialMount(pec_t::mountType)) {
coord_t ha, dec;
logDebug("Input slew coordinates are AZ-ALT: convert it to HA-DEC ...");
ast_err = astrom_engine->azalt2hadec(slew_point.x, slew_point.y, ha, dec);
if (!ast_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
slew_point.x = ha;
slew_point.y = dec;
res_err = self(std::move(slew_point));
}
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) { // compute encoder coordinates
coord_t eps = 1.0 / 3600.0 * std::numbers::pi / 180.0;
logDebug("Input slew coordinates are AZ-ALT: convert it to hardware encoder ones ...");
typename pec_t::pec_result_t pec_res;
// pec_err = pec->reverseCompute(slew_point.x, slew_point.y, pec_res, context.eps, context.maxIter);
pec_err = pec->compute(slew_point.x, slew_point.y, pec_res);
if (!pec_err) {
slew_point.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
slew_point.x -= pec_res.dx;
slew_point.y -= pec_res.dy;
res_err = self(std::move(slew_point));
}
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
}
} else if (slew_point.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_AZZD) {
//
// WARNING: it is assumed that coordinates are in radians!
@ -407,6 +391,33 @@ protected:
}
}
// compute ICRS RA and DEC if needed
if (!slew_point.stopAfterSlew) {
if (slew_point.coordPairKind != mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
jd_t jd;
ast_err = astrom_engine.greg2jul(astrom_engine_t::timePointNow(), jd);
if (!ast_err) {
ast_err = astrom_engine.obs2icrs(slew_point.coordPairKind, slew_point.x, slew_point.y, jd,
ra_icrs, dec_icrs);
}
if (ast_err) {
if constexpr (std::same_as<decltype(ast_err), error_t>) {
logError(
std::format("An error occured while performing astrometry computations: code = {} ({})",
ast_err.value(), ast_err.message()));
return ast_err;
} else {
if constexpr (traits::mcc_formattable<decltype(ast_err)>) {
logError(std::format(
"An error occured while performing astrometry computations: code = {}", ast_err));
}
return MccSimpleSlewModelErrorCode::ERROR_ASTROM_COMP;
}
}
}
}
// move mount (it is assumed this is asynchronous operation!!!)
typename hardware_t::error_t err = hardware->setPos(ax_pos);
@ -425,40 +436,35 @@ protected:
size_t i_iter = 0;
// context.guidingRateEps *= context.guidingRateEps;
typename hardware_t::axes_pos_t::time_point_t prev_time_point{};
// typename telemetry_t::mount_telemetry_data_t::time_point_t prev_time_point{};
// typename telemetry_t::mount_telemetry_data_t::coord_t xrate, yrate, mount_rate2;
typename telemetry_t::mount_telemetry_data_t::coord_t xr, yr, coord_diff2,
adjRad2 = slew_point.adjustCoordDiff * slew_point.adjustCoordDiff;
std::array<bool, Nzones> in_zone_flag;
auto start_poll_tm = std::chrono::steady_clock::now();
while (true) {
// check prohibited zones
res_err = check_zones(std::make_index_sequence<Nzones>{});
if (res_err) {
hardware.stop();
return res_err;
}
t_err = mccCheckInZonePZTuple(*telemetry, p_mount_controls->prohibitedZones, in_zone_flag);
// it is assumed here that telemetry data is in actual state!
// t_err = telemetry.data(t_data);
// if (t_err) {
// hardware.stop();
// if constexpr (std::same_as<decltype(t_err), error_t>) {
// logError(
// std::format("An telemetry error occured: code = {} ({})", t_err.value(),
// t_err.message()));
// return t_err;
// } else {
// if constexpr (traits::mcc_formattable<decltype(t_err)>) {
// logError(std::format("An telemetry error occured: code = {}", t_err));
// }
// return MccSimpleSlewModelErrorCode::ERROR_TELEMETRY_DATA;
// }
// }
if (t_err) {
if constexpr (std::same_as<decltype(t_err), error_t>) {
logError(
std::format("An telemetry error occured: code = {} ({})", t_err.value(), t_err.message()));
return t_err;
} else {
if constexpr (traits::mcc_formattable<decltype(t_err)>) {
logError(std::format("An telemetry error occured: code = {}", t_err));
}
return MccSimpleSlewModelErrorCode::ERROR_TELEMETRY_DATA;
}
}
err = hardware->getPos(ax_pos);
@ -474,12 +480,27 @@ protected:
}
}
if constexpr (mccIsEquatorialMount(pec_t::mountType)) {
xr = slew_point.x - t_data.mntHA;
yr = slew_point.y - t_data.mntDEC;
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
xr = slew_point.x - t_data.mntAZ;
yr = slew_point.y - t_data.mntALT;
} else {
static_assert(false, "UNSUPPORTED MOUNT TYPE!");
}
coord_diff2 = xr * xr + yr * yr;
if (coord_diff2 < adjRad2) { // switch to adjusting mode
}
// if (prev_time_point == t_data.time_point) {
if (prev_time_point == ax_pos.time_point) {
continue;
}
if (context.stopAfterSlew) { // slew and stop, so mount moving rate must be 0 at the end
if (slew_point.stopAfterSlew) { // slew and stop, so mount moving rate must be 0 at the end
if (ax_pos.state == hardware_t::hw_state_t::HW_STATE_STOP) {
break;
}
@ -511,7 +532,7 @@ protected:
prev_time_point = t_data.time_point;
if ((std::chrono::steady_clock::now() - start_poll_tm) > context.timeout) {
if ((std::chrono::steady_clock::now() - start_poll_tm) > slew_point.timeout) {
logError("Waiting time for completion of slewing expired!");
return MccSimpleSlewModelErrorCode::ERROR_SLEW_TIMEOUT;
}