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() endif()
set(MCC_SRC mcc_concepts.h mcc_constants.h mcc_epoch.h mcc_angle.h mcc_coordinate.h mcc_error.h 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_traits.h mcc_utils.h mcc_ccte_iers.h mcc_ccte_iers_default.h mcc_ccte_erfa.h mcc_pzone.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_pzone_container.h mcc_pcm.h mcc_telemetry.h mcc_serializer.h)
if (USE_SPDLOG) if (USE_SPDLOG)
list(APPEND MCC_SRC mcc_spdlog.h) list(APPEND MCC_SRC mcc_spdlog.h)

View File

@@ -208,6 +208,10 @@ public:
std::lock_guard lock{*_stateMutex}; std::lock_guard lock{*_stateMutex};
_currentState = std::move(state); _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 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 // 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!!! // 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 = 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>();
} }
if (zd) { if (zd) {

View File

@@ -12,7 +12,7 @@
#include <cstdint> #include <cstdint>
#include <expected> // #include <expected>
#include <string_view> #include <string_view>
@@ -50,10 +50,11 @@ struct MccNullLogger {
/* AND CLASS METHODS RETURNED VALUE */ /* AND CLASS METHODS RETURNED VALUE */
template <typename T> template <typename T>
concept mcc_error_c = 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) { // std::formattable<T, char> && std::default_initializable<T> && (std::convertible_to<T, bool>
(bool)T() == false; // default constucted value must be a "non-error"! // || requires(const T t) {
}); (bool)T() == false; // default constucted value must be a "non-error"!
});
template <mcc_error_c ErrT, mcc_error_c DefErrT> template <mcc_error_c ErrT, mcc_error_c DefErrT>
@@ -67,27 +68,27 @@ DefErrT mcc_deduced_err(ErrT const& err, DefErrT const& default_err)
} }
template <typename T, typename VT> // template <typename T, typename VT>
concept mcc_retval_c = requires(T t) { // concept mcc_retval_c = requires(T t) {
// // //
[]<mcc_error_c ErrT>(std::expected<VT, ErrT>) {}(t); // []<mcc_error_c ErrT>(std::expected<VT, ErrT>) {}(t);
}; // };
// deduce an error from mcc_retval_c and default error value // // deduce an error from mcc_retval_c and default error value
template <typename VT, mcc_retval_c<VT> RetT, mcc_error_c DefErrT> // template <typename VT, mcc_retval_c<VT> RetT, mcc_error_c DefErrT>
DefErrT mcc_deduced_err(RetT const& ret, DefErrT const& default_err) // DefErrT mcc_deduced_err(RetT const& ret, DefErrT const& default_err)
{ // {
if (ret) { // if (ret) {
return DefErrT{}; // no error // return DefErrT{}; // no error
} // }
if constexpr (std::same_as<typename RetT::error_type, DefErrT>) { // if constexpr (std::same_as<typename RetT::error_type, DefErrT>) {
return ret.error(); // return ret.error();
} else { // } else {
return default_err; // return default_err;
} // }
} // }
/* MOUNT CONSTRUCTION-RELATED STUFF */ /* MOUNT CONSTRUCTION-RELATED STUFF */
@@ -374,7 +375,7 @@ struct mcc_coord_pair_interface_t {
}; };
template <typename 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 // 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) // (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&>; // std::constructible_from<T, typename T::x_t const&, typename T::y_t const&>;
{ t.x() } -> std::same_as<typename T::x_t>; { t_const.x() } -> std::same_as<typename T::x_t>;
{ t.y() } -> std::same_as<typename T::y_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.setX(std::declval<typename T::x_t const&>()) };
{ t.setY(std::declval<typename T::y_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()}; : 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 } // namespace mcc

View File

@@ -69,6 +69,7 @@ public:
template <mcc_coord_epoch_c EpT = MccCelestialCoordEpoch> 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) 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; MccCoordPair(const MccCoordPair&) = default;
@@ -85,6 +86,8 @@ public:
setX((double)other.x()); setX((double)other.x());
setY((double)other.y()); setY((double)other.y());
setEpoch(other.epoch()); setEpoch(other.epoch());
normalize();
} }
@@ -96,6 +99,8 @@ public:
setX((double)other.x()); setX((double)other.x());
setY((double)other.y()); setY((double)other.y());
setEpoch(other.epoch()); setEpoch(other.epoch());
normalize();
} }
@@ -107,6 +112,8 @@ public:
setX((double)other.x()); setX((double)other.x());
setY((double)other.y()); setY((double)other.y());
setEpoch(other.epoch()); setEpoch(other.epoch());
normalize();
} }
@@ -118,6 +125,8 @@ public:
setX((double)other.x()); setX((double)other.x());
setY((double)other.y()); setY((double)other.y());
setEpoch(other.epoch()); setEpoch(other.epoch());
normalize();
} }
@@ -161,11 +170,15 @@ public:
void setX(const CO_LON_T& x) void setX(const CO_LON_T& x)
{ {
_x = x; _x = x;
normalize();
} }
void setY(const CO_LAT_T& y) void setY(const CO_LAT_T& y)
{ {
_y = y; _y = y;
normalize();
} }
void setEpoch(mcc_coord_epoch_c auto const& ep) void setEpoch(mcc_coord_epoch_c auto const& ep)
@@ -178,6 +191,26 @@ protected:
CO_LAT_T _y; CO_LAT_T _y;
MccCelestialCoordEpoch _epoch; 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> template <mcc_coord_pair_c PT>
operator PT() operator PT() const
{ {
if constexpr (PT::pairKind == MccCoordPairKind::COORDS_KIND_LONLAT) { // returns geographic site coordinates if constexpr (PT::pairKind == MccCoordPairKind::COORDS_KIND_LONLAT) { // returns geographic site coordinates
std::pair<double, double> pos; std::pair<double, double> pos;
@@ -447,7 +480,7 @@ public:
PT res; PT res;
to(res); toAtSameEpoch(res);
return res; return res;
} }
@@ -457,8 +490,14 @@ public:
error_t toAtSameEpoch(PT& cpair, PTs&... cpairs) const error_t toAtSameEpoch(PT& cpair, PTs&... cpairs) const
{ {
if constexpr (PT::pairKind != MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { if constexpr (PT::pairKind != MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
cpair.setEpoch(_epoch); 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); auto err = toHelper(cpair);
if (err) { if (err) {
return err; return err;
@@ -620,6 +659,16 @@ protected:
// HA, DEC to AZ, ALT (AZ from the South through the West) // 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 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_phi = std::cos(phi), sin_phi = std::sin(phi);
const auto cos_dec = std::cos(dec), sin_dec = std::sin(dec); const auto cos_dec = std::cos(dec), sin_dec = std::sin(dec);
const auto cos_ha = std::cos(ha), sin_ha = std::sin(ha); 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); az = utils::isEqual(r, 0.0) ? 0.0 : std::atan2(y, x);
if (az < 0.0) { 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] 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) // 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 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_phi = std::cos(phi), sin_phi = std::sin(phi);
const auto cos_az = std::cos(az), sin_az = std::sin(az); const auto cos_az = std::cos(az), sin_az = std::sin(az);
const auto cos_alt = std::cos(alt), sin_alt = std::sin(alt); 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 // here, from APP or OBS to ICRS and exit
if (pkind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS && if (pkind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS &&
PT::pairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { PT::pairKind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
@@ -896,7 +954,26 @@ protected:
return error_t{}; 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>, ""); static_assert(mcc::mcc_error_c<mcc::impl::MccError>, "");
// a helper formatter impelementation // // a helper formatter impelementation
template <typename T> // template <typename T>
requires std::is_enum_v<T> // requires std::is_enum_v<T>
struct std::formatter<T, char> : std::formatter<std::underlying_type_t<T>, char> { // struct std::formatter<T, char> : std::formatter<std::underlying_type_t<T>, char> {
auto format(T e, auto& ctx) const // auto format(T e, auto& ctx) const
{ // {
return formatter<std::underlying_type_t<T>>::format(std::underlying_type_t<T>(e), ctx); // 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 #pragma once
/****************************************************************************************
* *
* MOUNT CONTROL COMPONENTS LIBRARY *
* *
* *
* IMPLEMENTATION OF SERIALIZER CLASSES *
* *
****************************************************************************************/
#include <atomic> #include <atomic>
#include <concepts> #include <concepts>
#include "mcc_concepts.h" #include "mcc_concepts.h"
#include "mcc_constants.h"
#include "mcc_coordinate.h"
#include "mcc_epoch.h"
#include "mcc_error.h" #include "mcc_error.h"
#include "mcc_traits.h" #include "mcc_traits.h"
#include "mcc_utils.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 { struct mcc_serializer_interface_t {
virtual ~mcc_serializer_interface_t() = default; virtual ~mcc_serializer_interface_t() = default;
typedef RetT error_t;
template <std::derived_from<mcc_serializer_interface_t> SelfT, template <std::derived_from<mcc_serializer_interface_t> SelfT,
traits::mcc_output_char_range R, traits::mcc_output_char_range R,
typename ValueT, typename ValueT,
typename FmtT> typename FmtT>
requires(std::same_as<std::format_string<ValueT>, FmtT> || std::same_as<std::string_view, 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> 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 */ /* fallback template */
template <typename VT> 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: public:
// default delimiter // 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; MccSerializer() = default;
@@ -52,15 +248,19 @@ public:
if constexpr (std::is_null_pointer_v<FmtT>) { if constexpr (std::is_null_pointer_v<FmtT>) {
std::format_to(std::back_inserter(output), "{}", value); std::format_to(std::back_inserter(output), "{}", value);
} else if constexpr (traits::mcc_input_char_range<FmtT>) { } 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::string_view sfmt{fmt.begin(), fmt.end()};
std::vformat_to(std::back_inserter(output), sfmt, std::make_format_args(value)); 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 { } else {
static_assert(false, "INVALID FORMAT STRING TYPE!!!"); static_assert(false, "INVALID FORMAT STRING TYPE!!!");
} }
} else if constexpr (std::convertible_to<VT, std::string>) { } 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>) { } else if constexpr (std::ranges::range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>; using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
@@ -69,37 +269,77 @@ public:
std::string str; std::string str;
std::ranges::copy(value, std::back_inserter(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 { } else {
MccSerializer<value_t> sr; MccSerializer<value_t> sr;
size_t i = 0, N = std::ranges::size(value); return serializingRange<value_t>(sr, value, output, std::move(fmt));
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);
}
}
} }
} else {
static_assert(false, "UNSUPPORTED TYPE!!!");
} }
return {}; return MccSerializerErrorCode::ERROR_OK;
} }
protected:
std::string _delimiter{defaultDelimiter};
}; };
/* SPECIALIZATION FOR THE SOME CONCEPTS */ /* SPECIALIZATION FOR THE SOME CONCEPTS */
template <typename VT> template <typename VT>
requires(mcc_angle_c<VT> || requires(traits::mcc_systime_c<VT> ||
(std::ranges::input_range<VT> && mcc_angle_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>)) (std::ranges::input_range<VT> && traits::mcc_systime_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
class MccSerializer<VT> : public mcc_serializer_interface_t 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: public:
enum SerializedCoordFormat { enum SerializedCoordFormat {
@@ -113,7 +353,9 @@ public:
uint8_t deg_prec = 1; // number of decimal places in arcseconds 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) 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) error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{ {
if constexpr (std::ranges::input_range<VT>) { 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.setCoordFormat(_coordFormat);
sr.setCoordPrec(_coordPrec); sr.setCoordPrec(_coordPrec);
error_t err; return serializingRange<value_t>(sr, value, output, std::move(fmt));
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{};
} else { // scalar } else { // scalar
double v = (double)value; // radians double v = (double)value; // radians
std::string sgm; std::string sgm;
@@ -167,27 +397,565 @@ public:
v *= 180.0 / std::numbers::pi; v *= 180.0 / std::numbers::pi;
return MccSerializer<double>{}(output, v, std::move(fmt)); return MccSerializer<double>{}(output, v, std::move(fmt));
case MccSerializer::CFMT_SGM_HOURS: case MccSerializer::CFMT_SGM_HOURS:
sgm = utils::rad2sxg(value, true, _coordPrec.load().hour_prec); sgm = utils::rad2sxg(v, true, _coordPrec.load().hour_prec);
break; break;
case MccSerializer::CFMT_SGM_DEGS: case MccSerializer::CFMT_SGM_DEGS:
sgm = utils::rad2sxg(value, false, _coordPrec.load().deg_prec); sgm = utils::rad2sxg(v, false, _coordPrec.load().deg_prec);
break; break;
default: default:
break; 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: 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}}; 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>::SerializedCoordFormat>::is_always_lock_free, "!!!");
static_assert(std::atomic<MccSerializer<impl::MccAngle>::SexagesimalCoordPrec>::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() void f()
{ {
@@ -209,4 +977,4 @@ void f()
s(str, impl::MccAngleALT{1.1}); 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) // https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
template <typename T> template <typename T>
concept mcc_formattable = requires(T v) { [](T vv) { return std::format("{}", vv); }(v); }; concept mcc_formattable =
// concept mcc_formattable = requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
// 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 // 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 // round to given precision of arcseconds/seconds
degs = std::round(degs * 3600.0 * term) / term / 3600.0; 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 d = std::trunc(degs);
auto s = (degs - d) * 60.0; auto s = (degs - d) * 60.0;

View File

@@ -2,7 +2,7 @@
// #include <mcc_ccte_erfa.h> // #include <mcc_ccte_erfa.h>
#include <mcc_coordinate.h> #include <mcc_coordinate.h>
#include <mcc_serializer.h>
using namespace mcc::impl; 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>, "!!!!!!!!!!!!"); 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() int main()
{ {
skypt_t::cctEngine.setStateERFA(saoras); skypt_t::cctEngine.setStateERFA(saoras);
@@ -60,9 +75,12 @@ int main()
azzd.setX(111.0_degs), azzd.setY(111.0_degs); azzd.setX(111.0_degs), azzd.setY(111.0_degs);
azalt.setX(111.0_degs), azalt.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); 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 = 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 << "\n\nFROM OBSERVED TO ICRS:\n";
std::cout << "OBS COORD EPOCH: " << radec_obs.epoch().UTC() << "\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 << "HA_OBS = " << hadec_obs.x().sexagesimal(true) << "\n";
std::cout << "AZ = " << azzd.x().sexagesimal() << "\n"; std::cout << "AZ = " << azzd.x().sexagesimal() << "\n";
std::cout << "ZD = " << azzd.y().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; azalt = pt;
std::cout << "ALT = " << azalt.y().sexagesimal() << "\n"; 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; return 0;
} }