#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* A REFERENCE "POINTING-CORRECTION-MODEL" CLASS IMPLEMENTATION */ #include #include "bsplines/mcc_bsplines.h" #include "mcc_generics.h" namespace mcc { enum class MccDefaultPCMErrorCode : int { ERROR_OK, ERROR_INVALID_INPUTS_BISPLEV, ERROR_EXCEED_MAX_ITERS }; /* error category definition */ // error category struct MccDefaultPCMCategory : public std::error_category { MccDefaultPCMCategory() : std::error_category() {} const char* name() const noexcept { return "ADC_GENERIC_DEVICE"; } std::string message(int ec) const { MccDefaultPCMErrorCode err = static_cast(ec); switch (err) { case MccDefaultPCMErrorCode::ERROR_OK: return "OK"; case MccDefaultPCMErrorCode::ERROR_INVALID_INPUTS_BISPLEV: return "invalid input arguments for bispev"; case MccDefaultPCMErrorCode::ERROR_EXCEED_MAX_ITERS: return "exceed maximum of iterations number"; default: return "UNKNOWN"; } } static const MccDefaultPCMCategory& get() { static const MccDefaultPCMCategory constInst; return constInst; } }; inline std::error_code make_error_code(MccDefaultPCMErrorCode ec) { return std::error_code(static_cast(ec), MccDefaultPCMCategory::get()); } } // namespace mcc namespace std { template <> class is_error_code_enum : public true_type { }; } // namespace std namespace mcc { namespace details { template struct _pcm_result_t { CT dx, dy; }; } // namespace details // type of PCM corrections (algorithm used): // PCM_TYPE_GEOMETRY - "classic" geometry-based correction coefficients // PCM_TYPE_GEOMETRY_BSPLINE - previous one and additional 2D B-spline corrections // PCM_TYPE_BSPLINE - pure 2D B-spline corrections enum class MccDefaultPCMType { PCM_TYPE_GEOMETRY, PCM_TYPE_GEOMETRY_BSPLINE, PCM_TYPE_BSPLINE }; template class MccDefaultPCM : public mcc_PCM_interface_t> { public: static constexpr MccMountType mountType = MOUNT_TYPE; typedef std::error_code error_t; typedef double coord_t; typedef details::_pcm_result_t pcm_result_t; // "classic" geometric PEC coefficients struct pcm_geom_coeffs_t { typedef double coeff_t; coeff_t zeroPointX; coeff_t zeroPointY; coeff_t collimationErr; // tube collimation error coeff_t nonperpendErr; // X-Y axes nonperpendicularity coeff_t misalignErr1; // misalignment of hour-angle/azimuth axis: left-right for equatorial, East-West for // alt-azimuthal coeff_t misalignErr2; // misalignment of hour-angle/azimuth axis: vertical for equatorial, North-South for // alt-azimuthal coeff_t tubeFlexure; coeff_t forkFlexure; coeff_t DECaxisFlexure; // declination axis flexure }; // B-splines related data structure (coefficients, knots ...) struct pcm_bspline_t { typedef double knot_t; typedef double coeff_t; size_t bsplDegreeX = 3; size_t bsplDegreeY = 3; std::vector knotsX{}; std::vector knotsY{}; std::vector coeffsX{}; std::vector coeffsY{}; }; struct pcm_data_t { MccDefaultPCMType type{MccDefaultPCMType::PCM_TYPE_GEOMETRY}; double siteLatitude{0.0}; // in radians pcm_geom_coeffs_t geomCoefficients{}; pcm_bspline_t bspline{}; }; // constructors MccDefaultPCM(pcm_data_t pdata) : _pecData(std::move(pdata)), _pecDataMutex(new std::mutex) {} MccDefaultPCM(MccDefaultPCM&& other) = default; MccDefaultPCM& operator=(MccDefaultPCM&& other) = default; MccDefaultPCM(const MccDefaultPCM&) = delete; MccDefaultPCM& operator=(const MccDefaultPCM&) = delete; virtual ~MccDefaultPCM() = default; void setData(pcm_data_t pdata) { std::lock_guard lock(*_pecDataMutex); _pecData = std::move(pdata); } pcm_data_t getData() const { std::lock_guard lock(*_pecDataMutex); return _pecData; } void setType(MccDefaultPCMType type) { std::lock_guard lock(*_pecDataMutex); _pecData.type = type; } MccDefaultPCMType getType() const { std::lock_guard lock(*_pecDataMutex); return _pecData.type; } // The computed PEC quantities must be interpretated as: // apparent_X = encoder_X + pcm_result_t.dx // apparent_Y = encoder_Y + pcm_result_t.dy // 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) { std::lock_guard lock(*_pecDataMutex); if constexpr (mcc_is_equatorial_mount) { // equatorial if (_pecData.type == MccDefaultPCMType::PCM_TYPE_GEOMETRY) { const auto cosPhi = std::cos(_pecData.siteLatitude); const auto sinPhi = std::sin(_pecData.siteLatitude); const auto tanY = std::tan(pt.Y); const auto sinX = std::sin(pt.X); const auto cosX = std::cos(pt.X); const auto cosY = std::cos(pt.Y); if (utils::isEqual(cosY, 0.0)) { res.dx = _pecData.geomCoefficients.zeroPointX; } else { res.dx = _pecData.geomCoefficients.zeroPointX + _pecData.geomCoefficients.collimationErr / cosY + _pecData.geomCoefficients.nonperpendErr * tanY - _pecData.geomCoefficients.misalignErr1 * cosX * tanY + _pecData.geomCoefficients.misalignErr2 * sinX * tanY + _pecData.geomCoefficients.tubeFlexure * cosPhi * sinX / cosY - _pecData.geomCoefficients.DECaxisFlexure * (cosPhi * cosX + sinPhi * tanY); } res.dy = _pecData.geomCoefficients.zeroPointY + _pecData.geomCoefficients.misalignErr1 * sinX + _pecData.geomCoefficients.misalignErr2 * cosX + _pecData.geomCoefficients.tubeFlexure * (cosPhi * cosX * std::sin(pt.Y) - sinPhi * cosY); if constexpr (mountType == MccMountType::FORK_TYPE) { if (!utils::isEqual(cosX, 0.0)) { res.dy += _pecData.geomCoefficients.forkFlexure / cosX; } } } if (_pecData.type == MccDefaultPCMType::PCM_TYPE_BSPLINE || _pecData.type == MccDefaultPCMType::PCM_TYPE_GEOMETRY_BSPLINE) { double spl_valX, spl_valY; int ret = bsplines::fitpack_eval_spl2d(_pecData.bspline.knotsX, _pecData.bspline.knotsY, _pecData.bspline.coeffsX, pt.X, pt.Y, spl_valX, _pecData.bspline.bsplDegreeX, _pecData.bspline.bsplDegreeY); if (ret) { res.dx = std::numeric_limits::quiet_NaN(); res.dy = std::numeric_limits::quiet_NaN(); return MccDefaultPCMErrorCode::ERROR_INVALID_INPUTS_BISPLEV; } ret = bsplines::fitpack_eval_spl2d(_pecData.bspline.knotsX, _pecData.bspline.knotsY, _pecData.bspline.coeffsY, pt.X, pt.Y, spl_valY, _pecData.bspline.bsplDegreeX, _pecData.bspline.bsplDegreeY); if (ret) { res.dx = std::numeric_limits::quiet_NaN(); res.dy = std::numeric_limits::quiet_NaN(); return MccDefaultPCMErrorCode::ERROR_INVALID_INPUTS_BISPLEV; } res.dx += spl_valX; res.dy += spl_valY; } } else if constexpr (mcc_is_altaz_mount) { static_assert(false, "NOT IMPLEMENTED!"); } else { static_assert(false, "UNSUPPORTED"); } return MccDefaultPCMErrorCode::ERROR_OK; } private: pcm_data_t _pecData; std::unique_ptr _pecDataMutex; }; typedef MccDefaultPCM MccMountDefaultAltAzPec; typedef MccDefaultPCM MccMountDefaultForkPec; static_assert(mcc_PCM_c, ""); static_assert(std::movable); } // namespace mcc