From 5b4456d6ef5d06d119811822ba3fc0a83f78eee3 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Mon, 2 Feb 2026 19:43:18 +0300 Subject: [PATCH] ... --- mcc_constants.h | 3 + mcc_deserializer.h | 6 +- mcc_serialization_common.h | 312 +++++++++++++++++++++++++++++++++++++ tests/mcc_coord_test.cpp | 33 +++- 4 files changed, 350 insertions(+), 4 deletions(-) create mode 100644 mcc_serialization_common.h diff --git a/mcc_constants.h b/mcc_constants.h index 8645771..bc62a65 100644 --- a/mcc_constants.h +++ b/mcc_constants.h @@ -18,6 +18,9 @@ namespace mcc { +constexpr double MCC_DEGRESS_TO_RADS = std::numbers::pi / 180.0; +constexpr double MCC_RADS_TO_DEGRESS = 1.0 / MCC_DEGRESS_TO_RADS; + constexpr double MCC_HALF_PI = std::numbers::pi / 2.0; constexpr double MCC_TWO_PI = std::numbers::pi * 2.0; diff --git a/mcc_deserializer.h b/mcc_deserializer.h index d2fc59f..b7c71c1 100644 --- a/mcc_deserializer.h +++ b/mcc_deserializer.h @@ -198,7 +198,7 @@ public: error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) { if constexpr (std::is_arithmetic_v) { - auto v = mcc::utils::numFromStr(trimSpaces(input)); + auto v = mcc::utils::numFromStr(utils::trimSpaces(input)); if (!v.has_value()) { return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; } @@ -302,8 +302,8 @@ public: { if constexpr (mcc_angle_c) { // scalar auto res = utils::parsAngleString(input, hms); - if (res) { - value = res.value(); + if (res) { // returned angle is in degrees!!! + value = res.value() * MCC_DEGRESS_TO_RADS; } else { return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; } diff --git a/mcc_serialization_common.h b/mcc_serialization_common.h new file mode 100644 index 0000000..42ab720 --- /dev/null +++ b/mcc_serialization_common.h @@ -0,0 +1,312 @@ +#pragma once + + +/**************************************************************************************** + * * + * MOUNT CONTROL COMPONENTS LIBRARY * + * * + * * + * COMMON DEFINITIONS FOR DATA SERIALIZATION/DESERIALIZATION * + * * + ****************************************************************************************/ + +#include "mcc_concepts.h" +#include "mcc_error.h" +#include "mcc_traits.h" + +namespace mcc +{ + +enum class MccSerializedCoordPairFormat { + MCC_SERIALIZED_FORMAT_DEGREES, // both angles are in degrees as floating-point number + MCC_SERIALIZED_FORMAT_SXGM_HOURDEG, // X is in hour and Y is in degree sexagesimal representation + MCC_SERIALIZED_FORMAT_SXGM_DEGDEG, // both angles are in sexagesimal degrees + MCC_SERIALIZED_FORMAT_UNKNOWN +}; + +static constexpr std::string_view MCC_SERIALIZED_FORMAT_DEGREES_STR = "SRCP-FORMAT-DEGREES"; +static constexpr std::string_view MCC_SERIALIZED_FORMAT_SXGM_HOURDEG_STR = "SRCP-FORMAT-SXGM_HOURDEG"; +static constexpr std::string_view MCC_SERIALIZED_FORMAT_SXGM_DEGDEG_STR = "SRCP-FORMAT-SXGM_DEGDEG"; + + +static constexpr std::string_view MccSerializedCoordPairFormatToStr(MccSerializedCoordPairFormat fmt) +{ + return fmt == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_DEGREES ? MCC_SERIALIZED_FORMAT_DEGREES_STR + : fmt == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG + ? MCC_SERIALIZED_FORMAT_SXGM_HOURDEG_STR + : fmt == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGDEG + ? MCC_SERIALIZED_FORMAT_SXGM_DEGDEG_STR + : MCC_SERIALIZED_FORMAT_DEGREES_STR; +} + +template +static constexpr MccSerializedCoordPairFormat MccSerializedCoordPairFormatStrToValue(R&& str) +{ + if constexpr (std::is_array_v>) { + return MccSerializedCoordPairFormatStrToValue(std::string_view{str}); + } + + const auto hash = mcc::utils::FNV1aHash(std::forward(str)); + + return hash == mcc::utils::FNV1aHash(MCC_SERIALIZED_FORMAT_DEGREES_STR) + ? MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_DEGREES + : hash == mcc::utils::FNV1aHash(MCC_SERIALIZED_FORMAT_SXGM_HOURDEG_STR) + ? MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG + : hash == mcc::utils::FNV1aHash(MCC_SERIALIZED_FORMAT_SXGM_DEGDEG_STR) + ? MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGDEG + : MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_UNKNOWN; +} + + +struct MccSerializedCoordPairFormatPrec { + uint8_t hour_prec = 2; // number of decimal places in hour seconds (sexagesimal format) + uint8_t deg_prec = 1; // number of decimal places in arcseconds (sexagesimal format) + // slightly better than 0.1 arcsecond precision + uint8_t decimals = 6; // number of decimal places in degrees (floating-point number format) + // if 0, then number of decimal places is according to formating rules of 'double' type +}; + +enum class MccTimePointFormat { + MCC_TIMEPOINT_FORMAT_DATE, // UTC date + MCC_TIMEPOINT_FORMAT_MJD, // MJD + MCC_TIMEPOINT_FORMAT_JD, // JD + MCC_TIMEPOINT_FORMAT_JEPOCH, // Julian epoch + MCC_TIMEPOINT_FORMAT_UNKNOWN +}; + +static constexpr std::string_view MCC_TIMEPOINT_FORMAT_DATE_STR = "TP-FORMAT-DATE"; +static constexpr std::string_view MCC_TIMEPOINT_FORMAT_MJD_STR = "TP-FORMAT-MJD"; +static constexpr std::string_view MCC_TIMEPOINT_FORMAT_JD_STR = "TP-FORMAT-JD"; +static constexpr std::string_view MCC_TIMEPOINT_FORMAT_JEPOCH_STR = "TP-FORMAT-JEPOCH"; + +static constexpr std::string_view MccTimePointFormatToStr(MccTimePointFormat fmt) +{ + return fmt == MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE ? MCC_TIMEPOINT_FORMAT_DATE_STR + : fmt == MccTimePointFormat::MCC_TIMEPOINT_FORMAT_MJD ? MCC_TIMEPOINT_FORMAT_MJD_STR + : fmt == MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JD ? MCC_TIMEPOINT_FORMAT_JD_STR + : fmt == MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JEPOCH ? MCC_TIMEPOINT_FORMAT_JEPOCH_STR + : MCC_TIMEPOINT_FORMAT_MJD_STR; +} + +template +static constexpr MccTimePointFormat MccTimePointFormatStrToValue(R&& str) +{ + if constexpr (std::is_array_v>) { + return MccTimePointFormatStrToValue(std::string_view{str}); + } + + const auto hash = mcc::utils::FNV1aHash(std::forward(str)); + + return hash == mcc::utils::FNV1aHash(MCC_TIMEPOINT_FORMAT_DATE_STR) ? MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE + : hash == mcc::utils::FNV1aHash(MCC_TIMEPOINT_FORMAT_MJD_STR) ? MccTimePointFormat::MCC_TIMEPOINT_FORMAT_MJD + : hash == mcc::utils::FNV1aHash(MCC_TIMEPOINT_FORMAT_JD_STR) ? MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JD + : hash == mcc::utils::FNV1aHash(MCC_TIMEPOINT_FORMAT_JEPOCH_STR) + ? MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JEPOCH + : MccTimePointFormat::MCC_TIMEPOINT_FORMAT_UNKNOWN; +} + + +/* SERIALIZATION/DESERIALIZATION PROCESS TUNING PARAMETERS */ + +// delimiter between items of serializing values sequence +static constexpr std::string_view MCC_SERIALIZING_DEFAULT_SEQ_DELIMITER{";"}; + +// delimiter between items of aggregative (multi-element) serializing value +static constexpr std::string_view MCC_SERIALIZING_DEFAULT_ELEM_DELIMITER{","}; + + +template +concept mcc_serialization_params_c = requires(T t) { + requires traits::mcc_output_char_range; + requires traits::mcc_output_char_range; + + requires std::same_as; + requires std::same_as; + requires std::same_as; + + // if true - normalize angle in sexagesimal format (to control rounding) + // (to avoid something like "24:00:00.0" for sexagesimal 'hours:minutes:seconds' format) + requires std::convertible_to; + + // if true - interpretate serialized angle in sexagesimal format as 'hours:minutes:seconds' + // otherwise as 'degrees:arcmins:arcsecs' + requires std::convertible_to; +}; + + +/* SERIALIZER/DESERIALIZER CONCEPTS */ + +template +struct mcc_serializer_interface_t { + virtual ~mcc_serializer_interface_t() = default; + + typedef RetT error_t; + + template SelfT, traits::mcc_output_char_range R, typename ValueT> + RetT operator()(this SelfT&& self, R& output, ValueT const& value) + { + return std::forward(self)(output, value); + } + +protected: + mcc_serializer_interface_t() = default; +}; + + +template +concept mcc_serializer_c = + std::derived_from> && requires(T t, const T t_const) { + // static const variable with name of the serializer + requires std::formattable && std::is_const_v; + + // must define a type "params_t" + requires mcc_serialization_params_c; + + { t.setParams(std::declval()) }; + + { t_const.getParams() } -> std::same_as; + }; + + + +template +struct mcc_deserializer_interface_t { + virtual ~mcc_deserializer_interface_t() = default; + + typedef RetT error_t; + + template SelfT, traits::mcc_input_char_range R, typename ValueT> + RetT operator()(this SelfT&& self, R const& input, ValueT& value) + { + return std::forward(self)(input, value); + } + +protected: + mcc_deserializer_interface_t() = default; +}; + + +template +concept mcc_deserializer_c = + std::derived_from> && requires(T t, const T t_const) { + // static const variable with name of the deserializer + requires std::formattable && + std::is_const_v; + + // must define a type "params_t" + requires mcc_serialization_params_c; + + { t.setParams(std::declval()) }; + + { t_const.getParams() } -> std::same_as; + }; + + + +/* BASE CLASS IMPLEMENTATION FOR SERIALIZER/DESERIALIZER */ + +namespace impl +{ + +// default definition of serialization/deserialization process parameters structure +struct mcc_serialization_params_t { + std::string seq_delim{MCC_SERIALIZING_DEFAULT_SEQ_DELIMITER}; + std::string elem_delim{MCC_SERIALIZING_DEFAULT_ELEM_DELIMITER}; + + MccSerializedCoordPairFormat coordpair_format{MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG}; + + MccSerializedCoordPairFormatPrec coordpair_prec{ + MccSerializedCoordPairFormatPrec{.hour_prec = 2, .deg_prec = 1, .decimals = 6}}; + + MccTimePointFormat timepoint_format{MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE}; + + bool norm_sxgm{false}; + + bool sxgm_hms{false}; +}; + + +namespace details +{ + +template +struct _params_manipulator_t { + virtual ~_params_manipulator_t() = default; + + typedef ParamsT params_t; + + template + void setParams(p_t const& pars) + { + if constexpr (std::same_as) { + _params = pars; + } else { + _params.seq_delim = pars.seq_delim; + _params.elem_delim = pars.elem_delim; + _params.coordpair_format = pars.coordpair_format; + _params.coordpair_prec = pars.coordpair_prec; + _params.timepoint_format = pars.timepoint_format; + _params.norm_sxgm = pars.norm_sxgm; + _params.sxgm_hms = pars.sxgm_hms; + } + } + + template + requires(!std::same_as) + p_t getParams() const + { + p_t pars; + + pars.seq_delim = _params.seq_delim; + pars.elem_delim = _params.elem_delim; + pars.coordpair_format = _params.coordpair_format; + pars.coordpair_prec = _params.coordpair_prec; + pars.timepoint_format = _params.timepoint_format; + pars.norm_sxgm = _params.norm_sxgm; + pars.sxgm_hms = _params.sxgm_hms; + + return pars; + } + + + params_t getParams() const + { + return _params; + } + +protected: + _params_manipulator_t() = default; + + params_t _params; +}; + +} // namespace details + + +template +struct MccSerializerBase : mcc_serializer_interface_t, details::_params_manipulator_t { + virtual ~MccSerializerBase() = default; + + using typename mcc_serializer_interface_t::error_t; + + using typename details::_params_manipulator_t::params_t; + +protected: + MccSerializerBase() = default; +}; + +template +struct MccDeserializerBase : mcc_deserializer_interface_t, details::_params_manipulator_t { + virtual ~MccDeserializerBase() = default; + + using typename mcc_deserializer_interface_t::error_t; + + using typename details::_params_manipulator_t::params_t; + +protected: + MccDeserializerBase() = default; +}; + +} // namespace impl + +} // namespace mcc diff --git a/tests/mcc_coord_test.cpp b/tests/mcc_coord_test.cpp index ce8f82c..2ebed01 100644 --- a/tests/mcc_coord_test.cpp +++ b/tests/mcc_coord_test.cpp @@ -2,6 +2,7 @@ // #include #include +#include #include using namespace mcc::impl; @@ -28,12 +29,25 @@ void serialize(VT const& value) auto err = ser(s, value); if (err) { - std::cout << "SERIALIZing ERR: " << err << "\n"; + std::cout << "SERIALIZING ERR: " << err << "\n"; } else { std::cout << "SERIALIZED: " << s << "\n"; } } +template +void deserialize(mcc::traits::mcc_input_char_range auto const& s, VT& value) +{ + MccDeserializer deser; + + auto err = deser(s, value); + if (err) { + std::cout << "DESERIALIZING ERR: " << err << "\n"; + } else { + std::cout << "DESERIALIZION IS OK\n"; + } +} + int main() { skypt_t::cctEngine.setStateERFA(saoras); @@ -141,5 +155,22 @@ int main() std::cout << "\tfor 'sky point' type: "; serialize(pt); + + std::cout << "\n\nDESERIALIZATION TEST:\n"; + v = 0.0; + + std::string s = "123.7687"; + + deserialize(s, v); + std::cout << "\tfor 'double' type: v = " << v << "\n"; + + s = " 11:22:33.453, -32.19820931,65632.87987987,RADEC-OBS "; + deserialize(s, pt); + pt.toAtSameEpoch(radec_obs); + std::cout << "\tfor 'sky point' type: x = " << radec_obs.x().sexagesimal(true) + << ", y = " << radec_obs.y().degrees() << ", epoch = " << pt.epoch().MJD() + << ", kind = " << MccCoordPairKindToStr(pt.pairKind()) << "\n"; + + return 0; }