This commit is contained in:
Timur A. Fatkhullin
2026-01-30 22:20:42 +03:00
parent 9aea122b08
commit a686731c0a
111 changed files with 27707 additions and 50 deletions

455
mcc_pzone.h Normal file
View File

@@ -0,0 +1,455 @@
#pragma once
/****************************************************************************************
* *
* MOUNT CONTROL COMPONENTS LIBRARY *
* *
* *
* IMPLEMENTATION OF SOME SIMPLE PROHIBITED ZONES *
* *
****************************************************************************************/
#include "mcc_concepts.h"
#include "mcc_constants.h"
#include "mcc_coordinate.h"
namespace mcc::impl
{
enum class MccPZoneErrorCode : int { ERROR_OK, ERROR_NULLPTR, ERROR_COORD_TRANSFROM, ERROR_PCM_COMP };
} // namespace mcc::impl
namespace std
{
template <>
class is_error_code_enum<mcc::impl::MccPZoneErrorCode> : public true_type
{
};
} // namespace std
namespace mcc::impl
{
// error category
struct MccPZoneCategory : public std::error_category {
MccPZoneCategory() : std::error_category() {}
const char* name() const noexcept
{
return "ALTITUDE-LIMIT-PZ";
}
std::string message(int ec) const
{
MccPZoneErrorCode err = static_cast<MccPZoneErrorCode>(ec);
switch (err) {
case MccPZoneErrorCode::ERROR_OK:
return "OK";
case MccPZoneErrorCode::ERROR_NULLPTR:
return "input argument os nullptr";
case MccPZoneErrorCode::ERROR_COORD_TRANSFROM:
return "coordinate transformation error";
case MccPZoneErrorCode::ERROR_PCM_COMP:
return "PCM computation error";
default:
return "UNKNOWN";
}
}
static const MccPZoneCategory& get()
{
static const MccPZoneCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(MccPZoneErrorCode ec)
{
return std::error_code(static_cast<int>(ec), MccPZoneCategory::get());
}
enum class MccAltLimitKind { MIN_ALT_LIMIT, MAX_ALT_LIMIT };
template <MccAltLimitKind KIND = MccAltLimitKind::MIN_ALT_LIMIT>
class MccAltLimitPZ : public mcc_pzone_interface_t<std::error_code>
{
public:
static constexpr MccProhibitedZonePolicy pzPolicy = MccProhibitedZonePolicy::PZ_POLICY_STOP;
typedef std::error_code error_t;
MccAltLimitPZ(mcc_angle_c auto const& alt_limit, mcc_angle_c auto const& latitude)
: _altLimit(MccAngle(alt_limit).normalize<MccAngle::NORM_KIND_90_90>()),
_cosALim(cos(_altLimit)),
_sinAlim(sin(_altLimit)),
_cosLat(cos(latitude)),
_sinLat(sin(latitude)),
_absLat(abs(latitude)),
_latLim(MCC_TWO_PI - _altLimit)
{
}
MccAltLimitPZ(MccAltLimitPZ&&) = default;
MccAltLimitPZ(const MccAltLimitPZ&) = default;
static constexpr std::string_view pzoneName = KIND == MccAltLimitKind::MIN_ALT_LIMIT ? "MINALT-ZONE"
: KIND == MccAltLimitKind::MAX_ALT_LIMIT ? "MAXALT-ZONE"
: "ALTLIMIT-UNKNOWN";
error_t inPZone(mcc_skypoint_c auto const& coords, bool* result)
{
if (result == nullptr) {
return MccPZoneErrorCode::ERROR_NULLPTR;
}
error_t ret = MccPZoneErrorCode::ERROR_OK;
MccSkyAZALT azalt;
auto ccte_err = coords.to(azalt);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccPZoneErrorCode::ERROR_COORD_TRANSFROM);
}
if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) {
*result = azalt.y() <= _altLimit;
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) {
*result = azalt.y() >= _altLimit;
}
return ret;
}
error_t timeToPZone(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time)
{
using res_t = std::remove_cvref_t<decltype(*res_time)>;
if (res_time == nullptr) {
return MccPZoneErrorCode::ERROR_NULLPTR;
}
error_t ret = MccPZoneErrorCode::ERROR_OK;
bool inzone;
ret = inPZone(coords, &inzone);
if (ret) {
return ret;
}
if (inzone) {
*res_time = res_t{0};
return ret;
}
MccSkyHADEC_OBS hadec;
auto ccte_err = coords.to(hadec);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccPZoneErrorCode::ERROR_COORD_TRANSFROM);
}
if (!doesObjectReachZone(hadec.y())) {
*res_time = MCC_INFINITE_DURATION_V<res_t>;
return ret;
}
if constexpr (KIND ==
MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one after upper culmination
compute(hadec.x(), hadec.y(), false, res_time);
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one before upper
// culmination
compute(hadec.x(), hadec.y(), true, res_time);
}
return ret;
}
error_t timeFromPZone(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time)
{
using res_t = std::remove_cvref_t<decltype(*res_time)>;
if (res_time == nullptr) {
return MccPZoneErrorCode::ERROR_NULLPTR;
}
error_t ret = MccPZoneErrorCode::ERROR_OK;
bool inzone;
ret = inPZone(coords, &inzone);
if (ret) {
return ret;
}
if (!inzone) {
*res_time = res_t{0};
return ret;
}
MccSkyHADEC_OBS hadec;
auto ccte_err = coords.to(hadec);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccPZoneErrorCode::ERROR_COORD_TRANSFROM);
}
if (!doesObjectExitFromZone(hadec.y())) {
*res_time = MCC_INFINITE_DURATION_V<res_t>;
return ret;
}
if (!doesObjectReachZone(hadec.y())) {
*res_time = res_t{0};
return ret;
}
if constexpr (KIND ==
MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one before upper culmination
compute(hadec.x(), hadec.y(), true, res_time);
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one after upper
// culmination
compute(hadec.x(), hadec.y(), false, res_time);
}
return ret;
}
protected:
double _altLimit, _cosALim, _sinAlim;
double _cosLat, _sinLat, _absLat, _latLim;
bool doesObjectReachZone(const double& dec_app)
{
// check for limit conditions
auto dd = std::abs(dec_app);
if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) {
dd += _altLimit;
if (dd > _latLim) { // never fall below altitude limit
return false;
}
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) {
auto z = std::numbers::pi / 2.0 - _altLimit;
if ((dd < (_absLat - z)) || (dd > (_absLat + z))) { // never rise above altitude limit
return false;
}
// if ((dd < (_absLat - _altLimit)) || (dd > (_absLat + _altLimit))) { // never rise above altitude limit
// return false;
// }
} else {
static_assert(false, "UNKNOWN ALTITUDE LIMIT TYPE!");
}
return true;
}
bool doesObjectExitFromZone(const double& dec_app)
{
// check for limit conditions
auto dd = std::abs(dec_app);
if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) {
dd -= _altLimit;
if (-dd <= -_latLim) { // always below altitude limit
return false;
}
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) {
if ((dd >= (_absLat - _altLimit)) || (dd <= (_absLat + _altLimit))) { // always above altitude limit
return false;
}
} else {
static_assert(false, "UNKNOWN ALTITUDE LIMIT TYPE!");
}
return true;
}
void compute(const double& ha_app,
const double& dec_app,
bool before_upper_culm,
traits::mcc_time_duration_c auto* result)
{
using res_t = std::remove_cvref_t<decltype(*result)>;
using period_t = typename res_t::period;
double cos_ha = (_sinAlim - std::sin(dec_app) * _sinLat) / std::cos(dec_app) / _cosLat;
if (cos_ha > 1.0) { // should not be!
*result = MCC_INFINITE_DURATION_V<res_t>;
return;
}
double ha;
// WARNING: what about south hemisphere?!!!
if (before_upper_culm) {
ha = -std::acos(cos_ha); // HA before upper culmination
} else {
ha = std::acos(cos_ha); // HA after upper culmination!!
}
auto time_ang = ha - ha_app; // in sideral time scale
if (time_ang < 0.0) { // next day
time_ang += MCC_TWO_PI;
}
time_ang /= MCC_SIDERAL_TO_UT1_RATIO; // to UT1 time scale
std::chrono::nanoseconds ns{
static_cast<std::chrono::nanoseconds::rep>(time_ang * 43200.0 / std::numbers::pi * 1.0E9)};
period_t rat;
*result = res_t{static_cast<typename res_t::rep>(time_ang * 43200.0 / std::numbers::pi * rat.den / rat.num)};
}
};
/* co-longitude axis (HA or AZ) limit switch prohibited zone */
template <MccCoordKind AXIS_KIND>
class MccAxisLimitSwitchPZ : public mcc_pzone_interface_t<std::error_code>
{
public:
static_assert(AXIS_KIND == MccCoordKind::COORDS_KIND_AZ || AXIS_KIND == MccCoordKind::COORDS_KIND_HA_OBS,
"UNSUPPORTED AXIS TYPE!");
typedef std::error_code error_t;
static constexpr MccCoordKind axisKind = AXIS_KIND;
static constexpr MccProhibitedZonePolicy pzPolicy = MccProhibitedZonePolicy::PZ_POLICY_FLIP;
//
// min_limit_val and max_limit_val are hardware encoder angles in radians!
//
MccAxisLimitSwitchPZ(mcc_angle_c auto const& min_limit_val,
mcc_angle_c auto const& max_limit_val,
mcc_pcm_c auto* pcm)
: _minLimit(min_limit_val), _maxLimit(max_limit_val)
{
_correctForPCM = [pcm](MccSkyPoint const& skypt, MccGenXY* hw_coords) {
struct pcm_res_t {
double pcmX, pcmY;
};
pcm_res_t res;
auto err = pcm->computeInversePCM(skypt, &res, hw_coords);
return mcc_deduced_err(err, MccPZoneErrorCode::ERROR_PCM_COMP);
};
}
static constexpr std::string_view pzoneName = axisKind == MccCoordKind::COORDS_KIND_AZ ? "AZ_AXIS-LIMITSWITCH_ZONE"
: axisKind == MccCoordKind::COORDS_KIND_HA_OBS
? "HA_AXIS-LIMITSWITCH_ZONE"
: "UKNOWN";
error_t inPZone(mcc_skypoint_c auto const& coords, bool* result)
{
if (result == nullptr) {
return MccPZoneErrorCode::ERROR_NULLPTR;
}
MccGenXY xy;
if (coords.pairKind() != MccCoordPairKind::COORDS_KIND_XY) {
auto err = _correctForPCM(coords, &xy);
if (err) {
return err;
}
} else { // 'coords' is interpretated as hardware (encoder readout) coordinates
coords.to(xy);
}
double x = xy.x();
*result = (x > _maxLimit) || (x < _minLimit);
return MccPZoneErrorCode::ERROR_OK;
}
// time to reach maximal limit
error_t timeToPZone(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time)
{
return _computeTime(coords, res_time, false);
}
// time to reach minimal limit
error_t timeFromPZone(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time)
{
return _computeTime(coords, res_time, true);
}
protected:
double _minLimit, _maxLimit;
std::function<error_t(MccSkyPoint const&, MccGenXY*)> _correctForPCM{};
error_t _computeTime(mcc_skypoint_c auto const& coords, traits::mcc_time_duration_c auto* res_time, bool from_time)
{
using res_t = std::remove_cvref_t<decltype(*res_time)>;
using period_t = typename res_t::period;
double time_ang;
if (res_time == nullptr) {
return MccPZoneErrorCode::ERROR_NULLPTR;
}
MccGenXY xy;
if (coords.pairKind() != MccCoordPairKind::COORDS_KIND_XY) {
auto err = _correctForPCM(coords, &xy);
if (err) {
return err;
}
} else { // 'coords' is interpretated as hardware (encoder readout) coordinates
coords.to(xy);
}
double x = xy.x();
if constexpr (AXIS_KIND == MccCoordKind::COORDS_KIND_HA_OBS) {
if (from_time) { // timeFromPZone
time_ang = (_minLimit - x) / MCC_SIDERAL_TO_UT1_RATIO; // to UT1 scale
} else { // timeToPZone
time_ang = (_maxLimit - x) / MCC_SIDERAL_TO_UT1_RATIO; // to UT1 scale
}
} else if constexpr (AXIS_KIND == MccCoordKind::COORDS_KIND_AZ) {
}
std::chrono::nanoseconds ns{
static_cast<std::chrono::nanoseconds::rep>(time_ang * 43200.0 / std::numbers::pi * 1.0E9)};
period_t rat;
*res_time = res_t{static_cast<typename res_t::rep>(time_ang * 43200.0 / std::numbers::pi * rat.den / rat.num)};
return MccPZoneErrorCode::ERROR_OK;
}
};
} // namespace mcc::impl