This commit is contained in:
Timur A. Fatkhullin 2025-07-21 23:49:12 +03:00
parent 743ade7916
commit 2faa3f0aca
7 changed files with 144 additions and 62 deletions

View File

@ -125,7 +125,7 @@ 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 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_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_utils.h mcc_spdlog.h) mcc_slew_model.h mcc_utils.h mcc_spdlog.h)
set(MCC_LIBRARY mcc) set(MCC_LIBRARY mcc)
add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC}) add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC})
target_compile_features(${MCC_LIBRARY} INTERFACE cxx_std_23) target_compile_features(${MCC_LIBRARY} INTERFACE cxx_std_23)

View File

@ -119,6 +119,8 @@ AsibFM700Hardware::error_t AsibFM700Hardware::getPos(AsibFM700Hardware::axes_pos
// according to hardware configuration (encoders pins (Eddy said)) X is DEC-axis and Y is HA-axis // according to hardware configuration (encoders pins (Eddy said)) X is DEC-axis and Y is HA-axis
pos.x = data.encposition.Y; pos.x = data.encposition.Y;
pos.y = data.encposition.X; pos.y = data.encposition.X;
// ERROR: how can I get rates?!!!!!!!!!!
} }
return err; return err;

View File

@ -69,6 +69,7 @@ public:
struct axes_pos_t { struct axes_pos_t {
time_point_t time_point; time_point_t time_point;
coord_t x, y; coord_t x, y;
coord_t xrate, yrate;
}; };

View File

@ -171,11 +171,15 @@ concept mcc_mount_hardware_c = !std::copyable<T> && std::movable<T> && requires(
{ t_const.id() } -> mcc_formattable; { t_const.id() } -> mcc_formattable;
// a class that contains at least time of measurement and coordinates for x,y axes // a class that contains at least time of measurement, coordinates for x,y axes and its moving rates
requires requires(typename T::axes_pos_t pos) { requires requires(typename T::axes_pos_t pos) {
requires std::same_as<decltype(pos.time_point), typename T::time_point_t>; requires std::same_as<decltype(pos.time_point), typename T::time_point_t>;
requires std::same_as<decltype(pos.x), typename T::coord_t>; requires std::same_as<decltype(pos.x), typename T::coord_t>;
requires std::same_as<decltype(pos.y), typename T::coord_t>; requires std::same_as<decltype(pos.y), typename T::coord_t>;
requires std::same_as<decltype(pos.xrate), typename T::coord_t>;
requires std::same_as<decltype(pos.yrate), typename T::coord_t>;
}; };
{ t.setPos(std::declval<typename T::axes_pos_t>()) } -> std::same_as<typename T::error_t>; { t.setPos(std::declval<typename T::axes_pos_t>()) } -> std::same_as<typename T::error_t>;
@ -223,12 +227,8 @@ concept mcc_mount_telemetry_data_c = requires(T telemetry) {
typename T::coord_t; typename T::coord_t;
typename T::time_point_t; typename T::time_point_t;
// // target current coordinates // time point
// requires std::same_as<decltype(telemetry.tagRA), typename T::coord_t>; // apparent RA requires std::same_as<decltype(telemetry.time_point), typename T::time_point_t>;
// requires std::same_as<decltype(telemetry.tagDEC), typename T::coord_t>; // apparent DEC
// requires std::same_as<decltype(telemetry.tagHA), typename T::coord_t>; // hour angle
// requires std::same_as<decltype(telemetry.tagAZ), typename T::coord_t>; // azimuth
// requires std::same_as<decltype(telemetry.tagALT), typename T::coord_t>; // altitude
// mount current coordinates // mount current coordinates
requires std::same_as<decltype(telemetry.mntRA), typename T::coord_t>; // apparent RA requires std::same_as<decltype(telemetry.mntRA), typename T::coord_t>; // apparent RA
@ -236,8 +236,11 @@ concept mcc_mount_telemetry_data_c = requires(T telemetry) {
requires std::same_as<decltype(telemetry.mntHA), typename T::coord_t>; // hour angle requires std::same_as<decltype(telemetry.mntHA), typename T::coord_t>; // hour angle
requires std::same_as<decltype(telemetry.mntAZ), typename T::coord_t>; // azimuth requires std::same_as<decltype(telemetry.mntAZ), typename T::coord_t>; // azimuth
requires std::same_as<decltype(telemetry.mntALT), typename T::coord_t>; // altitude requires std::same_as<decltype(telemetry.mntALT), typename T::coord_t>; // altitude
requires std::same_as<decltype(telemetry.mntPosX), typename T::coord_t>; // hardware encoder X-axis position requires std::same_as<decltype(telemetry.mntPosX), typename T::coord_t>; // hardware encoder X-axis position
requires std::same_as<decltype(telemetry.mntPosY), typename T::coord_t>; // hardware encoder Y-axis position requires std::same_as<decltype(telemetry.mntPosY), typename T::coord_t>; // hardware encoder Y-axis position
requires std::same_as<decltype(telemetry.mntRateX), typename T::coord_t>; // hardware encoder X-axis rate
requires std::same_as<decltype(telemetry.mntRateY), typename T::coord_t>; // hardware encoder Y-axis rate
}; };

View File

@ -47,6 +47,8 @@ concept mcc_mount_telemetry_enh_data_c = mcc_mount_telemetry_data_c<T> && requir
// X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one
requires std::same_as<decltype(t.mntPosX), typename T::coord_t>; requires std::same_as<decltype(t.mntPosX), typename T::coord_t>;
requires std::same_as<decltype(t.mntPosY), typename T::coord_t>; requires std::same_as<decltype(t.mntPosY), typename T::coord_t>;
requires std::same_as<decltype(t.mntRateX), typename T::coord_t>;
requires std::same_as<decltype(t.mntRateY), typename T::coord_t>;
// current refraction coefficients // current refraction coefficients
requires std::same_as<decltype(t.currRefrCoeffs), typename PEC_T::pec_result_t>; requires std::same_as<decltype(t.currRefrCoeffs), typename PEC_T::pec_result_t>;
@ -72,7 +74,7 @@ struct MccMountTelemetryData {
typedef typename ASTROM_ENGINE_T::pa_t pa_t; typedef typename ASTROM_ENGINE_T::pa_t pa_t;
// time-related // time-related
time_point_t utc; // time point of measurements, UTC time_point_t time_point; // time point of measurements, UTC
juldate_t jd; // Julian date juldate_t jd; // Julian date
sideral_time_t siderTime; // local apperant sideral time sideral_time_t siderTime; // local apperant sideral time
@ -82,9 +84,10 @@ struct MccMountTelemetryData {
coord_t mntAZ, mntALT; coord_t mntAZ, mntALT;
pa_t mntPA; pa_t mntPA;
// encoder-measured (non-corrected for PCS) current mount position // encoder-measured (non-corrected for PCS) current mount position and axes rates
// X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one // X - HA, Y - DEC for equatorial-type mount; X - AZ, Y - ALT for horizontal-type one
coord_t mntPosX, mntPosY; coord_t mntPosX, mntPosY;
coord_t mntRateX, mntRateY;
// current refraction coefficients // current refraction coefficients
typename PEC_T::pec_result_t currRefrCoeffs; typename PEC_T::pec_result_t currRefrCoeffs;
@ -163,9 +166,11 @@ public:
_data.utc = ax_pos.time_point; _data.utc = ax_pos.time_point;
_data.mntPosX = static_cast<typename astrom_engine_t::coord_t>(ax_pos.x); _data.mntPosX = static_cast<typename astrom_engine_t::coord_t>(ax_pos.x);
_data.mntPosY = static_cast<typename astrom_engine_t::coord_t>(ax_pos.y); _data.mntPosY = static_cast<typename astrom_engine_t::coord_t>(ax_pos.y);
_data.mntRateX = static_cast<typename astrom_engine_t::coord_t>(ax_pos.xrate);
_data.mntRateY = static_cast<typename astrom_engine_t::coord_t>(ax_pos.yrate);
// compute Julian date // compute Julian date
auto ast_err = _astromEngine.greg2jul(_data.utc, _data.jd); auto ast_err = _astromEngine.greg2jul(_data.time_point, _data.jd);
if (ast_err) { if (ast_err) {
return TEL_ERROR_ASTROMETRY_COMP; return TEL_ERROR_ASTROMETRY_COMP;
} }

View File

@ -19,7 +19,8 @@ enum class MccSimpleSlewModelErrorCode : int {
ERROR_ASTROM_COMP, ERROR_ASTROM_COMP,
ERROR_TELEMETRY_DATA, ERROR_TELEMETRY_DATA,
ERROR_PEC_COMP, ERROR_PEC_COMP,
ERROR_HARDWARE_SETPOS ERROR_HARDWARE_SETPOS,
ERROR_SLEW_TIMEOUT
}; };
} // namespace mcc } // namespace mcc
@ -105,12 +106,29 @@ public:
coord_t x{0.0}; coord_t x{0.0};
coord_t y{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}; bool stop{false};
}; };
struct context_t {
// double eps{0.01};
// size_t maxIter{5};
slew_params_t::coord_t guidingRateX;
slew_params_t::coord_t guidingRateY;
slew_params_t::coord_t guidingRateEps;
size_t maxRateCycles{5};
std::chrono::seconds timeout{300};
};
template <traits::mcc_mount_controls_c MOUNT_CONTROLS_T> template <traits::mcc_mount_controls_c MOUNT_CONTROLS_T>
MccSimpleSlewModel(MOUNT_CONTROLS_T& mount_controls) MccSimpleSlewModel(MOUNT_CONTROLS_T& mount_controls, context_t context)
{ {
// deduce controls types // deduce controls types
using astrom_engine_t = decltype(mount_controls.astrometryEngine); using astrom_engine_t = decltype(mount_controls.astrometryEngine);
@ -159,7 +177,8 @@ public:
}; };
_slewFunc = [p_mount_controls, check_zones](this auto&& self, const slew_params_t& slew_pars) { _slewFunc = [p_mount_controls, context = std::move(context), check_zones](this auto&& self,
slew_params_t slew_pars) {
auto& astrom_engine = p_mount_controls->astrometryEngine; auto& astrom_engine = p_mount_controls->astrometryEngine;
auto& hardware = p_mount_controls->hardware; auto& hardware = p_mount_controls->hardware;
auto& pec = p_mount_controls->PEC; auto& pec = p_mount_controls->PEC;
@ -178,6 +197,9 @@ public:
// trivial case (the pair is interpretated as raw encoder coordinates) // trivial case (the pair is interpretated as raw encoder coordinates)
ax_pos.x = slew_pars.x; ax_pos.x = slew_pars.x;
ax_pos.y = slew_pars.y; ax_pos.y = slew_pars.y;
ax_pos.xrate = slew_pars.xrate;
ax_pos.yrate = slew_pars.yrate;
} else if (slew_pars.coordPairKind == } else if (slew_pars.coordPairKind ==
mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // catalog coordinates mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // catalog coordinates
jd_t jd; jd_t jd;
@ -191,15 +213,17 @@ public:
if (!ast_err) { if (!ast_err) {
if constexpr (mccIsEquatorialMount(pec_t::mountType)) { if constexpr (mccIsEquatorialMount(pec_t::mountType)) {
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP, slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
.x = ha, slew_pars.x = ha;
.y = dec_app, slew_pars.y = dec_app;
.stop = slew_pars.stop});
res_err = self(std::move(slew_pars));
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) { } else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT;
.x = az, slew_pars.x = az;
.y = alt, slew_pars.y = alt;
.stop = slew_pars.stop});
res_err = self(std::move(slew_pars));
} else { } else {
static_assert(false, "UNKNOWN MOUNT TYPE!"); static_assert(false, "UNKNOWN MOUNT TYPE!");
} }
@ -218,10 +242,10 @@ public:
if (!ast_err) { if (!ast_err) {
ast_err = astrom_engine->eqOrigins(jd, eo); ast_err = astrom_engine->eqOrigins(jd, eo);
if (!ast_err) { if (!ast_err) {
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP, slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
.x = lst - slew_pars.x + eo, // HA = LST - RA_APP + EO slew_pars.x = lst - slew_pars.x + eo; // HA = LST - RA_APP + EO
.y = slew_pars.y,
.stop = slew_pars.stop}); res_err = self(std::move(slew_pars));
} }
} }
} }
@ -231,12 +255,14 @@ public:
typename pec_t::pec_result_t pec_res; typename pec_t::pec_result_t pec_res;
pec_err = pec->reverseCompute(slew_pars.x, slew_pars.y, pec_res, eps, 10); // pec_err = pec->reverseCompute(slew_pars.x, slew_pars.y, pec_res, context.eps, context.maxIter);
pec_err = pec->compute(slew_pars.x, slew_pars.y, pec_res);
if (!pec_err) { if (!pec_err) {
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY, slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
.x = slew_pars.x - pec_res.dx, slew_pars.x -= pec_res.dx;
.y = slew_pars.y - pec_res.dy, slew_pars.y -= pec_res.dy;
.stop = slew_pars.stop});
res_err = self(std::move(slew_pars));
} }
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) { } else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
coord_t az, alt; coord_t az, alt;
@ -244,10 +270,11 @@ public:
ast_err = astrom_engine->hadec2azalt(slew_pars.x, slew_pars.y, az, alt); ast_err = astrom_engine->hadec2azalt(slew_pars.x, slew_pars.y, az, alt);
if (!ast_err) { if (!ast_err) {
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT;
.x = az, slew_pars.x = az;
.y = alt, slew_pars.y = alt;
.stop = slew_pars.stop});
res_err = self(std::move(slew_pars));
} }
} else { } else {
static_assert(false, "UNKNOWN MOUNT TYPE!"); static_assert(false, "UNKNOWN MOUNT TYPE!");
@ -259,22 +286,25 @@ public:
ast_err = astrom_engine->azalt2hadec(slew_pars.x, slew_pars.y, ha, dec); ast_err = astrom_engine->azalt2hadec(slew_pars.x, slew_pars.y, ha, dec);
if (!ast_err) { if (!ast_err) {
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP, slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
.x = ha, slew_pars.x = ha;
.y = dec, slew_pars.y = dec;
.stop = slew_pars.stop});
res_err = self(std::move(slew_pars));
} }
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) { // compute encoder coordinates } else if constexpr (mccIsAltAzMount(pec_t::mountType)) { // compute encoder coordinates
coord_t eps = 1.0 / 3600.0 * std::numbers::pi / 180.0; coord_t eps = 1.0 / 3600.0 * std::numbers::pi / 180.0;
typename pec_t::pec_result_t pec_res; typename pec_t::pec_result_t pec_res;
pec_err = pec->reverseCompute(slew_pars.x, slew_pars.y, pec_res, eps, 10); // pec_err = pec->reverseCompute(slew_pars.x, slew_pars.y, pec_res, context.eps, context.maxIter);
pec_err = pec->compute(slew_pars.x, slew_pars.y, pec_res);
if (!pec_err) { if (!pec_err) {
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY, slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
.x = slew_pars.x - pec_res.dx, slew_pars.x -= pec_res.dx;
.y = slew_pars.y - pec_res.dy, slew_pars.y -= pec_res.dy;
.stop = slew_pars.stop});
res_err = self(std::move(slew_pars));
} }
} else { } else {
@ -284,10 +314,9 @@ public:
// //
// WARNING: it is assumed that coordinates are in radians! // WARNING: it is assumed that coordinates are in radians!
// //
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, slew_pars.y = std::numbers::pi / 2.0 - slew_pars.y;
.x = slew_pars.x,
.y = std::numbers::pi / 2.0 - slew_pars.y, res_err = self(std::move(slew_pars));
.stop = slew_pars.stop});
} }
if (res_err) { if (res_err) {
@ -324,9 +353,15 @@ public:
} }
} }
// size_t i_iter = 0;
// what is the condition for mount reaches given position?!!
// context.guidingRateEps *= context.guidingRateEps;
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;
auto start_poll_tm = std::chrono::high_resolution_clock::now();
while (true) { while (true) {
// check prohibited zones // check prohibited zones
res_err = check_zones(std::make_index_sequence<Nzones>{}); res_err = check_zones(std::make_index_sequence<Nzones>{});
@ -347,6 +382,40 @@ public:
return MccSimpleSlewModelErrorCode::ERROR_TELEMETRY_DATA; return MccSimpleSlewModelErrorCode::ERROR_TELEMETRY_DATA;
} }
} }
if (prev_time_point == t_data.time_point) {
continue;
}
if (slew_pars.stop) { // slew and stop, so mount moving rate must be 0 at the end
mount_rate2 = t_data.mntRateX * t_data.mntRateX + t_data.mntRateY * t_data.mntRateY;
if (utils::isEqual((double)mount_rate2, 0.0)) {
++i_iter;
} else {
i_iter = 0;
}
} else { // slew and guiding, so mount rate must be near guiding rate at the end
xrate = t_data.mntRateX - context.guidingRateX;
yrate = t_data.mntRateY - context.guidingRateY;
mount_rate2 = xrate * xrate + yrate * yrate;
if (mount_rate2 <= context.guidingRateEps) {
++i_iter;
} else {
i_iter = 0;
}
}
if (i_iter >= context.maxRateCycles) {
break;
}
prev_time_point = t_data.time_point;
if ((std::chrono::high_resolution_clock::now() - start_poll_tm) > context.timeout) {
return MccSimpleSlewModelErrorCode::ERROR_SLEW_TIMEOUT;
}
} }
@ -354,9 +423,9 @@ public:
}; };
} }
error_t slew(const slew_params_t& pars) error_t slew(slew_params_t pars)
{ {
error_t res_err = _slewFunc(pars); error_t res_err = _slewFunc(std::move(pars));
return res_err; return res_err;
} }

View File

@ -12,9 +12,11 @@ struct tel_data_t {
typedef mcc::MccAngle coord_t; typedef mcc::MccAngle coord_t;
typedef std::chrono::system_clock::time_point time_point_t; typedef std::chrono::system_clock::time_point time_point_t;
time_point_t time_point;
coord_t mntRA, mntDEC, mntHA; coord_t mntRA, mntDEC, mntHA;
coord_t mntAZ, mntALT; coord_t mntAZ, mntALT;
coord_t mntPosX, mntPosY; coord_t mntPosX, mntPosY;
coord_t mntRateX, mntRateY;
}; };
int main(int argc, char* argv[]) int main(int argc, char* argv[])
@ -207,7 +209,7 @@ int main(int argc, char* argv[])
mcc::MccMinAltPZ minalt_pz(10.0_degs, state.lat); mcc::MccMinAltPZ minalt_pz(10.0_degs, state.lat);
mcc::MccMaxAltPZ maxalt_pz(85.0_degs, state.lat); mcc::MccMaxAltPZ maxalt_pz(85.0_degs, state.lat);
tel_data_t tdata{ra_o, dec_o, ha1, az1, alt1, 0.0, 0.0}; tel_data_t tdata{std::chrono::system_clock::now(), ra_o, dec_o, ha1, az1, alt1, 0.0, 0.0, 0.0, 0.0};
bool ask = minalt_pz.inZone(tdata); bool ask = minalt_pz.inZone(tdata);
std::cout << "in zone? " << std::boolalpha << ask << "\n"; std::cout << "in zone? " << std::boolalpha << ask << "\n";
@ -242,7 +244,7 @@ int main(int argc, char* argv[])
std::cout << "DEC_app = " << dec_o.sexagesimal() << "\n"; std::cout << "DEC_app = " << dec_o.sexagesimal() << "\n";
tdata = {ra_o, dec_o, ha1, az1, alt1, 0.0, 0.0}; tdata = {std::chrono::system_clock::now(), ra_o, dec_o, ha1, az1, alt1, 0.0, 0.0, 0.0, 0.0};
jd.mjd += 1.0; jd.mjd += 1.0;
res = erfa.icrs2obs(ra1, dec1, jd, ra_o, dec_o, ha1, az1, alt1, eor); res = erfa.icrs2obs(ra1, dec1, jd, ra_o, dec_o, ha1, az1, alt1, eor);