314 lines
12 KiB
C++
314 lines
12 KiB
C++
#pragma once
|
|
|
|
|
|
#include <mcc/mcc_keyvalue.h>
|
|
#include <mcc/mcc_pcm.h>
|
|
#include <filesystem>
|
|
|
|
namespace asibfm700
|
|
{
|
|
|
|
template <mcc::impl::mcc_variant_valid_type_c T>
|
|
struct config_record_t : mcc::impl::mcc_simple_kv_record_t<T> {
|
|
std::vector<std::string_view> head_comment;
|
|
std::string_view inline_comment;
|
|
};
|
|
|
|
|
|
template <mcc::impl::mcc_variant_valid_type_c T>
|
|
static config_record_t<T> make_config_record(
|
|
std::string_view key,
|
|
T value,
|
|
std::vector<std::string_view> hcomm = {},
|
|
std::string_view icomm = {},
|
|
mcc::impl::mcc_serialization_params_t const& spars = mcc::impl::mcc_serialization_params_t{})
|
|
{
|
|
return config_record_t<T>{key, value, value, spars, hcomm, icomm};
|
|
}
|
|
|
|
|
|
static auto Asibfm700MountConfigurationDefaults = std::make_tuple(
|
|
|
|
/* geographic coordinates of the observation site */
|
|
|
|
make_config_record("siteLatitude", mcc::impl::MccAngle(43.646711_degs), {"site latitude in degrees"}),
|
|
make_config_record("siteLongitude", mcc::impl::MccAngle(41.440732_degs), {"site longitude in degrees"}),
|
|
make_config_record("siteElevation", 2070.0, {"site elevation in meters"}),
|
|
|
|
/* celestial coordinate transformation */
|
|
|
|
make_config_record("refractWavelength", 0.55, {"wavelength at which refraction is calculated (in mkm)"}),
|
|
make_config_record("leapSecondFilename", std::string(), {"an empty filename means default precompiled string"}),
|
|
make_config_record("bulletinAFilename", std::string(), {"an empty filename means default precompiled string"}),
|
|
|
|
/* pointing correction model */
|
|
|
|
// PCM type
|
|
make_config_record(
|
|
"pcmType",
|
|
mcc::impl::MccDefaultPCMType::PCM_TYPE_GEOMETRY,
|
|
{"PCM type:", "a case-sensetive string:", " GEOMETRY - 'classic' geometry-based correction coefficients",
|
|
" GEOMETRY-BSPLINE - previous one and additional 2D B-spline corrections",
|
|
" BSPLINE - pure 2D B-spline corrections"}),
|
|
|
|
// PCM geometrical coefficients
|
|
make_config_record("pcmGeomCoeffs",
|
|
std::vector<double>{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
|
{"PCM geometrical coefficients"}),
|
|
|
|
// make_config_record("pcmBsplineDegree", std::vector<size_t>{3, 3}, {"PCM B-spline degrees"}),
|
|
|
|
// PCM B-spline knots along X-axis (HA-angle or azimuth). By default from 0 to 2*PI radians
|
|
// NOTE: The first and last values are interpretated as border knots!!!
|
|
// Thus the array length must be equal to or greater than 2!
|
|
make_config_record("pcmBsplineXknots",
|
|
std::vector<double>{0.0, 0.6981317, 1.3962634, 2.0943951, 2.7925268, 3.4906585, 4.1887902,
|
|
4.88692191, 5.58505361, 6.28318531},
|
|
{"PCM B-spline knots along X-axis (HA-angle or azimuth). By default from 0 to 2*PI radians",
|
|
"NOTE: The first and last values are interpretated as border knots!!!",
|
|
" Thus the array length must be equal to or greater than 2!"}),
|
|
|
|
// PCM B-spline knots along Y-axis (declination or zenithal distance). By default from -PI/6 to PI/2 radians
|
|
// NOTE: The first and last values are interpretated as border knots!!!
|
|
// Thus the array length must be equal to or greater than 2!
|
|
make_config_record(
|
|
"pcmBsplineYknots",
|
|
std::vector<double>{-0.52359878, -0.29088821, -0.05817764, 0.17453293, 0.40724349, 0.63995406, 0.87266463,
|
|
1.10537519, 1.33808576, 1.57079633},
|
|
{"PCM B-spline knots along Y-axis (declination or zenithal distance). By default from -PI/6 to PI/2 radians",
|
|
"NOTE: The first and last values are interpretated as border knots!!!",
|
|
" Thus the array length must be equal to or greater than 2!"}),
|
|
|
|
// PCM B-spline coeffs for along X-axis (HA-angle or azimuth)
|
|
make_config_record("pcmBsplineXcoeffs", std::vector<double>{}, {"PCM B-spline coeffs for along X-axis (HA-angle)"}),
|
|
|
|
// PCM B-spline coeffs for along Y-axis (declination or zenithal distance)
|
|
make_config_record("pcmBsplineYcoeffs",
|
|
std::vector<double>{},
|
|
{"PCM B-spline coeffs for along Y-axis (declination angle)"}));
|
|
|
|
class Asibfm700MountConfiguration : public mcc::impl::MccKeyValueHolder<decltype(Asibfm700MountConfigurationDefaults)>
|
|
{
|
|
using base_t = mcc::impl::MccKeyValueHolder<decltype(Asibfm700MountConfigurationDefaults)>;
|
|
|
|
public:
|
|
Asibfm700MountConfiguration() : base_t(Asibfm700MountConfigurationDefaults)
|
|
{
|
|
// fill comments
|
|
|
|
auto get_comm = [this]<size_t I>() {
|
|
auto& rec = std::get<I>(Asibfm700MountConfigurationDefaults);
|
|
for (auto const& comm : rec.head_comment) {
|
|
if (comm.size()) {
|
|
_headComment[_hashes[I]].emplace_back(std::string{comm.begin(), comm.end()});
|
|
} else {
|
|
_headComment[_hashes[I]].emplace_back(std::nullopt);
|
|
}
|
|
}
|
|
_inlineComment[_hashes[I]] = rec.inline_comment;
|
|
};
|
|
|
|
[&]<size_t... Is>(std::index_sequence<Is...>) {
|
|
(get_comm.template operator()<Is>(), ...);
|
|
}(std::make_index_sequence<Asibfm700MountConfiguration::NUMBER_OF_RECORDS>{});
|
|
}
|
|
|
|
~Asibfm700MountConfiguration() = default;
|
|
|
|
std::error_code load(const std::filesystem::path& path)
|
|
{
|
|
std::string buffer;
|
|
|
|
std::error_code ec;
|
|
auto sz = std::filesystem::file_size(path, ec);
|
|
|
|
if (!ec && sz) {
|
|
std::ifstream fst(path);
|
|
|
|
try {
|
|
buffer.resize(sz);
|
|
|
|
fst.read(buffer.data(), sz);
|
|
|
|
fst.close();
|
|
|
|
// remove possible header (see "save" method)
|
|
size_t nskip = headerLines;
|
|
if (!std::regex_match(buffer, headerRx)) {
|
|
nskip = 0;
|
|
}
|
|
// auto lines = std::views::split(buffer, DEFAULT_RECORD_DELIMITER);
|
|
// if (std::ranges::distance(lines.begin(), lines.end()) >= 5) {
|
|
// bool head_exists = true;
|
|
// std::vector<std::string_view> tl;
|
|
// for (auto const& l : lines) { // lines must start with COMMENT_SEQ
|
|
// tl.emplace_back(mcc::utils::trimSpaces(l));
|
|
// if (tl.back().size() >= COMMENT_SEQ.size()) {
|
|
// auto found = std::ranges::search(tl.back(), COMMENT_SEQ);
|
|
// if (tl.back().begin() != found.begin()) {
|
|
// head_exists = false;
|
|
// break;
|
|
// }
|
|
// } else {
|
|
// head_exists = false;
|
|
// break;
|
|
// }
|
|
// }
|
|
// if (head_exists) {
|
|
// // the first and last lines must contain only a comment sequence and possible spaces
|
|
// if (tl[0].size() != COMMENT_SEQ.size() || tl[3].size() != COMMENT_SEQ.size()) {
|
|
// head_exists = false;
|
|
// } else {
|
|
// if (tl[1] != "# ASTROSIB FM-700 MOUNT CONFIGURATION") {
|
|
// head_exists = false;
|
|
// } else {
|
|
// std::regex rx{
|
|
// "# \\(created at "
|
|
// "20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9] "
|
|
// "UTC\\)"};
|
|
|
|
// if (!std::regex_match(std::string{tl[2]}, rx)) {
|
|
// head_exists = false;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// if (!head_exists) { // delete the first N lines
|
|
// nskip = 0;
|
|
// }
|
|
// }
|
|
|
|
ec = base_t::fromCharRange(buffer, nskip);
|
|
if (!ec) {
|
|
// remove possible spaces in filenames
|
|
|
|
std::string val = getValue<std::string>("leapSecondFilename").value_or("");
|
|
auto fname = mcc::utils::trimSpaces(val);
|
|
setValue("leapSecondFilename", fname);
|
|
|
|
|
|
val = getValue<std::string>("bulletinAFilename").value_or("");
|
|
fname = mcc::utils::trimSpaces(val);
|
|
setValue("bulletinAFilename", fname);
|
|
|
|
val = getValue<std::string>("MountDevPath").value_or(std::string{});
|
|
fname = mcc::utils::trimSpaces(val);
|
|
setValue("MountDevPath", fname);
|
|
|
|
val = getValue<std::string>("EncoderDevPath").value_or(std::string{});
|
|
fname = mcc::utils::trimSpaces(val);
|
|
setValue("EncoderDevPath", fname);
|
|
|
|
val = getValue<std::string>("EncoderXDevPath").value_or(std::string{});
|
|
fname = mcc::utils::trimSpaces(val);
|
|
setValue("EncoderXDevPath", fname);
|
|
|
|
val = getValue<std::string>("EncoderYDevPath").value_or(std::string{});
|
|
fname = mcc::utils::trimSpaces(val);
|
|
setValue("EncoderYDevPath", fname);
|
|
|
|
val = getValue<std::string>("slewingPathFilename").value_or(std::string{});
|
|
fname = mcc::utils::trimSpaces(val);
|
|
setValue("slewingPathFilename", fname);
|
|
|
|
val = getValue<std::string>("trackingPathFilename").value_or(std::string{});
|
|
fname = mcc::utils::trimSpaces(val);
|
|
setValue("trackingPathFilename", fname);
|
|
}
|
|
} catch (std::ios_base::failure const& ex) {
|
|
ec = ex.code();
|
|
} catch (std::length_error const& ex) {
|
|
ec = std::make_error_code(std::errc::no_buffer_space);
|
|
} catch (std::bad_alloc const& ex) {
|
|
ec = std::make_error_code(std::errc::not_enough_memory);
|
|
} catch (...) {
|
|
ec = std::make_error_code(std::errc::operation_canceled);
|
|
}
|
|
|
|
_lastConfigPath = path;
|
|
}
|
|
|
|
return ec;
|
|
}
|
|
|
|
std::error_code reloadCurrentConfig()
|
|
{
|
|
return load(_lastConfigPath);
|
|
}
|
|
|
|
|
|
std::error_code save(const std::filesystem::path& path)
|
|
{
|
|
std::error_code ec;
|
|
std::string buff;
|
|
|
|
ec = toCharRange(buff);
|
|
if (ec) {
|
|
return ec;
|
|
}
|
|
|
|
std::ofstream fst(path, std::ios_base::trunc);
|
|
if (!fst.is_open()) {
|
|
ec = std::make_error_code(std::errc::io_error);
|
|
} else {
|
|
try {
|
|
fst << generateHeader();
|
|
fst << buff;
|
|
} catch (std::ios_base::failure const& ex) {
|
|
ec = ex.code();
|
|
} catch (...) {
|
|
ec = std::make_error_code(std::errc::operation_canceled);
|
|
}
|
|
}
|
|
|
|
return ec;
|
|
}
|
|
|
|
std::error_code save()
|
|
{
|
|
return save(_lastConfigPath);
|
|
}
|
|
|
|
std::filesystem::path configFilename() const
|
|
{
|
|
return _lastConfigPath;
|
|
}
|
|
|
|
private:
|
|
std::filesystem::path _lastConfigPath{};
|
|
|
|
static constexpr size_t headerLines = 5; // number of lines in the header
|
|
|
|
static std::string generateHeader()
|
|
{
|
|
std::string s{
|
|
"#\n"
|
|
"# ASTROSIB FM-700 MOUNT CONFIGURATION\n"
|
|
"#\n"};
|
|
|
|
std::format_to(
|
|
std::back_inserter(s), "# (created at {:%FT%T} UTC)\n#\n",
|
|
std::chrono::round<std::chrono::duration<int64_t, std::ratio<1, 10>>>(std::chrono::system_clock::now()));
|
|
|
|
// auto time_stamp =
|
|
// std::chrono::round<std::chrono::duration<int64_t, std::ratio<1, 10>>>(std::chrono::system_clock::now());
|
|
// std::string s = std::format(
|
|
// "{0:}{1:}{0:} ASTROSIB FM-700 MOUNT CONFIGURATION{1:}{0:}{1:}{0:} (created at {2:%FT%T}
|
|
// UTC){1:}{0:}{1:}", COMMENT_SEQ, rec_delim, time_stamp);
|
|
|
|
return s;
|
|
}
|
|
|
|
// regex for buffer started with standard header
|
|
inline static const std::regex headerRx{
|
|
"^[ \\n]*"
|
|
"# *\\n"
|
|
"# *ASTROSIB FM-700 MOUNT CONFIGURATION *\\n"
|
|
"# *\\n"
|
|
"# *\\(created at 20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9] UTC\\) *\\n"
|
|
"# *\\n"
|
|
"[\\s\\S]*"};
|
|
};
|
|
|
|
} // namespace asibfm700
|