This commit is contained in:
Timur A. Fatkhullin
2026-02-08 21:50:48 +03:00
parent 86bb53d358
commit 07cf211b3d
10 changed files with 2085 additions and 1984 deletions

View File

@@ -51,17 +51,6 @@ endif()
# ******* ERFA LIBRARY ******* # ******* ERFA LIBRARY *******
find_program(MESON_PROG NAMES meson HINTS ENV PATHS)
if (NOT MESON_PROG)
message(FATAL "meson executable can not be found!!!")
endif()
find_program(NINJA_PROG NAMES ninja ninja-build)
if (NOT NINJA_PROG)
message(FATAL "ninja executable can not be found!!!")
endif()
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
@@ -92,6 +81,18 @@ if (NOT ERFALIB_FOUND)
# set(CACHE{ERFALIB_LIBRARY_DIRS} TYPE PATH VALUE "${CMAKE_BINARY_DIR}/erfa_lib") # set(CACHE{ERFALIB_LIBRARY_DIRS} TYPE PATH VALUE "${CMAKE_BINARY_DIR}/erfa_lib")
# set(CACHE{ERFALIB_LIBRARIES} TYPE STRING VALUE "erfa;m") # set(CACHE{ERFALIB_LIBRARIES} TYPE STRING VALUE "erfa;m")
find_program(MESON_PROG NAMES meson HINTS ENV PATHS)
if (NOT MESON_PROG)
message(FATAL "meson executable can not be found!!!")
endif()
find_program(NINJA_PROG NAMES ninja ninja-build)
if (NOT NINJA_PROG)
message(FATAL "ninja executable can not be found!!!")
endif()
FetchContent_Declare(erfalib_project FetchContent_Declare(erfalib_project
GIT_REPOSITORY "https://github.com/liberfa/erfa.git" GIT_REPOSITORY "https://github.com/liberfa/erfa.git"

View File

@@ -715,21 +715,21 @@ static constexpr MccCoordPairKind MccCoordStrToPairKind(R&& spair)
} }
enum class MccCoordinatePairRep : int { // enum class MccCoordinatePairRep : int {
MCC_COORDPAIR_REP_DEGREES, // both angles are in decimal degrees // MCC_COORDPAIR_REP_DEGREES, // both angles are in decimal degrees
MCC_COORDPAIR_REP_SXGM_HOURDEG, // X is in hour and Y is in degree sexagesimal representation // MCC_COORDPAIR_REP_SXGM_HOURDEG, // X is in hour and Y is in degree sexagesimal representation
MCC_COORDPAIR_REP_SXGM_DEGDEG // both angles are in sexagesimal degrees // MCC_COORDPAIR_REP_SXGM_DEGDEG // both angles are in sexagesimal degrees
}; // };
// default wide-acceptable sexagesimal representation // // default wide-acceptable sexagesimal representation
static constexpr MccCoordinatePairRep MccCoordinatePairToSxgmRep(MccCoordPairKind kind) // static constexpr MccCoordinatePairRep MccCoordinatePairToSxgmRep(MccCoordPairKind kind)
{ // {
return kind == MccCoordPairKind::COORDS_KIND_AZALT || kind == MccCoordPairKind::COORDS_KIND_AZZD || // return kind == MccCoordPairKind::COORDS_KIND_AZALT || kind == MccCoordPairKind::COORDS_KIND_AZZD ||
kind == MccCoordPairKind::COORDS_KIND_XY || kind == MccCoordPairKind::COORDS_KIND_LONLAT || // kind == MccCoordPairKind::COORDS_KIND_XY || kind == MccCoordPairKind::COORDS_KIND_LONLAT ||
kind == MccCoordPairKind::COORDS_KIND_GENERIC // kind == MccCoordPairKind::COORDS_KIND_GENERIC
? MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_DEGDEG // ? MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_DEGDEG
: MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG; // RA-DEC or HA-DEC // : MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG; // RA-DEC or HA-DEC
} // }
} // namespace mcc::impl } // namespace mcc::impl

View File

@@ -1,301 +0,0 @@
#pragma once
#include <algorithm>
#include "mcc_epoch.h"
#include "mcc_serialization_common.h"
namespace mcc::impl
{
enum class MccDeserializerErrorCode : int {
ERROR_OK,
ERROR_UNDERLYING_DESERIALIZER,
ERROR_INVALID_SERIALIZED_VALUE,
ERROR_COORD_TRANSFORM
};
} // namespace mcc::impl
namespace std
{
template <>
class is_error_code_enum<mcc::impl::MccDeserializerErrorCode> : public true_type
{
};
} // namespace std
namespace mcc::impl
{
// error category
struct MccDeserializerCategory : public std::error_category {
MccDeserializerCategory() : std::error_category() {}
const char* name() const noexcept
{
return "MCC-DESERIALIZER-ERR-CATEGORY";
}
std::string message(int ec) const
{
MccDeserializerErrorCode err = static_cast<MccDeserializerErrorCode>(ec);
switch (err) {
case MccDeserializerErrorCode::ERROR_OK:
return "OK";
case MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER:
return "error returned by underlying deserializer";
case MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE:
return "invalid serialized value";
case MccDeserializerErrorCode::ERROR_COORD_TRANSFORM:
return "coordinates transformation error";
default:
return "UNKNOWN";
}
}
static const MccDeserializerCategory& get()
{
static const MccDeserializerCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(MccDeserializerErrorCode ec)
{
return std::error_code(static_cast<int>(ec), MccDeserializerCategory::get());
}
/* BASE DESERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
struct MccDeserializerBase : mcc_deserializer_interface_t<MccError> {
using typename mcc_deserializer_interface_t<MccError>::error_t;
virtual ~MccDeserializerBase() = default;
protected:
MccDeserializerBase() = default;
//
// empty == true, if the 'input' is empty or if all elements consist of only spaces
//
static std::vector<std::string_view> splitValueIntoElements(traits::mcc_input_char_range auto const& input,
mcc_serialization_params_c auto const& params,
bool& empty)
{
static_assert(std::ranges::contiguous_range<decltype(input)>, "NOT IMPLEMENTED FOR NON-CONTIGUIUS RANGES!!!");
std::vector<std::string_view> res;
if (std::ranges::size(input)) {
empty = true;
std::ranges::for_each(std::views::split(input, params.elem_delim), [&res, &empty](auto const& el) {
std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()});
if (empty && res.back().size()) {
empty = false;
}
});
} else {
empty = true;
}
return res;
}
template <typename VT, std::ranges::output_range<VT> R, typename... DeserParamsT>
static error_t deserializingRange(mcc_deserializer_c auto& dsr,
traits::mcc_input_char_range auto const& input,
R& r,
mcc_serialization_params_c auto const& params)
{
if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!!
r = R{};
return MccDeserializerErrorCode::ERROR_OK;
}
auto r_str = std::views::split(input, params.seq_delim);
VT val;
auto it = r.begin();
for (auto const& el : r_str) {
auto err = dsr(el, val, std::forward<DeserParamsT>(params)...);
if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
if (it == r.end()) {
std::back_inserter(r) = val;
it = r.end();
} else {
*it = val;
++it;
}
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION */
template <typename VT>
struct MccDeserializer : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-FALLBACK-DESERIALIZER"};
virtual ~MccDeserializer() = default;
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
if constexpr (std::is_arithmetic_v<VT>) {
auto v = mcc::utils::numFromStr<VT>(utils::trimSpaces(input));
if (!v.has_value()) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
value = v.value();
} else if constexpr (mcc::traits::mcc_output_char_range<VT>) {
VT r;
if constexpr (traits::mcc_array_c<VT>) {
size_t N =
std::ranges::size(r) <= std::ranges::size(input) ? std::ranges::size(r) : std::ranges::size(input);
for (size_t i = 0; i < N; ++i) {
r[i] = input[i];
}
if (std::ranges::size(r) > N) {
for (size_t i = N; i < std::ranges::size(r); ++i) {
r[i] = '\0';
}
}
} else {
std::ranges::copy(input, std::back_inserter(r));
}
value = r;
} else if constexpr (std::ranges::range<VT>) {
using el_t = std::ranges::range_value_t<VT>;
static_assert(std::ranges::output_range<VT, el_t>, "INVALID RANGE TYPE!!!");
// no reference or constants allowed
static_assert(std::is_reference_v<el_t> || std::is_const_v<el_t>, "INVALID RANGE ELEMENT TYPE!!!");
MccDeserializer<el_t> dsr;
return deserializingRange<el_t>(dsr, input, value, params);
} else if constexpr (traits::mcc_time_duration_c<VT>) {
typename VT::rep vd;
MccDeserializer<typename VT::rep> dsr;
auto err = dsr(trimSpaces(input), vd, params);
if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
value = VT{vd};
} else {
static_assert(false, "UNSUPPORTED VALUE TYPE!!!");
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
/* SPECIALIZATION FOR THE SOME CONCEPTS */
template <mcc_coord_epoch_c VT>
struct MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-COORD-EPOCH-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
bool ok = value.fromCharRange(input);
if (!ok) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <typename VT>
requires(!std::is_arithmetic_v<VT> && mcc_angle_c<VT>)
struct MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-ANGLE-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
bool hms = params.angle_format == MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS;
auto res = utils::parsAngleString(input, hms);
if (res) { // returned angle is in degrees!!!
value = res.value() * MCC_DEGRESS_TO_RADS;
} else {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <mcc_skypoint_c VT>
struct MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-SKYPOINT-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
// valid format: X<elem-delim>Y[<elem-delim>TIME-POINT<elem-delim>PAIR-KIND]
// X<elem-delim>Y (assumed RADEC_ICRS and J2000.0 epoch)
bool empty;
auto elems = MccDeserializerBase::splitValueIntoElements(input, params, empty);
if (empty || (elems.size() < 2) || (elems.size() == 3)) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
MccCelestialCoordEpoch epoch; // J2000.0
MccAngle x, y;
MccDeserializer<MccAngle> dsr_ang;
typename MccDeserializer<MccAngle>::error_t dsr_err;
}
};
} // namespace mcc::impl

View File

@@ -1,23 +1,10 @@
#pragma once #pragma once
/****************************************************************************************
* *
* MOUNT CONTROL COMPONENTS LIBRARY *
* *
* *
* IMPLEMENTATION OF DESERIALIZER CLASSES *
* *
****************************************************************************************/
#include <algorithm> #include <algorithm>
#include <atomic>
#include <string_view>
#include "mcc_concepts.h"
#include "mcc_coordinate.h" #include "mcc_coordinate.h"
#include "mcc_epoch.h" #include "mcc_epoch.h"
#include "mcc_error.h" #include "mcc_serialization_common.h"
namespace mcc::impl namespace mcc::impl
{ {
@@ -89,34 +76,11 @@ inline std::error_code make_error_code(MccDeserializerErrorCode ec)
} }
template <mcc_error_c RetT>
struct mcc_deserializer_interface_t {
virtual ~mcc_deserializer_interface_t() = default;
typedef RetT error_t; /* BASE DESERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
template <std::derived_from<mcc_deserializer_interface_t> SelfT, struct MccDeserializerBase : mcc_deserializer_interface_t<MccError> {
traits::mcc_input_char_range R, using typename mcc_deserializer_interface_t<MccError>::error_t;
typename ValueT,
typename... DeserParamsT>
RetT operator()(this SelfT&& self, R const& input, ValueT& value, DeserParamsT&&... params)
{
return std::forward<SelfT>(self)(input, value);
}
protected:
mcc_deserializer_interface_t() = default;
};
template <typename T>
concept mcc_deserializer_c = std::derived_from<T, mcc_deserializer_interface_t<typename T::error_t>>;
namespace details
{
struct MccDeserializerBase : mcc_deserializer_interface_t<impl::MccError>, utils::mcc_elem_sequence_with_delim_t {
using typename mcc_deserializer_interface_t<impl::MccError>::error_t;
virtual ~MccDeserializerBase() = default; virtual ~MccDeserializerBase() = default;
@@ -127,14 +91,18 @@ protected:
// //
// empty == true, if the 'input' is empty or if all elements consist of only spaces // empty == true, if the 'input' is empty or if all elements consist of only spaces
// //
std::vector<std::string_view> splitAggregateValue(traits::mcc_input_char_range auto const& input, bool& empty) const static std::vector<std::string_view> splitValueIntoElements(traits::mcc_input_char_range auto const& input,
mcc_serialization_params_c auto const& params,
bool& empty)
{ {
static_assert(std::ranges::contiguous_range<decltype(input)>, "NOT IMPLEMENTED FOR NON-CONTIGUIUS RANGES!!!");
std::vector<std::string_view> res; std::vector<std::string_view> res;
if (std::ranges::size(input)) { if (std::ranges::size(input)) {
empty = true; empty = true;
std::ranges::for_each(std::views::split(input, this->_elementDelimiter), [&res, &empty](auto const& el) { std::ranges::for_each(std::views::split(input, params.elem_delim), [&res, &empty](auto const& el) {
std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()}); std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()});
if (empty && res.back().size()) { if (empty && res.back().size()) {
empty = false; empty = false;
@@ -148,17 +116,17 @@ protected:
} }
template <typename VT, std::ranges::output_range<VT> R, typename... DeserParamsT> template <typename VT, std::ranges::output_range<VT> R, typename... DeserParamsT>
error_t deserializingRange(mcc_deserializer_c auto& dsr, static error_t deserializingRange(mcc_deserializer_c auto& dsr,
traits::mcc_input_char_range auto const& input, traits::mcc_input_char_range auto const& input,
R& r, R& r,
DeserParamsT&&... params) const mcc_serialization_params_c auto const& params)
{ {
if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!! if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!!
r = R{}; r = R{};
return MccDeserializerErrorCode::ERROR_OK; return MccDeserializerErrorCode::ERROR_OK;
} }
auto r_str = std::views::split(input, _seqDelimiter); auto r_str = std::views::split(input, params.seq_delim);
VT val; VT val;
auto it = r.begin(); auto it = r.begin();
@@ -181,21 +149,21 @@ protected:
} }
}; };
} // namespace details
/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION */
/* fallback template: basic deserializer */
template <typename VT> template <typename VT>
class MccDeserializer : public details::MccDeserializerBase struct MccDeserializer : MccDeserializerBase {
{ static constexpr std::string_view deserializerName{"MCC-FALLBACK-DESERIALIZER"};
public:
using typename details::MccDeserializerBase::error_t;
virtual ~MccDeserializer() = default; virtual ~MccDeserializer() = default;
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{ {
if constexpr (std::is_arithmetic_v<VT>) { if constexpr (std::is_arithmetic_v<VT>) {
auto v = mcc::utils::numFromStr<VT>(utils::trimSpaces(input)); auto v = mcc::utils::numFromStr<VT>(utils::trimSpaces(input));
@@ -232,13 +200,13 @@ public:
static_assert(std::is_reference_v<el_t> || std::is_const_v<el_t>, "INVALID RANGE ELEMENT TYPE!!!"); static_assert(std::is_reference_v<el_t> || std::is_const_v<el_t>, "INVALID RANGE ELEMENT TYPE!!!");
MccDeserializer<el_t> dsr; MccDeserializer<el_t> dsr;
return deserializingRange<el_t>(dsr, input, value); return deserializingRange<el_t>(dsr, input, value, params);
} else if constexpr (traits::mcc_time_duration_c<VT>) { } else if constexpr (traits::mcc_time_duration_c<VT>) {
typename VT::rep vd; typename VT::rep vd;
MccDeserializer<typename VT::rep> dsr; MccDeserializer<typename VT::rep> dsr;
auto err = dsr(trimSpaces(input), vd); auto err = dsr(trimSpaces(input), vd, params);
if (err) { if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
} }
@@ -254,94 +222,69 @@ public:
}; };
/* SPECIALIZATION FOR THE SOME CONCEPTS */ /* SPECIALIZATION FOR THE SOME CONCEPTS */
template <mcc_coord_epoch_c VT>
struct MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-COORD-EPOCH-DESERIALIZER"};
template <typename VT> template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
requires(mcc_coord_epoch_c<VT> || (std::ranges::output_range<VT, std::ranges::range_value_t<VT>> && error_t operator()(traits::mcc_input_char_range auto const& input,
mcc_coord_epoch_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>)) VT& value,
class MccDeserializer<VT> : public details::MccDeserializerBase ParamsT const& params = mcc_serialization_params_t{})
{
public:
using typename details::MccDeserializerBase::error_t;
virtual ~MccDeserializer() = default;
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value)
{ {
if constexpr (mcc_coord_epoch_c<VT>) { // scalar
bool ok = value.fromCharRange(input); bool ok = value.fromCharRange(input);
if (!ok) { if (!ok) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
} }
} else { // range
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccDeserializer<value_t> dsr;
return deserializingRange(dsr, input, value);
}
}
};
template <typename VT>
requires(!std::is_arithmetic_v<VT> &&
(mcc_angle_c<VT> || (std::ranges::output_range<VT, std::ranges::range_value_t<VT>> &&
mcc_angle_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>)))
class MccDeserializer<VT> : public details::MccDeserializerBase
{
public:
using typename details::MccDeserializerBase::error_t;
virtual ~MccDeserializer() = default;
// if hms == true and the input sequence is a sexagesimal string then it interpretates angle as hours:mins:secs
// otherwise the input sequence is interpretated as decimal or sexagesimal degrees
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value, bool hms = false)
{
if constexpr (mcc_angle_c<VT>) { // scalar
auto res = utils::parsAngleString(input, hms);
if (res) { // returned angle is in degrees!!!
value = res.value() * MCC_DEGRESS_TO_RADS;
} else {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
} else { // range
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccDeserializer<value_t> dsr;
if constexpr (std::invocable<MccDeserializer<value_t>, decltype(input), VT&, bool>) {
// it is assumed here, the angle deserializer has the same behavior as the one implemented above
// (that is, it can interpret the third argument as a flag "false/true = only
// degrees/hour-like")
return deserializingRange<value_t>(dsr, input, value, hms);
} else {
return deserializingRange<value_t>(dsr, input, value);
}
}
return MccDeserializerErrorCode::ERROR_OK; return MccDeserializerErrorCode::ERROR_OK;
} }
}; };
template <typename VT> template <typename VT>
requires(mcc_skypoint_c<VT> || (std::ranges::output_range<VT, std::ranges::range_value_t<VT>> && requires(!std::is_arithmetic_v<VT> && mcc_angle_c<VT>)
mcc_skypoint_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>)) struct MccDeserializer<VT> : MccDeserializerBase {
class MccDeserializer<VT> : public details::MccDeserializerBase static constexpr std::string_view deserializerName{"MCC-ANGLE-DESERIALIZER"};
{
public:
using typename details::MccDeserializerBase::error_t;
virtual ~MccDeserializer() = default; template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{ {
if constexpr (mcc_skypoint_c<VT>) { // scalar: X[elem-delim]Y{[elem-delim]TIME-POINT[elem-delim]PAIR-KIND} bool hms = params.angle_format == MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS;
bool empty; // exactly 2 or >3 elements
auto elems = splitAggregateValue(input, empty); auto res = utils::parsAngleString(input, hms);
if (res) { // returned angle is in degrees!!!
value = res.value() * MCC_DEGRESS_TO_RADS;
} else {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <mcc_skypoint_c VT>
struct MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-SKYPOINT-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
auto pars = params;
// valid format: X<elem-delim>Y[<elem-delim>TIME-POINT<elem-delim>PAIR-KIND]
// X<elem-delim>Y (assumed RADEC_ICRS and J2000.0 epoch)
bool empty;
auto elems = MccDeserializerBase::splitValueIntoElements(input, params, empty);
if (empty || (elems.size() < 2) || (elems.size() == 3)) { if (empty || (elems.size() < 2) || (elems.size() == 3)) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
@@ -355,8 +298,8 @@ public:
typename MccDeserializer<MccAngle>::error_t dsr_err; typename MccDeserializer<MccAngle>::error_t dsr_err;
if (elems.size() >= 4) { // X, Y, TIME-POINT, PAIR-KIND if (elems.size() > 3) { // full format
// first, get pair-kind // deserialize pair kind string
pair_kind = MccCoordStrToPairKind(elems[3]); pair_kind = MccCoordStrToPairKind(elems[3]);
if (pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) { if (pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
@@ -369,32 +312,23 @@ public:
} }
} }
if (elems.size() >= 2) { // if == 2: just X and Y (if so it is interpretated as RADEC_ICRS) // deserialize X and Y
if (MccCoordinatePairToSxgmRep(pair_kind) == MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG) { if (params.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG) {
// if input X-angle is in sexagesimal from then interpretate it in hours::mins::secs format pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS;
if constexpr (std::invocable<MccDeserializer<MccAngle>, decltype(elems[0]), MccAngle&, bool>) {
// it is assumed here, the angle deserializer has the same behavior as the one implemented above
// (that is, it can interpret the third argument as a flag "false/true = only
// degrees/hour-like")
dsr_err = dsr_ang(elems[0], x, true);
} else { } else {
dsr_err = dsr_ang(elems[0], x); pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS;
}
} else {
dsr_err = dsr_ang(elems[0], x);
} }
dsr_err = dsr_ang(elems[0], x, pars);
if (dsr_err) { if (dsr_err) {
return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
} }
dsr_err = dsr_ang(elems[1], y); pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS;
dsr_err = dsr_ang(elems[1], y, pars);
if (dsr_err) { if (dsr_err) {
return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
} }
}
switch (pair_kind) { switch (pair_kind) {
case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: case MccCoordPairKind::COORDS_KIND_RADEC_ICRS:
@@ -431,15 +365,9 @@ public:
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
} }
} else { // range
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccDeserializer<value_t> dsr;
return deserializingRange<value_t>(dsr, input, value);
}
return MccDeserializerErrorCode::ERROR_OK; return MccDeserializerErrorCode::ERROR_OK;
} }
}; };
} // namespace mcc::impl } // namespace mcc::impl

445
mcc_deserializer.h.old Normal file
View File

@@ -0,0 +1,445 @@
#pragma once
/****************************************************************************************
* *
* MOUNT CONTROL COMPONENTS LIBRARY *
* *
* *
* IMPLEMENTATION OF DESERIALIZER CLASSES *
* *
****************************************************************************************/
#include <algorithm>
#include <atomic>
#include <string_view>
#include "mcc_concepts.h"
#include "mcc_coordinate.h"
#include "mcc_epoch.h"
#include "mcc_error.h"
namespace mcc::impl
{
enum class MccDeserializerErrorCode : int {
ERROR_OK,
ERROR_UNDERLYING_DESERIALIZER,
ERROR_INVALID_SERIALIZED_VALUE,
ERROR_COORD_TRANSFORM
};
} // namespace mcc::impl
namespace std
{
template <>
class is_error_code_enum<mcc::impl::MccDeserializerErrorCode> : public true_type
{
};
} // namespace std
namespace mcc::impl
{
// error category
struct MccDeserializerCategory : public std::error_category {
MccDeserializerCategory() : std::error_category() {}
const char* name() const noexcept
{
return "MCC-DESERIALIZER-ERR-CATEGORY";
}
std::string message(int ec) const
{
MccDeserializerErrorCode err = static_cast<MccDeserializerErrorCode>(ec);
switch (err) {
case MccDeserializerErrorCode::ERROR_OK:
return "OK";
case MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER:
return "error returned by underlying deserializer";
case MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE:
return "invalid serialized value";
case MccDeserializerErrorCode::ERROR_COORD_TRANSFORM:
return "coordinates transformation error";
default:
return "UNKNOWN";
}
}
static const MccDeserializerCategory& get()
{
static const MccDeserializerCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(MccDeserializerErrorCode ec)
{
return std::error_code(static_cast<int>(ec), MccDeserializerCategory::get());
}
template <mcc_error_c RetT>
struct mcc_deserializer_interface_t {
virtual ~mcc_deserializer_interface_t() = default;
typedef RetT error_t;
template <std::derived_from<mcc_deserializer_interface_t> SelfT,
traits::mcc_input_char_range R,
typename ValueT,
typename... DeserParamsT>
RetT operator()(this SelfT&& self, R const& input, ValueT& value, DeserParamsT&&... params)
{
return std::forward<SelfT>(self)(input, value);
}
protected:
mcc_deserializer_interface_t() = default;
};
template <typename T>
concept mcc_deserializer_c = std::derived_from<T, mcc_deserializer_interface_t<typename T::error_t>>;
namespace details
{
struct MccDeserializerBase : mcc_deserializer_interface_t<impl::MccError>, utils::mcc_elem_sequence_with_delim_t {
using typename mcc_deserializer_interface_t<impl::MccError>::error_t;
virtual ~MccDeserializerBase() = default;
protected:
MccDeserializerBase() = default;
//
// empty == true, if the 'input' is empty or if all elements consist of only spaces
//
std::vector<std::string_view> splitAggregateValue(traits::mcc_input_char_range auto const& input, bool& empty) const
{
std::vector<std::string_view> res;
if (std::ranges::size(input)) {
empty = true;
std::ranges::for_each(std::views::split(input, this->_elementDelimiter), [&res, &empty](auto const& el) {
std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()});
if (empty && res.back().size()) {
empty = false;
}
});
} else {
empty = true;
}
return res;
}
template <typename VT, std::ranges::output_range<VT> R, typename... DeserParamsT>
error_t deserializingRange(mcc_deserializer_c auto& dsr,
traits::mcc_input_char_range auto const& input,
R& r,
DeserParamsT&&... params) const
{
if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!!
r = R{};
return MccDeserializerErrorCode::ERROR_OK;
}
auto r_str = std::views::split(input, _seqDelimiter);
VT val;
auto it = r.begin();
for (auto const& el : r_str) {
auto err = dsr(el, val, std::forward<DeserParamsT>(params)...);
if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
if (it == r.end()) {
std::back_inserter(r) = val;
it = r.end();
} else {
*it = val;
++it;
}
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
} // namespace details
/* fallback template: basic deserializer */
template <typename VT>
class MccDeserializer : public details::MccDeserializerBase
{
public:
using typename details::MccDeserializerBase::error_t;
virtual ~MccDeserializer() = default;
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value)
{
if constexpr (std::is_arithmetic_v<VT>) {
auto v = mcc::utils::numFromStr<VT>(utils::trimSpaces(input));
if (!v.has_value()) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
value = v.value();
} else if constexpr (mcc::traits::mcc_output_char_range<VT>) {
VT r;
if constexpr (traits::mcc_array_c<VT>) {
size_t N =
std::ranges::size(r) <= std::ranges::size(input) ? std::ranges::size(r) : std::ranges::size(input);
for (size_t i = 0; i < N; ++i) {
r[i] = input[i];
}
if (std::ranges::size(r) > N) {
for (size_t i = N; i < std::ranges::size(r); ++i) {
r[i] = '\0';
}
}
} else {
std::ranges::copy(input, std::back_inserter(r));
}
value = r;
} else if constexpr (std::ranges::range<VT>) {
using el_t = std::ranges::range_value_t<VT>;
static_assert(std::ranges::output_range<VT, el_t>, "INVALID RANGE TYPE!!!");
// no reference or constants allowed
static_assert(std::is_reference_v<el_t> || std::is_const_v<el_t>, "INVALID RANGE ELEMENT TYPE!!!");
MccDeserializer<el_t> dsr;
return deserializingRange<el_t>(dsr, input, value);
} else if constexpr (traits::mcc_time_duration_c<VT>) {
typename VT::rep vd;
MccDeserializer<typename VT::rep> dsr;
auto err = dsr(trimSpaces(input), vd);
if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
value = VT{vd};
} else {
static_assert(false, "UNSUPPORTED VALUE TYPE!!!");
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
/* SPECIALIZATION FOR THE SOME CONCEPTS */
template <typename VT>
requires(mcc_coord_epoch_c<VT> || (std::ranges::output_range<VT, std::ranges::range_value_t<VT>> &&
mcc_coord_epoch_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
class MccDeserializer<VT> : public details::MccDeserializerBase
{
public:
using typename details::MccDeserializerBase::error_t;
virtual ~MccDeserializer() = default;
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value)
{
if constexpr (mcc_coord_epoch_c<VT>) { // scalar
bool ok = value.fromCharRange(input);
if (!ok) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
} else { // range
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccDeserializer<value_t> dsr;
return deserializingRange(dsr, input, value);
}
}
};
template <typename VT>
requires(!std::is_arithmetic_v<VT> &&
(mcc_angle_c<VT> || (std::ranges::output_range<VT, std::ranges::range_value_t<VT>> &&
mcc_angle_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>)))
class MccDeserializer<VT> : public details::MccDeserializerBase
{
public:
using typename details::MccDeserializerBase::error_t;
virtual ~MccDeserializer() = default;
// if hms == true and the input sequence is a sexagesimal string then it interpretates angle as hours:mins:secs
// otherwise the input sequence is interpretated as decimal or sexagesimal degrees
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value, bool hms = false)
{
if constexpr (mcc_angle_c<VT>) { // scalar
auto res = utils::parsAngleString(input, hms);
if (res) { // returned angle is in degrees!!!
value = res.value() * MCC_DEGRESS_TO_RADS;
} else {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
} else { // range
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccDeserializer<value_t> dsr;
if constexpr (std::invocable<MccDeserializer<value_t>, decltype(input), VT&, bool>) {
// it is assumed here, the angle deserializer has the same behavior as the one implemented above
// (that is, it can interpret the third argument as a flag "false/true = only
// degrees/hour-like")
return deserializingRange<value_t>(dsr, input, value, hms);
} else {
return deserializingRange<value_t>(dsr, input, value);
}
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <typename VT>
requires(mcc_skypoint_c<VT> || (std::ranges::output_range<VT, std::ranges::range_value_t<VT>> &&
mcc_skypoint_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
class MccDeserializer<VT> : public details::MccDeserializerBase
{
public:
using typename details::MccDeserializerBase::error_t;
virtual ~MccDeserializer() = default;
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value)
{
if constexpr (mcc_skypoint_c<VT>) { // scalar: X[elem-delim]Y{[elem-delim]TIME-POINT[elem-delim]PAIR-KIND}
bool empty; // exactly 2 or >3 elements
auto elems = splitAggregateValue(input, empty);
if (empty || (elems.size() < 2) || (elems.size() == 3)) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
MccCelestialCoordEpoch epoch; // J2000.0
MccAngle x, y;
MccDeserializer<MccAngle> dsr_ang;
typename MccDeserializer<MccAngle>::error_t dsr_err;
if (elems.size() >= 4) { // X, Y, TIME-POINT, PAIR-KIND
// first, get pair-kind
pair_kind = MccCoordStrToPairKind(elems[3]);
if (pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
// epoch
bool ok = epoch.fromCharRange(elems[2]);
if (!ok) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
}
if (elems.size() >= 2) { // if == 2: just X and Y (if so it is interpretated as RADEC_ICRS)
if (MccCoordinatePairToSxgmRep(pair_kind) == MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG) {
// if input X-angle is in sexagesimal from then interpretate it in hours::mins::secs format
if constexpr (std::invocable<MccDeserializer<MccAngle>, decltype(elems[0]), MccAngle&, bool>) {
// it is assumed here, the angle deserializer has the same behavior as the one implemented above
// (that is, it can interpret the third argument as a flag "false/true = only
// degrees/hour-like")
dsr_err = dsr_ang(elems[0], x, true);
} else {
dsr_err = dsr_ang(elems[0], x);
}
} else {
dsr_err = dsr_ang(elems[0], x);
}
if (dsr_err) {
return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
dsr_err = dsr_ang(elems[1], y);
if (dsr_err) {
return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
}
switch (pair_kind) {
case MccCoordPairKind::COORDS_KIND_RADEC_ICRS:
value.from(MccSkyRADEC_ICRS{(double)x, (double)y});
break;
case MccCoordPairKind::COORDS_KIND_RADEC_OBS:
value.from(MccSkyRADEC_OBS{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_RADEC_APP:
value.from(MccSkyRADEC_APP{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_HADEC_OBS:
value.from(MccSkyHADEC_OBS{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_HADEC_APP:
value.from(MccSkyHADEC_APP{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_AZZD:
value.from(MccSkyAZZD{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_AZALT:
value.from(MccSkyAZALT{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_XY:
value.from(MccGenXY{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_GENERIC:
value.from(MccCoordPair{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_LONLAT:
value.from(MccGeoLONLAT{(double)x, (double)y});
break;
default:
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
} else { // range
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccDeserializer<value_t> dsr;
return deserializingRange<value_t>(dsr, input, value);
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
} // namespace mcc::impl

693
mcc_ser.h
View File

@@ -1,693 +0,0 @@
#pragma once
#include "mcc_coordinate.h"
#include "mcc_serialization_common.h"
namespace mcc::impl
{
enum class MccSerializerErrorCode : int {
ERROR_OK,
ERROR_UNDERLYING_SERIALIZER,
ERROR_INVALID_EPOCH,
ERROR_COORD_TRANSFORM
};
} // namespace mcc::impl
namespace std
{
template <>
class is_error_code_enum<mcc::impl::MccSerializerErrorCode> : public true_type
{
};
} // namespace std
namespace mcc::impl
{
// error category
struct MccSerializerCategory : public std::error_category {
MccSerializerCategory() : std::error_category() {}
const char* name() const noexcept
{
return "MCC-SERIALIZER-ERR-CATEGORY";
}
std::string message(int ec) const
{
MccSerializerErrorCode err = static_cast<MccSerializerErrorCode>(ec);
switch (err) {
case MccSerializerErrorCode::ERROR_OK:
return "OK";
case MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER:
return "error returned by underlying serializer";
case MccSerializerErrorCode::ERROR_INVALID_EPOCH:
return "invalid coordinate epoch";
case MccSerializerErrorCode::ERROR_COORD_TRANSFORM:
return "coordinates transformation error";
default:
return "UNKNOWN";
}
}
static const MccSerializerCategory& get()
{
static const MccSerializerCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(MccSerializerErrorCode ec)
{
return std::error_code(static_cast<int>(ec), MccSerializerCategory::get());
}
/* BASE SERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
struct MccSerializerBase : mcc_serializer_interface_t<MccError> {
virtual ~MccSerializerBase() = default;
using typename mcc_serializer_interface_t<MccError>::error_t;
protected:
MccSerializerBase() = default;
enum CoordType { CO_LON, CO_LAT };
static void addElemDelimiter(traits::mcc_output_char_range auto& output,
mcc_serialization_params_c auto const& params)
{
std::format_to(std::back_inserter(output), "{}", params.elem_delim);
}
static void addSeqDelimiter(traits::mcc_output_char_range auto& output,
mcc_serialization_params_c auto const& params)
{
std::format_to(std::back_inserter(output), "{}", params.seq_delim);
}
// set serialized angle format according to coordinates pair format and type of
// serializing mcc_coord_pair_c::pairKind
template <MccCoordPairKind PAIRKIND, CoordType TYPE = MccSerializerBase::CO_LON>
static void angleFormatFromCoordPairType(mcc_serialization_params_c auto& pars)
{
if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_DEGREES) {
pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_DEGREES;
} else { // format to sexagesimal form according to celestial coordinate type
if constexpr (TYPE == MccSerializerBase::CO_LON) {
if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGDEG) {
pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS;
} else if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG) {
if constexpr (PAIRKIND == MccCoordPairKind::COORDS_KIND_AZZD ||
PAIRKIND == MccCoordPairKind::COORDS_KIND_AZALT ||
PAIRKIND == MccCoordPairKind::COORDS_KIND_XY ||
PAIRKIND == MccCoordPairKind::COORDS_KIND_GENERIC) { // azimuth is in degrees
pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS;
} else { // RA or HA
pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS;
}
} else {
// !!!!!!!!!!!!!!!!!!
}
} else { // Y-coordinates (co-latitude one, DEC, ALT, ZD, generic X) is always in degrees for celestial
// point
pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS;
}
}
}
template <typename VT, typename R>
requires(std::ranges::input_range<R> && std::same_as<VT, std::ranges::range_value_t<R>>)
static error_t serializeRange(mcc_serializer_c auto& sr,
R const& r,
traits::mcc_output_char_range auto& output,
mcc_serialization_params_c auto const& params)
{
size_t i = 0, N = std::ranges::size(r);
for (auto const& el : r) {
auto err = sr(output, el, params);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
if (++i < N) {
MccSerializerBase::addSeqDelimiter(output, params);
}
}
return MccSerializerErrorCode::ERROR_OK;
}
};
/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION */
template <typename VT>
struct MccSerializer : MccSerializerBase {
constexpr static std::string_view serializerName{"MCC-FALLBACK-SERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_output_char_range auto& output,
VT const& value,
ParamsT const& params = mcc_serialization_params_t{})
{
if constexpr (std::formattable<VT, char>) {
} else if constexpr (std::convertible_to<VT, std::string>) {
auto err = MccSerializer<std::string>{}(output, static_cast<std::string>(value), params);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
} else if constexpr (std::ranges::range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
// special range (character sequence)
if constexpr (std::same_as<value_t, char>) {
std::string str;
std::ranges::copy(value, std::back_inserter(str));
auto err = MccSerializer<std::string>{}(output, str, params);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
} else {
MccSerializer<value_t> sr;
return MccSerializerBase::serializeRange<value_t>(sr, value, output, params);
}
} else {
static_assert(false, "UNSUPPORTED TYPE!!!");
}
return MccSerializerErrorCode::ERROR_OK;
}
};
/* SPECIALIZATION FOR THE SOME CONCEPTS */
/* std::chrono::sys_time variants and its sequence */
template <typename VT>
requires traits::mcc_systime_c<VT>
struct MccSerializer<VT> : MccSerializerBase {
virtual ~MccSerializer() = default;
constexpr static std::string_view serializerName{"MCC-SYSTIME-SERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_output_char_range auto& output,
VT const& value,
ParamsT const& params = mcc_serialization_params_t{})
{
std::vformat_to(std::back_inserter(output), params.systime_format, std::make_format_args(value));
return MccSerializerErrorCode::ERROR_OK;
}
};
template <typename VT>
requires(!std::is_arithmetic_v<VT> && mcc_angle_c<VT>)
struct MccSerializer<VT> : MccSerializerBase {
constexpr static std::string_view serializerName{"MCC-ANGLE-SERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_output_char_range auto& output,
VT const& value,
ParamsT const& params = mcc_serialization_params_t{})
{
double v = (double)value; // radians (see mcc_angle_c concept)
std::string sgm;
switch (params.angle_format) {
case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_DEGREES:
v *= MCC_RADS_TO_DEGRESS;
return MccSerializer<double>{}(output, v, params);
case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS:
if (params.norm_sxgm) {
sgm = utils::rad2sxg<true>(v, false, params.angle_prec.deg_prec);
} else {
sgm = utils::rad2sxg<false>(v, false, params.angle_prec.deg_prec);
}
break;
case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS:
if (params.norm_sxgm) {
sgm = utils::rad2sxg<true>(v, true, params.angle_prec.hour_prec);
} else {
sgm = utils::rad2sxg<false>(v, true, params.angle_prec.hour_prec);
}
break;
default:
break;
}
auto err = MccSerializer<std::string>{}(output, sgm, params);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
return MccSerializerErrorCode::ERROR_OK;
};
};
template <mcc_coord_epoch_c VT>
struct MccSerializer<VT> : MccSerializerBase {
constexpr static std::string_view serializerName{"MCC-COORD-EPOCH-SERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_output_char_range auto& output,
VT const& value,
ParamsT const& params = mcc_serialization_params_t{})
{
double jd;
switch (params.timepoint_format) {
case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE: {
auto tp = value.UTC();
auto err = MccSerializer<decltype(tp)>{}(output, tp, params);
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JEPOCH: {
auto ep = value.JEpoch();
auto err = MccSerializer<decltype(ep)>{}(output, ep, params);
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JD:
jd = value.MJD() + MCC_J2000_MJD;
break;
case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_MJD:
jd = value.MJD();
break;
}
auto err = MccSerializer<double>{}(output, jd, params);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
return MccSerializerErrorCode::ERROR_OK;
}
};
template <mcc_coord_pair_c VT>
struct MccSerializer<VT> : MccSerializerBase {
constexpr static std::string_view serializerName{"MCC-COORD-PAIR-SERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_output_char_range auto& output,
VT const& value,
ParamsT const& params = mcc_serialization_params_t{})
{
auto pars = params;
pars.norm_sxgm = true;
pars.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG;
// X-coordinate
MccSerializerBase::angleFormatFromCoordPairType<VT::pairKind, MccSerializerBase::CO_LON>(pars);
auto err = MccSerializer<MccAngle>{}(output, value.x(), params);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, params);
pars.norm_sxgm = false; // do not normalize co-latitude angle
// Y-coordinate
MccSerializerBase::angleFormatFromCoordPairType<VT::pairKind, MccSerializerBase::CO_LAT>(pars);
err = MccSerializer<MccAngle>{}(output, value.y(), params);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, params);
// epoch
auto ep = value.epoch();
auto ep_err = MccSerializer<decltype(ep)>{}(output, ep, params);
if (ep_err) {
return mcc_deduced_err(ep_err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, params);
// pair kind
return mcc_deduced_err(MccSerializer<std::string_view>{}(output, MccCoordPairKindToStr(VT::pairKind), params),
MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
return MccSerializerErrorCode::ERROR_OK;
}
};
template <mcc_skypoint_c VT>
struct MccSerializer<VT> : MccSerializerBase {
constexpr static std::string_view serializerName{"MCC-SKYPOINT-SERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_output_char_range auto& output,
VT const& value,
ParamsT const& params = mcc_serialization_params_t{})
{
auto serialize_cpair = [&]<typename T>(T& cp) {
auto ccte_err = value.to(cp);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
auto err = MccSerializer<T>{}(output, cp, params);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
return MccSerializerErrorCode::ERROR_OK;
};
switch (value.pairKind()) {
case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: {
MccSkyRADEC_ICRS cp;
return serialize_cpair(cp);
}
case MccCoordPairKind::COORDS_KIND_RADEC_OBS: {
MccSkyRADEC_OBS cp;
return serialize_cpair(cp);
}
case MccCoordPairKind::COORDS_KIND_RADEC_APP: {
MccSkyRADEC_APP cp;
return serialize_cpair(cp);
}
case MccCoordPairKind::COORDS_KIND_HADEC_OBS: {
MccSkyHADEC_OBS cp;
return serialize_cpair(cp);
}
case MccCoordPairKind::COORDS_KIND_HADEC_APP: {
MccSkyHADEC_APP cp;
return serialize_cpair(cp);
}
case MccCoordPairKind::COORDS_KIND_AZZD: {
MccSkyAZZD cp;
return serialize_cpair(cp);
}
case MccCoordPairKind::COORDS_KIND_AZALT: {
MccSkyAZALT cp;
return serialize_cpair(cp);
}
case MccCoordPairKind::COORDS_KIND_XY: {
MccGenXY cp;
return serialize_cpair(cp);
}
case MccCoordPairKind::COORDS_KIND_GENERIC: {
MccGenXY cp;
return serialize_cpair(cp);
}
default:
// !!!!!!!!!!!!
}
}
};
template <mcc_telemetry_data_c VT>
struct MccSerializer<VT> : MccSerializerBase {
constexpr static std::string_view serializerName{"MCC-TELEMETRY-DATA-SERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_output_char_range auto& output,
VT const& value,
ParamsT const& params = mcc_serialization_params_t{})
{
// FORMAT: RA_OBS_MOUNT, DEC_OBS_MOUNT, RA_APP_MOUNT, DEC_APP_MOUNT, HA_APP_MOUNT, AZ_MOUNT, ZD_MOUNT,
// REFR_CORR_MOUNT, ENC_X, ENC_Y, PCM_X, PCM_Y, RA_APP_TAG, DEC_APP_TAG, AZ_TAG, ZD_TAG, LAST, EO, TIMEPOINT,
// STATUS
// NOTE: One must assume that the returned RA coordinates are in format of underlying celestial coordinate
// transformation engine used in the mcc_skypoint_c class implementation. E.g. ERFA-library uses the
// CIO-based representation of RA
auto pars_h = params;
auto pars_d = params;
pars_h.norm_sxgm = true;
pars_h.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG;
pars_d.norm_sxgm = true;
pars_d.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG;
MccSkyRADEC_OBS rd_obs;
MccSkyRADEC_APP rd_app;
MccSkyHADEC_APP hd_app;
MccSkyAZZD azzd;
// quantities in hour representation
MccSerializerBase::angleFormatFromCoordPairType<VT::pairKind, MccSerializerBase::CO_LON>(pars_h);
// quantities in degree representation
MccSerializerBase::angleFormatFromCoordPairType<VT::pairKind, MccSerializerBase::CO_LON>(pars_d);
MccSerializer<MccAngle> ang_sr;
// RA_OBS_MOUNT, DEC_OBS_MOUNT
auto ccte_err = value.mountPos.toAtSameEpoch(rd_obs);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
auto err = ang_sr(output, rd_obs.x(), pars_h);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
err = ang_sr(output, rd_obs.y(), pars_h);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// RA_APP_MOUNT, DEC_APP_MOUNT
ccte_err = value.mountPos.toAtSameEpoch(rd_app);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
err = ang_sr(output, rd_app.x(), pars_h);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
err = ang_sr(output, rd_app.y(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// HA_APP_MOUNT
ccte_err = value.mountPos.toAtSameEpoch(hd_app);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
err = ang_sr(output, hd_app.x(), pars_h);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// AZ_MOUNT, ZD_MOUNT
ccte_err = value.mountPos.toAtSameEpoch(azzd);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
err = ang_sr(output, azzd.x(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
err = ang_sr(output, azzd.y(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// refraction correction
MccAngle ang;
ccte_err = value.mountPos.refractCorrection(&ang);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
err = ang_sr(output, ang, pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// encoder X and Y
err = ang_sr(output, value.hwState.XY.x(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
err = ang_sr(output, value.hwState.XY.y(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// PCM X and Y
err = ang_sr(output, value.pcmCorrection.pcmX(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
err = ang_sr(output, value.pcmCorrection.pcmY(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// RA_APP_TAG, DEC_APP_TAG
ccte_err = value.targetPos.toAtSameEpoch(rd_app);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
err = ang_sr(output, rd_app.x(), pars_h);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
err = ang_sr(output, rd_app.y(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// AZ_TAG, ZD_TAG
ccte_err = value.targetPos.toAtSameEpoch(azzd);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
err = ang_sr(output, azzd.x(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
err = ang_sr(output, azzd.y(), pars_d);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// LAST, local apparent sideral time
ccte_err = value.mountPos.appSideralTime(&ang, true);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
err = ang_sr(output, ang, pars_h);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// EO, equation of origins
ccte_err = value.mountPos.EO(&ang);
if (ccte_err) {
return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
err = ang_sr(output, ang, pars_h);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// coordinates epoch
auto ep_err = MccSerializer<MccCelestialCoordEpoch>{}(output, value.mountPos.epoch(), pars_d);
if (ep_err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
MccSerializerBase::addElemDelimiter(output, pars_h);
// status (it must be formattable, see mcc_concepts.h)
auto st_err =
MccSerializer<decltype(value.hwState.movementState)>{}(output, value.hwState.movementState, pars_d);
if (st_err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
return MccSerializerErrorCode::ERROR_OK;
}
};
static_assert(mcc_serializer_c<MccSerializer<MccAngle>>, "!!!");
} // namespace mcc::impl

File diff suppressed because it is too large Load Diff

910
mcc_serializer.h.old Normal file
View File

@@ -0,0 +1,910 @@
#pragma once
/****************************************************************************************
* *
* MOUNT CONTROL COMPONENTS LIBRARY *
* *
* *
* IMPLEMENTATION OF SERIALIZER CLASSES *
* *
****************************************************************************************/
#include <atomic>
#include <concepts>
#include "mcc_concepts.h"
#include "mcc_constants.h"
#include "mcc_coordinate.h"
#include "mcc_epoch.h"
#include "mcc_error.h"
#include "mcc_traits.h"
#include "mcc_utils.h"
namespace mcc::impl
{
enum class MccSerializerErrorCode : int {
ERROR_OK,
ERROR_UNDERLYING_SERIALIZER,
ERROR_INVALID_EPOCH,
ERROR_COORD_TRANSFORM
};
} // namespace mcc::impl
namespace std
{
template <>
class is_error_code_enum<mcc::impl::MccSerializerErrorCode> : public true_type
{
};
} // namespace std
namespace mcc::impl
{
// error category
struct MccSerializerCategory : public std::error_category {
MccSerializerCategory() : std::error_category() {}
const char* name() const noexcept
{
return "MCC-SERIALIZER-ERR-CATEGORY";
}
std::string message(int ec) const
{
MccSerializerErrorCode err = static_cast<MccSerializerErrorCode>(ec);
switch (err) {
case MccSerializerErrorCode::ERROR_OK:
return "OK";
case MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER:
return "error returned by underlying serializer";
case MccSerializerErrorCode::ERROR_INVALID_EPOCH:
return "invalid coordinate epoch";
case MccSerializerErrorCode::ERROR_COORD_TRANSFORM:
return "coordinates transformation error";
default:
return "UNKNOWN";
}
}
static const MccSerializerCategory& get()
{
static const MccSerializerCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(MccSerializerErrorCode ec)
{
return std::error_code(static_cast<int>(ec), MccSerializerCategory::get());
}
template <mcc_error_c RetT>
struct mcc_serializer_interface_t {
virtual ~mcc_serializer_interface_t() = default;
typedef RetT error_t;
template <std::derived_from<mcc_serializer_interface_t> SelfT,
traits::mcc_output_char_range R,
typename ValueT,
typename FmtT>
requires(std::same_as<std::format_string<ValueT>, FmtT> || std::same_as<std::string_view, FmtT>)
RetT operator()(this SelfT&& self, R& output, ValueT const& value, FmtT fmt)
{
return std::forward<SelfT>(self)(output, value, std::move(fmt));
}
protected:
mcc_serializer_interface_t() = default;
};
template <typename T>
concept mcc_serializer_c = std::derived_from<T, mcc_serializer_interface_t<typename T::error_t>>;
namespace details
{
struct MccSerializerBase : mcc_serializer_interface_t<impl::MccError>, utils::mcc_elem_sequence_with_delim_t {
using typename mcc_serializer_interface_t<impl::MccError>::error_t;
virtual ~MccSerializerBase() = default;
protected:
MccSerializerBase() = default;
template <typename VT, typename R>
requires(std::ranges::input_range<R> && std::same_as<VT, std::ranges::range_value_t<R>>)
error_t serializingRange(mcc_serializer_c auto& sr,
R const& r,
traits::mcc_output_char_range auto& output,
auto fmt) const
{
size_t i = 0, N = std::ranges::size(r);
for (auto const& el : r) {
auto err = sr(output, el, std::move(fmt));
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
if (++i < N) {
std::format_to(std::back_inserter(output), "{}", _seqDelimiter);
}
}
return MccSerializerErrorCode::ERROR_OK;
}
};
} // namespace details
/* fallback template */
template <typename VT>
class MccSerializer : public details::MccSerializerBase
// class MccSerializer : public mcc_serializer_interface_t<impl::MccError>
{
public:
// default delimiter
static constexpr std::string_view defaultRangeDelimiter{","};
// typedef impl::MccError error_t;
MccSerializer() = default;
~MccSerializer() = default;
template <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::formattable<VT, char>) {
if constexpr (std::is_null_pointer_v<FmtT>) {
std::format_to(std::back_inserter(output), "{}", value);
} else if constexpr (traits::mcc_input_char_range<FmtT>) {
std::string_view sfmt{fmt.begin(), fmt.end()};
std::vformat_to(std::back_inserter(output), sfmt, std::make_format_args(value));
} else if constexpr (std::same_as<std::format_string<VT>, FmtT>) {
std::format_to(std::back_inserter(output), std::move(fmt), value);
} else {
static_assert(false, "INVALID FORMAT STRING TYPE!!!");
}
} else if constexpr (std::convertible_to<VT, std::string>) {
auto err = MccSerializer<std::string>{}(output, static_cast<std::string>(value), std::move(fmt));
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
} else if constexpr (std::ranges::range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
// special range (character sequence)
if constexpr (std::same_as<value_t, char>) {
std::string str;
std::ranges::copy(value, std::back_inserter(str));
auto err = MccSerializer<std::string>{}(output, str, std::move(fmt));
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
} else {
MccSerializer<value_t> sr;
return serializingRange<value_t>(sr, value, output, std::move(fmt));
}
} else {
static_assert(false, "UNSUPPORTED TYPE!!!");
}
return MccSerializerErrorCode::ERROR_OK;
}
};
/* SPECIALIZATION FOR THE SOME CONCEPTS */
template <typename VT>
requires(traits::mcc_systime_c<VT> ||
(std::ranges::input_range<VT> && traits::mcc_systime_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
class MccSerializer<VT> : public details::MccSerializerBase
{
public:
virtual ~MccSerializer() = default;
template <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::ranges::input_range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccSerializer<value_t> sr;
return serializingRange<value_t>(sr, value, output, std::move(fmt));
} else { // scalar
if constexpr (std::is_null_pointer_v<FmtT>) {
std::format_to(std::back_inserter(output), "{:%FT%T}", value);
} else if constexpr (traits::mcc_input_char_range<FmtT>) {
std::string_view sfmt{fmt.begin(), fmt.end()};
if (sfmt == "{}") {
sfmt = "{:%FT%T}";
}
std::vformat_to(std::back_inserter(output), sfmt, std::make_format_args(value));
} else if constexpr (std::same_as<std::format_string<VT>, FmtT>) {
std::string_view sfmt{fmt.get().begin(), fmt.get().end()};
if (sfmt == "{}") {
std::format_to(std::back_inserter(output), "{:%FT%T}", value);
} else {
std::format_to(std::back_inserter(output), std::move(fmt), value);
}
} else {
static_assert(false, "INVALID FORMAT STRING TYPE!!!");
}
}
return MccSerializerErrorCode::ERROR_OK;
}
};
template <typename VT>
requires(!std::is_arithmetic_v<VT> &&
(mcc_angle_c<VT> ||
(std::ranges::input_range<VT> && mcc_angle_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>)))
class MccSerializer<VT> : public virtual details::MccSerializerBase
// class MccSerializer<VT> : public mcc_serializer_interface_t<impl::MccError>
{
public:
enum SerializedCoordFormat {
CFMT_DEGREES, // floating-point representation in degrees
CFMT_SGM_HOURS, // sexagesimal representation: HH:MM:SS.SSSSSS (hours:minutes:seconds)
CFMT_SGM_DEGS // sexagesimal representation: 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
};
// typedef impl::MccError error_t;
virtual ~MccSerializer() = default;
void setCoordFormat(SerializedCoordFormat format)
{
_coordFormat = format;
}
SerializedCoordFormat getCoordFormat() const
{
return _coordFormat;
}
void setCoordPrec(SexagesimalCoordPrec prec)
{
_coordPrec = std::move(prec);
}
SexagesimalCoordPrec getCoordPrec() const
{
return _coordPrec;
}
template <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::ranges::input_range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccSerializer<value_t> sr;
sr.setCoordFormat(_coordFormat);
sr.setCoordPrec(_coordPrec);
return serializingRange<value_t>(sr, value, output, std::move(fmt));
} else { // scalar
double v = (double)value; // radians
std::string sgm;
switch (_coordFormat) {
case MccSerializer::CFMT_DEGREES:
v *= 180.0 / std::numbers::pi;
return MccSerializer<double>{}(output, v, std::move(fmt));
case MccSerializer::CFMT_SGM_HOURS:
sgm = utils::rad2sxg(v, true, _coordPrec.load().hour_prec);
break;
case MccSerializer::CFMT_SGM_DEGS:
sgm = utils::rad2sxg(v, false, _coordPrec.load().deg_prec);
break;
default:
break;
}
auto err = MccSerializer<std::string>{}(output, sgm, std::move(fmt));
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
}
return MccSerializerErrorCode::ERROR_OK;
}
protected:
// std::atomic<SerializedCoordFormat> _coordFormat{MccSerializer::CFMT_DEGREES};
std::atomic<SerializedCoordFormat> _coordFormat{MccSerializer::CFMT_SGM_DEGS};
std::atomic<SexagesimalCoordPrec> _coordPrec{SexagesimalCoordPrec{.hour_prec = 2, .deg_prec = 1}};
};
template <typename VT>
requires(mcc_coord_epoch_c<VT> ||
(std::ranges::input_range<VT> && mcc_coord_epoch_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
class MccSerializer<VT> : public virtual details::MccSerializerBase
{
public:
enum TimePointFormat { TP_FORMAT_DATE, TP_FORMAT_MJD, TP_FORMAT_JEPOCH };
virtual ~MccSerializer() = default;
void setTimePointFormat(TimePointFormat format)
{
_tpFormat = format;
}
TimePointFormat getTimePointFormat() const
{
return _tpFormat;
}
template <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::ranges::input_range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccSerializer<value_t> sr;
sr.setTimePointFormat(_tpFormat);
return serializingRange<value_t>(sr, value, output, std::move(fmt));
} else {
switch (_tpFormat) {
case TP_FORMAT_DATE: {
auto utc = value.UTC();
auto err = MccSerializer<std::remove_cvref_t<decltype(utc)>>{}(output, utc, std::move(fmt));
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
} break;
case TP_FORMAT_MJD: {
double mjd = value.MJD();
auto err = MccSerializer<double>{}(output, mjd, std::move(fmt));
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
} break;
case TP_FORMAT_JEPOCH: {
auto jepoch = value.JEpoch();
auto err = MccSerializer<std::remove_cvref_t<decltype(jepoch)>>{}(output, jepoch, std::move(fmt));
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
}
default:
break;
}
return MccSerializerErrorCode::ERROR_OK;
}
}
protected:
std::atomic<TimePointFormat> _tpFormat{TP_FORMAT_DATE};
};
template <typename VT>
requires(mcc_coord_pair_c<VT> ||
(std::ranges::input_range<VT> && mcc_coord_pair_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
class MccSerializer<VT> : public std::conditional_t<std::ranges::input_range<VT>,
MccSerializer<std::vector<MccCelestialCoordEpoch>>,
MccSerializer<MccCelestialCoordEpoch>>,
public std::conditional_t<std::ranges::input_range<VT>,
MccSerializer<std::vector<MccAngle>>,
MccSerializer<MccAngle>>
// class MccSerializer<VT> : public mcc_serializer_interface_t<MccError>
{
protected:
typedef std::conditional_t<std::ranges::input_range<VT>,
MccSerializer<std::vector<MccCelestialCoordEpoch>>,
MccSerializer<MccCelestialCoordEpoch>>
base_ser_epoch_t;
typedef std::
conditional_t<std::ranges::input_range<VT>, MccSerializer<std::vector<MccAngle>>, MccSerializer<MccAngle>>
base_ser_angle_t;
public:
using typename details::MccSerializerBase::error_t;
template <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::ranges::input_range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccSerializer<value_t> sr;
sr.setTimePointFormat(this->_tpFormat);
sr.setCoordFormat(this->_coordFormat);
sr.setCoordPrec(this->_coordPrec);
return serializingRange<value_t>(sr, value, output, std::move(fmt));
} else { // scalar: format X[elem-delimiter]Y[elem-delimiter]epoch[elem-delimiter]pair-kind
// WARNING: still ignore format string!!!
error_t err;
// format to sexagesimal form according to celestial coordinate type (general XY-type is skipped)
if (this->_coordFormat != base_ser_angle_t::CFMT_DEGREES) {
auto prev_format = this->_coordFormat.load();
if constexpr (VT::pairKind == MccCoordPairKind::COORDS_KIND_AZZD ||
VT::pairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // azimuth is in degrees
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS);
err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()});
} else { // RA or HA are in hours
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS);
err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()});
}
this->setCoordFormat(prev_format); // restore previous value
} else { // here all angles in deciamal degrees
err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()});
}
if (err) {
return err;
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
// format to sexagesimal form according to celestial coordinate type (general XY-type is skipped)
// Y-coordinate of the celestial point is always in degrees
if (this->_coordFormat != base_ser_angle_t::CFMT_DEGREES) {
auto prev_format = this->_coordFormat.load();
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS);
err = base_ser_angle_t::operator()(output, MccAngle{(double)value.y()});
this->setCoordFormat(prev_format); // restore previous value
} else { // here all angles in deciamal degrees
err = base_ser_angle_t::operator()(output, MccAngle{(double)value.y()});
}
if (err) {
return err;
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
auto ep = value.epoch();
if constexpr (std::convertible_to<decltype(ep), MccCelestialCoordEpoch>) {
auto err_e = base_ser_epoch_t::operator()(output, MccCelestialCoordEpoch{ep});
if (err_e) {
return err_e;
}
} else {
MccCelestialCoordEpoch ep1;
bool ok = ep1.fromMJD(ep.MJD());
if (!ok) {
return MccSerializerErrorCode::ERROR_INVALID_EPOCH;
}
auto err_e = (*this)(output, ep1);
if (err_e) {
return err_e;
}
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
auto err_s = MccSerializer<std::string_view>{}(output, MccCoordPairKindToStr(VT::pairKind));
if (err_s) {
return mcc_deduced_err(err_s, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
}
return MccSerializerErrorCode::ERROR_OK;
}
};
template <typename VT>
requires(mcc_skypoint_c<VT> ||
(std::ranges::input_range<VT> && mcc_skypoint_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
class MccSerializer<VT> : public std::conditional_t<std::ranges::input_range<VT>,
MccSerializer<std::vector<MccCelestialCoordEpoch>>,
MccSerializer<MccCelestialCoordEpoch>>,
public std::conditional_t<std::ranges::input_range<VT>,
MccSerializer<std::vector<MccAngle>>,
MccSerializer<MccAngle>>
{
protected:
typedef std::conditional_t<std::ranges::input_range<VT>,
MccSerializer<std::vector<MccCelestialCoordEpoch>>,
MccSerializer<MccCelestialCoordEpoch>>
base_ser_epoch_t;
typedef std::
conditional_t<std::ranges::input_range<VT>, MccSerializer<std::vector<MccAngle>>, MccSerializer<MccAngle>>
base_ser_angle_t;
public:
using typename details::MccSerializerBase::error_t;
enum SkyPointFormat {
SKYPOINT_FORMAT_FULL, // return RA_ICRS, DEC_ICRS, RA_APP, DEC_APP, RA_OBS, DEC_OBS, AZ, ZD, HA_APP,
// TIME-POINT
SKYPOINT_FORMAT_OBS, // return observed coordinates: RA_OBS, DEC_OBS, AZ, ZD, HA_OBS, TIME-POINT
SKYPOINT_FORMAT_APP, // return apparent (in vacuo) coordinates: RA_APP, DEC_APP, HA_APP, TIME-POINT
SKYPOINT_FORMAT_PAIR // return current stored coordinate pair: X, Y, TIME-POINT, PAIR-KIND
};
virtual ~MccSerializer() = default;
void setSkyPointFormat(SkyPointFormat format)
{
_skptFormat = format;
}
SkyPointFormat getSkyPointFormat() const
{
return _skptFormat;
}
template <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::ranges::input_range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccSerializer<value_t> sr;
sr.setTimePointFormat(this->_tpFormat);
sr.setCoordFormat(this->_coordFormat);
sr.setCoordPrec(this->_coordPrec);
return serializingRange<value_t>(sr, value, output, std::move(fmt));
} else { // scalar
auto serialize_pair_func = [&output, this]<typename PT>(PT const& pt) -> error_t {
MccSerializer<PT> pt_sr;
pt_sr.setTimePointFormat(this->_tpFormat);
pt_sr.setCoordFormat(this->_coordFormat);
pt_sr.setCoordPrec(this->_coordPrec);
auto err = pt_sr(output, pt);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
return MccSerializerErrorCode::ERROR_OK;
};
// WARNING: still ignore format string!!!
auto skpt_fmt = _skptFormat.load();
if (skpt_fmt == SKYPOINT_FORMAT_PAIR) {
switch (value.pairKind()) {
case MccCoordPairKind::COORDS_KIND_RADEC_ICRS:
return serialize_pair_func(value.operator MccSkyRADEC_ICRS());
case MccCoordPairKind::COORDS_KIND_RADEC_APP:
return serialize_pair_func(value.operator MccSkyRADEC_APP());
case MccCoordPairKind::COORDS_KIND_RADEC_OBS:
return serialize_pair_func(value.operator MccSkyRADEC_OBS());
case MccCoordPairKind::COORDS_KIND_HADEC_APP:
return serialize_pair_func(value.operator MccSkyHADEC_APP());
case MccCoordPairKind::COORDS_KIND_HADEC_OBS:
return serialize_pair_func(value.operator MccSkyHADEC_OBS());
case MccCoordPairKind::COORDS_KIND_AZZD:
return serialize_pair_func(value.operator MccSkyAZZD());
case MccCoordPairKind::COORDS_KIND_AZALT:
return serialize_pair_func(value.operator MccSkyAZALT());
case MccCoordPairKind::COORDS_KIND_XY:
return serialize_pair_func(value.operator MccGenXY());
default:
return serialize_pair_func(value.operator MccGenXY()); // ???????!!!!!!!!!!!
}
} else {
MccSkyRADEC_ICRS radec_icrs;
MccSkyRADEC_OBS radec_obs;
MccSkyHADEC_OBS hadec_obs;
MccSkyRADEC_APP radec_app;
MccSkyHADEC_APP hadec_app;
MccSkyAZZD azzd;
// MccGenXY xy;
if (value.pairKind() == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
auto ep_now = MccCelestialCoordEpoch::now();
radec_obs.setEpoch(ep_now);
radec_app.setEpoch(ep_now);
hadec_obs.setEpoch(ep_now);
hadec_app.setEpoch(ep_now);
azzd.setEpoch(ep_now);
} else {
auto ep = value.epoch();
radec_obs.setEpoch(ep);
radec_app.setEpoch(ep);
hadec_obs.setEpoch(ep);
hadec_app.setEpoch(ep);
azzd.setEpoch(ep);
}
auto prev_format = this->_coordFormat.load();
if (skpt_fmt == SKYPOINT_FORMAT_FULL) { // RA_ICRS, DEC_ICRS, RA_APP, DEC_APP
auto err = value.to(radec_icrs, radec_app);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
// RA_ICRS is in sexagesimal hours
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS);
}
auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_icrs.x()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
// DEC_ICRS is in sexagesimal degrees
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS);
}
err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_icrs.y()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
// RA_APP is in sexagesimal hours
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS);
}
err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.x()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
// DEC_APP is in sexagesimal degrees
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS);
}
err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.y()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
}
if (skpt_fmt == SKYPOINT_FORMAT_OBS || skpt_fmt == SKYPOINT_FORMAT_FULL) { // RA_OBS, DEC_OBS, AZ, ZD
auto err = value.to(radec_obs, azzd);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
// RA_OBS is in sexagesimal hours
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS);
}
auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_obs.x()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
// DEC_OBS, AZ and ZD are in sexagesimal degrees
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS);
}
err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_obs.y()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)azzd.x()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)azzd.y()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
}
if (skpt_fmt == SKYPOINT_FORMAT_FULL) { // HA_APP
auto err = value.to(hadec_app);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
// HA_APP is in sexagesimal hours
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS);
}
auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_app.x()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
}
if (skpt_fmt == SKYPOINT_FORMAT_OBS) { // HA_OBS
auto err = value.to(hadec_obs);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
// HA_OBS is in sexagesimal hours
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS);
}
auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_obs.x()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
}
if (skpt_fmt == SKYPOINT_FORMAT_APP) { // RA_APP, DEC_APP, HA_APP
auto err = value.to(radec_app, hadec_app);
if (err) {
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM);
}
// RA_APP is in sexagesimal hours
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS);
}
auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.x()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
// DEC_APP is in sexagesimal degrees
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS);
}
err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.y()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
// HA_APP is in sexagesimal hours
if (prev_format != base_ser_angle_t::CFMT_DEGREES) {
this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS);
}
err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_app.x()});
if (err_ang) {
return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
}
std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter);
}
this->setCoordFormat(prev_format); // restore previous value
// epoch of app/obs coordinates
auto ep = value.epoch();
if constexpr (std::convertible_to<decltype(ep), MccCelestialCoordEpoch>) {
auto err_e = base_ser_epoch_t::operator()(output, MccCelestialCoordEpoch{ep});
if (err_e) {
return err_e;
}
} else {
MccCelestialCoordEpoch ep1;
bool ok = ep1.fromMJD(ep.MJD());
if (!ok) {
return MccSerializerErrorCode::ERROR_INVALID_EPOCH;
}
auto err_e = (*this)(output, ep1);
if (err_e) {
return err_e;
}
}
}
return MccSerializerErrorCode::ERROR_OK;
}
}
protected:
std::atomic<SkyPointFormat> _skptFormat{SKYPOINT_FORMAT_FULL};
};
static_assert(mcc_serializer_c<MccSerializer<float>>, "!!!");
static_assert(mcc_serializer_c<MccSerializer<impl::MccAngle>>, "!!!");
static_assert(std::atomic<MccSerializer<impl::MccAngle>::SerializedCoordFormat>::is_always_lock_free, "!!!");
static_assert(std::atomic<MccSerializer<impl::MccAngle>::SexagesimalCoordPrec>::is_always_lock_free, "!!!");
static_assert(std::formattable<impl::MccAngle, char>, "!!!");
void f()
{
MccSerializer<impl::MccAngle> s;
std::string str;
s(str, impl::MccAngleALT{1.1});
}
} // namespace mcc::impl

View File

@@ -1,7 +1,7 @@
#include <iostream> #include <iostream>
// #include <mcc_ccte_erfa.h> // #include <mcc_ccte_erfa.h>
#include <mcc_coordinate.h> // #include <mcc_coordinate.h>
#include <mcc_deserializer.h> #include <mcc_deserializer.h>
#include <mcc_serializer.h> #include <mcc_serializer.h>
@@ -26,10 +26,13 @@ void serialize(VT const& value)
{ {
MccSerializer<VT> ser; MccSerializer<VT> ser;
std::string s; std::string s;
mcc_serialization_params_t pars{};
pars.norm_sxgm = true;
pars.coordpair_format = mcc::MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG;
auto err = ser(s, value); auto err = ser(s, value, pars);
if (err) { if (err) {
std::cout << "SERIALIZING ERR: " << err << "\n"; std::cout << "SERIALIZING ERR: " << err.message() << "\n";
} else { } else {
std::cout << "SERIALIZED: " << s << "\n"; std::cout << "SERIALIZED: " << s << "\n";
} }
@@ -136,6 +139,9 @@ int main()
std::cout << "\tfor 'double' type: "; std::cout << "\tfor 'double' type: ";
serialize(v); serialize(v);
std::cout << "\tfor 'coord pair' type: ";
serialize(icrs);
std::cout << "\tfor 'coord pair' type: "; std::cout << "\tfor 'coord pair' type: ";
serialize(radec_obs); serialize(radec_obs);

View File

@@ -61,6 +61,17 @@ struct hw_t {
} }
}; };
template <>
struct std::formatter<hw_t::hardware_movement_state_t, char>
: std::formatter<std::underlying_type_t<hw_t::hardware_movement_state_t>, char> {
auto format(hw_t::hardware_movement_state_t e, auto& ctx) const
{
return formatter<std::underlying_type_t<hw_t::hardware_movement_state_t>>::format(
std::underlying_type_t<hw_t::hardware_movement_state_t>(e), ctx);
}
};
static_assert(mcc::mcc_hardware_c<hw_t>, "!!!!!"); static_assert(mcc::mcc_hardware_c<hw_t>, "!!!!!");