#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* MCC LIBRARY DEFAULT IMPLEMENTATION OF SOME CLASSES */ // #include #include "mcc_generics.h" namespace mcc { /* DEFAULT TIME-RELATED CLASSES */ typedef std::chrono::system_clock::time_point MccTimePoint; typedef std::chrono::duration MccTimeDuration; // seconds as floating-point value template static constexpr DT mcc_infinite_duration_v = std::floating_point ? DT{std::numeric_limits::infinity()} : DT{std::numeric_limits::max()}; /* DEFAULT JULIAN DAY CLASS */ struct MccJulianDay { static constexpr double MJD0 = 2400000.5; MccJulianDay() = default; MccJulianDay(double jd) : mjd(jd - MJD0) {} constexpr operator double() const { return MccJulianDay::MJD0 + mjd; } MccJulianDay& operator=(double jd) { mjd = jd - MJD0; return *this; } double MJD() const { return mjd; } constexpr auto operator<=>(const MccJulianDay&) const = default; constexpr auto operator<=>(double v) const { return v <=> (MccJulianDay::MJD0 + mjd); }; protected: double mjd{51544.5}; // J2000.0 }; /* DEFAULT CELESTIAL COORDINATES EPOCH CLASS */ class MccCelestialCoordEpoch : public mcc_coord_epoch_interface_t { inline static const std::regex dateTimeISO8601Rx{ " *[0-9]{4}-[0,1][0-9]-[0-2][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](\\.[0-9]+)? *"}; inline static const std::regex dateTimeJEpochRx{" *J[0-9]{4}(\\.[0-9]{1,})+ *"}; inline static const std::regex dateTimeBEpochRx{" *B[0-9]{4}(\\.[0-9]{1,})+ *"}; inline static const std::regex dateTimeMJDRx{" *([0-9]*[.])?[0-9]+([eE][-+]?[0-9]+)? *"}; typedef std::chrono::duration> year_fp_t; typedef std::chrono::duration> day_fp_t; public: static constexpr auto J2000_UTC = std::chrono::sys_days( std::chrono::year_month_day(std::chrono::January / std::chrono::day(1) / std::chrono::year(2000))) + std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816); static constexpr double J2000_MJD = 51544.5; static MccCelestialCoordEpoch now() { MccCelestialCoordEpoch ep; ep.fromTimePoint(std::chrono::system_clock::now()); return ep; } MccCelestialCoordEpoch() : _UTC(J2000_UTC), _MJD(J2000_MJD), _JEpoch(2000.0) {} template bool fromCharRange(IR&& str) { if constexpr (std::is_pointer_v>) { return fromCharRange(std::string_view{str}); } bool ret = false; std::string_view sv = utils::trimSpaces(std::forward(str)); std::istringstream ist{std::string(sv)}; // try ISO8601 date ... std::chrono::from_stream(ist, "%FT%T", _UTC); if (ist.fail()) { // not ISO8601 date // try MJD (floating-point number) ... std::optional mjd = utils::numFromStr(sv); if (mjd) { _MJD = mjd.value(); ret = fromMJD(); } else { // not MJD // try epoch (e.g. J2010.32) if (sv[0] == 'J') { auto jep = utils::numFromStr(sv.substr(1)); if (jep) { _JEpoch = jep.value(); ret = fromJEpoch(); } else { // ERROR!!! ret = false; } } else { // ERROR!!! ret = false; } } } else { ret = fromUTC(); } return ret; } template bool fromTimePoint(std::chrono::time_point&& tp) { if constexpr (std::same_as) { _UTC = std::chrono::time_point_cast(std::forward(tp)); } else if constexpr (std::same_as) { auto stp = std::chrono::utc_clock::to_sys(std::forward(tp)); _UTC = std::chrono::time_point_cast(std::forward(stp)); } else if constexpr (std::same_as) { return fromTimePoint(ClockT::to_utc(std::forward(tp))); } else if constexpr (std::same_as) { return fromTimePoint(ClockT::to_utc(std::forward(tp))); } else { static_assert(false, "UNSUPPORTED CLOCK!!!"); } return fromUTC(); } template bool fromMJD(VT&& mjd) requires std::is_arithmetic_v { _MJD = static_cast(std::forward(mjd)); return fromMJD(); } template MccCelestialCoordEpoch& operator+=(DT&& dt) { _UTC += std::chrono::duration_cast(std::forward
(dt)); _MJD += std::chrono::duration_cast(std::forward
(dt)).count(); _JEpoch += std::chrono::duration_cast(std::forward
(dt)).count(); return *this; } template MccCelestialCoordEpoch& operator-=(DT&& dt) { _UTC -= std::chrono::duration_cast(std::forward
(dt)); _MJD -= std::chrono::duration_cast(std::forward
(dt)).count(); _JEpoch -= std::chrono::duration_cast(std::forward
(dt)).count(); return *this; } template friend MccCelestialCoordEpoch operator+(const MccCelestialCoordEpoch& lhs, const DT& dt) { MccCelestialCoordEpoch ep = lhs; ep += dt; return ep; } template friend MccCelestialCoordEpoch operator+(const DT& dt, const MccCelestialCoordEpoch& rhs) { return rhs + dt; } template friend MccCelestialCoordEpoch operator-(const MccCelestialCoordEpoch& lhs, const DT& dt) { MccCelestialCoordEpoch ep = lhs; ep -= dt; return ep; } friend auto operator-(const MccCelestialCoordEpoch& lhs, const MccCelestialCoordEpoch& rhs) { return lhs._UTC - rhs._UTC; } template requires std::is_arithmetic_v VT MJD() const { return _MJD; } double MJD() const { return _MJD; } template std::chrono::sys_time
UTC() const { return std::chrono::time_point_cast
(_UTC); } std::chrono::system_clock::time_point UTC() const { return _UTC; } template R JEpoch(uint8_t prec = 0) const { std::string prec_str{"J{:"}; if (prec > 0) { prec_str += "."; prec_str += std::to_string(prec); } prec_str += "f}"; std::string res = std::vformat(std::string_view{prec_str}, std::make_format_args(_JEpoch)); if constexpr (std::same_as) { return res; } R r; std::ranges::copy(res, std::back_inserter(r)); return r; } std::string JEpoch(uint8_t prec = 0) const { return JEpoch(prec); } auto operator<=>(const MccCelestialCoordEpoch& rhs) const { return _UTC <=> rhs._UTC; } auto operator==(const MccCelestialCoordEpoch& rhs) const { return _UTC == rhs._UTC; } protected: std::chrono::system_clock::time_point _UTC; double _MJD; double _JEpoch; bool fromUTC() { // modified Julian date (based on ERFA eraCal2jd) auto dd = std::chrono::floor(_UTC); std::chrono::year_month_day ymd{dd}; static constexpr std::chrono::year MIN_YEAR{-4799}; if (ymd.year() < MIN_YEAR) { return false; } if (!ymd.month().ok()) { return false; } int64_t im = (unsigned)ymd.month(); int64_t id = (unsigned)ymd.day(); int64_t iy = (int)ymd.year(); int64_t my = (im - 14LL) / 12LL; int64_t iypmy = iy + my; // integer part of result MJD int64_t mjd_int = (1461LL * (iypmy + 4800LL)) / 4LL + (367LL * (im - 2LL - 12LL * my)) / 12LL - (3LL * ((iypmy + 4900LL) / 100LL)) / 4LL + id - 2432076LL; _MJD = static_cast(mjd_int) + std::chrono::duration_cast(_UTC - dd).count(); _JEpoch = std::chrono::duration_cast(_UTC - J2000_UTC).count() + 2000.0; return true; } bool fromMJD(bool only_time_point = false) { // Gregorian date from modified Julian date (based on ERFS eraJd2cal) double f1 = -0.5; long jd = std::round(_MJD); double f2 = _MJD - jd; jd += 2400001.0; double s = 0.5, cs = 0.0; // double v[] = {f1, f2}; auto lmd = [&](double v) { // double x = v; // double t = s + x; // cs += std::fabs(s) >= std::fabs(x) ? (s - t) + x : (x - t) + s; double t = s + v; cs += std::fabs(s) >= std::fabs(v) ? (s - t) + v : (v - t) + s; s = t; if (s >= 1.0) { jd++; s -= 1.0; } }; lmd(f1); lmd(f2); double f = s + cs; cs = f - s; if (f < 0.0) { f = s + 1.0; cs += (1.0 - f) + s; s = f; f = s + cs; cs = f - s; jd--; } if ((f - 1.0) >= -std::numeric_limits::epsilon() / 4.0) { /* Compensated summation: assume that |s| <= 1.0. */ double t = s - 1.0; cs += (s - t) - 1.0; s = t; f = s + cs; if (-std::numeric_limits::epsilon() / 2.0 < f) { jd++; f = std::max(f, 0.0); } } long l = jd + 68569L; long n = (4L * l) / 146097L; l -= (146097L * n + 3L) / 4L; long i = (4000L * (l + 1L)) / 1461001L; l -= (1461L * i) / 4L - 31L; long k = (80L * l) / 2447L; auto day = std::chrono::day(l - (2447L * k) / 80L); l = k / 11L; auto month = std::chrono::month(k + 2L - 12L * l); auto year = std::chrono::year(100L * (n - 49L) + i + l); auto day_frac = day_fp_t(f); // _UTC = years + month + days + day_frac; _UTC = std::chrono::sys_days(std::chrono::year_month_day(month / day / year)); _UTC += std::chrono::duration_cast(day_frac); if (!only_time_point) { _JEpoch = 2000.0 + (_MJD - J2000_MJD) / 365.25; } return true; } bool fromJEpoch() { _MJD = J2000_MJD + (_JEpoch - 2000.0) * 365.25; return fromMJD(true); } }; static_assert(mcc_coord_epoch_c, "!!!"); /* DEFAULT CELESTIAL POINT CLASS */ template struct MccGenericCelestialPoint { typedef CoordT coord_t; MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; // MccTimePoint time_point{std::chrono::sys_days(std::chrono::year_month_day{std::chrono::January / 1 / 2000}) + // std::chrono::hours(12)}; // J2000.0 MccTimePoint time_point{std::chrono::sys_days(std::chrono::year_month_day( std::chrono::January / std::chrono::day(1) / std::chrono::year(2000))) + std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816)}; // J2000.0 UTC coord_t X{}, Y{}; }; typedef MccGenericCelestialPoint MccCelestialPoint; template struct MccGenericEqtHrzCoords : MccGenericCelestialPoint { using typename MccGenericCelestialPoint::coord_t; using MccGenericCelestialPoint::time_point; coord_t RA_APP{}, DEC_APP{}, HA{}, AZ{}, ZD{}, ALT{}; }; typedef MccGenericEqtHrzCoords MccEqtHrzCoords; template struct MccGenericPointingTarget : MccGenericEqtHrzCoords { using typename MccGenericEqtHrzCoords::coord_t; coord_t RA_ICRS{}, DEC_ICRS{}; }; typedef MccGenericPointingTarget MccPointingTarget; template struct MccGenericPCMResult { CoordT pcmX, pcmY; }; typedef MccGenericPCMResult MccPCMResult; /* DEFAULT TELEMETRY DATA CLASS */ template struct MccGenericTelemetryData : MccGenericEqtHrzCoords { using typename MccGenericEqtHrzCoords::coord_t; MccJulianDay JD; coord_t LST; // local apparent sideral time MccGenericPointingTarget target{}; coord_t speedX, speedY; coord_t pcmX, pcmY; coord_t refCorr; coord_t EO; }; typedef MccGenericTelemetryData MccTelemetryData; template struct MccPositionControls : CCTE_T, HARDWARE_T, PCM_T { virtual ~MccPositionControls() = default; }; /* JUST CHECK FOR CONCEPT CONSISTENCY */ static_assert(mcc_julday_c, ""); static_assert(mcc_celestial_point_c, ""); static_assert(mcc_pointing_target_coord_c>, ""); static_assert(mcc_telemetry_data_c, ""); /* DEFAULT SERIALIZER/DESERIALIZER FOR COORDINATE-RELATED CLASSES */ enum class MccCoordinateConvErrorCode : int { ERROR_OK, ERROR_ARG_LEN, ERROR_NONNUM_ARG, ERROR_NONSGM_ARG, ERROR_INVALID_CPAIR, ERROR_TIMEPOINT, ERROR_INVALID_COORD_FMT, ERROR_INVALID_COORD_PREC }; class MccCoordinateConvErrorCategory : public std::error_category { public: const char* name() const noexcept { return "MCC-COORD-CONV"; } std::string message(int ec) const { MccCoordinateConvErrorCode err = static_cast(ec); switch (err) { case MccCoordinateConvErrorCode::ERROR_OK: return "OK"; case MccCoordinateConvErrorCode::ERROR_ARG_LEN: return "invalid argument length or number of argument elements"; case MccCoordinateConvErrorCode::ERROR_NONNUM_ARG: return "non-numeric or out-of-range argument"; case MccCoordinateConvErrorCode::ERROR_NONSGM_ARG: return "non-sexagesimal argument"; case MccCoordinateConvErrorCode::ERROR_INVALID_CPAIR: return "invalid coordinate pair name"; case MccCoordinateConvErrorCode::ERROR_TIMEPOINT: return "invalid time point"; case MccCoordinateConvErrorCode::ERROR_INVALID_COORD_FMT: return "invalid coordinate formatter string"; case MccCoordinateConvErrorCode::ERROR_INVALID_COORD_PREC: return "invalid coordinate formatter precision string"; defaut: return "unknown"; }; return "unknown"; } static const MccCoordinateConvErrorCategory& get() { static const MccCoordinateConvErrorCategory constInst; return constInst; } }; } // namespace mcc namespace std { template <> class is_error_code_enum : public true_type { }; } // namespace std namespace mcc { inline std::error_code make_error_code(mcc::MccCoordinateConvErrorCode ec) { return std::error_code(static_cast(ec), mcc::MccCoordinateConvErrorCategory::get()); } static constexpr std::string_view MccCoordinateDefaultDelimiter{","}; // base class class MccCoordinateDeserializer { public: MccCoordinateDeserializer() = default; template MccCoordinateDeserializer(R&& delim) : MccCoordinateDeserializer() { setDelimiter(std::forward(delim)); } virtual ~MccCoordinateDeserializer() = default; template void setDelimiter(R&& delim) { if constexpr (std::is_pointer_v>) { setDelimiter(std::string_view(delim)); } else { _delimiter.clear(); std::ranges::copy(std::forward(delim), std::back_inserter(_delimiter)); } } template SelfT, traits::mcc_input_char_range IR, typename VT> std::error_code operator()(this SelfT&& self, IR&& bytes, VT& value) { if constexpr (std::same_as, MccCoordinateDeserializer>) { static_assert(false, "!!!!!"); } else { return std::forward(self)(std::forward(bytes), value); } } protected: std::string _delimiter{MccCoordinateDefaultDelimiter}; template std::vector splitToElements(IR&& bytes) { std::vector res; auto els = std::views::split(std::forward(bytes), _delimiter); for (auto const& el : els) { std::back_inserter(res) = std::string_view(el.begin(), el.end()); } return res; } template std::error_code parseTimePoint(const IR& bytes, T& cp) { MccCelestialCoordEpoch cep; bool ok = cep.fromCharRange(bytes); if (!ok) { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_TIMEPOINT; } mcc_tp2tp(cep.UTC(), cp.time_point); return {}; } template std::error_code parseHourRepr(const IR& bytes, double& ang_value) { std::optional ang; ang = mcc::utils::parsAngleString(bytes, true); if (ang) { ang_value = MccAngle(ang.value(), MccDegreeTag{}); } else { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_NONNUM_ARG; } return {}; } template std::error_code parseDegreeRepr(const IR& bytes, double& ang_value) { std::optional ang; ang = mcc::utils::parsAngleString(bytes); if (ang) { ang_value = MccAngle(ang.value(), MccDegreeTag{}); } else { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_NONNUM_ARG; } return {}; } }; // base class class MccCoordinateSerializer { public: enum SerializedCoordFormat { CFMT_DEGREES, // floating-point representation in degrees CFMT_SGM // sexagesimal representation: HH:MM:SS.SSSSSS (hours:minutes:seconds) or DD:MM:SS.SSSSS // (degrees:arcmins::arcsecs) }; struct SexagesimalCoordPrec { uint8_t hour_prec = 2; // number of decimal places in hour seconds uint8_t deg_prec = 1; // number of decimal places in arcseconds }; constexpr MccCoordinateSerializer() = default; constexpr MccCoordinateSerializer(SerializedCoordFormat fmt, SexagesimalCoordPrec prec) { setFormat(fmt); setPrecision(std::move(prec)); } virtual ~MccCoordinateSerializer() = default; void setFormat(SerializedCoordFormat fmt) { _currentFormat = fmt; } void setPrecision(SexagesimalCoordPrec prec) { _currentPrec = std::move(prec); } template void setDelimiter(R&& delim) { if constexpr (std::is_pointer_v>) { setDelimiter(std::string_view(delim)); } else { _delimiter.clear(); std::ranges::copy(std::forward(delim), std::back_inserter(_delimiter)); } } template SelfT, typename T, traits::mcc_output_char_range OR> void operator()(this SelfT&& self, const T& value, OR& bytes) { if constexpr (std::same_as, MccCoordinateSerializer>) { static_assert(false, "!!!!!"); } else { std::forward(self)(value, bytes); } } protected: SerializedCoordFormat _currentFormat{CFMT_DEGREES}; SexagesimalCoordPrec _currentPrec{.hour_prec = 2, .deg_prec = 1}; std::string _delimiter{MccCoordinateDefaultDelimiter}; template void serializePairKindTimePoint(const T& value, OR& bytes) { std::format_to(std::back_inserter(bytes), "{0:}{1:}{2:%F}T{2:%T}", MccCoordPairKindToStr(value.pair_kind), _delimiter, value.time_point); } template ... Ts> void toDegrees(OR& bytes, double angle, const Ts&... angles) { std::format_to(std::back_inserter(bytes), "{}", MccAngle(angle).degrees()); if constexpr (sizeof...(Ts)) { std::format_to(std::back_inserter(bytes), "{}", _delimiter); toDegrees(bytes, angles...); } } template ... Ts> void toSexagesimalHour(OR& bytes, double angle, const Ts&... angles) { std::format_to(std::back_inserter(bytes), "{}", MccAngle(angle).sexagesimal(true, _currentPrec.hour_prec)); if constexpr (sizeof...(Ts)) { std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalHour(bytes, angles...); } } template ... Ts> void toSexagesimalDeg(OR& bytes, double angle, const Ts&... angles) { std::format_to(std::back_inserter(bytes), "{}", MccAngle(angle).sexagesimal(false, _currentPrec.deg_prec)); if constexpr (sizeof...(Ts)) { std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalDeg(bytes, angles...); } } }; class MccCelestialPointSerializer : public MccCoordinateSerializer { public: using MccCoordinateSerializer::MccCoordinateSerializer; template void operator()(const T& value, OR& bytes) { if (_currentFormat == SerializedCoordFormat::CFMT_DEGREES) { // std::format_to(std::back_inserter(bytes), "{}{}{}", MccAngle(value.X).degrees(), _delimiter, // MccAngle(value.Y).degrees()); toDegrees(bytes, value.X, value.Y); } else { switch (value.pair_kind) { case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: case MccCoordPairKind::COORDS_KIND_RADEC_APP: case MccCoordPairKind::COORDS_KIND_HADEC_APP: // std::format_to(std::back_inserter(bytes), "{}{}", // MccAngle(value.X).sexagesimal(true, _currentPrec.hour_prec), _delimiter); toSexagesimalHour(bytes, value.X); break; default: // std::format_to(std::back_inserter(bytes), "{}{}", // MccAngle(value.X).sexagesimal(false, _currentPrec.deg_prec), _delimiter); toSexagesimalDeg(bytes, value.X); } // std::format_to(std::back_inserter(bytes), "{}", // MccAngle(value.Y).sexagesimal(false, _currentPrec.deg_prec)); std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalDeg(bytes, value.Y); } std::format_to(std::back_inserter(bytes), "{}", _delimiter); serializePairKindTimePoint(value, bytes); } }; class MccCelestialPointDeserializer : public MccCoordinateDeserializer { public: using MccCoordinateDeserializer::MccCoordinateDeserializer; template std::error_code operator()(IR&& bytes, T& value) { auto els = splitToElements(std::forward(bytes)); if (els.size() < 2) { // at least coordinate pair must be given // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_ARG_LEN; } MccCelestialPoint pt{.pair_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; if (els.size() > 2) { // pair of coordinates and the pair kind pt.pair_kind = MccCoordStrToPairKind(els[2]); if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_INVALID_CPAIR; } } if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // J2000.0: 11:58:55.816 1 January 2000 UTC auto tp = std::chrono::sys_days(std::chrono::year_month_day(std::chrono::January / std::chrono::day(1) / std::chrono::year(2000))) + std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816); mcc_tp2tp(tp, pt.time_point); } else { if (els.size() > 3) { // coordinates epoch is given auto err = parseTimePoint(els[3], pt); if (err) { return err; } } else { // no time point - use NOW mcc_tp2tp(std::chrono::system_clock::now(), pt.time_point); } } std::optional ang1, ang2; switch (pt.pair_kind) { // if sexagesimal then hours:minutes:seconds form case mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS: case mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP: case mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP: ang1 = mcc::utils::parsAngleString(els[0], true); break; default: // if sexagesimal then degrees:arcminutes:arcseconds form ang1 = mcc::utils::parsAngleString(els[0]); } if (!ang1) { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_NONNUM_ARG; } ang2 = mcc::utils::parsAngleString(els[1]); if (!ang2) { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_NONNUM_ARG; } pt.X = MccAngle(ang1.value(), mcc::MccDegreeTag{}); pt.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{}); mcc_copy_celestial_point(pt, &value); return {}; } }; class MccEqtHrzCoordsSerializer : public MccCoordinateSerializer { public: using MccCoordinateSerializer::MccCoordinateSerializer; template void operator()(const T& value, OR& bytes) { // output format: RA, DEC, HA, AZ, ZD, ALT, X, Y, pair-kind, time-point // in the case of sexagesimal output X,Y coordinates will be interpretated // according to value.pair_kind field if (_currentFormat == SerializedCoordFormat::CFMT_DEGREES) { toDegrees(bytes, value.RA_APP, value.DEC_APP, value.HA, value.AZ, value.ZD, value.ALT, value.X, value.Y); } else if (_currentFormat == SerializedCoordFormat::CFMT_SGM) { toSexagesimalHour(bytes, value.RA_APP); std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalDeg(bytes, value.DEC_APP); std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalHour(bytes, value.HA); std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalDeg(bytes, value.AZ, value.ZD, value.ALT); std::format_to(std::back_inserter(bytes), "{}", _delimiter); switch (value.pair_kind) { case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: case MccCoordPairKind::COORDS_KIND_RADEC_APP: case MccCoordPairKind::COORDS_KIND_HADEC_APP: toSexagesimalHour(bytes, value.X); break; default: toSexagesimalDeg(bytes, value.X); } std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalDeg(bytes, value.Y); } else { // !!!!! } std::format_to(std::back_inserter(bytes), "{}", _delimiter); serializePairKindTimePoint(value, bytes); } }; class MccEqtHrzCoordsDeserializer : public MccCoordinateDeserializer { public: using MccCoordinateDeserializer::MccCoordinateDeserializer; template std::error_code operator()(IR&& bytes, T& value) { // valid format: RA, DEC, HA, AZ, ZD, ALT, X, Y, pair-kind, time-point // in the case of sexagesimal input the X,Y coordinates will be interpretated // according to value.pair_kind field auto els = splitToElements(std::forward(bytes)); if (els.size() < 10) { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_ARG_LEN; } MccEqtHrzCoords pt; pt.pair_kind = MccCoordStrToPairKind(els[8]); if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_INVALID_CPAIR; } auto err = parseTimePoint(els[9], pt); if (err) { return err; } err = parseHourRepr(els[0], pt.RA_APP); if (err) { return err; } err = parseDegreeRepr(els[1], pt.DEC_APP); if (err) { return err; } err = parseHourRepr(els[2], pt.HA); if (err) { return err; } err = parseDegreeRepr(els[4], pt.AZ); if (err) { return err; } err = parseDegreeRepr(els[5], pt.ZD); if (err) { return err; } err = parseDegreeRepr(els[6], pt.ALT); if (err) { return err; } switch (value.pair_kind) { case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: case MccCoordPairKind::COORDS_KIND_RADEC_APP: case MccCoordPairKind::COORDS_KIND_HADEC_APP: err = parseHourRepr(els[7], pt.X); break; default: err = parseDegreeRepr(els[7], pt.X); } if (err) { return err; } err = parseDegreeRepr(els[8], pt.Y); if (err) { return err; } mcc_copy_eqt_hrz_coord(pt, &value); return {}; } }; class MccPointingTargetSerializer : public MccCoordinateSerializer { public: template void operator()(const T& value, OR& bytes) { static MccEqtHrzCoordsSerializer eqhrz_ser; // output format: RA_ICRS, DEC_ICRS, RA_APP, DEC_APP, HA, AZ, ZD, ALT, X, Y, pair-kind, time-point // in the case of sexagesimal output X,Y coordinates will be interpretated // according to value.pair_kind field if (_currentFormat == SerializedCoordFormat::CFMT_DEGREES) { toDegrees(bytes, value.RA_ICRS, value.DEC_ICRS); } else if (_currentFormat == SerializedCoordFormat::CFMT_SGM) { toSexagesimalHour(bytes, value.RA_ICRS); std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalDeg(bytes, value.DEC_ICRS); } std::format_to(std::back_inserter(bytes), "{}", _delimiter); eqhrz_ser.setFormat(_currentFormat); eqhrz_ser.setPrecision(_currentPrec); eqhrz_ser(value, bytes); // MccEqtHrzCoordsSerializer{}(value, bytes); } }; class MccPointingTargetDeserializer : public MccCoordinateDeserializer { public: template std::error_code operator()(IR&& bytes, T& value) { // valid format: RA_ICRS, DEC_ICRS, RA_APP, DEC_APP, HA, AZ, ZD, ALT, X, Y, pair-kind, time-point // in the case of sexagesimal input the X,Y coordinates will be interpretated // according to value.pair_kind field auto els = splitToElements(std::forward(bytes)); if (els.size() < 12) { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_ARG_LEN; } MccPointingTarget pt; auto err = parseHourRepr(els[0], pt.RA_ICRS); if (err) { return err; } err = parseDegreeRepr(els[1], pt.DEC_ICRS); if (err) { return err; } err = MccEqtHrzCoordsDeserializer{}(std::string_view{els[2].begin(), els[11].end()}, pt); if (err) { return err; } mcc_copy_pointing_target_coord(pt, &value); return {}; } }; class MccTelemetryDataSerializer : public MccCoordinateSerializer { public: template void operator()(const T& value, OR& bytes) { static MccEqtHrzCoordsSerializer eqhrz_ser; static MccPointingTargetSerializer pt_ser; // output format: , speedX, speedY, pcmX, pcmY, refCorr (in arcsecs), // RA-APP_mnt, DEC-APP_mnt, HA_mnt, AZ_mnt, ZD_mnt, ALT_mnt, X_mnt, Y_mnt, COO-PAIR_mnt, TIME-POINT_mnt, // LST, EO, SPEED_X_mnt, SPEED_Y_mnt, PCM_X, PCM_Y, REFCORR, // RA-ICRS_tag, DEC-ICRS_tag, RA-APP_tag, DEC-APP_tag, HA_tag, AZ_tag, ZD_tag, ALT_tag, X_tag, Y_tag, // COO-PAIR_tag, TIME-POINT_tag eqhrz_ser.setFormat(_currentFormat); eqhrz_ser.setPrecision(_currentPrec); eqhrz_ser(value, bytes); std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalHour(bytes, value.LST); std::format_to(std::back_inserter(bytes), "{}", _delimiter); toSexagesimalHour(bytes, value.EO); std::format_to(std::back_inserter(bytes), "{}", _delimiter); // '*3600.0' to express refraction correction in arcseconds! // toDegrees(bytes, value.speedX, value.speedY, value.pcmX, value.pcmY, value.refCorr * 3600.0); toSexagesimalDeg(bytes, value.speedX, value.speedY, value.pcmX, value.pcmY, value.refCorr); std::format_to(std::back_inserter(bytes), "{}", _delimiter); pt_ser.setFormat(_currentFormat); pt_ser.setPrecision(_currentPrec); pt_ser(value.target, bytes); } }; class MccTelemetryDataDeserializer : public MccCoordinateDeserializer { public: using MccCoordinateDeserializer::MccCoordinateDeserializer; template std::error_code operator()(IR&& bytes, T& value) { // valid format: , speedX, speedY, pcmX, pcmY, refCorr, auto els = splitToElements(std::forward(bytes)); if (els.size() < 29) { // return std::make_error_code(std::errc::invalid_argument); return MccCoordinateConvErrorCode::ERROR_ARG_LEN; } MccTelemetryData tdata; auto err = MccEqtHrzCoordsDeserializer{}(std::string_view{els[0].begin(), els[9].end()}, tdata); if (err) { return err; } size_t idx = 10; err = parseHourRepr(els[idx++], tdata.LST); if (err) { return err; } err = parseHourRepr(els[idx++], tdata.EO); if (err) { return err; } err = parseDegreeRepr(els[idx++], tdata.speedX); if (err) { return err; } err = parseDegreeRepr(els[idx++], tdata.speedY); if (err) { return err; } err = parseDegreeRepr(els[idx++], tdata.pcmX); if (err) { return err; } err = parseDegreeRepr(els[idx++], tdata.pcmY); if (err) { return err; } err = parseDegreeRepr(els[idx++], tdata.refCorr); if (err) { return err; } err = MccPointingTargetDeserializer{}(std::string_view{els[idx].begin(), els.back().end()}, tdata.target); if (err) { return err; } mcc_copy_telemetry_data(tdata, &value); return {}; } }; } // namespace mcc