diff --git a/cxx/CMakeLists.txt b/cxx/CMakeLists.txt index 1341366..2b9758e 100644 --- a/cxx/CMakeLists.txt +++ b/cxx/CMakeLists.txt @@ -134,7 +134,10 @@ target_include_directories(${MCC_LIBRARY} INTERFACE ${FITPACK_INCLUDE_DIR}) set(ASIBFM700_LIB_SRC asibfm700_hardware.h asibfm700_hardware.cpp) set(ASIBFM700_LIB asibfm700) -add_library(${ASIBFM700_LIB} STATIC ${ASIBFM700_LIB_SRC}) +add_library(${ASIBFM700_LIB} STATIC ${ASIBFM700_LIB_SRC} + asibfm700_slew_model.h asibfm700_slew_model.cpp + asibfm700_common.h) +target_include_directories(${ASIBFM700_LIB} PRIVATE ${FITPACK_INCLUDE_DIR}) # set(MOUNT_SERVER_APP_SRC mount.h mount_state.h mount_server.cpp comm_server.h comm_server_endpoint.h comm_server_configfile.h mount_astrom.h # mount_astrom_default.h mcc_coord.h mount_pz.h mcc_fsm.h mcc_fsm_utils.h mcc_finite_state_machine.h mcc_mount_events_states.h) diff --git a/cxx/asibfm700_common.h b/cxx/asibfm700_common.h new file mode 100644 index 0000000..adebb5f --- /dev/null +++ b/cxx/asibfm700_common.h @@ -0,0 +1,45 @@ +#pragma once + +/* AstroSIB-FM700 FORK MOUNT CONTROL LIBRARY */ + +/* COMMON DEFINITIONS */ + + +#include "mcc_mount_astro_erfa.h" +#include "mcc_mount_pec.h" +#include "mcc_mount_telemetry.h" + +#include "asibfm700_hardware.h" + +namespace asibfm700 +{ + +typedef mcc::astrom::erfa::MccMountAstromEngineERFA AsibFM700AstromEngine; + +typedef mcc::MccMountDefaultPEC AsibFM700PointingErrorCorrection; + +struct AsibFM700TelemetryData : mcc::MccMountTelemetryData { + // apparent target (user-input) current coordinates + coord_t tagRA, tagDEC; + coord_t tagHA; + coord_t tagAZ, tagALT; + coord_t tagPA; +}; + +typedef mcc::MccMountTelemetry + AsibFM700Telemetry; + + +// global mount configuration + +struct AsibFM700Config { + std::chrono::milliseconds hardwareAskingPeriod{100}; // main cycle period + + // mount hardware config + AsibFM700Hardware::hardware_config_t hardwareConfig; +}; + +} // namespace asibfm700 diff --git a/cxx/asibfm700_hardware.cpp b/cxx/asibfm700_hardware.cpp index 0d1492e..1a28d6a 100644 --- a/cxx/asibfm700_hardware.cpp +++ b/cxx/asibfm700_hardware.cpp @@ -93,7 +93,8 @@ std::string_view AsibFM700Hardware::id() const AsibFM700Hardware::error_t AsibFM700Hardware::setPos(AsibFM700Hardware::axes_pos_t pos) { - double X = pos.x, Y = pos.y; + // according to hardware configuration (encoders pins (Eddy said)) X is DEC-axis and Y is HA-axis + double X = pos.y, Y = pos.x; auto err = static_cast(Mount.moveTo(&X, &Y)); return err; @@ -115,8 +116,9 @@ AsibFM700Hardware::error_t AsibFM700Hardware::getPos(AsibFM700Hardware::axes_pos pos.time_point = time_point_t{std::chrono::duration_cast(secs)}; - pos.x = data.encposition.X; - pos.y = data.encposition.Y; + // according to hardware configuration (encoders pins (Eddy said)) X is DEC-axis and Y is HA-axis + pos.x = data.encposition.Y; + pos.y = data.encposition.X; } return err; diff --git a/cxx/asibfm700_hardware.h b/cxx/asibfm700_hardware.h index 2188cb1..0d1f34a 100644 --- a/cxx/asibfm700_hardware.h +++ b/cxx/asibfm700_hardware.h @@ -71,20 +71,6 @@ public: coord_t x, y; }; - // c++ish wrapper to 'conf_t' struct - - struct device_config_t { - std::string MountDevPath; // path to mount device - int MountDevSpeed; // serial speed - std::string EncoderDevPath; // path to encoder device - int EncoderDevSpeed; // serial speed - int SepEncoder; // ==1 if encoder works as separate serial device, ==2 if there's new version with two devices - std::string EncoderXDevPath; // paths to new controller devices - std::string EncoderYDevPath; - double MountReqInterval; // interval between subsequent mount requests (seconds) - double EncoderReqInterval; // interval between subsequent encoder requests (seconds) - }; - struct hardware_config_t { // the 'char*' fields from conf_t: @@ -113,6 +99,7 @@ public: error_t setPos(axes_pos_t); error_t getPos(axes_pos_t&); + error_t stop(); error_t init(); diff --git a/cxx/asibfm700_slew_model.cpp b/cxx/asibfm700_slew_model.cpp new file mode 100644 index 0000000..bd228b4 --- /dev/null +++ b/cxx/asibfm700_slew_model.cpp @@ -0,0 +1,110 @@ +#include "asibfm700_slew_model.h" + + +namespace asibfm700 +{ + +/* error category implementation */ + +const char* AsibFM700SlewModelErrorCategory::name() const noexcept +{ + return "ASTROSIB FM700 MOUNT SLEW MODEL ERROR CATEGORY"; +} + +std::string AsibFM700SlewModelErrorCategory::message(int ec) const +{ + AsibFM700SlewModelErrorCode code = static_cast(ec); + + std::string msg; + + switch (code) { + case AsibFM700SlewModelErrorCode::ERROR_OK: + msg = "OK"; + default: + msg = "UNKNOWN ERROR"; + } + + return msg; +} + +const AsibFM700SlewModelErrorCategory& AsibFM700SlewModelErrorCategory::get() +{ + static const AsibFM700SlewModelErrorCategory constInst; + return constInst; +} + + +AsibFM700SlewModel::AsibFM700SlewModel(AsibFM700Hardware& hw_control, AsibFM700AstromEngine& astrom_engine) + : _hwControl(hw_control), _astromEngine(astrom_engine) +{ +} + + +AsibFM700SlewModel::error_t AsibFM700SlewModel::slew(const slew_params_t& slew_pars, AsibFM700Telemetry& telemetry) +{ + using astrom_t = std::remove_reference_t; + using coord_t = typename astrom_t::coord_t; + using jd_t = typename astrom_t::juldate_t; + + AsibFM700SlewModel::error_t res_err = AsibFM700SlewModelErrorCode::ERROR_OK; + + AsibFM700Hardware::axes_pos_t ax_pos; + + if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_XY) { + // trivial case (the pair is interpretated as raw encoder coordinates) + ax_pos.x = slew_pars.x; + ax_pos.y = slew_pars.y; + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // catalog coordinates + jd_t jd; + coord_t ra_app, dec_app, ha, az, alt; + typename astrom_t::eo_t eo; + + auto err = _astromEngine.greg2jul(std::chrono::system_clock::now(), jd); + + if (!err) { + err = _astromEngine.icrs2obs(slew_pars.x, slew_pars.y, jd, ra_app, dec_app, ha, az, alt, eo); + + if (!err) { + res_err = slew({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP, + .x = ha, + .y = dec_app, + .stop = slew_pars.stop}, + telemetry); + } + } + + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP) { // apparent + jd_t jd; + typename astrom_t::eo_t eo; + + auto err = _astromEngine.greg2jul(std::chrono::system_clock::now(), jd); + if (!err) { + typename astrom_t::sideral_time_t lst; + err = _astromEngine.apparentSiderTime(jd, lst, true); + + if (!err) { + err = _astromEngine.eqOrigins(jd, eo); + if (!err) { + res_err = slew({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP, + .x = lst - slew_pars.x + eo, // HA = LST - RA_APP + EO + .y = slew_pars.y, + .stop = slew_pars.stop}, + telemetry); + } + } + } + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP) { // apparent + // compute encoders coordinates + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_AZALT) { + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_AZZD) { + } + + auto err = _hwControl.setPos(std::move(ax_pos)); + + while (true) { + } + + return res_err; +} + +} // namespace asibfm700 diff --git a/cxx/asibfm700_slew_model.h b/cxx/asibfm700_slew_model.h new file mode 100644 index 0000000..2f2cb61 --- /dev/null +++ b/cxx/asibfm700_slew_model.h @@ -0,0 +1,75 @@ +#pragma once + +/* AstroSIB-FM700 FORK MOUNT CONTROL LIBRARY */ + +/* SLEW MODEL IMPLEMENTATION */ + + +#include "asibfm700_common.h" + +namespace asibfm700 +{ + +enum class AsibFM700SlewModelErrorCode : int { ERROR_OK }; + +// error category +struct AsibFM700SlewModelErrorCategory : public std::error_category { + const char* name() const noexcept; + std::string message(int ec) const; + + static const AsibFM700SlewModelErrorCategory& get(); +}; + + +inline std::error_code make_error_code(AsibFM700SlewModelErrorCode ec) +{ + return std::error_code(static_cast(ec), AsibFM700SlewModelErrorCategory::get()); +} + +} // namespace asibfm700 + + + +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + + + +namespace asibfm700 +{ + +class AsibFM700SlewModel final +{ +public: + typedef std::error_code error_t; + + struct slew_params_t { + typedef mcc::MccAngle coord_t; + + mcc::MccCoordPairKind coordPairKind{mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP}; + + coord_t x{0.0}; + coord_t y{0.0}; + + bool stop{false}; + }; + + AsibFM700SlewModel(AsibFM700Hardware& hw_control, AsibFM700AstromEngine& astrom_engine); + + ~AsibFM700SlewModel(); + + error_t slew(const slew_params_t&, AsibFM700Telemetry&); + +private: + AsibFM700Hardware& _hwControl; + AsibFM700AstromEngine& _astromEngine; +}; + +} // namespace asibfm700 diff --git a/cxx/mcc_mount_concepts.h b/cxx/mcc_mount_concepts.h index 2d43117..57b42e4 100644 --- a/cxx/mcc_mount_concepts.h +++ b/cxx/mcc_mount_concepts.h @@ -186,6 +186,7 @@ concept mcc_mount_hardware_c = !std::copyable && std::movable && requires( { t.setPos(std::declval()) } -> std::same_as; { t.getPos(std::declval()) } -> std::same_as; + { t.stop() } -> std::same_as; { t.init() } -> std::same_as; }; @@ -215,7 +216,7 @@ concept mcc_mount_pec_c = requires(T t, const T t_const) { { t.compute(std::declval(), std::declval(), - std::declval()) + std::declval()) } -> std::same_as; }; @@ -226,6 +227,7 @@ concept mcc_mount_pec_c = requires(T t, const T t_const) { template concept mcc_mount_telemetry_data_c = requires(T telemetry) { typename T::coord_t; + typename T::time_point_t; // // target current coordinates // requires std::same_as; // apparent RA @@ -297,7 +299,7 @@ concept mcc_slew_params_c = std::movable && requires(T t) { template concept mcc_slew_model_c = mcc_mount_telemetry_c && requires(T t) { - typename T::error_t; + requires mcc_error_c; requires mcc_slew_params_c; { diff --git a/cxx/mcc_mount_coord.h b/cxx/mcc_mount_coord.h index 6d7e65b..0f01f11 100644 --- a/cxx/mcc_mount_coord.h +++ b/cxx/mcc_mount_coord.h @@ -397,7 +397,9 @@ static auto operator*(const T1& v1, const T2& v2) } else if constexpr (std::is_arithmetic_v) { return v1 *= v2; } else { - static_assert(false, "INCOMPATIBLE TYPES!"); + using res_t = std::conditional_t && std::derived_from, T1, T2>; + return res_t{(double)v1 * (double)v2}; + // static_assert(false, "INCOMPATIBLE TYPES!"); } } diff --git a/cxx/mcc_mount_pec.h b/cxx/mcc_mount_pec.h index 411196b..c5d579b 100644 --- a/cxx/mcc_mount_pec.h +++ b/cxx/mcc_mount_pec.h @@ -28,6 +28,7 @@ class MccMountDefaultPEC final public: static constexpr MccMountType mountType = MOUNT_TYPE; + typedef std::error_code error_t; typedef MccAngle coord_t; struct pec_result_t { @@ -117,11 +118,12 @@ public: return _pecData.type; } - // X and Y axis encoder coordinates - pec_result_t compute(const coord_t& x, const coord_t& y) + // The computed PEC quantities must be interpretated as: + // apparent_X = encoder_X + pec_result_t.dx + // apparent_Y = encoder_Y + pec_result_t.dy + // so, input x and y are assumed to be mount axis encoder coordinates + error_t compute(const coord_t& x, const coord_t& y, pec_result_t& res) { - pec_result_t res{0.0, 0.0}; - std::lock_guard lock(_pecDataMutex); if constexpr (mcc_is_equatorial_mount) { // equatorial @@ -163,7 +165,7 @@ public: if (ret) { res.dx = std::numeric_limits::quiet_NaN(); res.dy = std::numeric_limits::quiet_NaN(); - return res; + return std::error_code(); // !!!!!!!!!!!!!! } @@ -173,7 +175,7 @@ public: if (ret) { res.dx = std::numeric_limits::quiet_NaN(); res.dy = std::numeric_limits::quiet_NaN(); - return res; + return std::error_code(); // !!!!!!!!!!!!!! } @@ -185,9 +187,48 @@ public: static_assert(false, "UNSUPPORTED"); } - return res; + return std::error_code(); } + // from celestial to encoder (use of iterative scheme) + error_t reverseCompute(const coord_t& x, const coord_t& y, pec_result_t& res, coord_t eps, size_t max_iter = 5) + { + coord_t e2 = eps * eps; + + coord_t xi = x, yi = y; + coord_t xp, yp; + size_t iter = 1; + + // the first iteration + auto err = compute(x, y, res); + + if (!err) { + xp = x - res.dx; + yp = y - res.dy; + + bool ok = ((xp - x) * (xp - x) + (yp - y) * (yp - y)) <= e2; + if (ok) { + return std::error_code(); + } + + while (iter < max_iter) { + xi = xp; + yi = xp; + err = compute(xi, yi, res); + xp -= res.dx; + yp -= res.dy; + + ok = ((xp - xi) * (xp - xi) + (yp - yi) * (yp - yi)) <= e2; + if (ok) { + return std::error_code(); + } + } + + // err = "exceed max iterations";!!! + } + + return err; + } private: @@ -203,4 +244,7 @@ private: typedef MccMountDefaultPEC MccMountDefaultAltAzPec; typedef MccMountDefaultPEC MccMountDefaultForkPec; +static_assert(traits::mcc_mount_pec_c, ""); + + } // namespace mcc diff --git a/cxx/mcc_mount_telemetry.h b/cxx/mcc_mount_telemetry.h index 76cba6b..633ba19 100644 --- a/cxx/mcc_mount_telemetry.h +++ b/cxx/mcc_mount_telemetry.h @@ -13,9 +13,95 @@ namespace mcc { +namespace traits +{ + +/* enhanced telemetry data concept */ +/* in general it correspond to definitions used in astrometry engine (see its concept) */ + +template +concept mcc_mount_telemetry_enh_data_c = mcc_mount_telemetry_data_c && requires(T t) { + requires traits::mcc_astrom_engine_c; + requires traits::mcc_mount_pec_c; + + // check for types definitions and its consistency + requires std::same_as; + + requires std::same_as; + + requires std::same_as; // a type to represent Julian date + + requires std::same_as; // a type to represent sideral time + + requires std::same_as; // a type to represent parallactic angle + // typename T::eo_t; // a type to represent equation of origins + + requires std::same_as; // Julian date + requires std::same_as; // sideral time + requires std::same_as; // parallactic angle + + // encoder-measured (non-corrected for PCS) current mount position and moving speed (in radians, radians/s) + // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one + requires std::same_as; + requires std::same_as; + + // current refraction coefficients + requires std::same_as; + // current refraction correction (for mntALT) + requires std::same_as; + + // PEC (pointing error correction): + // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one + requires std::same_as; + requires std::same_as; +}; + +} // namespace traits + + +// default generic telemetry data definition +template +struct MccMountTelemetryData { + typedef typename ASTROM_ENGINE_T::coord_t coord_t; + typedef typename ASTROM_ENGINE_T::time_point_t time_point_t; + typedef typename ASTROM_ENGINE_T::juldate_t juldate_t; + typedef typename ASTROM_ENGINE_T::sideral_time_t sideral_time_t; + typedef typename ASTROM_ENGINE_T::pa_t pa_t; + + // time-related + time_point_t utc; // time point of measurements, UTC + juldate_t jd; // Julian date + sideral_time_t siderTime; // local apperant sideral time + + // encoder-measured current mount coordinates + coord_t mntRA, mntDEC; + coord_t mntHA; + coord_t mntAZ, mntALT; + pa_t mntPA; + + // encoder-measured (non-corrected for PCS) current mount position + // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one + coord_t mntPosX, mntPosY; + + // current refraction coefficients + typename PEC_T::pec_result_t currRefrCoeffs; + + // current refraction correction (for mntALT) + coord_t currRefr; + + // PEC (pointing error correction): + // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one + coord_t pecX, pecY; +}; + template + traits::mcc_mount_hardware_c HARDWARE_T, + traits::mcc_mount_telemetry_enh_data_c DATA_T = + MccMountTelemetryData> class MccMountTelemetry { public: @@ -51,42 +137,44 @@ public: static_assert(std::convertible_to, "HARDWARE TIME-POINT TYPE MUST BE CONVERTIBLE TO ASTROMETRY ENGINE ONE!"); + typedef DATA_T mount_telemetry_data_t; + // mount current telemetry data: time, position and related quantities - struct mount_telemetry_data_t { - typedef typename astrom_engine_t::coord_t coord_t; + // struct mount_telemetry_data_t { + // typedef typename astrom_engine_t::coord_t coord_t; - // time-related - typename astrom_engine_t::time_point_t utc; // time point of measurements, UTC - typename astrom_engine_t::juldate_t jd; // Julian date - typename astrom_engine_t::sideral_time_t siderTime; // local apperant sideral time - // typename astrom_engine_t::time_point_t ut1; // Universal time - // typename astrom_engine_t::time_point_t tt; // Terrestial time + // // time-related + // typename astrom_engine_t::time_point_t utc; // time point of measurements, UTC + // typename astrom_engine_t::juldate_t jd; // Julian date + // typename astrom_engine_t::sideral_time_t siderTime; // local apperant sideral time + // // typename astrom_engine_t::time_point_t ut1; // Universal time + // // typename astrom_engine_t::time_point_t tt; // Terrestial time - // apparent target (user-input) current coordinates (in radians) - coord_t tagRA, tagDEC; - coord_t tagHA; - coord_t tagAZ, tagALT; - coord_t tagPA; // paralactic angle + // // apparent target (user-input) current coordinates (in radians) + // coord_t tagRA, tagDEC; + // coord_t tagHA; + // coord_t tagAZ, tagALT; + // coord_t tagPA; // paralactic angle - // encoder-measured current mount coordinates (in radians) - coord_t mntRA, mntDEC; - coord_t mntHA; - coord_t mntAZ, mntALT; - typename astrom_engine_t::pa_t mntPA; + // // encoder-measured current mount coordinates (in radians) + // coord_t mntRA, mntDEC; + // coord_t mntHA; + // coord_t mntAZ, mntALT; + // typename astrom_engine_t::pa_t mntPA; - // encoder-measured (non-corrected for PCS) current mount position and moving speed (in radians, radians/s) - // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one - coord_t mntPosX, mntPosY; + // // encoder-measured (non-corrected for PCS) current mount position and moving speed (in radians, radians/s) + // // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one + // coord_t mntPosX, mntPosY; - // current refraction coefficients - typename pec_t::pec_result_t currRefrCoeffs; - // current refraction correction (for mntALT) - coord_t currRefr; + // // current refraction coefficients + // typename pec_t::pec_result_t currRefrCoeffs; + // // current refraction correction (for mntALT) + // coord_t currRefr; - // PEC (pointing error correction): - // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one - coord_t pecX, pecY; - }; + // // PEC (pointing error correction): + // // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one + // coord_t pecX, pecY; + // }; MccMountTelemetry(astrom_engine_t& astrom_engine, pec_t& pec, hardware_t& hardware) : _astromEngine(astrom_engine), _pec(pec), _hardware(hardware) diff --git a/cxx/mcc_slew_model.h b/cxx/mcc_slew_model.h new file mode 100644 index 0000000..f955bbe --- /dev/null +++ b/cxx/mcc_slew_model.h @@ -0,0 +1,212 @@ +#pragma once + + +/* MOUNT CONTROL COMPONENTS LIBRARY */ + + +/* A VERY SIMPLE SLEW MODEL GENERIC IMPLEMENTATION */ + + +#include "mcc_mount_concepts.h" + +namespace mcc +{ + + +/* + * WARNING: it is assumed that coordinates are in radians! + * but this fact is only used if slew coordinate pair are given as + * [azimuth, zenithal distance] (see sources code below) + */ + +template +class MccSimpleSlewModel +{ +public: + typedef std::error_code error_t; + + struct slew_params_t { + typedef mcc::MccAngle coord_t; + + mcc::MccCoordPairKind coordPairKind{mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP}; + + coord_t x{0.0}; + coord_t y{0.0}; + + bool stop{false}; + }; + + + template + MccSimpleSlewModel(HARDWARE_T& hardware, ASTROM_T& astrom_engine, PEC_T& pec) + { + const auto p_hardware = &hardware; + const auto p_astrom_engine = &astrom_engine; + const auto p_pec = &pec; + + + + _slewFunc = [p_hardware, p_astrom_engine, p_pec](this auto&& self, const slew_params_t& slew_pars, + TELEMETRY_T& telemetry) { + using coord_t = typename ASTROM_T::coord_t; + using jd_t = typename ASTROM_T::juldate_t; + + typename HARDWARE_T::axes_pos_t ax_pos; + + error_t res_err; + typename ASTROM_T::error_t ast_err; + + if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_XY) { + // trivial case (the pair is interpretated as raw encoder coordinates) + ax_pos.x = slew_pars.x; + ax_pos.y = slew_pars.y; + } else if (slew_pars.coordPairKind == + mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // catalog coordinates + jd_t jd; + coord_t ra_app, dec_app, ha, az, alt; + typename ASTROM_T::eo_t eo; + + ast_err = p_astrom_engine->greg2jul(std::chrono::system_clock::now(), jd); + + if (!ast_err) { + ast_err = p_astrom_engine->icrs2obs(slew_pars.x, slew_pars.y, jd, ra_app, dec_app, ha, az, alt, eo); + + if (!ast_err) { + if constexpr (mccIsEquatorialMount(PEC_T::mountType)) { + res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP, + .x = ha, + .y = dec_app, + .stop = slew_pars.stop}, + telemetry); + } else if constexpr (mccIsAltAzMount(PEC_T::mountType)) { + res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, + .x = az, + .y = alt, + .stop = slew_pars.stop}, + telemetry); + } else { + static_assert(false, "UNKNOWN MOUNT TYPE!"); + } + } + } + + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP) { // apparent + jd_t jd; + typename ASTROM_T::eo_t eo; + + ast_err = p_astrom_engine->greg2jul(std::chrono::system_clock::now(), jd); + if (!ast_err) { + typename ASTROM_T::sideral_time_t lst; + ast_err = p_astrom_engine->apparentSiderTime(jd, lst, true); + + if (!ast_err) { + ast_err = p_astrom_engine->eqOrigins(jd, eo); + if (!ast_err) { + res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP, + .x = lst - slew_pars.x + eo, // HA = LST - RA_APP + EO + .y = slew_pars.y, + .stop = slew_pars.stop}, + telemetry); + } + } + } + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP) { // apparent + if constexpr (mccIsEquatorialMount(PEC_T::mountType)) { // compute encoder coordinates + coord_t eps = 1.0 / 3600.0 * std::numbers::pi / 180.0; + + typename PEC_T::pec_result_t pec_res; + + auto err = p_pec->reverseCompute(slew_pars.x, slew_pars.y, pec_res, eps, 10); + if (!err) { + res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY, + .x = slew_pars.x - pec_res.dx, + .y = slew_pars.y - pec_res.dy, + .stop = slew_pars.stop}, + telemetry); + } + } else if constexpr (mccIsAltAzMount(PEC_T::mountType)) { + coord_t az, alt; + + ast_err = p_astrom_engine->hadec2azalt(slew_pars.x, slew_pars.y, az, alt); + + if (!ast_err) { + res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, + .x = az, + .y = alt, + .stop = slew_pars.stop}, + telemetry); + } + } else { + static_assert(false, "UNKNOWN MOUNT TYPE!"); + } + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_AZALT) { + if constexpr (mccIsEquatorialMount(PEC_T::mountType)) { + coord_t ha, dec; + + ast_err = p_astrom_engine->azalt2hadec(slew_pars.x, slew_pars.y, ha, dec); + + if (!ast_err) { + res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP, + .x = ha, + .y = dec, + .stop = slew_pars.stop}, + telemetry); + } + } else if constexpr (mccIsAltAzMount(PEC_T::mountType)) { // compute encoder coordinates + coord_t eps = 1.0 / 3600.0 * std::numbers::pi / 180.0; + + typename PEC_T::pec_result_t pec_res; + + auto err = p_pec->reverseCompute(slew_pars.x, slew_pars.y, pec_res, eps, 10); + if (!err) { + res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY, + .x = slew_pars.x - pec_res.dx, + .y = slew_pars.y - pec_res.dy, + .stop = slew_pars.stop}, + telemetry); + } + + } else { + static_assert(false, "UNKNOWN MOUNT TYPE!"); + } + } else if (slew_pars.coordPairKind == mcc::MccCoordPairKind::COORDS_KIND_AZZD) { + // + // WARNING: it is assumed that coordinates are in radians! + // + res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, + .x = slew_pars.x, + .y = std::numbers::pi / 2.0 - slew_pars.y, + .stop = slew_pars.stop}, + telemetry); + } + + if (res_err) { + return res_err; + } + + if (!ast_err) { + auto err = p_hardware->setPos(std::move(ax_pos)); + + while (true) { + } + } + }; + } + + error_t slew(const slew_params_t& pars, TELEMETRY_T& telemetry) + { + error_t res_err = _slewFunc(pars, telemetry); + + return res_err; + } + +protected: + std::function _slewFunc{}; +}; + + +// static_assert(traits::mcc_slew_model_c<>); + +} // namespace mcc