diff --git a/cxx/mcc_mount_astro_erfa.h b/cxx/mcc_mount_astro_erfa.h index be9a28f..a15ed00 100644 --- a/cxx/mcc_mount_astro_erfa.h +++ b/cxx/mcc_mount_astro_erfa.h @@ -21,6 +21,12 @@ namespace mcc::astrom::erfa #include #include + +/* A concept for ERFA-library compatible type to represent anglular quantities */ +template +concept mcc_erfa_angle_t = std::constructible_from && std::convertible_to; + +template class MccMountAstromEngineERFA { protected: @@ -34,7 +40,7 @@ protected: "unacceptable year"}; public: - enum engine_err_t : size_t { + enum error_t : size_t { ERROR_OK = 0, ERROR_INVALID_INPUT_ARG, ERROR_JULDATE_INVALID_YEAR, @@ -62,9 +68,11 @@ public: double wavelength = 0.55; // observed wavelength in mkm - MccAngle lat = "00:00:00"_dms; // site latitude - MccAngle lon = "00:00:00"_dms; // site longitude - double elev = 0.0; // site elevation (in meters) + AngleT lat = "00:00:00"_dms; // site latitude + AngleT lon = "00:00:00"_dms; // site longitude + // MccAngle lat = "00:00:00"_dms; // site latitude + // MccAngle lon = "00:00:00"_dms; // site longitude + double elev = 0.0; // site elevation (in meters) mcc::astrom::iers::MccLeapSeconds _leapSeconds{}; mcc::astrom::iers::MccIersBulletinA _bulletinA{}; @@ -77,12 +85,20 @@ public: double mjd{51544.5}; // J2000.0 }; - typedef MccAngle coord_t; + // typedef MccAngle coord_t; - typedef MccAngle sideral_time_t; - // typedef juldate_t terr_time_t; - typedef MccAngle pa_t; - typedef MccAngle eo_t; + // typedef MccAngle sideral_time_t; + // typedef MccAngle pa_t; + // typedef MccAngle eo_t; + + + /* use of the same type fro representation of celestial and geodetic coordinates, celestial angles (e.g. P.A.), + * sideral time */ + typedef AngleT coord_t; + + typedef AngleT sideral_time_t; + typedef AngleT pa_t; + typedef AngleT eo_t; struct refract_result_t { double refa, refb; @@ -110,7 +126,7 @@ public: } - std::string errorString(engine_err_t err) const + std::string errorString(error_t err) const { return engineErrorString[err]; } @@ -119,7 +135,7 @@ public: // templated generic version template - engine_err_t greg2jul(TpT time_point, juldate_t& juldate) + error_t greg2jul(TpT time_point, juldate_t& juldate) { using namespace std::literals::chrono_literals; @@ -152,13 +168,13 @@ public: return ERROR_OK; } - engine_err_t greg2jul(time_point_t time_point, juldate_t& juldate) + error_t greg2jul(time_point_t time_point, juldate_t& juldate) { return greg2jul(time_point, juldate); } - engine_err_t terrestrialTime(juldate_t juldate, juldate_t& tt) + error_t terrestrialTime(juldate_t juldate, juldate_t& tt) { std::lock_guard lock{_stateMutex}; @@ -179,7 +195,7 @@ public: } - engine_err_t apparentSiderTime(juldate_t juldate, sideral_time_t& gst, bool islocal = false) + error_t apparentSiderTime(juldate_t juldate, sideral_time_t& gst, bool islocal = false) { // std::lock_guard lock{_stateMutex}; @@ -227,7 +243,7 @@ public: } - engine_err_t eqOrigins(juldate_t juldate, eo_t& eo) + error_t eqOrigins(juldate_t juldate, eo_t& eo) { juldate_t tt; @@ -243,7 +259,7 @@ public: /* atmospheric refraction-related methods */ - engine_err_t refraction(refract_result_t& refr) + error_t refraction(refract_result_t& refr) { std::lock_guard lock{_stateMutex}; @@ -254,7 +270,7 @@ public: } - engine_err_t refractCorrection(const coord_t& alt, const refract_result_t& ref_params, coord_t& corr) + error_t refractCorrection(const coord_t& alt, const refract_result_t& ref_params, coord_t& corr) { if (alt <= 0.0) { corr = 35.4 / 60.0 * std::numbers::pi / 180.0; // 35.4 arcminutes @@ -268,14 +284,14 @@ public: /* coordinates conversional methods */ - engine_err_t icrs2obs(coord_t ra, - coord_t dec, - juldate_t juldate, - coord_t& ra_app, - coord_t& dec_app, - coord_t& ha, - coord_t& az, - coord_t& alt) + error_t icrs2obs(coord_t ra, + coord_t dec, + juldate_t juldate, + coord_t& ra_app, + coord_t& dec_app, + coord_t& ha, + coord_t& az, + coord_t& alt) { std::lock_guard lock{_stateMutex}; @@ -316,7 +332,7 @@ public: } - engine_err_t hadec2azalt(coord_t ha, coord_t dec, coord_t& az, coord_t& alt) + error_t hadec2azalt(coord_t ha, coord_t dec, coord_t& az, coord_t& alt) { std::lock_guard lock{_stateMutex}; @@ -329,7 +345,7 @@ public: return ERROR_OK; } - engine_err_t azalt2hadec(coord_t az, coord_t alt, coord_t& ha, coord_t& dec) + error_t azalt2hadec(coord_t az, coord_t alt, coord_t& ha, coord_t& dec) { std::lock_guard lock{_stateMutex}; @@ -344,7 +360,7 @@ public: } - engine_err_t hadec2pa(coord_t ha, coord_t dec, pa_t& pa) + error_t hadec2pa(coord_t ha, coord_t dec, pa_t& pa) { std::lock_guard lock{_stateMutex}; @@ -386,4 +402,4 @@ protected: } // namespace mcc::astrom::erfa -static_assert(mcc::traits::mcc_astrom_engine_c, ""); +static_assert(mcc::traits::mcc_astrom_engine_c>, ""); diff --git a/cxx/mcc_mount_concepts.h b/cxx/mcc_mount_concepts.h index fd510a1..ca5dca0 100644 --- a/cxx/mcc_mount_concepts.h +++ b/cxx/mcc_mount_concepts.h @@ -64,7 +64,7 @@ namespace mcc::traits template concept mcc_astrom_engine_c = requires(T t, const T t_const) { - typename T::engine_err_t; + requires mcc_error_c; typename T::engine_state_t; requires std::movable; @@ -81,7 +81,7 @@ concept mcc_astrom_engine_c = requires(T t, const T t_const) { { t_const.getState() } -> std::same_as; - { t_const.errorString(std::declval()) } -> mcc_formattable; + { t_const.errorString(std::declval()) } -> mcc_formattable; /* coordinates conversional methods */ @@ -91,25 +91,30 @@ concept mcc_astrom_engine_c = requires(T t, const T t_const) { std::declval(), std::declval(), std::declval(), std::declval(), std::declval(), std::declval()) - } -> std::same_as; + } -> std::same_as; // compute hour angle and declination from azimuth and altitude: hadec2azalt(ha, dec, az, alt) { t.hadec2azalt(std::declval(), std::declval(), std::declval(), std::declval()) - } -> std::same_as; + } -> std::same_as; // compute azimuth and altitude from hour angle and declination: azalt2hadec(az, alt, ha, dec) { t.azalt2hadec(std::declval(), std::declval(), std::declval(), std::declval()) - } -> std::same_as; + } -> std::same_as; // compute parallactic angle: hadec2pa(ha, dec, pa) { t.hadec2pa(std::declval(), std::declval(), std::declval()) - } -> std::same_as; + } -> std::same_as; + + // compute equation of origins + { + t.eqOrigins(std::declval(), std::declval()) + } -> std::same_as; /* time-related methods */ @@ -117,95 +122,31 @@ concept mcc_astrom_engine_c = requires(T t, const T t_const) { // Gregorian Calendar time point to Julian Date: greg2jul(time_point, jd) { t.greg2jul(std::declval(), std::declval()) - } -> std::same_as; + } -> std::same_as; // apparent sideral time: apparentSiderTime(jd, gst, islocal) // if islocal == false then the method must return the Greenwich apparent sideral time, otherwise - local one { t.apparentSiderTime(std::declval(), std::declval(), std::declval()) - } -> std::same_as; + } -> std::same_as; /* atmospheric refraction-related methods */ // compute refraction-related quantities: refraction(refr_params) - { t.refraction(std::declval()) } -> std::same_as; + { t.refraction(std::declval()) } -> std::same_as; // compute refraction correction for given altitude: refractCorrection(alt, refr_params, refr_corr) { t.refractCorrection(std::declval(), std::declval(), std::declval()) - } -> std::same_as; + } -> std::same_as; }; -/* MOUNT AXES AND MOTORS HARDWARE GENERIC ABSTRACTION */ +/* A VERY GENERIC MOUNT HARDWARE CONCEPT */ -// // encoder basic concept (e.g. mount axis encoder or motor shaft one) -// template -// concept mcc_hw_encoder_c = requires(T t, const T t_const) { -// requires mcc_error_c; - -// typename T::time_point_t; -// typename T::coord_t; -// typename T::speed_t; -// typename T::accel_t; - -// requires requires(typename T::state_t st) { -// requires std::same_as; -// requires std::same_as; -// requires std::same_as; -// requires std::same_as; -// }; - -// { t_const.errorString(std::declval()) } -> mcc_formattable; - -// { t_const.id() } -> mcc_formattable; - -// { t.getState(std::declval()) } -> std::same_as; -// }; - - -// template -// concept mcc_hw_motor_c = requires(T t, const T t_const) { -// requires mcc_error_c; - -// typename T::coord_t; -// typename T::speed_t; -// typename T::accel_t; - -// requires requires(typename T::pos_t st) { -// requires std::same_as; -// requires std::same_as; // means maximal allowed speed -// requires std::same_as; // means a maximal allowed acceleration -// (jerk) -// }; - - -// { t_const.errorString(std::declval()) } -> mcc_formattable; - -// { t_const.id() } -> mcc_formattable; - -// { t.toPos(std::declval()) } -> std::same_as; - -// { t.stop() } -> std::same_as; -// }; - - - -// namespace details -// { -// template -// concept mcc_hw_enc_lvref_c = mcc_nonconst_lvref && mcc_hw_encoder_c>; - -// template -// concept mcc_hw_motor_lvref_c = mcc_nonconst_lvref && mcc_hw_motor_c>; - -// } // namespace details - - -// a very generic mount hardware concept template concept mcc_mount_hardware_c = requires(T t, const T t_const) { requires mcc_error_c; @@ -229,14 +170,6 @@ concept mcc_mount_hardware_c = requires(T t, const T t_const) { { t.setPos(std::declval()) } -> std::same_as; { t.getPos(std::declval()) } -> std::same_as; - - // // access to encoders - // { t.encoderPosX() } -> details::mcc_hw_enc_lvref_c; - // { t.encoderPosY() } -> details::mcc_hw_enc_lvref_c; - - // // access to motors - // { t.motorX() } -> details::mcc_hw_motor_lvref_c; - // { t.motorY() } -> details::mcc_hw_motor_lvref_c; }; @@ -244,16 +177,23 @@ concept mcc_mount_hardware_c = requires(T t, const T t_const) { template concept mcc_mount_pec_c = requires(T t, const T t_const) { + requires mcc_error_c; typename T::coord_t; typename T::pec_data_t; - typename T::pec_result_t; - { t.setData(std::declval()) }; - { t_const.getData() } -> std::same_as; + // at least contains .dx and .dy fields + requires requires(typename T::pec_result_t res) { + requires std::same_as; + requires std::same_as; + }; + + { t.setData(std::declval()) } -> std::same_as; + { t_const.getData(std::declval()) } -> std::same_as; { - t.compute(std::declval(), std::declval()) - } -> std::same_as; + t.compute(std::declval(), std::declval(), + std::declval()) + } -> std::same_as; }; diff --git a/cxx/mcc_mount_telemetry.h b/cxx/mcc_mount_telemetry.h index a2f2cce..45a3209 100644 --- a/cxx/mcc_mount_telemetry.h +++ b/cxx/mcc_mount_telemetry.h @@ -5,33 +5,14 @@ /* MOUNT TELEMETRY OBJECT CONCEPT AND POSSIBLE IMPLEMENTATION */ -#include #include -// #include "mcc_mount_config.h" #include "mcc_mount_concepts.h" namespace mcc { -// namespace traits -// { - -// template -// concept mcc_mount_telemetry_c = requires(T t, const T t_const) { -// typename T::error_t; -// typename T::mount_telemetry_data_t; - -// { t_const.errorString(std::declval()) } -> mcc_formattable; - -// { t.update() } -> std::same_as; - -// { t_const.data() } -> std::same_as; -// }; - -// } // namespace traits - template @@ -42,29 +23,44 @@ public: typedef PEC_T pec_t; typedef HARDWARE_T hardware_t; - enum error_t : int { TEL_ERROR_OK = 0, TEL_ERROR_HARDWARE, TEL_ERROR_ASTROMETRY_COMP }; + enum error_t : int { TEL_ERROR_OK = 0, TEL_ERROR_HARDWARE, TEL_ERROR_ASTROMETRY_COMP, TEL_ERROR_PEC }; // check for coordinate types consistency static_assert(std::convertible_to, "HARDWARE COORDINATE TYPE MUST BE CONVERTIBLE TO ASTROMETRY ENGINE ONE!"); - static_assert(std::convertible_to, - "ASTROMETRY ENGINE COORDINATE TYPE MUST BE CONVERTIBLE TO PEC ONE!"); + static_assert(std::convertible_to, + "HARDWARE COORDINATE TYPE MUST BE CONVERTIBLE TO PEC ONE!"); - static_assert(std::same_as, - "TIME-POINT TYPE IN ASTROMETRY ENGINE AND HARDWARE MUST BE THE SAME!"); + // static_assert(std::convertible_to, + // "ASTROMETRY ENGINE COORDINATE TYPE MUST BE CONVERTIBLE TO PEC ONE!"); + + // mandatory arithmetic operations + static_assert( // for CIO-based apparent RA computation and PEC correction addition (see below) + requires(typename astrom_engine_t::coord_t v1, + typename astrom_engine_t::coord_t v2, + typename pec_t::coord_t v3) { + { v1 + v2 } -> std::convertible_to; + { v1 - v2 } -> std::convertible_to; + v1 += v3; + }, + "ASTROMETRY ENGINE COORDINATE TYPE MUST DEFINE '+', '+=' AND '-' ARITHMETIC OPERATIONS!"); + + + // check for time point types consistency + static_assert(std::convertible_to, + "HARDWARE TIME-POINT TYPE MUST BE CONVERTIBLE TO ASTROMETRY ENGINE ONE!"); // mount current telemetry data: time, position and related quantities struct mount_telemetry_data_t { typedef astrom_engine_t::coord_t mnt_coord_t; - // typedef astrom_engine_t::coord_t mnt_speed_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 - // astrom_engine_t::time_point_t ut1; // Universal time - // astrom_engine_t::time_point_t tt; // Terrestial 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) mnt_coord_t tagRA, tagDEC; @@ -81,7 +77,6 @@ public: // 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 mnt_coord_t mntPosX, mntPosY; - // mnt_speed_t mntSpeedX, mntSpeedY; // current refraction coefficients typename pec_t::pec_result_t currRefrCoeffs; @@ -94,98 +89,8 @@ public: }; MccMountTelemetry(astrom_engine_t& astrom_engine, pec_t& pec, hardware_t& hardware) + : _astromEngine(astrom_engine), _pec(pec), _hardware(hardware) { - // to be sure that arguments are captured by reference - const auto astrom_engine_ptr = &astrom_engine; - const auto pec_ptr = &pec; - const auto hardware_ptr = &hardware; - - _updateImpl = [astrom_engine_ptr, pec_ptr, hardware_ptr, this]() { - mount_telemetry_data_t current_data; - - // computing ... - - typename hardware_t::axes_pos_t ax_pos; - - auto err = hardware_ptr->getPos(ax_pos); - if (err) { - // logging?!!! - return TEL_ERROR_HARDWARE; - } - - _data.utc = ax_pos.time_point; - _data.mntPosX = ax_pos.x; - _data.mntPosY = ax_pos.y; - - // compute Julian date - auto ast_err = astrom_engine_ptr->greg2jul(_data.utc, _data.jd); - if (ast_err) { - return TEL_ERROR_ASTROMETRY_COMP; - } - - // compute local apparent sideral time - ast_err = astrom_engine_ptr->apparentSiderTime(_data.jd, _data.siderTime, true); - if (ast_err) { - return TEL_ERROR_ASTROMETRY_COMP; - } - - // compute equation of origins - typename astrom_engine_t::eo_t eo; - ast_err = astrom_engine_ptr->eqOrigins(_data.jd, eo); - if (ast_err) { - return TEL_ERROR_ASTROMETRY_COMP; - } - - - - typename pec_t::pec_result_t pec_res; - pec_res = pec_ptr->compute(ax_pos.x, ax_pos.y); - - if constexpr (mccIsEquatorialMount(pec_t::mountType)) { - _data.mntHA = pec_res.x + ax_pos.x; - _data.mntDEC = pec_res.y + ax_pos.y; - - ast_err = astrom_engine_ptr->hadec2azalt(_data.mntHA, _data.mntDEC, _data.mntAZ, _data.mntALT); - if (ast_err) { - return TEL_ERROR_ASTROMETRY_COMP; - } - } else if constexpr (mccIsAltAzMount(pec_t::mountType)) { - _data.mntAZ = pec_res.x + ax_pos.x; - _data.mntALT = pec_res.y + ax_pos.x; - - ast_err = astrom_engine_ptr->azalt2hadec(_data.mntAZ, _data.mntALT, _data.mntHA, _data.mntDEC); - if (ast_err) { - return TEL_ERROR_ASTROMETRY_COMP; - } - } else { - static_assert(false, "UNSUPPORTED MOUNT TYPE!"); - } - - // compute CIO-based apparent RA - _data.mntRA = _data.siderTime - _data.mntHA + eo; - - // compute PA - ast_err = astrom_engine_ptr->hadec2pa(_data.mntHA, _data.mntDEC, _data.mntPA); - if (ast_err) { - return TEL_ERROR_ASTROMETRY_COMP; - } - - ast_err = astrom_engine_ptr->refraction(_data.currRefrCoeffs); - if (ast_err) { - return TEL_ERROR_ASTROMETRY_COMP; - } - - ast_err = astrom_engine_ptr->refractCorrection(_data.mntALT, _data.currRefrCoeffs, _data.currRefr); - if (ast_err) { - return TEL_ERROR_ASTROMETRY_COMP; - } - - - - std::lock_guard lock{_updateMutex}; - - _data = std::move(current_data); - }; } virtual ~MccMountTelemetry() = default; @@ -193,7 +98,93 @@ public: // update current data method error_t update() { - _updateImpl(); + mount_telemetry_data_t current_data; + + typename hardware_t::axes_pos_t ax_pos; + + auto err = _hardware.getPos(ax_pos); + if (err) { + // logging?!!! + return TEL_ERROR_HARDWARE; + } + + _data.utc = ax_pos.time_point; + _data.mntPosX = static_cast(ax_pos.x); + _data.mntPosY = static_cast(ax_pos.y); + + // compute Julian date + auto ast_err = _astromEngine.greg2jul(_data.utc, _data.jd); + if (ast_err) { + return TEL_ERROR_ASTROMETRY_COMP; + } + + // compute local apparent sideral time + ast_err = _astromEngine.apparentSiderTime(_data.jd, _data.siderTime, true); + if (ast_err) { + return TEL_ERROR_ASTROMETRY_COMP; + } + + // compute equation of origins + typename astrom_engine_t::eo_t eo; + ast_err = _astromEngine.eqOrigins(_data.jd, eo); + if (ast_err) { + return TEL_ERROR_ASTROMETRY_COMP; + } + + + + typename pec_t::pec_result_t pec_res; + auto pec_err = _pec.compute(ax_pos.x, ax_pos.y, pec_res); + if (pec_err) { + return TEL_ERROR_PEC; + } + + if constexpr (mccIsEquatorialMount(pec_t::mountType)) { + _data.mntHA = _data.mntPosX; + _data.mntDEC = _data.mntPosY; + _data.mntHA += pec_res.dx; + _data.mntDEC += pec_res.dy; + + ast_err = _astromEngine.hadec2azalt(_data.mntHA, _data.mntDEC, _data.mntAZ, _data.mntALT); + if (ast_err) { + return TEL_ERROR_ASTROMETRY_COMP; + } + } else if constexpr (mccIsAltAzMount(pec_t::mountType)) { + _data.mntAZ = _data.mntPosX; + _data.mntALT = _data.mntPosY; + _data.mntAZ += pec_res.dx; + _data.mntALT += pec_res.dy; + + ast_err = _astromEngine.azalt2hadec(_data.mntAZ, _data.mntALT, _data.mntHA, _data.mntDEC); + if (ast_err) { + return TEL_ERROR_ASTROMETRY_COMP; + } + } else { + static_assert(false, "UNSUPPORTED MOUNT TYPE!"); + } + + // compute CIO-based apparent RA + _data.mntRA = _data.siderTime - _data.mntHA + eo; + + // compute PA + ast_err = _astromEngine.hadec2pa(_data.mntHA, _data.mntDEC, _data.mntPA); + if (ast_err) { + return TEL_ERROR_ASTROMETRY_COMP; + } + + ast_err = _astromEngine.refraction(_data.currRefrCoeffs); + if (ast_err) { + return TEL_ERROR_ASTROMETRY_COMP; + } + + ast_err = _astromEngine.refractCorrection(_data.mntALT, _data.currRefrCoeffs, _data.currRefr); + if (ast_err) { + return TEL_ERROR_ASTROMETRY_COMP; + } + + std::lock_guard lock{_updateMutex}; + + _data = std::move(current_data); } mount_telemetry_data_t data(this auto&& self) @@ -206,12 +197,29 @@ public: } + std::string_view errorString(error_t err) const + { + if (err == TEL_ERROR_OK) { + return "OK"; + } else if (err == TEL_ERROR_ASTROMETRY_COMP) { + return "astrometry computation error"; + } else if (err == TEL_ERROR_PEC) { + return "PEC computation error"; + } else if (err == TEL_ERROR_HARDWARE) { + return "hardware request error"; + } else { + return "unknown error"; + } + } + protected: mount_telemetry_data_t _data{}; - - std::function _updateImpl{}; + astrom_engine_t& _astromEngine; + pec_t& _pec; + hardware_t& _hardware; std::mutex _updateMutex; }; + } // namespace mcc diff --git a/cxx/mcc_traits.h b/cxx/mcc_traits.h index c06a8c5..d675875 100644 --- a/cxx/mcc_traits.h +++ b/cxx/mcc_traits.h @@ -164,7 +164,8 @@ concept mcc_nonconst_lvref = std::is_lvalue_reference_v && !std::is_const_v -concept mcc_error_c = std::convertible_to && mcc_formattable; +concept mcc_error_c = std::convertible_to; +// concept mcc_error_c = std::convertible_to && mcc_formattable; namespace details { diff --git a/cxx/tests/astrom_test.cpp b/cxx/tests/astrom_test.cpp index 50c2e48..9bbd911 100644 --- a/cxx/tests/astrom_test.cpp +++ b/cxx/tests/astrom_test.cpp @@ -146,7 +146,8 @@ int main(int argc, char* argv[]) std::cout << "\n\n\n\n"; - mcc::astrom::erfa::MccMountAstromEngineERFA::engine_state_t state; + using engine_t = mcc::astrom::erfa::MccMountAstromEngineERFA<>; + engine_t::engine_state_t state; state.lon = 41.440732_degs; state.lat = 43.646711_degs; state.elev = 2100.0; @@ -156,8 +157,8 @@ int main(int argc, char* argv[]) std::cout << "LON = " << state.lon.sexagesimal() << "\n"; std::cout << "LAT = " << state.lat.sexagesimal() << "\n\n"; - mcc::astrom::erfa::MccMountAstromEngineERFA erfa(state); - mcc::astrom::erfa::MccMountAstromEngineERFA::juldate_t jd{60861.72}; + engine_t erfa(state); + engine_t::juldate_t jd{60861.72}; now = std::chrono::system_clock::now(); erfa.greg2jul(now, jd);