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}
asibfm700_mount.h asibfm700_mount.cpp
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)
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_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)
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 *******
@ -77,6 +80,8 @@ list(APPEND MCC_LIBRARY_SRC mcc_spdlog.h)
set(MCC_LIBRARY mcc)
add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC})
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
$<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>
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
{
protected:
@ -512,13 +524,13 @@ class MccNetMessage
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);
}
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]);
if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_GENERIC) {
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);
mcc_tp2tp(tp, pt.time_point);
} else {
if (vs.size() > 3) {
std::chrono::sys_time<std::chrono::system_clock::duration> tp;
if (vs.size() > 3) { // coordinates epoch is given
// 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);
if (iss.fail()) {
// std::chrono::from_stream(iss, "%FT%T", tp);
// 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);
}
mcc_tp2tp(tp, pt.time_point);
mcc_tp2tp(cep.UTC(), pt.time_point);
} else { // no time point - use NOW
mcc_tp2tp(std::chrono::system_clock::now(), pt.time_point);
}
@ -574,7 +594,7 @@ class MccNetMessage
pt.X = MccAngle(ang1.value(), mcc::MccDegreeTag{});
pt.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{});
mcc_copy_celestial_point(pt, value);
mcc_copy_celestial_point(pt, &value);
} else {
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
// (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})
class DefaultSerializer
{
R res;
MccNetMessageCoordFormat _currentCoordFormat = MccNetMessage::CFMT_SGM;
MccNetMessageCoordPrec _currentCoordPrec{2, 1};
if (degrees) {
std::format_to(std::back_inserter(res), "{}{}{}", MccAngle(pt.X).degrees(),
MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, MccAngle(pt.Y).degrees());
} else {
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));
public:
void setCoordFormat(MccNetMessageCoordFormat fmt)
{
_currentCoordFormat = fmt;
}
std::format_to(std::back_inserter(res), "{0:}{1:}{2:}{3:%F}T{3:%T}", MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ,
MccCoordPairKindToStr(pt.pair_kind), MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, pt.time_point);
void setCoordPrec(MccNetMessageCoordPrec prec)
{
_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,
bool degrees = false,
std::pair<int, int> prec = {2, 1})
{
return celestialPointToString<std::string>(pt, degrees, prec);
}
std::format_to(std::back_inserter(bytes), "{}",
MccAngle(value.Y).sexagesimal(false, _currentCoordPrec.deg_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 BYTEREPR_T byte_repr_t;
@ -768,81 +828,10 @@ public:
template <typename T>
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>
R byteRepr() const
{
@ -965,74 +954,35 @@ protected:
BYTEREPR_T _msgBuffer{};
inline static DefaultDeserializer _defaultDeserilizer{};
DefaultSerializer _defaultSerializer{};
template <typename T, typename... Ts>
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::is_arithmetic_v<T>) {
// std::format_to(std::back_inserter(_msgBuffer), "{}", par);
// } 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!!!");
if constexpr (std::same_as<T, MccNetMessageCoordFormat>) {
_defaultSerializer.setCoordFormat(par);
if constexpr (sizeof...(Ts)) {
convertFunc(idx, pars...);
}
}(_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)) {
std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end()));
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 << msg2_t::celestialPointToString(mcc::MccCelestialPoint{}) << "\n";
std::cout << msg2_t::celestialPointToString(
mcc::MccCelestialPoint{.pair_kind = mcc::MccCoordPairKind::COORDS_KIND_AZALT,
.time_point = std::chrono::system_clock::now(),
.X = 1.3284237648726384768273,
.Y = std::numbers::pi / 2.234},
true)
<< "\n";
mcc::MccCelestialPoint cpt{.pair_kind = mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP,
.time_point = std::chrono::system_clock::now(),
.X = mcc::MccAngle("10:23:56.12331", mcc::mcc_hms),
.Y = mcc::MccAngle(67.9827148715, mcc::mcc_degrees)};
// 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";