This commit is contained in:
Timur A. Fatkhullin 2025-08-31 01:54:15 +03:00
parent 4696daa2ee
commit c2627ecd89
11 changed files with 324 additions and 180 deletions

View File

@ -85,7 +85,7 @@ if (WITH_TESTS)
set(CTTE_TEST_APP ccte_test) set(CTTE_TEST_APP ccte_test)
add_executable(${CTTE_TEST_APP} tests/ccte_test.cpp) add_executable(${CTTE_TEST_APP} tests/ccte_test.cpp)
target_include_directories(${CTTE_TEST_APP} PRIVATE ${ERFA_INCLUDE_DIR}) target_include_directories(${CTTE_TEST_APP} PRIVATE ${ERFA_INCLUDE_DIR})
target_link_libraries(${CTTE_TEST_APP} ERFA_LIB) target_link_libraries(${CTTE_TEST_APP} ERFA_LIB bsplines)
enable_testing() enable_testing()
endif() endif()

View File

@ -399,7 +399,7 @@ public:
{ {
error_t ret = MccCCTE_ERFAErrorCode::ERROR_OK; error_t ret = MccCCTE_ERFAErrorCode::ERROR_OK;
MccJulianDay jd; // MccJulianDay jd;
if (to_pt == nullptr) { if (to_pt == nullptr) {
return ret; return ret;
@ -476,7 +476,8 @@ public:
return transformCoordinates(pt, to_pt); return transformCoordinates(pt, to_pt);
} }
} else { // the same time points } else { // the same time points
double eo, lst, ha, dec, az, alt; double eo, lst, ha;
// , dec, az, alt;
auto lst_eo = [&]() { auto lst_eo = [&]() {
// ret = eqOrigins(from_pt.time_point, &eo); // ret = eqOrigins(from_pt.time_point, &eo);
@ -517,7 +518,7 @@ public:
} else if (to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP) { } else if (to_pt->pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP) {
lst_eo(); lst_eo();
if (!ret) { if (!ret) {
to_pt->X = lst - from_pt.X + eo; to_pt->X = MccAngle(lst - from_pt.X + eo).normalize<MccAngle::NORM_KIND_0_360>();
to_pt->Y = from_pt.Y; to_pt->Y = from_pt.Y;
} else { } else {
return ret; return ret;
@ -541,7 +542,7 @@ public:
azalt2hadec(from_pt, to_pt); azalt2hadec(from_pt, to_pt);
lst_eo(); lst_eo();
if (!ret) { if (!ret) {
to_pt->X = lst - to_pt->X + eo; to_pt->X = MccAngle(lst - to_pt->X + eo).normalize<MccAngle::NORM_KIND_0_360>();
} }
} else { } else {
ret = MccCCTE_ERFAErrorCode::ERROR_UNSUPPORTED_COORD_PAIR; ret = MccCCTE_ERFAErrorCode::ERROR_UNSUPPORTED_COORD_PAIR;
@ -560,7 +561,7 @@ public:
{ {
error_t ret = MccCCTE_ERFAErrorCode::ERROR_OK; error_t ret = MccCCTE_ERFAErrorCode::ERROR_OK;
MccJulianDay jd; // MccJulianDay jd;
if (to_pt == nullptr) { if (to_pt == nullptr) {
return ret; return ret;

View File

@ -104,6 +104,16 @@ struct MccGenericPointingTarget : MccGenericEqtHrzCoords<CoordT> {
typedef MccGenericPointingTarget<MccCelestialPoint::coord_t> MccPointingTarget; typedef MccGenericPointingTarget<MccCelestialPoint::coord_t> MccPointingTarget;
template <mcc_angle_c CoordT>
struct MccGenericPCMResult {
CoordT pcmX, pcmY;
};
typedef MccGenericPCMResult<double> MccPCMResult;
/* DEFAULT TELEMETRY DATA CLASS */ /* DEFAULT TELEMETRY DATA CLASS */
template <mcc_angle_c CoordT> template <mcc_angle_c CoordT>

View File

@ -136,6 +136,13 @@ concept mcc_time_point_c = traits::mcc_systime_c<T>;
// concept mcc_time_point_c = requires(T t) { []<typename CT, typename DT>(std::chrono::time_point<CT, DT>) {}(t); }; // concept mcc_time_point_c = requires(T t) { []<typename CT, typename DT>(std::chrono::time_point<CT, DT>) {}(t); };
template <mcc_time_point_c T1, mcc_time_point_c T2>
static constexpr void mcc_tp2tp(const T1& from_tp1, T2& to_tp)
{
to_tp = std::chrono::time_point_cast<typename T2::duration>(from_tp1);
}
/* JULIAN DAY CLASS CONCEPT */ /* JULIAN DAY CLASS CONCEPT */
template <typename T> template <typename T>
@ -327,18 +334,66 @@ concept mcc_ccte_c = std::derived_from<T, mcc_CCTE_interface_t<typename T::error
template <typename T> template <typename T>
concept mcc_PCM_result_c = requires(T t) { concept mcc_PCM_result_c = requires(T t) {
requires mcc_angle_c<decltype(t.dx)>; requires mcc_angle_c<decltype(t.pcmX)>;
requires mcc_angle_c<decltype(t.dy)>; requires mcc_angle_c<decltype(t.pcmY)>;
}; };
template <mcc_error_c RetT, mcc_PCM_result_c ResT> template <mcc_error_c RetT>
struct mcc_PCM_interface_t { struct mcc_PCM_interface_t {
virtual ~mcc_PCM_interface_t() = default; virtual ~mcc_PCM_interface_t() = default;
// ignore app_pt->pair_kind and time points!!!
template <std::derived_from<mcc_PCM_interface_t> SelfT> template <std::derived_from<mcc_PCM_interface_t> SelfT>
RetT computePCM(this SelfT&& self, mcc_celestial_point_c auto pt, ResT* result) RetT computePCM(this SelfT&& self,
mcc_celestial_point_c auto pt,
mcc_PCM_result_c auto* result,
mcc_celestial_point_c auto* app_pt)
{ {
return std::forward<SelfT>(self).computePCM(std::move(pt), result); return std::forward<SelfT>(self).computePCM(std::move(pt), result, app_pt);
}
// for equatorial mounts the method must compute:
// app_pt->HA = pt.X + result->pcmX
// app_pt->DEC_APP = pt.Y + result->pcmY
// for alt-azimuthal:
// app_pt->AZ = pt.X + result->pcmX
// app_pt->ZD = pt.Y + result->pcmY
template <std::derived_from<mcc_PCM_interface_t> SelfT>
RetT computePCM(this SelfT&& self,
mcc_celestial_point_c auto pt,
mcc_PCM_result_c auto* result,
mcc_eqt_hrz_coord_c auto* app_pt)
{
return std::forward<SelfT>(self).computePCM(std::move(pt), result, app_pt);
}
template <std::derived_from<mcc_PCM_interface_t> SelfT>
RetT computeInversePCM(this SelfT&& self,
mcc_celestial_point_c auto app_pt,
mcc_PCM_result_c auto* inv_result,
mcc_celestial_point_c auto* hw_pt)
{
return std::forward<SelfT>(self).computePCM(std::move(app_pt), inv_result, hw_pt);
}
// NOTE: for computation of the corrections the method must use of app_pt.X and app_pt.Y
//
// for equatorial mounts the method must compute:
// hw_pt->X = app_pt.HA + inv_result.pcmX
// hw_pt->Y = app_pt.DEC_APP + inv_result.pcmY
// and inputs for the corrections computing are app_pt.HA and app_pt.DEC_APP
// for alt-azimuthal:
// hw_pt->X = app_pt.AZ + inv_result.pcmX
// hw_pt->Y = app_pt.ZD + inv_result.pcmY
// and inputs for the corrections computing are app_pt.ZA and app_pt.ZD
template <std::derived_from<mcc_PCM_interface_t> SelfT>
RetT computeInversePCM(this SelfT&& self,
mcc_eqt_hrz_coord_c auto app_pt,
mcc_PCM_result_c auto* inv_result,
mcc_celestial_point_c auto* hw_pt)
{
return std::forward<SelfT>(self).computePCM(std::move(app_pt), inv_result, hw_pt);
} }
protected: protected:
@ -346,15 +401,14 @@ protected:
}; };
template <typename T> template <typename T>
concept mcc_PCM_c = concept mcc_PCM_c = std::derived_from<T, mcc_PCM_interface_t<typename T::error_t>> && requires {
std::derived_from<T, mcc_PCM_interface_t<typename T::error_t, typename T::pcm_result_t>> && requires {
// the 'T' class must contain static constexpr member of 'MccMountType' type // the 'T' class must contain static constexpr member of 'MccMountType' type
requires std::same_as<decltype(T::mountType), const MccMountType>; requires std::same_as<decltype(T::mountType), const MccMountType>;
[]() { []() {
static constexpr MccMountType val = T::mountType; static constexpr MccMountType val = T::mountType;
return val; return val;
}(); // to ensure 'mountType' can be used in compile-time context }(); // to ensure 'mountType' can be used in compile-time context
}; };
@ -404,13 +458,13 @@ concept mcc_hardware_c = requires(T t, const T t_const) {
// a class that contains at least time point of measurement, coordinates for x,y axes, its moving rates and moving // a class that contains at least time point of measurement, coordinates for x,y axes,
// type // its moving rates and moving type
requires requires(typename T::hardware_state_t state) { requires mcc_celestial_point_c<typename T::hardware_state_t> && requires(typename T::hardware_state_t state) {
requires mcc_time_point_c<decltype(state.time_point)>; // time point // requires mcc_time_point_c<decltype(state.time_point)>; // time point
requires mcc_angle_c<decltype(state.X)>; // target or current co-longitude coordinate // requires mcc_angle_c<decltype(state.X)>; // target or current co-longitude coordinate
requires mcc_angle_c<decltype(state.Y)>; // target or current co-latitude coordinate // requires mcc_angle_c<decltype(state.Y)>; // target or current co-latitude coordinate
requires mcc_angle_c<decltype(state.speedX)>; // moving speed along co-longitude coordinate requires mcc_angle_c<decltype(state.speedX)>; // moving speed along co-longitude coordinate
requires mcc_angle_c<decltype(state.speedY)>; // moving speed along co-latitude coordinate requires mcc_angle_c<decltype(state.speedY)>; // moving speed along co-latitude coordinate

View File

@ -73,16 +73,6 @@ class is_error_code_enum<mcc::MccDefaultPCMErrorCode> : public true_type
namespace mcc namespace mcc
{ {
namespace details
{
template <mcc::mcc_angle_c CT>
struct _pcm_result_t {
CT dx, dy;
};
} // namespace details
// type of PCM corrections (algorithm used): // type of PCM corrections (algorithm used):
// PCM_TYPE_GEOMETRY - "classic" geometry-based correction coefficients // PCM_TYPE_GEOMETRY - "classic" geometry-based correction coefficients
@ -91,7 +81,7 @@ struct _pcm_result_t {
enum class MccDefaultPCMType { PCM_TYPE_GEOMETRY, PCM_TYPE_GEOMETRY_BSPLINE, PCM_TYPE_BSPLINE }; enum class MccDefaultPCMType { PCM_TYPE_GEOMETRY, PCM_TYPE_GEOMETRY_BSPLINE, PCM_TYPE_BSPLINE };
template <MccMountType MOUNT_TYPE> template <MccMountType MOUNT_TYPE>
class MccDefaultPCM : public mcc_PCM_interface_t<std::error_code, details::_pcm_result_t<double>> class MccDefaultPCM : public mcc_PCM_interface_t<std::error_code>
{ {
public: public:
static constexpr MccMountType mountType = MOUNT_TYPE; static constexpr MccMountType mountType = MOUNT_TYPE;
@ -99,7 +89,6 @@ public:
typedef std::error_code error_t; typedef std::error_code error_t;
typedef double coord_t; typedef double coord_t;
typedef details::_pcm_result_t<coord_t> pcm_result_t;
// "classic" geometric PEC coefficients // "classic" geometric PEC coefficients
struct pcm_geom_coeffs_t { struct pcm_geom_coeffs_t {
@ -143,7 +132,11 @@ public:
// constructors // constructors
MccDefaultPCM(pcm_data_t pdata) : _pecData(std::move(pdata)), _pecDataMutex(new std::mutex) {} MccDefaultPCM() : _pcmDataMutex(new std::mutex) {}
MccDefaultPCM(pcm_data_t pdata) : MccDefaultPCM()
{
_pcmData = std::move(pdata);
}
MccDefaultPCM(MccDefaultPCM&& other) = default; MccDefaultPCM(MccDefaultPCM&& other) = default;
MccDefaultPCM& operator=(MccDefaultPCM&& other) = default; MccDefaultPCM& operator=(MccDefaultPCM&& other) = default;
@ -153,47 +146,49 @@ public:
virtual ~MccDefaultPCM() = default; virtual ~MccDefaultPCM() = default;
void setData(pcm_data_t pdata) void setPCMData(pcm_data_t pdata)
{ {
std::lock_guard lock(*_pecDataMutex); std::lock_guard lock(*_pcmDataMutex);
_pecData = std::move(pdata); _pcmData = std::move(pdata);
} }
pcm_data_t getData() const pcm_data_t getPCMData() const
{ {
std::lock_guard lock(*_pecDataMutex); std::lock_guard lock(*_pcmDataMutex);
return _pecData; return _pcmData;
} }
void setType(MccDefaultPCMType type) void setPCMType(MccDefaultPCMType type)
{ {
std::lock_guard lock(*_pecDataMutex); std::lock_guard lock(*_pcmDataMutex);
_pecData.type = type; _pcmData.type = type;
} }
MccDefaultPCMType getType() const MccDefaultPCMType getPCMType() const
{ {
std::lock_guard lock(*_pecDataMutex); std::lock_guard lock(*_pcmDataMutex);
return _pecData.type; return _pcmData.type;
} }
// The computed PEC quantities must be interpretated as: // The computed PCM quantities must be interpretated as:
// apparent_X = encoder_X + pcm_result_t.dx // apparent_X = encoder_X + res.pcmX
// apparent_Y = encoder_Y + pcm_result_t.dy // apparent_Y = encoder_Y + res.pcmY
// so, input x and y are assumed to be mount axis encoder coordinates // so, input x and y are assumed to be mount axis encoder coordinates
error_t compute(mcc_celestial_point_c auto pt, pcm_result_t& res) template <typename T = std::nullptr_t>
error_t computePCM(mcc_celestial_point_c auto pt, mcc_PCM_c auto* res, T* app_pt = nullptr)
requires(mcc_celestial_point_c<T> || mcc_eqt_hrz_coord_c<T> || std::same_as<T, std::nullptr_t>)
{ {
std::lock_guard lock(*_pecDataMutex); std::lock_guard lock(*_pcmDataMutex);
if constexpr (mcc_is_equatorial_mount<MOUNT_TYPE>) { // equatorial if constexpr (mcc_is_equatorial_mount<MOUNT_TYPE>) { // equatorial
if (_pecData.type == MccDefaultPCMType::PCM_TYPE_GEOMETRY) { if (_pcmData.type == MccDefaultPCMType::PCM_TYPE_GEOMETRY) {
const auto cosPhi = std::cos(_pecData.siteLatitude); const auto cosPhi = std::cos(_pcmData.siteLatitude);
const auto sinPhi = std::sin(_pecData.siteLatitude); const auto sinPhi = std::sin(_pcmData.siteLatitude);
const auto tanY = std::tan(pt.Y); const auto tanY = std::tan(pt.Y);
const auto sinX = std::sin(pt.X); const auto sinX = std::sin(pt.X);
const auto cosX = std::cos(pt.X); const auto cosX = std::cos(pt.X);
@ -201,55 +196,55 @@ public:
if (utils::isEqual(cosY, 0.0)) { if (utils::isEqual(cosY, 0.0)) {
res.dx = _pecData.geomCoefficients.zeroPointX; res->pcmX = _pcmData.geomCoefficients.zeroPointX;
} else { } else {
res.dx = _pecData.geomCoefficients.zeroPointX + _pecData.geomCoefficients.collimationErr / cosY + res->pcmX = _pcmData.geomCoefficients.zeroPointX + _pcmData.geomCoefficients.collimationErr / cosY +
_pecData.geomCoefficients.nonperpendErr * tanY - _pcmData.geomCoefficients.nonperpendErr * tanY -
_pecData.geomCoefficients.misalignErr1 * cosX * tanY + _pcmData.geomCoefficients.misalignErr1 * cosX * tanY +
_pecData.geomCoefficients.misalignErr2 * sinX * tanY + _pcmData.geomCoefficients.misalignErr2 * sinX * tanY +
_pecData.geomCoefficients.tubeFlexure * cosPhi * sinX / cosY - _pcmData.geomCoefficients.tubeFlexure * cosPhi * sinX / cosY -
_pecData.geomCoefficients.DECaxisFlexure * (cosPhi * cosX + sinPhi * tanY); _pcmData.geomCoefficients.DECaxisFlexure * (cosPhi * cosX + sinPhi * tanY);
} }
res.dy = _pecData.geomCoefficients.zeroPointY + _pecData.geomCoefficients.misalignErr1 * sinX + res->pcmY = _pcmData.geomCoefficients.zeroPointY + _pcmData.geomCoefficients.misalignErr1 * sinX +
_pecData.geomCoefficients.misalignErr2 * cosX + _pcmData.geomCoefficients.misalignErr2 * cosX +
_pecData.geomCoefficients.tubeFlexure * (cosPhi * cosX * std::sin(pt.Y) - sinPhi * cosY); _pcmData.geomCoefficients.tubeFlexure * (cosPhi * cosX * std::sin(pt.Y) - sinPhi * cosY);
if constexpr (mountType == MccMountType::FORK_TYPE) { if constexpr (mountType == MccMountType::FORK_TYPE) {
if (!utils::isEqual(cosX, 0.0)) { if (!utils::isEqual(cosX, 0.0)) {
res.dy += _pecData.geomCoefficients.forkFlexure / cosX; res->pcmY += _pcmData.geomCoefficients.forkFlexure / cosX;
} }
} }
} }
if (_pecData.type == MccDefaultPCMType::PCM_TYPE_BSPLINE || if (_pcmData.type == MccDefaultPCMType::PCM_TYPE_BSPLINE ||
_pecData.type == MccDefaultPCMType::PCM_TYPE_GEOMETRY_BSPLINE) { _pcmData.type == MccDefaultPCMType::PCM_TYPE_GEOMETRY_BSPLINE) {
double spl_valX, spl_valY; double spl_valX, spl_valY;
int ret = bsplines::fitpack_eval_spl2d(_pecData.bspline.knotsX, _pecData.bspline.knotsY, int ret = bsplines::fitpack_eval_spl2d(_pcmData.bspline.knotsX, _pcmData.bspline.knotsY,
_pecData.bspline.coeffsX, pt.X, pt.Y, spl_valX, _pcmData.bspline.coeffsX, pt.X, pt.Y, spl_valX,
_pecData.bspline.bsplDegreeX, _pecData.bspline.bsplDegreeY); _pcmData.bspline.bsplDegreeX, _pcmData.bspline.bsplDegreeY);
if (ret) { if (ret) {
res.dx = std::numeric_limits<double>::quiet_NaN(); res->pcmX = std::numeric_limits<double>::quiet_NaN();
res.dy = std::numeric_limits<double>::quiet_NaN(); res->pcmY = std::numeric_limits<double>::quiet_NaN();
return MccDefaultPCMErrorCode::ERROR_INVALID_INPUTS_BISPLEV; return MccDefaultPCMErrorCode::ERROR_INVALID_INPUTS_BISPLEV;
} }
ret = bsplines::fitpack_eval_spl2d(_pecData.bspline.knotsX, _pecData.bspline.knotsY, ret = bsplines::fitpack_eval_spl2d(_pcmData.bspline.knotsX, _pcmData.bspline.knotsY,
_pecData.bspline.coeffsY, pt.X, pt.Y, spl_valY, _pcmData.bspline.coeffsY, pt.X, pt.Y, spl_valY,
_pecData.bspline.bsplDegreeX, _pecData.bspline.bsplDegreeY); _pcmData.bspline.bsplDegreeX, _pcmData.bspline.bsplDegreeY);
if (ret) { if (ret) {
res.dx = std::numeric_limits<double>::quiet_NaN(); res->pcmX = std::numeric_limits<double>::quiet_NaN();
res.dy = std::numeric_limits<double>::quiet_NaN(); res->pcmY = std::numeric_limits<double>::quiet_NaN();
return MccDefaultPCMErrorCode::ERROR_INVALID_INPUTS_BISPLEV; return MccDefaultPCMErrorCode::ERROR_INVALID_INPUTS_BISPLEV;
} }
res.dx += spl_valX; res->pcmX += spl_valX;
res.dy += spl_valY; res->pcmY += spl_valY;
} }
} else if constexpr (mcc_is_altaz_mount<MOUNT_TYPE>) { } else if constexpr (mcc_is_altaz_mount<MOUNT_TYPE>) {
static_assert(false, "NOT IMPLEMENTED!"); static_assert(false, "NOT IMPLEMENTED!");
@ -257,14 +252,65 @@ public:
static_assert(false, "UNSUPPORTED"); static_assert(false, "UNSUPPORTED");
} }
return MccDefaultPCMErrorCode::ERROR_OK; if constexpr (!std::is_null_pointer_v<T>) {
if constexpr (mcc_eqt_hrz_coord_c<T>) {
if constexpr (mccIsEquatorialMount(mountType)) {
app_pt->HA = pt.X + res->pcmX;
app_pt->DEC_APP = pt.Y + res->pcmY;
} else if constexpr (mccIsAltAzMount(mountType)) {
app_pt->AZ = pt.X + res->pcmX;
app_pt->ZD = pt.Y + res->pcmY;
} else {
static_assert(false, "UNKNOW MOUNT TYPE!");
}
} else {
app_pt->X = pt.X + res->pcmX;
app_pt->Y = pt.Y + res->pcmY;
}
} }
private: return MccDefaultPCMErrorCode::ERROR_OK;
pcm_data_t _pecData; }
std::unique_ptr<std::mutex> _pecDataMutex; template <typename T>
error_t computeInversePCM(T app_pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto* hw_pt = nullptr)
requires(mcc_celestial_point_c<T> || mcc_eqt_hrz_coord_c<T>)
{
// for small corrections only!!!
auto ret = computePCM(std::move(app_pt), result);
if (ret) {
return ret;
}
result->pcmX = -result->pcmX;
result->pcmY = -result->pcmY;
if (hw_pt != nullptr) {
if constexpr (mcc_eqt_hrz_coord_c<T>) {
if constexpr (mccIsEquatorialMount(mountType)) {
hw_pt->X = app_pt.HA + result->pcmX;
hw_pt->Y = app_pt.DEC_APP + result->pcmY;
} else if constexpr (mccIsAltAzMount(mountType)) {
hw_pt->X = app_pt.AZ + result->pcmX;
hw_pt->Y = app_pt.ZD + result->pcmY;
} else {
static_assert(false, "UNKNOW MOUNT TYPE!");
}
} else {
hw_pt->X = app_pt.X + result->pcmX;
hw_pt->Y = app_pt.Y + result->pcmY;
}
}
return ret;
}
private:
pcm_data_t _pcmData;
std::unique_ptr<std::mutex> _pcmDataMutex;
}; };

View File

@ -294,8 +294,9 @@ public:
ha = coords.HA; ha = coords.HA;
dec = coords.DEC_APP; dec = coords.DEC_APP;
} else { } else {
MccCelestialPoint to_pt{.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP, MccCelestialPoint to_pt{.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP};
.time_point = coords.time_point}; mcc_tp2tp(coords.time_point, to_pt.time_point);
ret = getCoord(coords, &to_pt); ret = getCoord(coords, &to_pt);
if (ret) { if (ret) {
return ret; return ret;
@ -310,7 +311,7 @@ public:
auto cos_ha = (_sinAlim - sinDec * _sinLat) / cosDec / _cosLat; auto cos_ha = (_sinAlim - sinDec * _sinLat) / cosDec / _cosLat;
if (cos_ha > 1.0) { // no intersection if (cos_ha > 1.0) { // no intersection
point->pair_kind = MccCoordPairKind::COORDS_KIND_GENERIC; // point->pair_kind = MccCoordPairKind::COORDS_KIND_GENERIC;
point->X = std::numeric_limits<double>::quiet_NaN(); point->X = std::numeric_limits<double>::quiet_NaN();
point->Y = std::numeric_limits<double>::quiet_NaN(); point->Y = std::numeric_limits<double>::quiet_NaN();
@ -328,14 +329,16 @@ public:
az = -std::acos(cosA); az = -std::acos(cosA);
} }
MccCelestialPoint pt{ MccCelestialPoint pt{.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT, .X = az, .Y = _altLimit};
.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT, .time_point = coords.time_point, .X = az, .Y = _altLimit}, mcc_tp2tp(coords.time_point, pt.time_point);
to_pt{.pair_kind = point->pair_kind, .time_point = point->time_point};
MccCelestialPoint to_pt{.pair_kind = point->pair_kind};
mcc_tp2tp(point->time_point, to_pt.time_point);
ret = _transformCoordinates(pt, &to_pt); ret = _transformCoordinates(pt, &to_pt);
if (!ret) { if (!ret) {
point->X = to_pt.X; point->X = MccAngle(to_pt.X).normalize<MccAngle::NORM_KIND_0_360>();
point->Y = to_pt.Y; point->Y = MccAngle(to_pt.Y).normalize<MccAngle::NORM_KIND_90_90>();
} }
return ret; return ret;

View File

@ -320,14 +320,18 @@ public:
MccCelestialPoint pt; MccCelestialPoint pt;
auto apply_func = [&](auto& func, auto& pt_arg, size_t i) { auto apply_func = [&](auto& func, auto& pt_arg, size_t i) {
auto ptr = result->begin();
std::ranges::advance(ptr, i);
pt.pair_kind = ptr->pair_kind;
pt.time_point = ptr->time_point;
error_t ret = func(pt_arg, &pt); error_t ret = func(pt_arg, &pt);
if (!ret) { if (!ret) {
// if (traits::mcc_range_size(*result) == i) { // if (traits::mcc_range_size(*result) == i) {
// std::back_inserter(*result) = CPT(); // std::back_inserter(*result) = CPT();
// } // }
auto ptr = result->begin();
std::ranges::advance(ptr, i);
mcc_copy_celestial_point(pt, &(*ptr)); mcc_copy_celestial_point(pt, &(*ptr));
} }

View File

@ -191,41 +191,39 @@ public:
} }
// compute hardware coordinates
// WARNING: It is assumed here that PCM corrections have small (arcseconds-arcminutes) values
// since ususaly there is no reverse transformation for "hardware-to-apparent" relation!
typename pcm_t::error_t pcm_err; typename pcm_t::error_t pcm_err;
struct { MccPCMResult pcm_res;
double dx, dy;
} pcm_res;
// MccCelestialPoint pt;
// pt.time_point =
// std::chrono::time_point_cast<typename decltype(pt.time_point)::duration>(_data.target.time_point);
MccCelestialPoint pt; pcm_err = pcm->computeInversePCM(_data, &pcm_res, &_data);
pt.time_point =
std::chrono::time_point_cast<typename decltype(pt.time_point)::duration>(_data.target.time_point);
if constexpr (mccIsEquatorialMount(pcm_t::mountType)) { // if constexpr (mccIsEquatorialMount(pcm_t::mountType)) {
pt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; // pcm_err = pcm->computeInversePCM(_data, &pcm_res, &_data);
pt.X = _data.target.HA;
pt.Y = _data.target.DEC_APP; // pt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP;
pcm_err = pcm->compute(std::move(pt), &pcm_res); // pt.X = _data.target.HA;
if (!pcm_err) { // pt.Y = _data.target.DEC_APP;
_data.target.X = _data.target.HA - pcm_res.dx; // pcm_err = pcm->computeInversePCM(std::move(pt), &pcm_res);
_data.target.Y = _data.target.DEC_APP - pcm_res.dy; // if (!pcm_err) {
} // _data.target.X = _data.target.HA - pcm_res.pcmX;
} else if constexpr (mccIsAltAzMount(pcm_t::mountType)) { // _data.target.Y = _data.target.DEC_APP - pcm_res.pcmY;
pt.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT; // }
pt.X = _data.target.AZ; // } else if constexpr (mccIsAltAzMount(pcm_t::mountType)) {
pt.Y = _data.target.ALT; // pt.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT;
pcm_err = pcm->compute(std::move(pt), &pcm_res); // pt.X = _data.target.AZ;
if (!pcm_err) { // pt.Y = _data.target.ALT;
_data.target.X = _data.target.AZ - pcm_res.dx; // pcm_err = pcm->computeInversePCM(std::move(pt), &pcm_res);
_data.target.Y = _data.target.ALT - pcm_res.dy; // if (!pcm_err) {
} // _data.target.X = _data.target.AZ - pcm_res.pcmX;
} else { // _data.target.Y = _data.target.ALT - pcm_res.pcmY;
static_assert(false, "UNKNOWN MOUNT TYPE!"); // }
} // } else {
// static_assert(false, "UNKNOWN MOUNT TYPE!");
// }
if (pcm_err) { if (pcm_err) {
return mcc_deduce_error<error_t>(pcm_err, MccTelemetryErrorCode::ERROR_PCM_COMP); return mcc_deduce_error<error_t>(pcm_err, MccTelemetryErrorCode::ERROR_PCM_COMP);
@ -281,18 +279,12 @@ public:
_data.speedX = (double)hw_pos.speedX; _data.speedX = (double)hw_pos.speedX;
_data.speedY = (double)hw_pos.speedY; _data.speedY = (double)hw_pos.speedY;
struct { // fill _data.pcmX, _data.pcmY and corresponded apparent coordinates
double dx, dy; auto pcm_err = pcm->computePCM(_data, &_data, &_data);
} pcm_res;
auto pcm_err = pcm->computePCM(_data, &pcm_res);
if (pcm_err) { if (pcm_err) {
return mcc_deduce_error(pcm_err, MccTelemetryErrorCode::ERROR_PCM_COMP); return mcc_deduce_error(pcm_err, MccTelemetryErrorCode::ERROR_PCM_COMP);
} }
_data.pcmX = pcm_res.dx;
_data.pcmY = pcm_res.dy;
if (stop_token.stop_requested()) { if (stop_token.stop_requested()) {
return MccTelemetryErrorCode::ERROR_UPDATE_STOPPED; return MccTelemetryErrorCode::ERROR_UPDATE_STOPPED;
} }
@ -300,8 +292,6 @@ public:
MccCelestialPoint pt{.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT, .time_point = _data.time_point}; MccCelestialPoint pt{.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT, .time_point = _data.time_point};
if constexpr (mccIsEquatorialMount(pcm_t::mountType)) { if constexpr (mccIsEquatorialMount(pcm_t::mountType)) {
_data.HA = (double)hw_pos.X + pcm_res.dx;
_data.DEC_APP = (double)hw_pos.Y + pcm_res.dy;
_data.RA_APP = (double)_data.LST - (double)_data.HA + eo; _data.RA_APP = (double)_data.LST - (double)_data.HA + eo;
_data.X = _data.HA; _data.X = _data.HA;
@ -316,14 +306,12 @@ public:
_data.ZD = std::numbers::pi / 2.0 - _data.ALT; _data.ZD = std::numbers::pi / 2.0 - _data.ALT;
} }
} else if constexpr (mccIsAltAzMount(pcm_t::mountType)) { } else if constexpr (mccIsAltAzMount(pcm_t::mountType)) {
_data.AZ = (double)hw_pos.X + pcm_res.dx; _data.ALT = std::numbers::pi / 2.0 - _data.ZD;
_data.ALT = (double)hw_pos.Y + pcm_res.dy;
_data.ZD = std::numbers::pi / 2.0 - _data.ALT;
_data.X = _data.AZ; _data.X = _data.AZ;
_data.Y = _data.ALT; _data.Y = _data.ZD;
_data.pair_kind = MccCoordPairKind::COORDS_KIND_AZALT; _data.pair_kind = MccCoordPairKind::COORDS_KIND_AZZD;
pt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; pt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP;
ccte_err = ccte->transformCoordinates(_data, &pt); ccte_err = ccte->transformCoordinates(_data, &pt);

View File

@ -18,7 +18,7 @@ enum class MccSimpleTrackingModelErrorCode : int {
ERROR_HW_GETSTATE, ERROR_HW_GETSTATE,
ERROR_HW_SETSTATE, ERROR_HW_SETSTATE,
ERROR_PCM_COMP, ERROR_PCM_COMP,
ERROR_TELEMETRY_TIMEOUT, ERROR_GET_TELEMETRY,
ERROR_PZONE_CONTAINER_COMP, ERROR_PZONE_CONTAINER_COMP,
ERROR_IN_PZONE, ERROR_IN_PZONE,
ERROR_NEAR_PZONE, ERROR_NEAR_PZONE,
@ -80,11 +80,9 @@ public:
// compute position in future // compute position in future
auto err = hardware->hardwareGetState(&hw_state); auto err = hardware->hardwareGetState(&hw_state);
if (err) { if (err) {
return mcc_deduce_error(err, MccSimpleTrackingModelErrorCode::ERROR_HW_GETSTATE); return mcc_deduce_error<error_t>(err, MccSimpleTrackingModelErrorCode::ERROR_HW_GETSTATE);
} }
cpt.time_point = std::chrono::time_point_cast<decltype(cpt.time_point)>(hw_state.time_point);
if constexpr (mccIsEquatorialMount(PcmT::mountType)) { if constexpr (mccIsEquatorialMount(PcmT::mountType)) {
cpt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; cpt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP;
} else if constexpr (mccIsAltAzMount(PcmT::mountType)) { } else if constexpr (mccIsAltAzMount(PcmT::mountType)) {
@ -94,62 +92,94 @@ public:
static_assert(false, "UNKNOW MOUNT TYPE!"); static_assert(false, "UNKNOW MOUNT TYPE!");
} }
cpt.X = hw_state.X;
cpt.Y = hw_state.Y;
struct { MccTelemetryData tdata;
double dx, dy; auto t_err = telemetry->waitForTelemetryData(&tdata, _currentTrackParams.telemetryTimeout);
} pcm_res; if (t_err) {
return mcc_deduce_error<error_t>(t_err, MccSimpleTrackingModelErrorCode::ERROR_GET_TELEMETRY);
auto pcm_err = pcm->computePCM(cpt, &pcm_res);
if (pcm_err) {
return mcc_deduce_error(pcm_err, MccSimpleTrackingModelErrorCode::ERROR_PCM_COMP);
} }
// to celestial coordinates mcc_tp2tp(tdata.time_point, cpt.time_point);
cpt.X += pcm_res.dx;
cpt.Y += pcm_res.dy;
std::vector<std::chrono::duration<double>> pz_timeto; // in seconds
std::chrono::duration<double> min_time{0.0};
std::vector<MccCelestialPoint> intsc_pt(pz_cont->sizePZones(), cpt);
// compute intersection points with the prohibited zones
auto pz_err = pz_cont->intersectPZone(tdata, &intsc_pt);
if (pz_err) {
return mcc_deduce_error<error_t>(pz_err, MccSimpleTrackingModelErrorCode::ERROR_PZONE_CONTAINER_COMP);
}
if constexpr (mccIsEquatorialMount(PcmT::mountType)) { if constexpr (mccIsEquatorialMount(PcmT::mountType)) {
cpt.pair_kind = MccCoordPairKind::COORDS_KIND_HADEC_APP; double dha_min = 0.0, dha;
// find the closest pzone
for (auto& ipt : intsc_pt) {
if (std::isfinite(ipt.X) && std::isfinite(ipt.Y)) {
dha = ipt.X - tdata.HA;
if (dha < 0.0) {
dha += std::numbers::pi * 2.0;
}
if (dha < dha_min) {
dha_min = dha;
cpt.X = ipt.X;
cpt.Y = ipt.Y;
}
}
}
if (utils::isEqual(dha_min, 0.0)) { // no intersections
cpt.X = tdata.HA - 1.0_mins;
cpt.Y = tdata.DEC_APP;
}
MccPCMResult pcm_inv_res;
// endpoint of the mount moving
auto pcm_err = pcm->computeInversePCM(cpt, &pcm_inv_res, &hw_state);
if (pcm_err) {
return mcc_deduce_error<error_t>(pcm_err, MccSimpleTrackingModelErrorCode::ERROR_PCM_COMP);
}
// just set sideral rate once // just set sideral rate once
// hw_state.time_point; mcc_tp2tp(cpt.time_point, hw_state.time_point);
hw_state.speedX = _currentTrackParams.trackSpeedX; hw_state.speedX = _currentTrackParams.trackSpeedX;
hw_state.speedY = _currentTrackParams.trackSpeedY; hw_state.speedY = _currentTrackParams.trackSpeedY;
hw_state.moving_type = HardwareT::hardware_moving_state_t::HW_MOVE_TRACKING; hw_state.moving_type = HardwareT::hardware_moving_state_t::HW_MOVE_TRACKING;
// start tracking
err = hardware->hardwareSetState(std::move(hw_state)); err = hardware->hardwareSetState(std::move(hw_state));
if (err) { if (err) {
return mcc_deduce_error(err, MccSimpleTrackingModelErrorCode::ERROR_HW_SETSTATE); return mcc_deduce_error<error_t>(err, MccSimpleTrackingModelErrorCode::ERROR_HW_SETSTATE);
} }
// control prohibited zones
MccTelemetryData tdata;
std::vector<std::chrono::duration<double>> pz_timeto; // in seconds
// std::vector<bool> pz_flags;
// bool inzone_flag;
while (!*_stopTracking) { while (!*_stopTracking) {
auto pz_err = pz_cont->timeToPZone(tdata, &pz_timeto); // control prohibited zones
pz_err = pz_cont->timeToPZone(tdata, &pz_timeto);
if (pz_err) { if (pz_err) {
return mcc_deduce_error(pz_err, MccSimpleTrackingModelErrorCode::ERROR_PZONE_CONTAINER_COMP); return mcc_deduce_error<error_t>(pz_err,
MccSimpleTrackingModelErrorCode::ERROR_PZONE_CONTAINER_COMP);
} }
min_time = std::chrono::duration<double>{0};
for (size_t i = 0; i < pz_cont->sizePZones(); ++i) { for (size_t i = 0; i < pz_cont->sizePZones(); ++i) {
if (pz_timeto[i] <= _currentTrackParams.minTimeToPZone) { if (pz_timeto[i] <= _currentTrackParams.minTimeToPZone) {
return MccSimpleTrackingModelErrorCode::ERROR_NEAR_PZONE; return MccSimpleTrackingModelErrorCode::ERROR_NEAR_PZONE;
} }
if (pz_timeto[i] < min_time) {
min_time = pz_timeto[i];
}
} }
auto t_err = telemetry->waitForTelemetryData(&tdata, _currentTrackParams.telemetryTimeout); t_err = telemetry->waitForTelemetryData(&tdata, _currentTrackParams.telemetryTimeout);
if (t_err) { if (t_err) {
return mcc_deduce_error(t_err, MccSimpleTrackingModelErrorCode::ERROR_TELEMETRY_TIMEOUT); return mcc_deduce_error<error_t>(t_err, MccSimpleTrackingModelErrorCode::ERROR_GET_TELEMETRY);
} }
if (*_stopTracking) { if (*_stopTracking) {
@ -161,16 +191,6 @@ public:
// unhandled stop state?!!! // unhandled stop state?!!!
return MccSimpleTrackingModelErrorCode::ERROR_UNEXPECTED_AXIS_RATES; return MccSimpleTrackingModelErrorCode::ERROR_UNEXPECTED_AXIS_RATES;
} }
// auto pz_err = mount->inPZone(tdata, &inzone_flag, &pz_flags);
// if (pz_err) {
// return mcc_deduce_error(t_err, MccSimpleTrackingModelErrorCode::ERROR_PZONE_CONTAINER_COMP);
// }
// if (inzone_flag) {
// // logging
// return MccSimpleTrackingModelErrorCode::ERROR_IN_PZONE;
// }
} }
return MccSimpleTrackingModelErrorCode::ERROR_OK; return MccSimpleTrackingModelErrorCode::ERROR_OK;

View File

@ -223,6 +223,11 @@ static R rad2sxg(double ang, bool hms = false, int prec = 2)
{ {
R res; R res;
if (!std::isfinite(ang)) {
std::vformat_to(std::back_inserter(res), "{}", std::make_format_args(ang));
return res;
}
std::string fmt = "{:02.0f}:{:02.0f}:{:0" + std::to_string(prec + 3) + "." + std::to_string(prec) + "f}"; std::string fmt = "{:02.0f}:{:02.0f}:{:0" + std::to_string(prec + 3) + "." + std::to_string(prec) + "f}";
// std::string fmt = "{:02.0f}:{:02.0f}:{:02." + std::to_string(prec) + "f}"; // std::string fmt = "{:02.0f}:{:02.0f}:{:02." + std::to_string(prec) + "f}";

View File

@ -105,6 +105,19 @@ int main()
std::cout << "TIME TO ZONE 2: " << vm[1] << "\n"; std::cout << "TIME TO ZONE 2: " << vm[1] << "\n";
std::vector<mcc::MccCelestialPoint> vpt(
2, {.pair_kind = mcc::MccCoordPairKind::COORDS_KIND_AZZD, .time_point = eqhrz.time_point});
ret = pzcont.intersectPZone(eqhrz, &vpt);
std::cout << "ret = " << ret.message() << "\n";
if (!ret) {
std::cout << "INTERSC POINT 1: AZ = " << mcc::MccAngle(vpt[0].X).sexagesimal()
<< ", ZD = " << mcc::MccAngle(vpt[0].Y).sexagesimal() << "\n";
std::cout << "INTERSC POINT 2: AZ = " << mcc::MccAngle(vpt[1].X).sexagesimal()
<< ", ZD = " << mcc::MccAngle(vpt[1].Y).sexagesimal() << "\n";
}
std::cout << "\n\n"; std::cout << "\n\n";
MccCCTE_ERFA::meteo_t meteo{.temperature = 10.0, .humidity = 0.7, .pressure = 1010.0}; MccCCTE_ERFA::meteo_t meteo{.temperature = 10.0, .humidity = 0.7, .pressure = 1010.0};