#pragma once #include #include #include namespace asibfm700 { template struct config_record_t : mcc::impl::mcc_simple_kv_record_t { std::vector head_comment; std::string_view inline_comment; }; template static config_record_t make_config_record( std::string_view key, T value, std::vector hcomm = {}, std::string_view icomm = {}, mcc::impl::mcc_serialization_params_t const& spars = mcc::impl::mcc_serialization_params_t{}) { return config_record_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{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{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{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{-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{}, {"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{}, {"PCM B-spline coeffs for along Y-axis (declination angle)"})); class Asibfm700MountConfiguration : public mcc::impl::MccKeyValueHolder { using base_t = mcc::impl::MccKeyValueHolder; public: Asibfm700MountConfiguration() : base_t(Asibfm700MountConfigurationDefaults) { // fill comments auto get_comm = [this]() { auto& rec = std::get(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; }; [&](std::index_sequence) { (get_comm.template operator()(), ...); }(std::make_index_sequence{}); } ~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 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("leapSecondFilename").value_or(""); auto fname = mcc::utils::trimSpaces(val); setValue("leapSecondFilename", fname); val = getValue("bulletinAFilename").value_or(""); fname = mcc::utils::trimSpaces(val); setValue("bulletinAFilename", fname); val = getValue("MountDevPath").value_or(std::string{}); fname = mcc::utils::trimSpaces(val); setValue("MountDevPath", fname); val = getValue("EncoderDevPath").value_or(std::string{}); fname = mcc::utils::trimSpaces(val); setValue("EncoderDevPath", fname); val = getValue("EncoderXDevPath").value_or(std::string{}); fname = mcc::utils::trimSpaces(val); setValue("EncoderXDevPath", fname); val = getValue("EncoderYDevPath").value_or(std::string{}); fname = mcc::utils::trimSpaces(val); setValue("EncoderYDevPath", fname); val = getValue("slewingPathFilename").value_or(std::string{}); fname = mcc::utils::trimSpaces(val); setValue("slewingPathFilename", fname); val = getValue("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::system_clock::now())); // auto time_stamp = // std::chrono::round>>(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