Files
ASIBFM700/asibfm700_config.h

895 lines
36 KiB
C++

#pragma once
#include <filesystem>
#include <mcc/mcc_keyvalue.h>
#include <mcc/mcc_pcm.h>
#include "mcc/mcc_pzone.h"
#include "asibfm700_common.h"
#include "asibfm700_servocontroller.h"
namespace asibfm700
{
static constexpr double SAORAS_LATITUDE = 43.646711_degs;
static constexpr double SAORAS_LONGITUDE = 41.440732_degs;
template <mcc::impl::mcc_record_value_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_record_value_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(SAORAS_LATITUDE), {" site latitude in degrees"}),
make_config_record("siteLongitude", mcc::impl::MccAngle(SAORAS_LONGITUDE), {" 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-sensitive 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",
mcc::impl::mccGenerateBsplineKnots(0.0, 360.0_degs, 8),
{" 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 -SAORAS-latitude 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",
mcc::impl::mccGenerateBsplineKnots(-SAORAS_LATITUDE, 90.0_degs, 8),
{" PCM B-spline knots along Y-axis (declination or zenithal distance).",
" By default from -SAORAS-latitude 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)"}),
// inverse PCM B-spline coeffs for along X-axis (HA-angle or azimuth)
make_config_record("pcmInverseBsplineXcoeffs",
std::vector<double>{},
{" Inverse PCM B-spline coeffs for along X-axis (HA-angle)"}),
// inverse PCM B-spline coeffs for along Y-axis (declination or zenithal distance)
make_config_record("pcmInverseBsplineYcoeffs",
std::vector<double>{},
{" Inverse PCM B-spline coeffs for along Y-axis (declination angle)"}),
/* prohibited zones */
// minimal altitude (default: 10.0 degrees and SAO RAS latitude)
make_config_record("altLimPZ",
mcc::impl::MccAltLimitPZ<mcc::impl::MccAltLimitKind::MIN_ALT_LIMIT>{10.0_degs, SAORAS_LATITUDE},
{" minimal altitude prohibited zone"}),
// HA-axis limit switch
make_config_record(
"axisLimitSwitchHA",
mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_HA_OBS>{-170.0_degs, 170.0_degs, nullptr},
{" HA-axis limit switch stop angles"}),
// DEC-axis limit switch
make_config_record(
"axisLimitSwitchDEC",
mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_DEC_OBS>{-90.0_degs, 90.0_degs, nullptr},
{" DEC-axis limit switch stop angles"}),
/* hardware-related */
// hardware polling period in millisecs (used in hardwareSetState method; see asibfm700_servocontroller.cpp)
make_config_record("hardwarePollingPeriod",
std::chrono::milliseconds{300},
{"hardware driver polling period in millisecs"}),
// hardware polling timeout in millisecs (used in hardwareSetState method; see asibfm700_servocontroller.cpp)
make_config_record("hardwarePollingTimeout",
std::chrono::milliseconds{30000},
{"hardware driver polling timeout in millisecs"}),
// hardware mode: 1 - model mode, otherwise real mode
make_config_record("RunModel", 0, {" hardware mode: 1 - model mode, otherwise real mode"}),
// mount serial device paths
make_config_record("MountDevPath", std::string("/dev/ttyUSB0"), {" mount serial device paths"}),
// mount serial device speed
make_config_record("MountDevSpeed", 19200, {" mount serial device speed"}),
// motor encoders serial device path
make_config_record("EncoderDevPath", std::string(""), {" motor encoders serial device path"}),
// X-axis encoder serial device path
make_config_record("EncoderXDevPath", std::string("/dev/encoder_X0"), {" X-axis encoder serial device path"}),
// Y-axis encoder serial device path
make_config_record("EncoderYDevPath", std::string("/dev/encoder_Y0"), {" Y-axis encoder serial device path"}),
// encoders serial device speed
make_config_record("EncoderDevSpeed", 153000, {" encoders serial device speed"}),
// ==1 if encoder works as separate serial device, ==2 if there's new version with two devices
make_config_record(
"SepEncoder",
2,
{" ==1 if encoder works as separate serial device, ==2 if there's new version with two devices"}),
// mount polling interval in millisecs
make_config_record("MountReqInterval", std::chrono::milliseconds(100), {" mount polling interval in millisecs"}),
// encoders polling interval in millisecs
make_config_record("EncoderReqInterval", std::chrono::milliseconds(1), {" encoders polling interval in millisecs"}),
// mount axes rate calculation interval in millisecs
make_config_record("EncoderSpeedInterval",
std::chrono::milliseconds(50),
{" mount axes rate calculation interval in millisecs"}),
make_config_record("PIDMaxDt",
std::chrono::milliseconds(1000),
{" maximal PID refresh time interval in millisecs",
" NOTE: if PID data will be refreshed with interval longer than this value (e.g. user polls "
" encoder data too rarely)",
" then the PID 'expired' data will be cleared and new computing loop is started"}),
make_config_record("PIDRefreshDt", std::chrono::milliseconds(100), {" PID refresh interval"}),
make_config_record("PIDCycleDt",
std::chrono::milliseconds(5000),
{" PID I cycle time (analog of 'RC' for PID on opamps)"}),
// // X-axis coordinate PID P,I,D-params
// make_config_record("XPIDC", std::vector<double>{0.5, 0.1, 0.2}, {" X-axis coordinate PID P,I,D-params"}),
// X-axis rate PID P,I,D-params
make_config_record("XPIDV", std::vector<double>{0.09, 0.0, 0.05}, {" X-axis rate PID P,I,D-params"}),
// // Y-axis coordinate PID P, I, D-params
// make_config_record("YPIDC", std::vector<double>{0.5, 0.1, 0.2}, {" Y-axis coordinate PID P, I, D-params"}),
// Y-axis rate PID P,I,D-params
make_config_record("YPIDV", std::vector<double>{0.09, 0.0, 0.05}, {" Y-axis rate PID P,I,D-params"}),
// maximal moving rate (degrees per second) along HA-axis (Y-axis of Sidereal servo microcontroller)
make_config_record(
"hwMaxRateHA",
mcc::impl::MccAngle(8.0_degs),
{" maximal moving rate (degrees per second) along HA-axis (Y-axis of Sidereal servo microcontroller)"}),
// maximal moving rate (degrees per second) along DEC-axis (X-axis of Sidereal servo microcontroller)
make_config_record(
"hwMaxRateDEC",
mcc::impl::MccAngle(10.0_degs),
{" maximal moving rate (degrees per second) along DEC-axis (X-axis of Sidereal servo microcontroller)"}),
make_config_record("MaxPointingErr",
mcc::impl::MccAngle(8.0_degs),
{" slewing-to-pointing mode angular limit in degrees"}),
make_config_record("MaxFinePointingErr",
mcc::impl::MccAngle(1.5_degs),
{" pointing-to-guiding mode angular limit in degrees"}),
make_config_record("MaxGuidingErr",
mcc::impl::MccAngle(0.5_arcsecs),
{" guiding 'good'-flag error cirle radius (mount-to-target distance) in degrees"}),
make_config_record("XEncZero", (int64_t)0, {" X-axis encoder zero-point in ticks"}),
make_config_record("YEncZero", (int64_t)0, {" Y-axis encoder zero-point in ticks"}),
/* movement related common parameters */
// timeout for telemetry updating in milliseconds
make_config_record("telemetryTimeout",
std::chrono::milliseconds(3000),
{" timeout for telemetry updating in milliseconds"}),
// mount stopping process timeout in seconds
make_config_record("stopTimeout", std::chrono::seconds(30), {" mount stopping process timeout in seconds"}),
// minimal allowed time in seconds to prohibited zone
make_config_record("minTimeToPZone",
std::chrono::seconds(10),
{" minimal allowed time in seconds to prohibited zone"}),
/* slewing and tracking parameters */
// telemetry request interval (in millisecs) in slewing mode
make_config_record("slewingTelemetryInterval",
std::chrono::milliseconds(100),
{" telemetry request interval (in millisecs) in slewing mode"}),
// slew process timeout in seconds
make_config_record("slewTimeout", std::chrono::seconds(3600), {" slew process timeout in seconds"}),
// coordinates difference in arcsecs to stop slewing
make_config_record("slewToleranceRadius", 5.0, {"coordinates difference in arcsecs to stop slewing"}),
// slewing trajectory filename (used for debugging purposes)
// if it is an empty - just skip saving
make_config_record("slewingPathFilename",
std::string(),
{" slewing trajectory filename", "if it is an empty - just skip saving"}),
make_config_record("trackingTelemetryInterval",
std::chrono::milliseconds(100),
{" telemetry request interval (in millisecs) in tracking mode"}),
// maximal valid target-to-mount distance for tracking process (arcsecs)
// if current distance is greater than assume current mount coordinate as target point
make_config_record("trackingMaxCoordDiff",
20.0,
{" maximal valid target-to-mount distance for tracking process (arcsecs)",
" if current distance is greater than assume current mount coordinate as target point"}),
make_config_record("trackingPathFilename",
std::string(),
{" tracking trajectory filename", "if it is an empty - just skip saving"})
);
static_assert(std::is_copy_assignable_v<mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_HA_OBS>>);
static_assert(std::is_copy_assignable_v<mcc::impl::MccAltLimitPZ<mcc::impl::MccAltLimitKind::MIN_ALT_LIMIT>>);
// static_assert(std::is_copy_assignable_v<decltype(Asibfm700MountConfigurationDefaults)>);
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;
Asibfm700MountConfiguration(const Asibfm700MountConfiguration&) = default;
Asibfm700MountConfiguration(Asibfm700MountConfiguration&&) = default;
Asibfm700MountConfiguration& operator=(const Asibfm700MountConfiguration&) = default;
Asibfm700MountConfiguration& operator=(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;
}
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::error_code dumpDefaultsToFile(const std::filesystem::path& path)
{
std::error_code ec{};
std::string output_buffer;
#ifdef __GNUG__
// to fix GCC compilation crash for the versions < 16
#if GCC_VERSION < 160000
auto write_rec = [&output_buffer, &ec, obj_ptr = this]<size_t I = 0>(this auto& self) -> void {
#else
auto write_rec = [&output_buffer, &ec, this]<size_t I = 0>(this auto& self) -> void {
#endif
#endif
if constexpr (I < NUMBER_OF_RECORDS) {
// add an empty string within records
std::format_to(std::back_inserter(output_buffer), "{}", DEFAULT_RECORD_DELIMITER);
#ifdef __GNUG__
// to fix GCC compilation crash for the versions < 16
#if GCC_VERSION < 160000
ec = obj_ptr->template formatRecord<I>(output_buffer, DEFAULT_RECORD_DELIMITER, true);
#else
ec = formatRecord<I>(output_buffer, DEFAULT_RECORD_DELIMITER, true);
#endif
#endif
if (ec) {
return;
}
self.template operator()<I + 1>();
}
};
write_rec();
// [&output_buffer, &ec, this]<size_t... Is>(std::index_sequence<Is...>) {
// ((std::format_to(std::back_inserter(output_buffer), "{}", DEFAULT_RECORD_DELIMITER),
// ec = formatRecord<Is>(output_buffer, DEFAULT_RECORD_DELIMITER, true)),
// ...);
// }(std::make_index_sequence<NUMBER_OF_RECORDS>());
if (!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 << output_buffer;
} catch (std::ios_base::failure const& ex) {
ec = ex.code();
} catch (...) {
ec = std::make_error_code(std::errc::operation_canceled);
}
}
}
return ec;
}
std::filesystem::path configFilename() const
{
return _lastConfigPath;
}
/* some most often used quantities */
template <mcc::mcc_angle_c T>
T siteLatitude() const
{
return static_cast<double>(getValue<mcc::impl::MccAngle>("siteLatitude").value_or(mcc::impl::MccAngle{}));
};
mcc::impl::MccAngle siteLatitude() const
{
return siteLatitude<mcc::impl::MccAngle>();
};
template <mcc::mcc_angle_c T>
T siteLongitude() const
{
return static_cast<double>(getValue<mcc::impl::MccAngle>("siteLongitude").value_or(mcc::impl::MccAngle{}));
};
mcc::impl::MccAngle siteLongitude() const
{
return siteLongitude<mcc::impl::MccAngle>();
};
template <typename T>
T siteElevation() const
requires std::is_arithmetic_v<T>
{
return getValue<double>("siteElevation").value_or(0.0);
}
double siteElevation() const
{
return getValue<double>("siteElevation").value_or(0.0);
};
template <typename T>
T refractWavelength() const
requires std::is_arithmetic_v<T>
{
return getValue<double>("refractWavelength").value_or(0.0);
}
double refractWavelength() const
{
return getValue<double>("refractWavelength").value_or(0.0);
};
template <mcc::traits::mcc_output_char_range R>
R leapSecondFilename() const
{
R r;
std::string val = getValue<std::string>("leapSecondFilename").value_or("");
std::ranges::copy(val, std::back_inserter(r));
return r;
}
std::string leapSecondFilename() const
{
return leapSecondFilename<std::string>();
};
template <mcc::traits::mcc_output_char_range R>
R bulletinAFilename() const
{
R r;
std::string val = getValue<std::string>("bulletinAFilename").value_or("");
std::ranges::copy(val, std::back_inserter(r));
return r;
}
std::string bulletinAFilename() const
{
return bulletinAFilename<std::string>();
};
AsibFM700ServoController::hardware_config_t servoControllerConfig() const
{
AsibFM700ServoController::hardware_config_t hw_cfg;
hw_cfg.hwConfig = {};
hw_cfg.pollingInterval =
getValue<std::chrono::milliseconds>("hardwarePollingPeriod").value_or(std::chrono::milliseconds(300));
hw_cfg.pollingInterval =
getValue<std::chrono::milliseconds>("hardwarePollingTimeout").value_or(std::chrono::milliseconds(30000));
hw_cfg.MountDevPath = getValue<std::string>("MountDevPath").value_or(std::string{});
hw_cfg.EncoderDevPath = getValue<std::string>("EncoderDevPath").value_or(std::string{});
hw_cfg.EncoderXDevPath = getValue<std::string>("EncoderXDevPath").value_or(std::string{});
hw_cfg.EncoderYDevPath = getValue<std::string>("EncoderYDevPath").value_or(std::string{});
hw_cfg.devConfig.MountDevPath = hw_cfg.MountDevPath.data();
hw_cfg.devConfig.EncoderDevPath = hw_cfg.EncoderDevPath.data();
hw_cfg.devConfig.EncoderXDevPath = hw_cfg.EncoderXDevPath.data();
hw_cfg.devConfig.EncoderYDevPath = hw_cfg.EncoderYDevPath.data();
hw_cfg.devConfig.RunModel = getValue<int>("RunModel").value_or(int{});
hw_cfg.devConfig.MountDevSpeed = getValue<int>("MountDevSpeed").value_or(int{});
hw_cfg.devConfig.EncoderDevSpeed = getValue<int>("EncoderDevSpeed").value_or(int{});
hw_cfg.devConfig.SepEncoder = getValue<int>("SepEncoder").value_or(int{});
std::chrono::duration<double> secs; // seconds as floating-point
secs = getValue<std::chrono::milliseconds>("MountReqInterval").value_or(std::chrono::milliseconds{});
hw_cfg.devConfig.MountReqInterval = secs.count();
secs = getValue<std::chrono::milliseconds>("EncoderReqInterval").value_or(std::chrono::milliseconds{});
hw_cfg.devConfig.EncoderReqInterval = secs.count();
secs = getValue<std::chrono::milliseconds>("EncoderSpeedInterval").value_or(std::chrono::milliseconds{});
hw_cfg.devConfig.EncoderSpeedInterval = secs.count();
secs = getValue<std::chrono::milliseconds>("PIDMaxDt").value_or(std::chrono::milliseconds{1000});
hw_cfg.devConfig.PIDMaxDt = secs.count();
secs = getValue<std::chrono::milliseconds>("PIDRefreshDt").value_or(std::chrono::milliseconds{100});
hw_cfg.devConfig.PIDRefreshDt = secs.count();
secs = getValue<std::chrono::milliseconds>("PIDCycleDt").value_or(std::chrono::milliseconds{5000});
hw_cfg.devConfig.PIDCycleDt = secs.count();
std::vector<double> pid = getValue<std::vector<double>>("XPIDV").value_or(std::vector<double>{});
if (pid.size() > 2) {
hw_cfg.devConfig.XPIDV.P = pid[0];
hw_cfg.devConfig.XPIDV.I = pid[1];
hw_cfg.devConfig.XPIDV.D = pid[2];
}
pid = getValue<std::vector<double>>("YPIDV").value_or(std::vector<double>{});
if (pid.size() > 2) {
hw_cfg.devConfig.YPIDV.P = pid[0];
hw_cfg.devConfig.YPIDV.I = pid[1];
hw_cfg.devConfig.YPIDV.D = pid[2];
}
double ang = getValue<mcc::impl::MccAngle>("MaxPointingErr").value_or(mcc::impl::MccAngle(8.0_degs));
hw_cfg.devConfig.MaxPointingErr = ang;
ang = getValue<mcc::impl::MccAngle>("MaxFinePointingErr").value_or(mcc::impl::MccAngle(1.5_degs));
hw_cfg.devConfig.MaxFinePointingErr = ang;
ang = getValue<mcc::impl::MccAngle>("MaxGuidingErr").value_or(mcc::impl::MccAngle(0.5_arcsecs));
hw_cfg.devConfig.MaxGuidingErr = ang;
ang = getValue<int64_t>("XEncZero").value_or(0);
hw_cfg.devConfig.XEncZero = ang;
ang = getValue<int64_t>("YEncZero").value_or(0);
hw_cfg.devConfig.YEncZero = ang;
return hw_cfg;
}
Asibfm700PCM::pcm_data_t pcmData() const
{
Asibfm700PCM::pcm_data_t pcm_data;
std::vector<double> empty_vec;
pcm_data.type = getValue<decltype(pcm_data.type)>("pcmType").value_or(pcm_data.type);
pcm_data.siteLatitude = getValue<mcc::impl::MccAngle>("siteLatitude").value_or(pcm_data.siteLatitude);
std::vector<double> vec = getValue<std::vector<double>>("pcmGeomCoeffs").value_or(empty_vec);
if (vec.size() >= 9) { // must be 9 coefficients
pcm_data.geomCoefficients = {.zeroPointX = vec[0],
.zeroPointY = vec[1],
.collimationErr = vec[2],
.nonperpendErr = vec[3],
.misalignErr1 = vec[4],
.misalignErr2 = vec[5],
.tubeFlexure = vec[6],
.DECaxisFlexure = vec[7],
.forkFlexure = vec[8]};
}
#ifdef USE_BSPLINE_PCM
// std::vector<size_t> dd = getValue<decltype(dd)>("pcmBsplineDegree").value_or(dd);
// if (dd.size() >= 2) {
// pcm_data.bspline.bsplDegreeX = dd[0] > 0 ? dd[0] : 3;
// pcm_data.bspline.bsplDegreeY = dd[1] > 0 ? dd[1] : 3;
// }
vec = getValue<std::vector<double>>("pcmBsplineXknots").value_or(empty_vec);
// pid must contains interior and border (single point for each border) knots so minimal length must be 2
if (vec.size() >= 2) {
// generate full knots array (with border knots)
size_t Nknots = vec.size() + pcm_data.bspline.bsplDegreeX * 2 - 2;
pcm_data.bspline.knotsX.resize(Nknots);
for (size_t i = 0; i <= pcm_data.bspline.bsplDegreeX; ++i) { // border knots
pcm_data.bspline.knotsX[i] = vec[0];
pcm_data.bspline.knotsX[Nknots - i - 1] = vec.back();
}
for (size_t i = 0; i < (vec.size() - 2); ++i) { // interior knots
pcm_data.bspline.knotsX[i + pcm_data.bspline.bsplDegreeX] = vec[1 + i];
}
}
vec = getValue<std::vector<double>>("pcmBsplineYknots").value_or(empty_vec);
// pid must contains interior and border (single point for each border) knots so minimal length must be 2
if (vec.size() >= 2) {
// generate full knots array (with border knots)
size_t Nknots = vec.size() + pcm_data.bspline.bsplDegreeY * 2 - 2;
pcm_data.bspline.knotsY.resize(Nknots);
for (size_t i = 0; i <= pcm_data.bspline.bsplDegreeY; ++i) { // border knots
pcm_data.bspline.knotsY[i] = vec[0];
pcm_data.bspline.knotsY[Nknots - i - 1] = vec.back();
}
for (size_t i = 0; i < (vec.size() - 2); ++i) { // interior knots
pcm_data.bspline.knotsY[i + pcm_data.bspline.bsplDegreeY] = vec[1 + i];
}
}
// minimal allowed number of B-spline coefficients
size_t Ncoeffs = pcm_data.type == mcc::impl::MccDefaultPCMType::PCM_TYPE_GEOMETRY
? 0
: (pcm_data.bspline.knotsX.size() - pcm_data.bspline.bsplDegreeX - 1) *
(pcm_data.bspline.knotsY.size() - pcm_data.bspline.bsplDegreeY - 1);
vec = getValue<std::vector<double>>("pcmBsplineXcoeffs").value_or(empty_vec);
if (vec.size() >= Ncoeffs) {
pcm_data.bspline.coeffsX.clear();
std::ranges::copy_n(vec.begin(), Ncoeffs, std::back_inserter(pcm_data.bspline.coeffsX));
// pcm_data.bspline.coeffsX.resize(Ncoeffs);
// for (size_t i = 0; i < Ncoeffs; ++i) {
// pcm_data.bspline.coeffsX[i] = vec[i];
// }
}
vec = getValue<std::vector<double>>("pcmBsplineYcoeffs").value_or(empty_vec);
if (vec.size() >= Ncoeffs) {
pcm_data.bspline.coeffsY.clear();
std::ranges::copy_n(vec.begin(), Ncoeffs, std::back_inserter(pcm_data.bspline.coeffsY));
// pcm_data.bspline.coeffsY.resize(Ncoeffs);
// for (size_t i = 0; i < Ncoeffs; ++i) {
// pcm_data.bspline.coeffsY[i] = vec[i];
// }
}
vec = getValue<std::vector<double>>("pcmInverseBsplineXcoeffs").value_or(empty_vec);
if (vec.size() >= Ncoeffs) {
pcm_data.bspline.inverseCoeffsX.clear();
std::ranges::copy_n(vec.begin(), Ncoeffs, std::back_inserter(pcm_data.bspline.inverseCoeffsX));
}
vec = getValue<std::vector<double>>("pcmInverseBsplineYcoeffs").value_or(empty_vec);
if (vec.size() >= Ncoeffs) {
pcm_data.bspline.inverseCoeffsY.clear();
std::ranges::copy_n(vec.begin(), Ncoeffs, std::back_inserter(pcm_data.bspline.inverseCoeffsY));
}
#endif
return pcm_data;
}
mcc::impl::MccAltLimitPZ<mcc::impl::MccAltLimitKind::MIN_ALT_LIMIT> minAltPZone()
{
return getValue<mcc::impl::MccAltLimitPZ<mcc::impl::MccAltLimitKind::MIN_ALT_LIMIT>>("altLimPZ")
.value_or(mcc::impl::MccAltLimitPZ<mcc::impl::MccAltLimitKind::MIN_ALT_LIMIT>{10.0, SAORAS_LATITUDE});
}
mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_HA_OBS> axisLimitSwitchHA()
{
return getValue<mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_HA_OBS>>(
"axisLimitSwitchHA")
.value_or(mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_HA_OBS>{
-170.0_degs, 170.0_degs, nullptr});
}
mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_DEC_OBS> axisLimitSwitchDEC()
{
return getValue<mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_DEC_OBS>>(
"axisLimitSwitchDEC")
.value_or(mcc::impl::MccAxisLimitSwitchPZ<mcc::impl::MccCoordKind::COORDS_KIND_DEC_OBS>{
-90.0_degs, 90.0_degs, nullptr});
}
details::movement_pars_t movingModelParams() const
{
details::movement_pars_t pars;
auto get_value = [this]<typename VT>(std::string_view name, VT& val) {
val = getValue<VT>(name).value_or(val);
};
pars.telemetryTimeout =
getValue<decltype(pars.telemetryTimeout)>("telemetryTimeout").value_or(pars.telemetryTimeout);
pars.minTimeToPZone = getValue<decltype(pars.minTimeToPZone)>("minTimeToPZone").value_or(pars.minTimeToPZone);
pars.slewToleranceRadius =
getValue<decltype(pars.slewToleranceRadius)>("slewToleranceRadius").value_or(pars.slewToleranceRadius) *
mcc::MCC_ARCSECS_TO_RADS;
get_value("slewingTelemetryInterval", pars.slewingTelemetryInterval);
pars.slewRateX = getValue<decltype(pars.slewRateX)>("hwMaxRateHA").value_or(pars.slewRateX);
pars.slewRateY = getValue<decltype(pars.slewRateY)>("hwMaxRateDEC").value_or(pars.slewRateY);
pars.adjustCoordDiff =
getValue<decltype(pars.adjustCoordDiff)>("adjustCoordDiff").value_or(pars.adjustCoordDiff) *
mcc::MCC_DEGRESS_TO_RADS;
pars.slewTimeout = getValue<decltype(pars.slewTimeout)>("slewTimeout").value_or(pars.slewTimeout);
pars.slewingPathFilename =
getValue<decltype(pars.slewingPathFilename)>("slewingPathFilename").value_or(std::string());
get_value("trackingTelemetryInterval", pars.trackingTelemetryInterval);
pars.trackingCycleInterval = getValue<decltype(pars.trackingCycleInterval)>("trackingCycleInterval")
.value_or(pars.trackingCycleInterval);
pars.trackingMaxCoordDiff =
getValue<decltype(pars.trackingMaxCoordDiff)>("trackingMaxCoordDiff").value_or(pars.trackingMaxCoordDiff) *
mcc::MCC_ARCSECS_TO_RADS;
pars.trackingPathFilename =
getValue<decltype(pars.trackingPathFilename)>("trackingPathFilename").value_or(std::string());
return pars;
}
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