372 lines
13 KiB
C++
372 lines
13 KiB
C++
#pragma once
|
|
|
|
|
|
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
|
|
|
|
|
/* PROHIBITED ZONE IMPLEMENTATION */
|
|
|
|
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <string_view>
|
|
|
|
#include "mcc_mount_concepts.h"
|
|
|
|
#include "mcc_mount_coord.h"
|
|
#include "mcc_traits.h"
|
|
|
|
|
|
namespace mcc
|
|
{
|
|
|
|
static constexpr double mcc_sideral_to_UT1_ratio = 1.002737909350795; // sideral/UT1
|
|
|
|
/* SOME SIMPLE PROHIBITED ZONE IMPLEMENTATIONS */
|
|
|
|
|
|
// minimal or maximal altitude prohibited zones
|
|
|
|
enum class MccAltLimitKind { MIN_ALT_LIMIT, MAX_ALT_LIMIT };
|
|
|
|
template <MccAltLimitKind KIND = MccAltLimitKind::MIN_ALT_LIMIT>
|
|
class MccAltLimitPZ
|
|
// class MccAltLimitPZ : public MccProhibitedZone
|
|
{
|
|
protected:
|
|
static constexpr auto pi2 = std::numbers::pi * 2.0;
|
|
|
|
public:
|
|
static constexpr MccCoordPairKind preferedCoordKind = MccCoordPairKind::COORDS_KIND_AZALT;
|
|
|
|
static constexpr MccAltLimitKind altLimitKind = KIND;
|
|
|
|
typedef double coord_t;
|
|
// typedef MccAngle coord_t;
|
|
// floating-point time duration (seconds)
|
|
typedef std::chrono::duration<double> duration_t;
|
|
|
|
typedef std::chrono::system_clock::time_point time_point_t;
|
|
|
|
static constexpr duration_t infiniteDuration{std::numeric_limits<double>::infinity()};
|
|
static constexpr duration_t zeroDuration{0.0};
|
|
|
|
MccAltLimitPZ(const coord_t& alt_limit, const coord_t& lat, traits::mcc_astrom_engine_c auto* astrom_engine)
|
|
// : MccProhibitedZone(KIND == MccAltLimitKind::MIN_ALT_LIMIT ? "MINALT-ZONE"
|
|
// : KIND == MccAltLimitKind::MAX_ALT_LIMIT ? "MAXALT-ZONE"
|
|
// : "ALTLIMIT-UNKNOWN"),
|
|
: _altLimit(alt_limit), _latitude(lat), _abs_lat(std::abs(_latitude)), _lat_lim(pi2 - _abs_lat)
|
|
{
|
|
_lat_lim = pi2 - _abs_lat;
|
|
// _altLimit.normalize<MccAngle::NORM_KIND_90_90>();
|
|
_altLimit = MccAngle(_altLimit).normalize<MccAngle::NORM_KIND_90_90>();
|
|
|
|
using astrom_engine_t = std::remove_cvref_t<decltype(*astrom_engine)>;
|
|
using astrom_coord_t = typename astrom_engine_t::coord_t;
|
|
|
|
static_assert(std::convertible_to<coord_t, astrom_coord_t>,
|
|
"ASTROMETRY ENGINE AND THE ZONE COORDINATE TYPE ARE NOT COMPATIBLE!");
|
|
static_assert(std::convertible_to<astrom_coord_t, coord_t>,
|
|
"ASTROMETRY ENGINE AND THE ZONE COORDINATE TYPE ARE NOT COMPATIBLE!");
|
|
|
|
_coord2coord = [astrom_engine, this](MccCoordPairKind kind_from, coord_t x_from, coord_t y_from,
|
|
time_point_t tpoint, MccCoordPairKind kind_to, coord_t& x_to,
|
|
coord_t& y_to) {
|
|
auto err = astrom_engine->coord2coord(kind_from, std::move(x_from), std::move(y_from), tpoint, kind_to,
|
|
x_to, y_to, tpoint);
|
|
};
|
|
}
|
|
|
|
|
|
consteval std::string_view name() const
|
|
{
|
|
return KIND == MccAltLimitKind::MIN_ALT_LIMIT ? "MINALT-ZONE"
|
|
: KIND == MccAltLimitKind::MAX_ALT_LIMIT ? "MAXALT-ZONE"
|
|
: "ALTLIMIT-UNKNOWN";
|
|
}
|
|
|
|
// check if current mount coordinates are within the zone
|
|
bool inZone(traits::mcc_mount_telemetry_data_c auto const& telemetry_data)
|
|
{
|
|
if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) {
|
|
return telemetry_data.mntALT <= _altLimit;
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) {
|
|
return telemetry_data.mntALT >= _altLimit;
|
|
}
|
|
}
|
|
|
|
|
|
// returns a time to reach the zone
|
|
duration_t timeTo(traits::mcc_mount_telemetry_data_c auto const& telemetry_data)
|
|
{
|
|
if (inZone(telemetry_data)) {
|
|
return zeroDuration;
|
|
}
|
|
|
|
if (!doesObjectReachZone(telemetry_data)) {
|
|
return infiniteDuration;
|
|
}
|
|
|
|
if constexpr (KIND ==
|
|
MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one after upper culmination
|
|
return compute(telemetry_data, false);
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one before upper
|
|
// culmination
|
|
return compute(telemetry_data, true);
|
|
}
|
|
}
|
|
|
|
// returns a time to exit from the zone
|
|
duration_t timeFrom(traits::mcc_mount_telemetry_data_c auto const& telemetry_data)
|
|
{
|
|
if (!inZone(telemetry_data)) {
|
|
return zeroDuration;
|
|
}
|
|
|
|
if (!doesObjectExitFromZone(telemetry_data)) {
|
|
return infiniteDuration;
|
|
}
|
|
|
|
if (!doesObjectReachZone(telemetry_data)) {
|
|
return zeroDuration;
|
|
}
|
|
|
|
if constexpr (KIND ==
|
|
MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one before upper culmination
|
|
return compute(telemetry_data, true);
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one after upper
|
|
// culmination
|
|
return compute(telemetry_data, false);
|
|
}
|
|
}
|
|
|
|
bool inZone(traits::mcc_celestial_point_c auto const& target)
|
|
{
|
|
coord_t alt, az;
|
|
|
|
_coord2coord(target.coordPairKind, target.x, target.y, target.time_point, MccCoordPairKind::COORDS_KIND_AZALT,
|
|
az, alt);
|
|
|
|
if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) {
|
|
return alt <= _altLimit;
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) {
|
|
return alt >= _altLimit;
|
|
}
|
|
}
|
|
|
|
duration_t timeTo(traits::mcc_celestial_point_c auto const& target)
|
|
{
|
|
coord_t ha, dec;
|
|
|
|
if (inZone(target)) {
|
|
return zeroDuration;
|
|
}
|
|
|
|
_coord2coord(target.coordPairKind, target.x, target.y, target.time_point,
|
|
MccCoordPairKind::COORDS_KIND_HADEC_APP, ha, dec);
|
|
|
|
if (!doesObjectReachZone(dec)) {
|
|
return infiniteDuration;
|
|
}
|
|
|
|
if constexpr (KIND ==
|
|
MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one after upper culmination
|
|
return compute(ha, dec, false);
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one before upper
|
|
// culmination
|
|
return compute(ha, dec, true);
|
|
}
|
|
}
|
|
|
|
duration_t timeFrom(traits::mcc_celestial_point_c auto const& target)
|
|
{
|
|
coord_t ha, dec;
|
|
|
|
if (!inZone(target)) {
|
|
return zeroDuration;
|
|
}
|
|
|
|
_coord2coord(target.coordPairKind, target.x, target.y, target.time_point,
|
|
MccCoordPairKind::COORDS_KIND_HADEC_APP, ha, dec);
|
|
|
|
if (!doesObjectExitFromZone(dec)) {
|
|
return infiniteDuration;
|
|
}
|
|
|
|
if (!doesObjectReachZone(dec)) {
|
|
return zeroDuration;
|
|
}
|
|
|
|
if constexpr (KIND ==
|
|
MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one before upper culmination
|
|
return compute(ha, dec, true);
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one after upper
|
|
// culmination
|
|
return compute(ha, dec, false);
|
|
}
|
|
}
|
|
|
|
// compute intersection point for the case of sideral-like moving
|
|
bool intersectPoint(traits::mcc_celestial_point_c auto const& target, traits::mcc_celestial_point_c auto& int_point)
|
|
{
|
|
coord_t ha, dec, az;
|
|
|
|
_coord2coord(target.coordPairKind, target.x, target.y, target.time_point,
|
|
MccCoordPairKind::COORDS_KIND_HADEC_APP, ha, dec);
|
|
|
|
// _altLimit = 0.001_degs;
|
|
// compute HA for intersection point
|
|
double cos_ha =
|
|
(std::sin(_altLimit) - std::sin(dec) * std::sin(_latitude)) / std::cos(dec) / std::cos(_latitude);
|
|
|
|
std::cout << "HA(inter) = " << MccAngle(ha).sexagesimal(true) << "\n";
|
|
std::cout << "DEC(inter) = " << MccAngle(dec).sexagesimal() << "\n";
|
|
std::cout << "HA(85.0) = " << MccAngle(acos(cos_ha)).sexagesimal(true) << "\n";
|
|
std::cout << "HA(85.0) = " << MccAngle(-acos(cos_ha)).sexagesimal(true) << "\n";
|
|
std::cout << "PHI = " << MccAngle(_latitude).sexagesimal() << "\n";
|
|
std::cout << "COS_HA = " << cos_ha << "\n";
|
|
|
|
if (cos_ha > 1.0) { // no intersection
|
|
// compute culmination points?
|
|
return false;
|
|
}
|
|
|
|
|
|
double cosA =
|
|
(-std::sin(dec) * std::cos(_latitude) + std::cos(dec) * std::sin(_latitude) * cos_ha) / cos(_altLimit);
|
|
// cosA /= std::cos(_altLimit);
|
|
|
|
std::cout << "COS_A = " << cosA << "\n";
|
|
|
|
double sinA = std::cos(dec) * sqrt(1.0 - cos_ha * cos_ha) / cos(_altLimit);
|
|
|
|
double tgA = sqrt(1.0 - cos_ha * cos_ha) / (cos(_latitude) * tan(dec) - sin(_latitude) * cos_ha);
|
|
|
|
auto z = (-std::sin(dec) * std::cos(_latitude) + std::cos(dec) * std::sin(_latitude) * cos_ha) / cosA;
|
|
// (-std::sin(dec) * std::cos(_latitude) + std::cos(dec) * std::sin(_latitude) * cos_ha) / cos(133.75_degs);
|
|
std::cout << "Z = " << MccAngle(asin(z)).sexagesimal() << "\n";
|
|
|
|
if constexpr (KIND ==
|
|
MccAltLimitKind::MIN_ALT_LIMIT) { // the closest time point is one after upper culmination
|
|
az = std::acos(cosA);
|
|
// az = atan2(sinA, cosA);
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) { // the closest time point is one before upper
|
|
// culmination
|
|
az = -std::acos(cosA) + std::numbers::pi; // to system of azimuth started from the North!!!
|
|
// az = atan(tgA);
|
|
// az = std::asin(sinA);
|
|
// az = atan2(sinA, cosA);
|
|
}
|
|
|
|
_coord2coord(MccCoordPairKind::COORDS_KIND_AZALT, az, _altLimit, target.time_point, int_point.coordPairKind,
|
|
int_point.x, int_point.y);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
coord_t _altLimit, _latitude, _abs_lat, _lat_lim;
|
|
|
|
// wrapper function
|
|
std::function<void(MccCoordPairKind, coord_t, coord_t, time_point_t, MccCoordPairKind, coord_t&, coord_t&)>
|
|
_coord2coord{};
|
|
|
|
bool doesObjectReachZone(const coord_t& dec_app)
|
|
{
|
|
// check for limit conditions
|
|
auto dd = std::abs(dec_app);
|
|
|
|
if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) {
|
|
dd += _altLimit;
|
|
|
|
if (dd > _lat_lim) { // never fall below altitude limit
|
|
return false;
|
|
}
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) {
|
|
if ((dd < (_abs_lat - _altLimit)) || (dd > (_abs_lat + _altLimit))) { // never rise above altitude limit
|
|
return false;
|
|
}
|
|
} else {
|
|
static_assert(false, "UNKNOWN ALTITUDE LIMIT TYPE!");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool doesObjectReachZone(traits::mcc_mount_telemetry_data_c auto const& telemetry_data)
|
|
{
|
|
return doesObjectReachZone(telemetry_data.mntDEC);
|
|
}
|
|
|
|
|
|
bool doesObjectExitFromZone(const coord_t& dec_app)
|
|
{
|
|
// check for limit conditions
|
|
auto dd = std::abs(dec_app);
|
|
|
|
if constexpr (KIND == MccAltLimitKind::MIN_ALT_LIMIT) {
|
|
dd -= _altLimit;
|
|
|
|
if (-dd <= -_lat_lim) { // always below altitude limit
|
|
return false;
|
|
}
|
|
} else if constexpr (KIND == MccAltLimitKind::MAX_ALT_LIMIT) {
|
|
if ((dd >= (_abs_lat - _altLimit)) || (dd <= (_abs_lat + _altLimit))) { // always above altitude limit
|
|
return false;
|
|
}
|
|
} else {
|
|
static_assert(false, "UNKNOWN ALTITUDE LIMIT TYPE!");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool doesObjectExitFromZone(traits::mcc_mount_telemetry_data_c auto const& telemetry_data)
|
|
{
|
|
return doesObjectExitFromZone(telemetry_data.mntDEC);
|
|
}
|
|
|
|
|
|
duration_t compute(const coord_t& ha_app, const coord_t& dec_app, bool before_upper_culm)
|
|
{
|
|
double cos_ha =
|
|
(std::sin(_altLimit) - std::sin(dec_app) * std::sin(_latitude)) / std::cos(dec_app) / std::cos(_latitude);
|
|
|
|
if (cos_ha > 1.0) { // should not be!
|
|
return infiniteDuration;
|
|
}
|
|
|
|
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!!
|
|
}
|
|
|
|
coord_t time_ang = ha - ha_app; // in sideral time scale
|
|
|
|
if (time_ang < 0.0) { // next day
|
|
time_ang += pi2;
|
|
}
|
|
|
|
time_ang /= mcc_sideral_to_UT1_ratio; // to UT1 time scale
|
|
|
|
return duration_t{MccAngle(time_ang).seconds()};
|
|
// return duration_t{time_ang.seconds()};
|
|
}
|
|
|
|
duration_t compute(traits::mcc_mount_telemetry_data_c auto const& telemetry_data, bool before_upper_culm)
|
|
{
|
|
return compute(telemetry_data.mntHA, telemetry_data.mntDEC, before_upper_culm);
|
|
}
|
|
};
|
|
|
|
typedef MccAltLimitPZ<MccAltLimitKind::MIN_ALT_LIMIT> MccMinAltPZ;
|
|
typedef MccAltLimitPZ<MccAltLimitKind::MAX_ALT_LIMIT> MccMaxAltPZ;
|
|
|
|
static_assert(std::movable<MccMinAltPZ>);
|
|
|
|
} // namespace mcc
|