This commit is contained in:
Timur A. Fatkhullin 2025-10-11 23:02:43 +03:00
parent f618fb64cb
commit cbe106fe95
4 changed files with 165 additions and 206 deletions

View File

@ -40,7 +40,7 @@ set(ASIBFM700_LIB asibfm700mount)
add_library(${ASIBFM700_LIB} STATIC ${ASIBFM700_LIB_SRC} add_library(${ASIBFM700_LIB} STATIC ${ASIBFM700_LIB_SRC}
asibfm700_mount.h asibfm700_mount.cpp asibfm700_mount.h asibfm700_mount.cpp
asibfm700_configfile.h) asibfm700_configfile.h)
target_include_directories(${ASIBFM700_LIB} PRIVATE mcc spdlog) target_include_directories(${ASIBFM700_LIB} PRIVATE mcc spdlog ${ERFA_INCLUDE_DIR})
target_link_libraries(${ASIBFM700_LIB} PRIVATE mcc spdlog) target_link_libraries(${ASIBFM700_LIB} PRIVATE mcc spdlog)
option(WITH_TESTS "Build tests" ON) option(WITH_TESTS "Build tests" ON)

View File

@ -16,26 +16,29 @@ include(ExternalProject)
set(SPDLOG_USE_STD_FORMAT ON CACHE INTERNAL "Use of C++20 std::format") set(SPDLOG_USE_STD_FORMAT ON CACHE INTERNAL "Use of C++20 std::format")
set(SPDLOG_FMT_EXTERNAL OFF CACHE INTERNAL "Turn off external fmt library") set(SPDLOG_FMT_EXTERNAL OFF CACHE INTERNAL "Turn off external fmt library")
FetchContent_Declare(spdlog
# ExternalProject_Add(spdlog
# SOURCE_DIR ${CMAKE_BINARY_DIR}/spdlog_lib
# BINARY_DIR ${CMAKE_BINARY_DIR}/spdlog_lib/build
GIT_REPOSITORY "https://github.com/gabime/spdlog.git"
GIT_TAG "v1.15.1"
GIT_SHALLOW TRUE
GIT_SUBMODULES ""
GIT_PROGRESS TRUE
CMAKE_ARGS "-DSPDLOG_USE_STD_FORMAT=ON -DSPDLOG_FMT_EXTERNAL=OFF"
# CONFIGURE_COMMAND ""
# BUILD_COMMAND ""
# INSTALL_COMMAND ""
# UPDATE_COMMAND ""
# SOURCE_SUBDIR cmake # turn off building
OVERRIDE_FIND_PACKAGE
)
find_package(spdlog CONFIG) find_package(spdlog CONFIG)
if (NOT ${spdlog_FOUND})
FetchContent_Declare(spdlog
# ExternalProject_Add(spdlog
# SOURCE_DIR ${CMAKE_BINARY_DIR}/spdlog_lib
# BINARY_DIR ${CMAKE_BINARY_DIR}/spdlog_lib/build
GIT_REPOSITORY "https://github.com/gabime/spdlog.git"
GIT_TAG "v1.15.1"
GIT_SHALLOW TRUE
GIT_SUBMODULES ""
GIT_PROGRESS TRUE
CMAKE_ARGS "-DSPDLOG_USE_STD_FORMAT=ON -DSPDLOG_FMT_EXTERNAL=OFF"
# CONFIGURE_COMMAND ""
# BUILD_COMMAND ""
# INSTALL_COMMAND ""
# UPDATE_COMMAND ""
# SOURCE_SUBDIR cmake # turn off building
OVERRIDE_FIND_PACKAGE
)
find_package(spdlog CONFIG)
endif()
message(STATUS "-----: " ${spdlog_FOUND})
# ******* ERFA LIBRARY ******* # ******* ERFA LIBRARY *******
@ -77,6 +80,8 @@ list(APPEND MCC_LIBRARY_SRC mcc_spdlog.h)
set(MCC_LIBRARY mcc) set(MCC_LIBRARY mcc)
add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC}) add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC})
target_compile_features(${MCC_LIBRARY} INTERFACE cxx_std_23) target_compile_features(${MCC_LIBRARY} INTERFACE cxx_std_23)
target_compile_definitions(${MCC_LIBRARY} INTERFACE SPDLOG_USE_STD_FORMAT=1 SPDLOG_FMT_EXTERNAL=0)
target_link_libraries(${MCC_LIBRARY} INTERFACE spdlog)
target_include_directories(${MCC_LIBRARY} INTERFACE ${ERFA_INCLUDE_DIR} ${BSPLINES_INCLUDE_DIR}) target_include_directories(${MCC_LIBRARY} INTERFACE ${ERFA_INCLUDE_DIR} ${BSPLINES_INCLUDE_DIR})
target_include_directories(${MCC_LIBRARY} INTERFACE target_include_directories(${MCC_LIBRARY} INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>

View File

@ -492,8 +492,20 @@ static_assert(mcc_netmsg_valid_keys_c<MccNetMessageValidKeywords>, "");
template <mcc_netmsg_valid_keys_c BASE_T, traits::mcc_char_range BYTEREPR_T> template <mcc_netmsg_valid_keys_c BASE_T, traits::mcc_char_range BYTEREPR_T>
class MccNetMessage class MccNetMessage
{ {
static inline utils::MccSimpleDeserializer defaultDeserilizer{MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ}; public:
/* helper types to format serialized celestial coordinates */
/* it can be used as inputs in "construct" method or corresponded constructor */
// format of output (serialized) coordinates
enum MccNetMessageCoordFormat { CFMT_DEGREES, CFMT_SGM };
// precision of sexagesimal coordinates (number of decimal places in seconds/arcseconds)
struct MccNetMessageCoordPrec {
uint8_t hour_prec = 2;
uint8_t deg_prec = 1;
};
protected:
class DefaultDeserializer : protected utils::MccSimpleDeserializer class DefaultDeserializer : protected utils::MccSimpleDeserializer
{ {
protected: protected:
@ -512,13 +524,13 @@ class MccNetMessage
return ec; return ec;
} }
if (vs.size() < 2) { if (vs.size() < 2) { // al least a pair of coordinates must be given
return std::make_error_code(std::errc::invalid_argument); return std::make_error_code(std::errc::invalid_argument);
} }
MccCelestialPoint pt{.pair_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; MccCelestialPoint pt{.pair_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
if (vs.size() > 2) { if (vs.size() > 2) { // pair of coordinates and the pair kind
pt.pair_kind = MccCoordStrToPairKind(vs[2]); pt.pair_kind = MccCoordStrToPairKind(vs[2]);
if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_GENERIC) { if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_GENERIC) {
return std::make_error_code(std::errc::invalid_argument); return std::make_error_code(std::errc::invalid_argument);
@ -532,17 +544,25 @@ class MccNetMessage
std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816); std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816);
mcc_tp2tp(tp, pt.time_point); mcc_tp2tp(tp, pt.time_point);
} else { } else {
if (vs.size() > 3) { if (vs.size() > 3) { // coordinates epoch is given
std::chrono::sys_time<std::chrono::system_clock::duration> tp; // std::chrono::sys_time<std::chrono::system_clock::duration> tp;
std::istringstream iss(std::string{utils::trimSpaces(vs[3])}); // std::istringstream iss(std::string{utils::trimSpaces(vs[3])});
std::chrono::from_stream(iss, "%FT%T", tp); // std::chrono::from_stream(iss, "%FT%T", tp);
if (iss.fail()) { // if (iss.fail()) {
// return std::make_error_code(std::errc::invalid_argument);
// }
// mcc_tp2tp(tp, pt.time_point);
MccCelestialCoordEpoch cep;
// bool ok = cep.fromCharRange(utils::trimSpaces(vs[3]));
bool ok = cep.fromCharRange(vs[3]);
if (!ok) {
return std::make_error_code(std::errc::invalid_argument); return std::make_error_code(std::errc::invalid_argument);
} }
mcc_tp2tp(cep.UTC(), pt.time_point);
mcc_tp2tp(tp, pt.time_point);
} else { // no time point - use NOW } else { // no time point - use NOW
mcc_tp2tp(std::chrono::system_clock::now(), pt.time_point); mcc_tp2tp(std::chrono::system_clock::now(), pt.time_point);
} }
@ -574,7 +594,7 @@ class MccNetMessage
pt.X = MccAngle(ang1.value(), mcc::MccDegreeTag{}); pt.X = MccAngle(ang1.value(), mcc::MccDegreeTag{});
pt.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{}); pt.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{});
mcc_copy_celestial_point(pt, value); mcc_copy_celestial_point(pt, &value);
} else { } else {
return base_t::operator()(std::forward<IR>(bytes), value); return base_t::operator()(std::forward<IR>(bytes), value);
} }
@ -583,48 +603,88 @@ class MccNetMessage
} }
}; };
public:
// helper method to convert celestial point to char range class DefaultSerializer
// (actually, to range of strings)
template <traits::mcc_output_char_range R>
static R celestialPointToString(mcc_celestial_point_c auto const& pt,
bool degrees = false,
std::pair<int, int> prec = {2, 1})
{ {
R res; MccNetMessageCoordFormat _currentCoordFormat = MccNetMessage::CFMT_SGM;
MccNetMessageCoordPrec _currentCoordPrec{2, 1};
if (degrees) { public:
std::format_to(std::back_inserter(res), "{}{}{}", MccAngle(pt.X).degrees(), void setCoordFormat(MccNetMessageCoordFormat fmt)
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, MccAngle(pt.Y).degrees()); {
} else { _currentCoordFormat = fmt;
switch (pt.pair_kind) {
case MccCoordPairKind::COORDS_KIND_RADEC_ICRS:
case MccCoordPairKind::COORDS_KIND_RADEC_APP:
case MccCoordPairKind::COORDS_KIND_HADEC_APP:
std::format_to(std::back_inserter(res), "{}{}", MccAngle(pt.X).sexagesimal(true, prec.first),
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ);
break;
default:
std::format_to(std::back_inserter(res), "{}{}", MccAngle(pt.X).sexagesimal(false, prec.second),
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ);
}
std::format_to(std::back_inserter(res), "{}", MccAngle(pt.Y).sexagesimal(false, prec.second));
} }
std::format_to(std::back_inserter(res), "{0:}{1:}{2:}{3:%F}T{3:%T}", MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, void setCoordPrec(MccNetMessageCoordPrec prec)
MccCoordPairKindToStr(pt.pair_kind), MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, pt.time_point); {
_currentCoordPrec = prec;
}
return res; template <typename T, traits::mcc_output_char_range OR>
} void operator()(const T& value, OR& bytes)
{
if constexpr (std::is_arithmetic_v<T>) {
std::format_to(std::back_inserter(bytes), "{}", value);
} else if constexpr (std::convertible_to<T, std::string>) {
std::ranges::copy(static_cast<std::string>(value), std::back_inserter(bytes));
} else if constexpr (std::constructible_from<std::string, T>) {
std::ranges::copy(std::string(value), std::back_inserter(bytes));
} else if constexpr (traits::mcc_char_range<T>) {
std::ranges::copy(std::string(value.begin(), value.end()), std::back_inserter(bytes));
} else if constexpr (std::same_as<T, MccCoordPairKind>) {
std::ranges::copy(mcc_pairkind2str(value), std::back_inserter(bytes));
} else if constexpr (traits::mcc_time_duration_c<T>) {
(*this)(value.count(), bytes);
} else if constexpr (mcc_celestial_point_c<T>) {
if (_currentCoordFormat == MccNetMessage::CFMT_DEGREES) {
std::format_to(std::back_inserter(bytes), "{}{}{}", MccAngle(value.X).degrees(),
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, MccAngle(value.Y).degrees());
} else {
switch (value.pair_kind) {
case MccCoordPairKind::COORDS_KIND_RADEC_ICRS:
case MccCoordPairKind::COORDS_KIND_RADEC_APP:
case MccCoordPairKind::COORDS_KIND_HADEC_APP:
std::format_to(std::back_inserter(bytes), "{}{}",
MccAngle(value.X).sexagesimal(true, _currentCoordPrec.hour_prec),
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ);
break;
default:
std::format_to(std::back_inserter(bytes), "{}{}",
MccAngle(value.X).sexagesimal(false, _currentCoordPrec.deg_prec),
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ);
}
static std::string celestialPointToString(mcc_celestial_point_c auto const& pt, std::format_to(std::back_inserter(bytes), "{}",
bool degrees = false, MccAngle(value.Y).sexagesimal(false, _currentCoordPrec.deg_prec));
std::pair<int, int> prec = {2, 1}) }
{
return celestialPointToString<std::string>(pt, degrees, prec);
}
std::format_to(std::back_inserter(bytes), "{0:}{1:}{2:}{3:%F}T{3:%T}",
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, MccCoordPairKindToStr(value.pair_kind),
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, value.time_point);
} else if constexpr (std::ranges::range<T>) {
auto sz = std::ranges::size(value);
if (sz == 0) {
return;
}
(*this)(*value.begin(), bytes); // the first element
if (sz > 1) {
for (auto const& el : value | std::views::drop(1)) {
std::ranges::copy(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, std::back_inserter(bytes));
(*this)(el, bytes);
}
}
} else if constexpr (std::formattable<T, char>) {
std::format_to(std::back_inserter(bytes), "{}", value);
} else {
static_assert(false, "UNSUPPORTED TYPE!!!");
}
}
};
public:
typedef BASE_T valid_keys_t; typedef BASE_T valid_keys_t;
typedef BYTEREPR_T byte_repr_t; typedef BYTEREPR_T byte_repr_t;
@ -768,81 +828,10 @@ public:
template <typename T> template <typename T>
std::expected<T, std::error_code> paramValue(size_t idx) std::expected<T, std::error_code> paramValue(size_t idx)
{ {
return paramValue<T>(idx, defaultDeserilizer); return paramValue<T>(idx, _defaultDeserilizer);
} }
// template <mcc_celestial_point_c CT>
// std::expected<CT, std::error_code> paramCelestialPoint(
// size_t from_idx,
// MccCoordPairKind default_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS)
// {
// if (_params.size() < (from_idx + 2)) {
// return std::unexpected(std::make_error_code(std::errc::message_size));
// }
// MccCelestialPoint pt{.pair_kind = default_kind};
// if (_params.size() > (from_idx + 2)) {
// pt.pair_kind = MccCoordStrToPairKind(_params[from_idx + 2]);
// if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_GENERIC) {
// return std::unexpected(std::make_error_code(std::errc::invalid_argument));
// }
// }
// if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
// // J2000.0: 11:58:55.816 1 January 2000 UTC
// auto tp = std::chrono::sys_days(std::chrono::year_month_day(std::chrono::January / std::chrono::day(1) /
// std::chrono::year(2000))) +
// std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816);
// mcc_tp2tp(tp, pt.time_point);
// } else {
// if (_params.size() > (from_idx + 3)) {
// std::chrono::sys_time<std::chrono::system_clock::duration> tp;
// std::istringstream iss(std::string{utils::trimSpaces(_params[from_idx + 3])});
// std::chrono::from_stream(iss, "%FT%T", tp);
// if (iss.fail()) {
// return std::unexpected(std::make_error_code(std::errc::invalid_argument));
// }
// mcc_tp2tp(tp, pt.time_point);
// } else { // no time point - use NOW
// mcc_tp2tp(std::chrono::system_clock::now(), pt.time_point);
// }
// }
// std::optional<double> ang1, ang2;
// switch (pt.pair_kind) {
// // if sexagesimal then hours:minutes:seconds form
// case mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS:
// case mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP:
// case mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP:
// ang1 = utils::parsAngleString(_params[from_idx], true);
// break;
// default:
// // if sexagesimal then degrees:arcminutes:arcseconds form
// ang1 = utils::parsAngleString(_params[from_idx]);
// }
// if (!ang1) {
// return std::unexpected(std::make_error_code(std::errc::invalid_argument));
// }
// ang2 = utils::parsAngleString(_params[from_idx + 1]);
// if (!ang2) {
// return std::unexpected(std::make_error_code(std::errc::invalid_argument));
// }
// pt.X = MccAngle(ang1.value(), mcc::MccDegreeTag{});
// pt.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{});
// return pt;
// }
template <traits::mcc_view_or_output_char_range R> template <traits::mcc_view_or_output_char_range R>
R byteRepr() const R byteRepr() const
{ {
@ -965,74 +954,35 @@ protected:
BYTEREPR_T _msgBuffer{}; BYTEREPR_T _msgBuffer{};
inline static DefaultDeserializer _defaultDeserilizer{};
DefaultSerializer _defaultSerializer{};
template <typename T, typename... Ts> template <typename T, typename... Ts>
void convertFunc(std::vector<size_t>& idx, const T& par, const Ts&... pars) void convertFunc(std::vector<size_t>& idx, const T& par, const Ts&... pars)
{ {
idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); if constexpr (std::same_as<T, MccNetMessageCoordFormat>) {
_defaultSerializer.setCoordFormat(par);
// if constexpr (std::is_arithmetic_v<T>) { if constexpr (sizeof...(Ts)) {
// std::format_to(std::back_inserter(_msgBuffer), "{}", par); convertFunc(idx, pars...);
// } else if constexpr (std::convertible_to<T, std::string>) {
// std::ranges::copy(static_cast<std::string>(par), std::back_inserter(_msgBuffer));
// } else if constexpr (std::constructible_from<std::string, T>) {
// std::ranges::copy(std::string(par), std::back_inserter(_msgBuffer));
// } else if constexpr (traits::mcc_char_range<T>) {
// std::ranges::copy(std::string(par.begin(), par.end()), std::back_inserter(_msgBuffer));
// } else if constexpr (std::same_as<T, MccCoordPairKind>) {
// std::ranges::copy(mcc_pairkind2str(par), std::back_inserter(_msgBuffer));
// } else if constexpr (traits::mcc_time_duration_c<T>) {
// idx.resize(idx.size() - 1);
// convertFunc(idx, par.count());
// idx.resize(idx.size() - 1);
// } else if constexpr (std::formattable<T, char>) {
// std::format_to(std::back_inserter(_msgBuffer), "{}", par);
// } else {
// static_assert(false, "UNSUPPORTED TYPE!!!");
// }
// use the lambda here to enable recursive calling with no special handling of 'idx'
[](this auto const& self, auto& buff, auto const& p) {
using p_t = std::decay_t<decltype(p)>;
if constexpr (std::is_arithmetic_v<p_t>) {
std::format_to(std::back_inserter(buff), "{}", p);
} else if constexpr (std::convertible_to<p_t, std::string>) {
std::ranges::copy(static_cast<std::string>(p), std::back_inserter(buff));
} else if constexpr (std::constructible_from<std::string, p_t>) {
std::ranges::copy(std::string(p), std::back_inserter(buff));
} else if constexpr (traits::mcc_char_range<p_t>) {
std::ranges::copy(std::string(p.begin(), p.end()), std::back_inserter(buff));
} else if constexpr (std::same_as<p_t, MccCoordPairKind>) {
std::ranges::copy(mcc_pairkind2str(p), std::back_inserter(buff));
} else if constexpr (traits::mcc_time_duration_c<p_t>) {
self(buff, p.count());
} else if constexpr (std::ranges::range<p_t>) {
auto sz = std::ranges::size(p);
if (sz == 0) {
return;
}
self(buff, *p.begin()); // the first element
if (sz > 1) {
for (auto const& el : p | std::views::drop(1)) {
std::ranges::copy(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, std::back_inserter(buff));
self(buff, el);
}
}
} else if constexpr (std::formattable<p_t, char>) {
std::format_to(std::back_inserter(buff), "{}", p);
} else {
static_assert(false, "UNSUPPORTED TYPE!!!");
} }
}(_msgBuffer, par); } else if constexpr (std::same_as<T, MccNetMessageCoordPrec>) {
_defaultSerializer.setCoordPrec(par);
if constexpr (sizeof...(Ts)) {
convertFunc(idx, pars...);
}
} else {
idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end()));
idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); _defaultSerializer(par, _msgBuffer);
if constexpr (sizeof...(Ts)) { idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end()));
std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
convertFunc(idx, pars...); if constexpr (sizeof...(Ts)) {
std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
convertFunc(idx, pars...);
}
} }
}; };
}; };

View File

@ -49,15 +49,19 @@ int main()
} }
std::cout << ">\n"; std::cout << ">\n";
std::cout << msg2_t::celestialPointToString(mcc::MccCelestialPoint{}) << "\n"; mcc::MccCelestialPoint cpt{.pair_kind = mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP,
std::cout << msg2_t::celestialPointToString( .time_point = std::chrono::system_clock::now(),
mcc::MccCelestialPoint{.pair_kind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, .X = mcc::MccAngle("10:23:56.12331", mcc::mcc_hms),
.time_point = std::chrono::system_clock::now(), .Y = mcc::MccAngle(67.9827148715, mcc::mcc_degrees)};
.X = 1.3284237648726384768273,
.Y = std::numbers::pi / 2.234},
true)
<< "\n";
// msg2.construct("ACK", "MOUNT", msg2_t::CFMT_DEGREES, msg2_t::MccNetMessageCoordPrec{2, 3}, cpt);
msg2.construct("ACK", "MOUNT", msg2_t::MccNetMessageCoordPrec{2, 3}, cpt);
std::cout << "MSG2: " << msg2.byteRepr() << "\n";
auto cpt1 = msg2.paramValue<mcc::MccCelestialPoint>(1);
if (cpt1) {
std::cout << "cpt.time_point = " << cpt1.value().time_point << "\n";
std::cout << "cpt.X = " << cpt1.value().X << "\n";
}
std::cout << "\n\n\n"; std::cout << "\n\n\n";