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
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)
add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC})
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
pos.x = data.encposition.Y;
pos.y = data.encposition.X;
// ERROR: how can I get rates?!!!!!!!!!!
}
return err;

View File

@ -69,6 +69,7 @@ public:
struct axes_pos_t {
time_point_t time_point;
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;
// 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 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.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>;
@ -223,21 +227,20 @@ 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<decltype(telemetry.tagRA), typename T::coord_t>; // apparent RA
// 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
// time point
requires std::same_as<decltype(telemetry.time_point), typename T::time_point_t>;
// mount current coordinates
requires std::same_as<decltype(telemetry.mntRA), typename T::coord_t>; // apparent RA
requires std::same_as<decltype(telemetry.mntDEC), typename T::coord_t>; // apparent DEC
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.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.mntPosY), typename T::coord_t>; // hardware encoder Y-axis position
requires std::same_as<decltype(telemetry.mntRA), typename T::coord_t>; // apparent RA
requires std::same_as<decltype(telemetry.mntDEC), typename T::coord_t>; // apparent DEC
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.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.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
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.mntRateX), typename T::coord_t>;
requires std::same_as<decltype(t.mntRateY), typename T::coord_t>;
// current refraction coefficients
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;
// 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
sideral_time_t siderTime; // local apperant sideral time
@ -82,9 +84,10 @@ struct MccMountTelemetryData {
coord_t mntAZ, mntALT;
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
coord_t mntPosX, mntPosY;
coord_t mntRateX, mntRateY;
// current refraction coefficients
typename PEC_T::pec_result_t currRefrCoeffs;
@ -163,9 +166,11 @@ public:
_data.utc = ax_pos.time_point;
_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.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
auto ast_err = _astromEngine.greg2jul(_data.utc, _data.jd);
auto ast_err = _astromEngine.greg2jul(_data.time_point, _data.jd);
if (ast_err) {
return TEL_ERROR_ASTROMETRY_COMP;
}

View File

@ -19,7 +19,8 @@ enum class MccSimpleSlewModelErrorCode : int {
ERROR_ASTROM_COMP,
ERROR_TELEMETRY_DATA,
ERROR_PEC_COMP,
ERROR_HARDWARE_SETPOS
ERROR_HARDWARE_SETPOS,
ERROR_SLEW_TIMEOUT
};
} // namespace mcc
@ -105,12 +106,29 @@ public:
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 {
// 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>
MccSimpleSlewModel(MOUNT_CONTROLS_T& mount_controls)
MccSimpleSlewModel(MOUNT_CONTROLS_T& mount_controls, context_t context)
{
// deduce controls types
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& hardware = p_mount_controls->hardware;
auto& pec = p_mount_controls->PEC;
@ -178,6 +197,9 @@ public:
// trivial case (the pair is interpretated as raw encoder coordinates)
ax_pos.x = slew_pars.x;
ax_pos.y = slew_pars.y;
ax_pos.xrate = slew_pars.xrate;
ax_pos.yrate = slew_pars.yrate;
} else if (slew_pars.coordPairKind ==
mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // catalog coordinates
jd_t jd;
@ -191,15 +213,17 @@ public:
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});
slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
slew_pars.x = ha;
slew_pars.y = dec_app;
res_err = self(std::move(slew_pars));
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
res_err = self({.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT,
.x = az,
.y = alt,
.stop = slew_pars.stop});
slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT;
slew_pars.x = az;
slew_pars.y = alt;
res_err = self(std::move(slew_pars));
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
}
@ -218,10 +242,10 @@ public:
if (!ast_err) {
ast_err = 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});
slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
slew_pars.x = lst - slew_pars.x + eo; // HA = LST - RA_APP + EO
res_err = self(std::move(slew_pars));
}
}
}
@ -231,12 +255,14 @@ public:
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) {
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});
slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
slew_pars.x -= pec_res.dx;
slew_pars.y -= pec_res.dy;
res_err = self(std::move(slew_pars));
}
} else if constexpr (mccIsAltAzMount(pec_t::mountType)) {
coord_t az, alt;
@ -244,10 +270,11 @@ public:
ast_err = 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});
slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_AZALT;
slew_pars.x = az;
slew_pars.y = alt;
res_err = self(std::move(slew_pars));
}
} else {
static_assert(false, "UNKNOWN MOUNT TYPE!");
@ -259,22 +286,25 @@ public:
ast_err = 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});
slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP;
slew_pars.x = ha;
slew_pars.y = dec;
res_err = self(std::move(slew_pars));
}
} 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;
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) {
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});
slew_pars.coordPairKind = mcc::MccCoordPairKind::COORDS_KIND_XY;
slew_pars.x -= pec_res.dx;
slew_pars.y -= pec_res.dy;
res_err = self(std::move(slew_pars));
}
} else {
@ -284,10 +314,9 @@ public:
//
// 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});
slew_pars.y = std::numbers::pi / 2.0 - slew_pars.y;
res_err = self(std::move(slew_pars));
}
if (res_err) {
@ -324,9 +353,15 @@ public:
}
}
//
// what is the condition for mount reaches given position?!!
//
size_t i_iter = 0;
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) {
// check prohibited zones
res_err = check_zones(std::make_index_sequence<Nzones>{});
@ -347,6 +382,40 @@ public:
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;
}

View File

@ -12,9 +12,11 @@ struct tel_data_t {
typedef mcc::MccAngle coord_t;
typedef std::chrono::system_clock::time_point time_point_t;
time_point_t time_point;
coord_t mntRA, mntDEC, mntHA;
coord_t mntAZ, mntALT;
coord_t mntPosX, mntPosY;
coord_t mntRateX, mntRateY;
};
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::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);
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";
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;
res = erfa.icrs2obs(ra1, dec1, jd, ra_o, dec_o, ha1, az1, alt1, eor);