Files
mcc/mcc_ser.h
Timur A. Fatkhullin 97b908838c ...
2026-02-06 00:09:15 +03:00

691 lines
24 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 = 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 <typename 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 <typename 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 <typename 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 <typename 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 <typename 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 <typename 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;
}
};
} // namespace mcc::impl