This commit is contained in:
Timur A. Fatkhullin
2026-02-02 02:36:23 +03:00
parent a4d6f17114
commit ae91e7320c
11 changed files with 1311 additions and 103 deletions

View File

@@ -135,8 +135,8 @@ if (USE_BSPLINE_PCM)
endif()
set(MCC_SRC mcc_concepts.h mcc_constants.h mcc_epoch.h mcc_angle.h mcc_coordinate.h mcc_error.h
mcc_traits.h mcc_utils.h
mcc_ccte_iers.h mcc_ccte_iers_default.h mcc_ccte_erfa.h mcc_pzone.h mcc_pzone_container.h mcc_pcm.h mcc_telemetry.h)
mcc_traits.h mcc_utils.h mcc_ccte_iers.h mcc_ccte_iers_default.h mcc_ccte_erfa.h mcc_pzone.h
mcc_pzone_container.h mcc_pcm.h mcc_telemetry.h mcc_serializer.h)
if (USE_SPDLOG)
list(APPEND MCC_SRC mcc_spdlog.h)

View File

@@ -208,6 +208,10 @@ public:
std::lock_guard lock{*_stateMutex};
_currentState = std::move(state);
// update refraction model coefficients
eraRefco(_currentState.meteo.pressure, _currentState.meteo.temperature, _currentState.meteo.humidity,
_currentState.wavelength, &_currentRefractModel.refa, &_currentRefractModel.refb);
}
engine_state_t getStateERFA() const
@@ -770,8 +774,8 @@ protected:
// NOTE: according to definition of astronomical azimuth it is counted from the South through the West, but
// in the ERFA the azimuth is counted from the North through the East!!!
//
*az = impl::MccAngle(a - std::numbers::pi).normalize<impl::MccAngle::NORM_KIND_0_360>();
// *az = MccAngle(a + std::numbers::pi).normalize<MccAngle::NORM_KIND_0_360>();
// *az = impl::MccAngle(a - std::numbers::pi).normalize<impl::MccAngle::NORM_KIND_0_360>();
*az = impl::MccAngle(a + std::numbers::pi).normalize<impl::MccAngle::NORM_KIND_0_360>();
}
if (zd) {

View File

@@ -12,7 +12,7 @@
#include <cstdint>
#include <expected>
// #include <expected>
#include <string_view>
@@ -50,8 +50,9 @@ struct MccNullLogger {
/* AND CLASS METHODS RETURNED VALUE */
template <typename T>
concept mcc_error_c =
std::formattable<T, char> && std::default_initializable<T> && (std::convertible_to<T, bool> || requires(const T t) {
concept mcc_error_c = std::default_initializable<T> && (std::convertible_to<T, bool> || requires(const T t) {
// std::formattable<T, char> && std::default_initializable<T> && (std::convertible_to<T, bool>
// || requires(const T t) {
(bool)T() == false; // default constucted value must be a "non-error"!
});
@@ -67,27 +68,27 @@ DefErrT mcc_deduced_err(ErrT const& err, DefErrT const& default_err)
}
template <typename T, typename VT>
concept mcc_retval_c = requires(T t) {
//
[]<mcc_error_c ErrT>(std::expected<VT, ErrT>) {}(t);
};
// template <typename T, typename VT>
// concept mcc_retval_c = requires(T t) {
// //
// []<mcc_error_c ErrT>(std::expected<VT, ErrT>) {}(t);
// };
// deduce an error from mcc_retval_c and default error value
template <typename VT, mcc_retval_c<VT> RetT, mcc_error_c DefErrT>
DefErrT mcc_deduced_err(RetT const& ret, DefErrT const& default_err)
{
if (ret) {
return DefErrT{}; // no error
}
// // deduce an error from mcc_retval_c and default error value
// template <typename VT, mcc_retval_c<VT> RetT, mcc_error_c DefErrT>
// DefErrT mcc_deduced_err(RetT const& ret, DefErrT const& default_err)
// {
// if (ret) {
// return DefErrT{}; // no error
// }
if constexpr (std::same_as<typename RetT::error_type, DefErrT>) {
return ret.error();
} else {
return default_err;
}
}
// if constexpr (std::same_as<typename RetT::error_type, DefErrT>) {
// return ret.error();
// } else {
// return default_err;
// }
// }
/* MOUNT CONSTRUCTION-RELATED STUFF */
@@ -374,7 +375,7 @@ struct mcc_coord_pair_interface_t {
};
template <typename T>
concept mcc_coord_pair_c = std::derived_from<T, mcc_coord_pair_interface_t> && requires(T t) {
concept mcc_coord_pair_c = std::derived_from<T, mcc_coord_pair_interface_t> && requires(T t, const T t_const) {
// the 'T' class must contain static constexpr member of 'pairKind' of some type
// (usually just a enum: see mcc_coordinate.h for an example of the implementation)
[]() {
@@ -386,8 +387,10 @@ concept mcc_coord_pair_c = std::derived_from<T, mcc_coord_pair_interface_t> && r
// std::constructible_from<T, typename T::x_t const&, typename T::y_t const&>;
{ t.x() } -> std::same_as<typename T::x_t>;
{ t.y() } -> std::same_as<typename T::y_t>;
{ t_const.x() } -> std::same_as<typename T::x_t>;
{ t_const.y() } -> std::same_as<typename T::y_t>;
{ t_const.epoch() } -> mcc_coord_epoch_c;
{ t.setX(std::declval<typename T::x_t const&>()) };
{ t.setY(std::declval<typename T::y_t const&>()) };

View File

@@ -31,5 +31,12 @@ static constexpr DT MCC_INFINITE_DURATION_V =
: DT{std::numeric_limits<typename DT::rep>::max()};
// serializer/deserializer delimiters
// delimiter between items of serializing values sequence
static constexpr std::string_view MCC_DEFAULT_SEQ_DELIMITER{";"};
// delimiter between items of aggregative (multi-element) serializing value
static constexpr std::string_view MCC_DEFAULT_ELEM_DELIMITER{","};
} // namespace mcc

View File

@@ -69,6 +69,7 @@ public:
template <mcc_coord_epoch_c EpT = MccCelestialCoordEpoch>
MccCoordPair(CO_LON_T const& x, CO_LAT_T const& y, EpT const& epoch = EpT::now()) : _x(x), _y(y), _epoch(epoch)
{
normalize();
}
MccCoordPair(const MccCoordPair&) = default;
@@ -85,6 +86,8 @@ public:
setX((double)other.x());
setY((double)other.y());
setEpoch(other.epoch());
normalize();
}
@@ -96,6 +99,8 @@ public:
setX((double)other.x());
setY((double)other.y());
setEpoch(other.epoch());
normalize();
}
@@ -107,6 +112,8 @@ public:
setX((double)other.x());
setY((double)other.y());
setEpoch(other.epoch());
normalize();
}
@@ -118,6 +125,8 @@ public:
setX((double)other.x());
setY((double)other.y());
setEpoch(other.epoch());
normalize();
}
@@ -161,11 +170,15 @@ public:
void setX(const CO_LON_T& x)
{
_x = x;
normalize();
}
void setY(const CO_LAT_T& y)
{
_y = y;
normalize();
}
void setEpoch(mcc_coord_epoch_c auto const& ep)
@@ -178,6 +191,26 @@ protected:
CO_LAT_T _y;
MccCelestialCoordEpoch _epoch;
void normalize()
{
if constexpr (pairKind != MccCoordPairKind::COORDS_KIND_GENERIC &&
pairKind != MccCoordPairKind::COORDS_KIND_XY) {
if constexpr (pairKind == MccCoordPairKind::COORDS_KIND_HADEC_APP ||
pairKind == MccCoordPairKind::COORDS_KIND_HADEC_OBS) {
_x = (double)MccAngle(_x).normalize<MccAngle::NORM_KIND_180_180>();
} else { // RA, AZ
_x = (double)MccAngle(_x).normalize<MccAngle::NORM_KIND_0_360>();
}
// DEC and ALT is [-90,90] degrees
if constexpr (pairKind != MccCoordPairKind::COORDS_KIND_AZZD) {
_y = (double)MccAngle(_y).normalize<MccAngle::NORM_KIND_90_90>();
} else { // ZD id [0, 180] degrees
_y = (double)MccAngle(_y).normalize<MccAngle::NORM_KIND_0_180>();
}
}
}
};
@@ -436,7 +469,7 @@ public:
template <mcc_coord_pair_c PT>
operator PT()
operator PT() const
{
if constexpr (PT::pairKind == MccCoordPairKind::COORDS_KIND_LONLAT) { // returns geographic site coordinates
std::pair<double, double> pos;
@@ -447,7 +480,7 @@ public:
PT res;
to(res);
toAtSameEpoch(res);
return res;
}
@@ -457,8 +490,14 @@ public:
error_t toAtSameEpoch(PT& cpair, PTs&... cpairs) const
{
if constexpr (PT::pairKind != MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
if (_pairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { // from ICRS: set current epoch for result
cpair.setEpoch(MccCelestialCoordEpoch::now());
} else {
cpair.setEpoch(_epoch);
}
}
auto err = toHelper(cpair);
if (err) {
return err;
@@ -620,6 +659,16 @@ protected:
// HA, DEC to AZ, ALT (AZ from the South through the West)
void hadec2azalt(double ha, double dec, double phi, double& az, double& alt) const
{
eraHd2ae(ha, dec, phi, &az, &alt);
// from ERFA "from N" to "from S"
if (az > std::numbers::pi) {
az -= std::numbers::pi;
} else {
az += std::numbers::pi;
}
return;
const auto cos_phi = std::cos(phi), sin_phi = std::sin(phi);
const auto cos_dec = std::cos(dec), sin_dec = std::sin(dec);
const auto cos_ha = std::cos(ha), sin_ha = std::sin(ha);
@@ -638,7 +687,6 @@ protected:
az = utils::isEqual(r, 0.0) ? 0.0 : std::atan2(y, x);
if (az < 0.0) {
// az += std::numbers::pi * 2.0; // to range of [0, 2*PI]
az += MCC_TWO_PI; // to range of [0, 2*PI]
}
@@ -649,6 +697,11 @@ protected:
// AZ, ALT to HA, DEC (AZ from the South through the West)
void azalt2hadec(double az, double alt, double phi, double& ha, double& dec) const
{
az += std::numbers::pi;
eraAe2hd(az, alt, phi, &ha, &dec);
return;
const auto cos_phi = std::cos(phi), sin_phi = std::sin(phi);
const auto cos_az = std::cos(az), sin_az = std::sin(az);
const auto cos_alt = std::cos(alt), sin_alt = std::sin(alt);
@@ -724,6 +777,11 @@ protected:
}
}
if (utils::isEqual(ra_icrs, MCC_TWO_PI)) {
ra_icrs = 0.0;
}
// here, from APP or OBS to ICRS and exit
if (pkind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS &&
PT::pairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
@@ -896,7 +954,26 @@ protected:
return error_t{};
};
return comp_func(pkind); // ran transformation
auto err = comp_func(pkind); // ran transformation
if (ra < 0.0) {
ra += MCC_TWO_PI;
cpair.setX(ra);
}
if (az < 0.0) {
az += MCC_TWO_PI;
cpair.setX(az);
}
// if (utils::isEqual(ra, MCC_TWO_PI)) {
// cpair.setX(0.0);
// }
// if (utils::isEqual(az, MCC_TWO_PI)) {
// cpair.setX(0.0);
// }
return err;
}
};

279
mcc_deserializer.h Normal file
View File

@@ -0,0 +1,279 @@
#pragma once
/****************************************************************************************
* *
* MOUNT CONTROL COMPONENTS LIBRARY *
* *
* *
* IMPLEMENTATION OF DESERIALIZER CLASSES *
* *
****************************************************************************************/
#include "mcc_concepts.h"
#include "mcc_constants.h"
#include "mcc_error.h"
namespace mcc::impl
{
enum class MccDeserializerErrorCode : int {
ERROR_OK,
ERROR_UNDERLYING_DESERIALIZER,
ERROR_INVALID_SERIAL_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_SERIAL_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>
RetT operator()(this SelfT&& self, R const& input, ValueT& value)
{
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> {
using typename mcc_deserializer_interface_t<impl::MccError>::error_t;
virtual ~MccDeserializerBase() = default;
// set delimiter for elements of the range-type serialized value
template <traits::mcc_input_char_range R>
void setSeqDelimiter(R const& delim)
{
if constexpr (std::is_array_v<std::decay_t<R>>) {
setSeqDelimiter(std::string_view{delim});
} else {
_seqDelimiter.clear();
std::ranges::copy(delim, std::back_inserter(_seqDelimiter));
}
}
template <traits::mcc_view_or_output_char_range R>
R getSeqDelimiter() const
{
if constexpr (std::same_as<R, decltype(_seqDelimiter)>) {
return _seqDelimiter;
} else if constexpr (std::ranges::view<R>) {
return R{_seqDelimiter.begin(), _seqDelimiter.end()};
} else {
R r;
std::ranges::copy(_seqDelimiter, std::back_inserter(r));
return r;
}
}
std::string getSeqDelimiter() const
{
return getSeqDelimiter<std::string>();
}
template <traits::mcc_input_char_range R>
void setElemDelimiter(R const& delim)
{
if constexpr (std::is_array_v<std::decay_t<R>>) {
setElemDelimiter(std::string_view{delim});
} else {
_elementDelimiter.clear();
std::ranges::copy(delim, std::back_inserter(_seqDelimiter));
}
}
template <traits::mcc_view_or_output_char_range R>
R getElemDelimiter() const
{
if constexpr (std::same_as<R, decltype(_seqDelimiter)>) {
return _elementDelimiter;
} else if constexpr (std::ranges::view<R>) {
return R{_elementDelimiter.begin(), _elementDelimiter.end()};
} else {
R r;
std::ranges::copy(_elementDelimiter, std::back_inserter(r));
return r;
}
}
std::string getElemDelimiter() const
{
return getElemDelimiter<std::string>();
}
protected:
MccDeserializerBase() = default;
// delimiter for sequence of serialized values
std::string _seqDelimiter{MCC_DEFAULT_SEQ_DELIMITER};
// delimiter for aggregative (multi-element) serialized value
std::string _elementDelimiter{MCC_DEFAULT_ELEM_DELIMITER};
template <typename VT, typename R>
requires(std::ranges::input_range<R> && std::same_as<VT, std::ranges::range_value_t<R>>)
error_t deserializingRange(mcc_deserializer_c auto& dsr, traits::mcc_input_char_range auto const& input, R& r) const
{
if (std::ranges::size(input) == 0) { // ignore empy input
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);
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
//
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>(trimSpaces(input));
if (!v.has_value()) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIAL_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 {
static_assert(false, "UNSUPPORTED VALUE TYPE!!!");
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
} // namespace mcc::impl

View File

@@ -108,17 +108,17 @@ private:
static_assert(mcc::mcc_error_c<mcc::impl::MccError>, "");
// a helper formatter impelementation
// // a helper formatter impelementation
template <typename T>
requires std::is_enum_v<T>
struct std::formatter<T, char> : std::formatter<std::underlying_type_t<T>, char> {
auto format(T e, auto& ctx) const
{
return formatter<std::underlying_type_t<T>>::format(std::underlying_type_t<T>(e), ctx);
}
};
// template <typename T>
// requires std::is_enum_v<T>
// struct std::formatter<T, char> : std::formatter<std::underlying_type_t<T>, char> {
// auto format(T e, auto& ctx) const
// {
// return formatter<std::underlying_type_t<T>>::format(std::underlying_type_t<T>(e), ctx);
// }
// };
enum class EE : int { A, B, C };
// enum class EE : int { A, B, C };
static_assert(mcc::mcc_error_c<EE>, "");
// static_assert(mcc::mcc_error_c<EE>, "");

View File

@@ -1,45 +1,241 @@
#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
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>)
mcc_error_c auto operator()(this SelfT&& self, R& output, ValueT const& value, FmtT fmt)
RetT operator()(this SelfT&& self, R& output, ValueT const& value, FmtT fmt)
{
return std::forward<SelfT>(self).operator=(output, value, std::move(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>;
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> {
using typename mcc_serializer_interface_t<impl::MccError>::error_t;
virtual ~MccSerializerBase() = default;
// set delimiter for elements of the range-type serialized value
template <traits::mcc_input_char_range R>
void setSeqDelimiter(R const& delim)
{
if constexpr (std::is_array_v<std::decay_t<R>>) {
setSeqDelimiter(std::string_view{delim});
} else {
_seqDelimiter.clear();
std::ranges::copy(delim, std::back_inserter(_seqDelimiter));
}
}
template <traits::mcc_view_or_output_char_range R>
R getSeqDelimiter() const
{
if constexpr (std::same_as<R, decltype(_seqDelimiter)>) {
return _seqDelimiter;
} else if constexpr (std::ranges::view<R>) {
return R{_seqDelimiter.begin(), _seqDelimiter.end()};
} else {
R r;
std::ranges::copy(_seqDelimiter, std::back_inserter(r));
return r;
}
}
std::string getSeqDelimiter() const
{
return getSeqDelimiter<std::string>();
}
template <traits::mcc_input_char_range R>
void setElemDelimiter(R const& delim)
{
if constexpr (std::is_array_v<std::decay_t<R>>) {
setElemDelimiter(std::string_view{delim});
} else {
_elementDelimiter.clear();
std::ranges::copy(delim, std::back_inserter(_seqDelimiter));
}
}
template <traits::mcc_view_or_output_char_range R>
R getElemDelimiter() const
{
if constexpr (std::same_as<R, decltype(_seqDelimiter)>) {
return _elementDelimiter;
} else if constexpr (std::ranges::view<R>) {
return R{_elementDelimiter.begin(), _elementDelimiter.end()};
} else {
R r;
std::ranges::copy(_elementDelimiter, std::back_inserter(r));
return r;
}
}
std::string getElemDelimiter() const
{
return getElemDelimiter<std::string>();
}
protected:
MccSerializerBase() = default;
// delimiter for sequence of serializing values
std::string _seqDelimiter{MCC_DEFAULT_SEQ_DELIMITER};
// delimiter for aggregative (multi-element) serializing value
std::string _elementDelimiter{MCC_DEFAULT_ELEM_DELIMITER};
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 mcc_serializer_interface_t
class MccSerializer : public details::MccSerializerBase
// class MccSerializer : public mcc_serializer_interface_t<impl::MccError>
{
public:
// default delimiter
static constexpr std::string_view defaultDelimiter{","};
static constexpr std::string_view defaultRangeDelimiter{","};
typedef impl::MccError error_t;
// typedef impl::MccError error_t;
MccSerializer() = default;
@@ -52,15 +248,19 @@ public:
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::format_to(std::back_inserter(output), fmt, value);
} else if constexpr (std::same_as<std::format_string<VT>, 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>) {
return MccSerializer<std::string>{}(output, static_cast<std::string>(value), std::move(fmt));
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>>;
@@ -69,37 +269,77 @@ public:
std::string str;
std::ranges::copy(value, std::back_inserter(str));
return MccSerializer<std::string>{}(output, str, std::move(fmt));
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;
size_t i = 0, N = std::ranges::size(value);
for (auto const& el : value) {
error_t err = sr(output, el, std::move(fmt));
if (err) {
return err;
}
if (++i < N) {
std::format_to(std::back_inserter(output), "{}", _delimiter);
}
}
return serializingRange<value_t>(sr, value, output, std::move(fmt));
}
} else {
static_assert(false, "UNSUPPORTED TYPE!!!");
}
return {};
return MccSerializerErrorCode::ERROR_OK;
}
protected:
std::string _delimiter{defaultDelimiter};
};
/* SPECIALIZATION FOR THE SOME CONCEPTS */
template <typename VT>
requires(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 mcc_serializer_interface_t
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 {
@@ -113,7 +353,9 @@ public:
uint8_t deg_prec = 1; // number of decimal places in arcseconds
};
typedef impl::MccError error_t;
// typedef impl::MccError error_t;
virtual ~MccSerializer() = default;
void setCoordFormat(SerializedCoordFormat format)
{
@@ -140,24 +382,12 @@ public:
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::ranges::input_range<VT>) {
MccSerializer<std::remove_cv_t<std::ranges::range_value_t<VT>>> sr;
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
MccSerializer<value_t> sr;
sr.setCoordFormat(_coordFormat);
sr.setCoordPrec(_coordPrec);
error_t err;
size_t i = 0, N = std::ranges::size(value);
for (auto const& el : value) {
err = sr(output, el, std::move(fmt));
if (err) {
return err;
}
if (++i < N) {
std::format_to(std::back_inserter(output), " ");
}
}
return error_t{};
return serializingRange<value_t>(sr, value, output, std::move(fmt));
} else { // scalar
double v = (double)value; // radians
std::string sgm;
@@ -167,27 +397,565 @@ public:
v *= 180.0 / std::numbers::pi;
return MccSerializer<double>{}(output, v, std::move(fmt));
case MccSerializer::CFMT_SGM_HOURS:
sgm = utils::rad2sxg(value, true, _coordPrec.load().hour_prec);
sgm = utils::rad2sxg(v, true, _coordPrec.load().hour_prec);
break;
case MccSerializer::CFMT_SGM_DEGS:
sgm = utils::rad2sxg(value, false, _coordPrec.load().deg_prec);
sgm = utils::rad2sxg(v, false, _coordPrec.load().deg_prec);
break;
default:
break;
}
return MccSerializer<std::string>{}(output, sgm, std::move(fmt));
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_DEGREES};
std::atomic<SerializedCoordFormat> _coordFormat{MccSerializer::CFMT_SGM_DEGS};
std::atomic<SexagesimalCoordPrec> _coordPrec{SexagesimalCoordPrec{.hour_prec = 2, .deg_prec = 1}};
};
template <mcc_skypoint_c VT>
class MccSerializer<VT> : public mcc_serializer_interface_t
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};
};
@@ -199,7 +967,7 @@ 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(traits::mcc_formattable<impl::MccAngle>, "!!!");
static_assert(std::formattable<impl::MccAngle, char>, "!!!");
void f()
{
@@ -209,4 +977,4 @@ void f()
s(str, impl::MccAngleALT{1.1});
}
} // namespace mcc
} // namespace mcc::impl

View File

@@ -60,9 +60,9 @@ concept mcc_range_of_output_char_range =
// https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
template <typename T>
concept mcc_formattable = requires(T v) { [](T vv) { return std::format("{}", vv); }(v); };
// concept mcc_formattable =
// requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
concept mcc_formattable =
requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
// concept mcc_formattable = requires(T v) { [](T vv) { return std::format("{}", vv); }(v); };
// from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types

View File

@@ -247,6 +247,17 @@ static R rad2sxg(double ang, bool hms = false, int prec = 2)
// round to given precision of arcseconds/seconds
degs = std::round(degs * 3600.0 * term) / term / 3600.0;
if constexpr (NORM) {
if (hms) {
if (isEqual(degs, 24.0)) {
degs = 0.0;
}
} else {
if (isEqual(degs, 360.0)) {
degs = 0.0;
}
}
}
auto d = std::trunc(degs);
auto s = (degs - d) * 60.0;

View File

@@ -2,7 +2,7 @@
// #include <mcc_ccte_erfa.h>
#include <mcc_coordinate.h>
#include <mcc_serializer.h>
using namespace mcc::impl;
@@ -19,6 +19,21 @@ static skypt_t::ccte_t::engine_state_t saoras{.meteo{.temperature = 0.0, .humidi
static_assert(mcc::mcc_angle_c<double>, "!!!!!!!!!!!!");
template <typename VT>
void serialize(VT const& value)
{
MccSerializer<VT> ser;
std::string s;
auto err = ser(s, value);
if (err) {
std::cout << "SERIALIZing ERR: " << err << "\n";
} else {
std::cout << "SERIALIZED: " << s << "\n";
}
}
int main()
{
skypt_t::cctEngine.setStateERFA(saoras);
@@ -60,9 +75,12 @@ int main()
azzd.setX(111.0_degs), azzd.setY(111.0_degs);
azalt.setX(111.0_degs), azalt.setY(111.0_degs);
hadec_obs.setX(111.0_degs), hadec_obs.setY(111.0_degs);
radec_app.setX(111.0_degs), radec_app.setY(111.0_degs);
pt = radec_obs;
pt.to(icrs, azzd, hadec_obs);
// pt.to(icrs, azzd, hadec_obs, radec_app);
pt.to(icrs, radec_app);
pt.to(radec_obs, hadec_obs, azzd);
std::cout << "\n\nFROM OBSERVED TO ICRS:\n";
std::cout << "OBS COORD EPOCH: " << radec_obs.epoch().UTC() << "\n";
@@ -78,9 +96,50 @@ int main()
std::cout << "HA_OBS = " << hadec_obs.x().sexagesimal(true) << "\n";
std::cout << "AZ = " << azzd.x().sexagesimal() << "\n";
std::cout << "ZD = " << azzd.y().sexagesimal() << "\n";
std::cout << "RA_APP = " << radec_app.x().sexagesimal(true) << "\n";
std::cout << "DEC_APP = " << radec_app.y().sexagesimal() << "\n";
azalt = pt;
std::cout << "ALT = " << azalt.y().sexagesimal() << "\n";
std::cout << "\nIN DEGREES:\n";
std::cout << "RA_ICRS = " << icrs.x().degrees() << "\n";
std::cout << "DEC_ICRS = " << icrs.y().degrees() << "\n";
std::cout << "RA_OBS = " << radec_obs.x().degrees() << "\n";
std::cout << "DEC_OBS = " << radec_obs.y().degrees() << "\n";
std::cout << "HA_OBS = " << hadec_obs.x().degrees() << "\n";
std::cout << "AZ = " << azzd.x().degrees() << "\n";
std::cout << "ZD = " << azzd.y().degrees() << "\n";
std::cout << "RA_APP = " << radec_app.x().degrees() << "\n";
std::cout << "DEC_APP = " << radec_app.y().degrees() << "\n";
std::cout << "\n\nSERIALIZATION TEST:\n";
double v = 7.7;
std::cout << "\tfor 'double' type: ";
serialize(v);
std::cout << "\tfor 'coord pair' type: ";
serialize(radec_obs);
radec_app = pt;
// pt.to(radec_app);
std::cout << "\tfor 'coord pair' type: ";
serialize(radec_app);
azzd = pt;
// pt.to(azzd);
std::cout << "\tfor 'coord pair' type: ";
serialize(azzd);
// pt.from(icrs);
pt = radec_obs;
std::cout << "\tfor 'sky point' type: ";
serialize(pt);
return 0;
}