759 lines
31 KiB
C++
759 lines
31 KiB
C++
#pragma once
|
|
|
|
/**/
|
|
|
|
#include <expected>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
|
|
#include <mcc_angle.h>
|
|
#include <mcc_moving_model_common.h>
|
|
#include <mcc_pcm.h>
|
|
#include <mcc_utils.h>
|
|
|
|
#include "asibfm700_common.h"
|
|
#include "asibfm700_servocontroller.h"
|
|
|
|
namespace asibfm700
|
|
{
|
|
|
|
|
|
/* A SIMPLE "KEYWORD - VALUE" HOLDER CLASS SUITABLE TO STORE SOME APPLICATION CONFIGURATION */
|
|
|
|
|
|
// to follow std::variant requirements (not references, not array, not void)
|
|
template <typename T>
|
|
concept config_record_valid_type_c = requires { !std::is_array_v<T> && !std::is_void_v<T> && !std::is_reference_v<T>; };
|
|
|
|
// simple minimal-requirement configuration record class
|
|
template <config_record_valid_type_c T>
|
|
struct simple_config_record_t {
|
|
std::string_view key;
|
|
T value;
|
|
std::vector<std::string_view> comment;
|
|
};
|
|
|
|
|
|
/* ASTOROSIB FM700 MOUNT CONFIGURATION CLASS */
|
|
|
|
// configuration description and its defaults
|
|
static auto Asibfm700MountConfigDefaults = std::make_tuple(
|
|
// main cycle period in millisecs
|
|
simple_config_record_t{"hardwarePollingPeriod", std::chrono::milliseconds{100}, {"main cycle period in millisecs"}},
|
|
|
|
/* geographic coordinates of the observation site */
|
|
|
|
// site latitude in degrees
|
|
simple_config_record_t{"siteLatitude", mcc::MccAngle(43.646711_degs), {"site latitude in degrees"}},
|
|
|
|
// site longitude in degrees
|
|
simple_config_record_t{"siteLongitude", mcc::MccAngle(41.440732_degs), {"site longitude in degrees"}},
|
|
|
|
// site elevation in meters
|
|
simple_config_record_t{"siteElevation", 2070.0, {"site elevation in meters"}},
|
|
|
|
/* celestial coordinate transformation */
|
|
|
|
// wavelength at which refraction is calculated (in mkm)
|
|
simple_config_record_t{"refractWavelength", 0.55, {"wavelength at which refraction is calculated (in mkm)"}},
|
|
|
|
// an empty filename means default precompiled string
|
|
simple_config_record_t{"leapSecondFilename", std::string(), {"an empty filename means default precompiled string"}},
|
|
|
|
// an empty filename means default precompiled string
|
|
simple_config_record_t{"bulletinAFilename", std::string(), {"an empty filename means default precompiled string"}},
|
|
|
|
/* pointing correction model */
|
|
|
|
// PCM default type
|
|
simple_config_record_t{"pcmType",
|
|
mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY,
|
|
{"PCM type:", "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
|
|
simple_config_record_t{"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"}},
|
|
|
|
// PCM B-spline degrees
|
|
simple_config_record_t{"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!
|
|
simple_config_record_t{"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!
|
|
simple_config_record_t{
|
|
"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)
|
|
simple_config_record_t{"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)
|
|
simple_config_record_t{"pcmBsplineYcoeffs",
|
|
std::vector<double>{},
|
|
{"PCM B-spline coeffs for along Y-axis (declination angle)"}},
|
|
|
|
|
|
/* slewing and tracking parameters */
|
|
|
|
// // arcseconds per second
|
|
// simple_config_record_t{"sideralRate", 15.0410686},
|
|
|
|
// timeout for telemetry updating in milliseconds
|
|
simple_config_record_t{"telemetryTimeout",
|
|
std::chrono::milliseconds(3000),
|
|
{"timeout for telemetry updating in milliseconds"}},
|
|
|
|
// minimal allowed time in seconds to prohibited zone
|
|
simple_config_record_t{"minTimeToPZone",
|
|
std::chrono::seconds(10),
|
|
{"minimal allowed time in seconds to prohibited zone"}},
|
|
|
|
// a time interval to update prohibited zones related quantities (millisecs)
|
|
simple_config_record_t{"updatingPZoneInterval",
|
|
std::chrono::milliseconds(5000),
|
|
{"a time interval to update prohibited zones related quantities (millisecs)"}},
|
|
|
|
// coordinates difference in arcsecs to stop slewing
|
|
simple_config_record_t{"slewToleranceRadius", 5.0, {"coordinates difference in arcsecs to stop slewing"}},
|
|
|
|
// target-mount coordinate difference in arcsecs to start adjusting of slewing
|
|
simple_config_record_t{"adjustCoordDiff",
|
|
50.0,
|
|
{"target-mount coordinate difference in arcsecs to start adjusting of slewing"}},
|
|
|
|
// minimum time in millisecs between two successive adjustments
|
|
simple_config_record_t{"adjustCycleInterval",
|
|
std::chrono::milliseconds(300),
|
|
{"minimum time in millisecs between two successive adjustments"}},
|
|
|
|
// slew process timeout in seconds
|
|
simple_config_record_t{"slewTimeout", std::chrono::seconds(3600), {"slew process timeout in seconds"}},
|
|
|
|
// a time shift into future to compute target position in future (UT1-scale time duration, millisecs)
|
|
simple_config_record_t{
|
|
"timeShiftToTargetPoint",
|
|
std::chrono::milliseconds(10000),
|
|
{"a time shift into future to compute target position in future (UT1-scale time duration, millisecs)"}},
|
|
|
|
// minimum time in millisecs between two successive tracking corrections
|
|
simple_config_record_t{"trackingCycleInterval",
|
|
std::chrono::milliseconds(300),
|
|
{"minimum time in millisecs between two successive tracking corrections"}},
|
|
|
|
// maximal valid target-to-mount distance for tracking process (arcsecs)
|
|
// if current distance is greater than assume current mount coordinate as target point
|
|
simple_config_record_t{"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"}},
|
|
|
|
|
|
/* prohibited zones */
|
|
|
|
// minimal altitude
|
|
simple_config_record_t{"pzMinAltitude", mcc::MccAngle(10.0_degs), {"minimal altitude"}},
|
|
|
|
// HA-axis limit switch minimal value
|
|
simple_config_record_t{"pzLimitSwitchHAMin", mcc::MccAngle(-170.0_degs), {"HA-axis limit switch minimal value"}},
|
|
|
|
// HA-axis limit switch maximal value
|
|
simple_config_record_t{"pzLimitSwitchHAMax", mcc::MccAngle(170.0_degs), {"HA-axis limit switch maximal value"}},
|
|
|
|
// DEC-axis limit switch minimal value
|
|
simple_config_record_t{"pzLimitSwitchDecMin", mcc::MccAngle(-90.0_degs), {"DEC-axis limit switch minimal value"}},
|
|
|
|
// DEC-axis limit switch maximal value
|
|
simple_config_record_t{"pzLimitSwitchDecMax", mcc::MccAngle(90.0_degs), {"DEC-axis limit switch maximal value"}},
|
|
|
|
|
|
/* hardware-related */
|
|
|
|
// hardware mode: 1 - model mode, otherwise real mode
|
|
simple_config_record_t{"RunModel", 0, {"hardware mode: 1 - model mode, otherwise real mode"}},
|
|
|
|
// mount serial device paths
|
|
simple_config_record_t{"MountDevPath", std::string("/dev/ttyUSB0"), {"mount serial device paths"}},
|
|
|
|
// mount serial device speed
|
|
simple_config_record_t{"MountDevSpeed", 19200, {"mount serial device speed"}},
|
|
|
|
// motor encoders serial device path
|
|
simple_config_record_t{"EncoderDevPath", std::string(""), {"motor encoders serial device path"}},
|
|
|
|
// X-axis encoder serial device path
|
|
simple_config_record_t{"EncoderXDevPath", std::string("/dev/encoderX0"), {"X-axis encoder serial device path"}},
|
|
|
|
// Y-axis encoder serial device path
|
|
simple_config_record_t{"EncoderYDevPath", std::string("/dev/encoderY0"), {"Y-axis encoder serial device path"}},
|
|
|
|
// encoders serial device speed
|
|
simple_config_record_t{"EncoderDevSpeed", 153000, {"encoders serial device speed"}},
|
|
|
|
// ==1 if encoder works as separate serial device, ==2 if there's new version with two devices
|
|
simple_config_record_t{
|
|
"SepEncoder",
|
|
2,
|
|
{"==1 if encoder works as separate serial device, ==2 if there's new version with two devices"}},
|
|
|
|
|
|
// mount polling interval in millisecs
|
|
simple_config_record_t{"MountReqInterval", std::chrono::milliseconds(100), {"mount polling interval in millisecs"}},
|
|
|
|
// encoders polling interval in millisecs
|
|
simple_config_record_t{"EncoderReqInterval",
|
|
std::chrono::milliseconds(50),
|
|
{"encoders polling interval in millisecs"}},
|
|
|
|
// mount axes rate calculation interval in millisecs
|
|
simple_config_record_t{"EncoderSpeedInterval",
|
|
std::chrono::milliseconds(100),
|
|
{"mount axes rate calculation interval in millisecs"}},
|
|
|
|
// X-axis coordinate PID P,I,D-params
|
|
simple_config_record_t{"XPIDC", std::vector<double>{0.8, 0.1, 0.3}, {"X-axis coordinate PID P,I,D-params"}},
|
|
|
|
// X-axis rate PID P,I,D-params
|
|
simple_config_record_t{"XPIDV", std::vector<double>{1.0, 0.01, 0.2}, {"X-axis rate PID P,I,D-params"}},
|
|
|
|
// Y-axis coordinate PID P, I, D-params
|
|
simple_config_record_t{"YPIDC", std::vector<double>{0.8, 0.1, 0.3}, {"Y-axis coordinate PID P, I, D-params"}},
|
|
|
|
// Y-axis rate PID P,I,D-params
|
|
simple_config_record_t{"YPIDV", std::vector<double>{0.5, 0.2, 0.5}, {"Y-axis rate PID P,I,D-params"}},
|
|
|
|
|
|
// maximal moving rate (degrees per second) along HA-axis (Y-axis of Sidereal servo microcontroller)
|
|
simple_config_record_t{
|
|
"hwMaxRateHA",
|
|
mcc::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)
|
|
simple_config_record_t{
|
|
"hwMaxRateDEC",
|
|
mcc::MccAngle(10.0_degs),
|
|
{"maximal moving rate (degrees per second) along DEC-axis (X-axis of Sidereal servo microcontroller)"}}
|
|
|
|
);
|
|
|
|
|
|
class Asibfm700MountConfig : public mcc::utils::KeyValueHolder<decltype(Asibfm700MountConfigDefaults)>
|
|
{
|
|
using base_t = mcc::utils::KeyValueHolder<decltype(Asibfm700MountConfigDefaults)>;
|
|
|
|
protected:
|
|
inline static auto deserializer = []<typename VT>(std::string_view str, VT& value) {
|
|
std::error_code ec{};
|
|
|
|
mcc::utils::MccSimpleDeserializer deser;
|
|
deser.setRangeDelim(base_t::VALUE_ARRAY_DELIM);
|
|
|
|
if constexpr (std::is_arithmetic_v<VT> || mcc::traits::mcc_output_char_range<VT> || std::ranges::range<VT> ||
|
|
mcc::traits::mcc_time_duration_c<VT>) {
|
|
// ec = base_t::defaultDeserializeFunc(str, value);
|
|
ec = deser(str, value);
|
|
} else if constexpr (std::same_as<VT, mcc::MccAngle>) { // assume here all angles are in degrees
|
|
double vd;
|
|
// ec = base_t::defaultDeserializeFunc(str, vd);
|
|
ec = deser(str, vd);
|
|
if (!ec) {
|
|
value = mcc::MccAngle(vd, mcc::MccDegreeTag{});
|
|
}
|
|
} else if constexpr (std::same_as<VT, mcc::MccDefaultPCMType>) {
|
|
std::string vstr;
|
|
// ec = base_t::defaultDeserializeFunc(str, vstr);
|
|
ec = deser(str, vstr);
|
|
|
|
if (!ec) {
|
|
auto s = mcc::utils::trimSpaces(vstr);
|
|
|
|
if (s == mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY>) {
|
|
value = mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY;
|
|
} else if (s == mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY_BSPLINE>) {
|
|
value = mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY;
|
|
} else if (s == mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_BSPLINE>) {
|
|
value = mcc::MccDefaultPCMType::PCM_TYPE_BSPLINE;
|
|
} else {
|
|
ec = std::make_error_code(std::errc::invalid_argument);
|
|
}
|
|
}
|
|
} else {
|
|
ec = std::make_error_code(std::errc::invalid_argument);
|
|
}
|
|
|
|
return ec;
|
|
};
|
|
|
|
|
|
public:
|
|
/* the most usefull config fields */
|
|
|
|
template <mcc::traits::mcc_time_duration_c DT>
|
|
DT hardwarePollingPeriod() const
|
|
{
|
|
return std::chrono::duration_cast<DT>(
|
|
getValue<std::chrono::milliseconds>("hardwarePollingPeriod").value_or(std::chrono::milliseconds{}));
|
|
};
|
|
|
|
std::chrono::milliseconds hardwarePollingPeriod() const
|
|
{
|
|
return hardwarePollingPeriod<std::chrono::milliseconds>();
|
|
};
|
|
|
|
template <mcc::mcc_angle_c T>
|
|
T siteLatitude() const
|
|
{
|
|
return static_cast<double>(getValue<mcc::MccAngle>("siteLatitude").value_or(mcc::MccAngle{}));
|
|
};
|
|
|
|
mcc::MccAngle siteLatitude() const
|
|
{
|
|
return siteLatitude<mcc::MccAngle>();
|
|
};
|
|
|
|
template <mcc::mcc_angle_c T>
|
|
T siteLongitude() const
|
|
{
|
|
return static_cast<double>(getValue<mcc::MccAngle>("siteLongitude").value_or(mcc::MccAngle{}));
|
|
};
|
|
|
|
mcc::MccAngle siteLongitude() const
|
|
{
|
|
return siteLongitude<mcc::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>();
|
|
};
|
|
|
|
|
|
template <mcc::mcc_angle_c T>
|
|
T pzMinAltitude() const
|
|
{
|
|
return static_cast<double>(getValue<mcc::MccAngle>("pzMinAltitude").value_or(mcc::MccAngle{}));
|
|
};
|
|
|
|
mcc::MccAngle pzMinAltitude() const
|
|
{
|
|
return pzMinAltitude<mcc::MccAngle>();
|
|
};
|
|
|
|
template <mcc::mcc_angle_c T>
|
|
T pzLimitSwitchHAMin() const
|
|
{
|
|
return static_cast<double>(getValue<mcc::MccAngle>("pzLimitSwitchHAMin").value_or(mcc::MccAngle{}));
|
|
};
|
|
|
|
mcc::MccAngle pzLimitSwitchHAMin() const
|
|
{
|
|
return pzLimitSwitchHAMin<mcc::MccAngle>();
|
|
};
|
|
|
|
template <mcc::mcc_angle_c T>
|
|
T pzLimitSwitchHAMax() const
|
|
{
|
|
return static_cast<double>(getValue<mcc::MccAngle>("pzLimitSwitchHAMax").value_or(mcc::MccAngle{}));
|
|
};
|
|
|
|
mcc::MccAngle pzLimitSwitchHAMax() const
|
|
{
|
|
return pzLimitSwitchHAMax<mcc::MccAngle>();
|
|
};
|
|
|
|
|
|
AsibFM700ServoController::hardware_config_t servoControllerConfig() const
|
|
{
|
|
AsibFM700ServoController::hardware_config_t hw_cfg;
|
|
|
|
hw_cfg.hwConfig = {};
|
|
|
|
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();
|
|
|
|
std::vector<double> pid = getValue<std::vector<double>>("XPIDC").value_or(std::vector<double>{});
|
|
if (pid.size() > 2) {
|
|
hw_cfg.devConfig.XPIDC.P = pid[0];
|
|
hw_cfg.devConfig.XPIDC.I = pid[1];
|
|
hw_cfg.devConfig.XPIDC.D = pid[2];
|
|
}
|
|
|
|
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>>("YPIDC").value_or(std::vector<double>{});
|
|
if (pid.size() > 2) {
|
|
hw_cfg.devConfig.YPIDC.P = pid[0];
|
|
hw_cfg.devConfig.YPIDC.I = pid[1];
|
|
hw_cfg.devConfig.YPIDC.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];
|
|
}
|
|
|
|
return hw_cfg;
|
|
}
|
|
|
|
|
|
mcc::MccSimpleMovingModelParams movingModelParams() const
|
|
{
|
|
static constexpr double arcsecs2rad = std::numbers::pi / 180.0 / 3600.0; // arcseconds to radians
|
|
|
|
mcc::MccSimpleMovingModelParams pars;
|
|
|
|
pars.telemetryTimeout =
|
|
getValue<decltype(pars.telemetryTimeout)>("telemetryTimeout").value_or(pars.telemetryTimeout);
|
|
|
|
pars.minTimeToPZone = getValue<decltype(pars.minTimeToPZone)>("minTimeToPZone").value_or(pars.minTimeToPZone);
|
|
|
|
pars.updatingPZoneInterval = getValue<decltype(pars.updatingPZoneInterval)>("updatingPZoneInterval")
|
|
.value_or(pars.updatingPZoneInterval);
|
|
|
|
pars.slewToleranceRadius =
|
|
getValue<decltype(pars.slewToleranceRadius)>("slewToleranceRadius").value_or(pars.slewToleranceRadius) *
|
|
arcsecs2rad;
|
|
|
|
pars.adjustCoordDiff =
|
|
getValue<decltype(pars.adjustCoordDiff)>("adjustCoordDiff").value_or(pars.adjustCoordDiff) * arcsecs2rad;
|
|
|
|
pars.adjustCycleInterval =
|
|
getValue<decltype(pars.adjustCycleInterval)>("adjustCycleInterval").value_or(pars.adjustCycleInterval);
|
|
|
|
pars.slewTimeout = getValue<decltype(pars.slewTimeout)>("slewTimeout").value_or(pars.slewTimeout);
|
|
|
|
pars.timeShiftToTargetPoint = getValue<decltype(pars.timeShiftToTargetPoint)>("timeShiftToTargetPoint")
|
|
.value_or(pars.timeShiftToTargetPoint);
|
|
|
|
pars.trackingCycleInterval = getValue<decltype(pars.trackingCycleInterval)>("trackingCycleInterval")
|
|
.value_or(pars.trackingCycleInterval);
|
|
|
|
pars.trackingMaxCoordDiff =
|
|
getValue<decltype(pars.trackingMaxCoordDiff)>("trackingMaxCoordDiff").value_or(pars.trackingMaxCoordDiff) *
|
|
arcsecs2rad;
|
|
|
|
return pars;
|
|
}
|
|
|
|
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::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],
|
|
.forkFlexure = vec[7],
|
|
.DECaxisFlexure = vec[8]};
|
|
}
|
|
|
|
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::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.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.resize(Ncoeffs);
|
|
for (size_t i = 0; i < Ncoeffs; ++i) {
|
|
pcm_data.bspline.coeffsY[i] = vec[i];
|
|
}
|
|
}
|
|
|
|
return pcm_data;
|
|
}
|
|
|
|
|
|
Asibfm700MountConfig() : base_t(Asibfm700MountConfigDefaults) {}
|
|
|
|
~Asibfm700MountConfig() = 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();
|
|
|
|
ec = base_t::fromCharRange(buffer, deserializer);
|
|
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);
|
|
}
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
return ec;
|
|
}
|
|
|
|
bool dumpDefaultsToFile(const std::filesystem::path& path)
|
|
{
|
|
std::ofstream fst(path);
|
|
if (!fst.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
fst << "#\n";
|
|
fst << "# ASTROSIB FM-700 MOUNT CONFIGURATION\n" << "#\n";
|
|
fst << "# (created at " << std::format("{:%FT%T UTC}", std::chrono::system_clock::now()) << ")\n";
|
|
fst << "#\n";
|
|
|
|
auto wrec = [&fst, this]<size_t I>() {
|
|
fst << "\n";
|
|
for (size_t i = 0; i < std::get<I>(_keyValue).comment.size(); ++i) {
|
|
fst << "# " << std::get<I>(_keyValue).comment[i] << "\n";
|
|
}
|
|
fst << std::get<I>(_keyValue).key << " = ";
|
|
auto v = std::get<I>(_keyValue).value;
|
|
using v_t = std::remove_cvref_t<decltype(v)>;
|
|
|
|
if constexpr (std::is_arithmetic_v<v_t> || mcc::traits::mcc_char_range<v_t>) {
|
|
fst << std::format("{}", v);
|
|
} else if constexpr (mcc::traits::mcc_time_duration_c<v_t>) {
|
|
fst << std::format("{}", v.count());
|
|
} else if constexpr (mcc::mcc_angle_c<v_t>) {
|
|
fst << std::format("{}", mcc::MccAngle(static_cast<double>(v)).degrees());
|
|
} else if constexpr (std::same_as<v_t, mcc::MccDefaultPCMType>) {
|
|
if (v == mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY) {
|
|
fst << mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY>;
|
|
} else if (v == mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY_BSPLINE) {
|
|
fst << mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY_BSPLINE>;
|
|
} else if (v == mcc::MccDefaultPCMType::PCM_TYPE_BSPLINE) {
|
|
fst << mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_BSPLINE>;
|
|
}
|
|
} else if constexpr (std::ranges::range<v_t> && std::formattable<std::ranges::range_value_t<v_t>, char>) {
|
|
size_t sz = std::ranges::size(v);
|
|
if (!sz) {
|
|
return;
|
|
}
|
|
--sz;
|
|
|
|
auto it = v.begin();
|
|
for (size_t j = 0; j < sz; ++j, ++it) {
|
|
fst << std::format("{}", *it) << base_t::VALUE_ARRAY_DELIM;
|
|
}
|
|
fst << std::format("{}", *it);
|
|
} else if constexpr (std::formattable<v_t, char>) {
|
|
fst << std::format("{}", v);
|
|
} else {
|
|
static_assert(false, "INVALID TYPE!");
|
|
}
|
|
|
|
fst << "\n";
|
|
};
|
|
|
|
[&wrec, this]<size_t... Is>(std::index_sequence<Is...>) {
|
|
(wrec.operator()<Is>(), ...);
|
|
}(std::make_index_sequence<std::tuple_size_v<decltype(Asibfm700MountConfigDefaults)>>());
|
|
|
|
fst.close();
|
|
|
|
return true;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
} // namespace asibfm700
|