Files
mcc/mcc_ser.h
2026-02-04 18:29:08 +03:00

448 lines
16 KiB
C++

#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 <typename ParamsT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, ParamsT const& params = nullptr)
{
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 <typename ParamsT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, ParamsT const& params = nullptr)
{
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 <typename ParamsT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, ParamsT const& params = nullptr)
{
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 <typename ParamsT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, ParamsT const& params = nullptr)
{
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 <typename ParamsT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, ParamsT const& params = nullptr)
{
std::conditional_t<std::is_null_pointer_v<ParamsT>, std::nullptr_t, mcc_serialization_params_t> pars =
std::is_null_pointer_v<ParamsT> ? nullptr : 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 <typename ParamsT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, ParamsT const& params = nullptr)
{
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 <typename ParamsT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, ParamsT const& params = nullptr)
{
// FORMAT: RA_OBS_MOUNT, DEC_OBS_MOUNT, RA_APP_MOUNT, DEC_APP_MOUNT, HA_APP_MOUNT, AZ_MOUNT, ZD_MOUNT,
// REFR_CORR_MOUNT, LAST, ENC_X, ENC_Y, PCM_X, PCM_Y, RA_APP_TAG, DEC_APP_TAG, AZ_TAG, ZD_TAG, TIMEPOINT
// 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
std::conditional_t<std::is_null_pointer_v<ParamsT>, std::nullptr_t, mcc_serialization_params_t> pars =
std::is_null_pointer_v<ParamsT> ? nullptr : params;
pars.norm_sxgm = true;
pars.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG;
// RA_OBS_MOUNT
MccSerializerBase::angleFormatFromCoordPairType<VT::pairKind, MccSerializerBase::CO_LON>(pars);
}
};
} // namespace mcc::impl