From 47c57dca72c275386105be7153a5ae11ceb8cb18 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Fri, 24 Oct 2025 12:16:44 +0300 Subject: [PATCH] ... --- mcc/mcc_defaults.h | 279 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) diff --git a/mcc/mcc_defaults.h b/mcc/mcc_defaults.h index 2e1eb92..0893c60 100644 --- a/mcc/mcc_defaults.h +++ b/mcc/mcc_defaults.h @@ -504,4 +504,283 @@ static_assert(mcc_celestial_point_c, ""); static_assert(mcc_telemetry_data_c, ""); +/* DEFAULT SERIALIZER/DESERIALIZER FOR COORDINATE-RELATED CLASSES */ + + +// base class +class MccCoordinateDeserializer +{ +public: + 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{","}; + + 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); + } + + mcc_tp2tp(cep.UTC(), cp.time_point); + + 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 + }; + + + MccCoordinateSerializer() = default; + + 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{","}; + + template + void serializePairKindTimePoint(const T& value, OR& bytes) + { + std::format_to(std::back_inserter(bytes), "{0:}{1:}{2:}{3:%F}T{3:%T}", _delimiter, + 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); + toSexagesimalHour(bytes, angles...); + } + } +}; + + +class MccCelestialPointSerializer : public MccCoordinateSerializer +{ +public: + 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); + } + + serializePairKindTimePoint(value, bytes); + } +}; + + +class MccCelestialPointDeserializer : public MccCoordinateDeserializer +{ +public: + 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); + } + + 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); + } + } + + 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); + } + + ang2 = mcc::utils::parsAngleString(els[1]); + if (!ang2) { + return std::make_error_code(std::errc::invalid_argument); + } + + + pt.X = MccAngle(ang1.value(), mcc::MccDegreeTag{}); + pt.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{}); + + mcc_copy_celestial_point(pt, &value); + } +}; + + +class MccEqtHrzCoordsSerializer : public MccCoordinateSerializer +{ +}; + +class MccEqtHrzCoordsDeserializer : public MccCoordinateDeserializer +{ +}; + } // namespace mcc