This commit is contained in:
2025-09-16 18:35:39 +03:00
parent bb41710645
commit 732cd33947
5 changed files with 433 additions and 175 deletions

View File

@@ -306,14 +306,6 @@ struct MccGenericEqtHrzCoords : MccGenericCelestialPoint<CoordT> {
typedef MccGenericEqtHrzCoords<MccCelestialPoint::coord_t> MccEqtHrzCoords;
template <mcc_angle_c CoordT>
struct MccGenericPointingTarget : MccGenericEqtHrzCoords<CoordT> {
using typename MccGenericEqtHrzCoords<CoordT>::coord_t;
coord_t RA_ICRS{}, DEC_ICRS{};
};
typedef MccGenericPointingTarget<MccCelestialPoint::coord_t> MccPointingTarget;
static constexpr void mcc_copy_eqt_hrz_coord(mcc_eqt_hrz_coord_c auto const& from_pt, mcc_eqt_hrz_coord_c auto* to_pt)
{
@@ -349,64 +341,84 @@ static constexpr void mcc_copy_eqt_hrz_coord(mcc_eqt_hrz_coord_c auto const& fro
template <typename T>
concept mcc_ccte_c = requires(const T t_const, T t) {
{ t_const.nameCCTE() } -> std::formattable<char>;
{ t_const.nameCCTE() } -> std::formattable<char>; // just a name
requires mcc_refract_model_c<typename T::refract_model_t>;
{ t.refractionModel(std::declval<typename T::refract_model_t*>()) } -> mcc_error_c;
/* there is no way to declare a concept for a class templated method, so mandatory implementation is only required
for default MCC library types */
[](T tt) -> mcc_error_c auto {
MccTimePoint tp{};
double jd, st, pa, dZ;
// timepointToJulday(mcc_time_point_c auto tp, mcc_julday_c auto* julday)
{ t.timepointToJulday(std::declval<MccTimePoint>(), std::declval<double*>()) } -> mcc_error_c;
{ t.timepointToJulday(std::declval<MccTimePoint>(), std::declval<MccJulianDay*>()) } -> mcc_error_c;
MccJulianDay jd_class;
// timepointToAppSideral(mcc_time_point_c auto tp, mcc_angle_c auto* st, bool islocal)
{
t.timepointToAppSideral(std::declval<MccTimePoint>(), std::declval<double*>(), std::declval<bool>())
} -> mcc_error_c;
{
t.timepointToAppSideral(std::declval<MccTimePoint>(), std::declval<MccAngle*>(), std::declval<bool>())
} -> mcc_error_c;
MccAngle st_class, pa_class, dZ_class;
MccCelestialPoint cp;
MccGenericCelestialPoint<MccAngle> cp_class;
MccEqtHrzCoords eqhr;
MccGenericEqtHrzCoords<MccAngle> eqhr_class;
// juldayToAppSideral(mcc_julday_c auto jd, mcc_angle_c auto* st, bool islocal)
{ t.juldayToAppSideral(std::declval<double>(), std::declval<double*>(), std::declval<bool>()) } -> mcc_error_c;
{ t.juldayToAppSideral(std::declval<double>(), std::declval<MccAngle*>(), std::declval<bool>()) } -> mcc_error_c;
{
t.juldayToAppSideral(std::declval<MccJulianDay>(), std::declval<double*>(), std::declval<bool>())
} -> mcc_error_c;
{
t.juldayToAppSideral(std::declval<MccJulianDay>(), std::declval<MccAngle*>(), std::declval<bool>())
} -> mcc_error_c;
auto ret = tt.timepointToJulday(tp, &jd);
ret = tt.timepointToJulday(tp, &jd_class);
// NOTE: ASSUMING THE AZIMUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!!
// transformCoordinates(mcc_celestial_point_c auto from_pt, mcc_celestial_point_c auto* to_pt)
{ t.transformCoordinates(std::declval<MccCelestialPoint>(), std::declval<MccCelestialPoint*>()) } -> mcc_error_c;
{
t.transformCoordinates(std::declval<MccCelestialPoint>(), std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.transformCoordinates(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.transformCoordinates(std::declval<MccGenericCelestialPoint<MccAngle>>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{ t.transformCoordinates(std::declval<MccCelestialPoint>(), std::declval<MccEqtHrzCoords*>()) } -> mcc_error_c;
{
t.transformCoordinates(std::declval<MccCelestialPoint>(), std::declval<MccGenericEqtHrzCoords<MccAngle>*>())
} -> mcc_error_c;
{
t.transformCoordinates(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccEqtHrzCoords*>())
} -> mcc_error_c;
{
t.transformCoordinates(std::declval<MccGenericCelestialPoint<MccAngle>>(),
std::declval<MccGenericEqtHrzCoords<MccAngle>*>())
} -> mcc_error_c;
ret = tt.timepointToAppSideral(tp, &st, true);
ret = tt.timepointToAppSideral(tp, &st_class, true);
// parallacticAngle(mcc_celestial_point_c auto pt, mcc_angle_c auto* pa)
{ t.parallacticAngle(std::declval<MccCelestialPoint>(), std::declval<double*>()) } -> mcc_error_c;
{ t.parallacticAngle(std::declval<MccCelestialPoint>(), std::declval<MccAngle*>()) } -> mcc_error_c;
{ t.parallacticAngle(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<double*>()) } -> mcc_error_c;
{
t.parallacticAngle(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccAngle*>())
} -> mcc_error_c;
ret = tt.timepointToAppSideral(jd, &st, true);
ret = tt.timepointToAppSideral(jd, &st_class, true);
ret = tt.timepointToAppSideral(jd_class, &st, true);
ret = tt.timepointToAppSideral(jd_class, &st_class, true);
// NOTE: ASSUMING THE AZIMUTH IS COUNTED FROM THE SOUTH THROUGH THE WEST!!!
ret = tt.transformCoordinates(cp, &cp);
ret = tt.transformCoordinates(cp, &cp_class);
ret = tt.transformCoordinates(cp_class, &cp);
ret = tt.transformCoordinates(cp_class, &cp_class);
ret = tt.transformCoordinates(cp, &eqhr);
ret = tt.transformCoordinates(cp, &eqhr_class);
ret = tt.transformCoordinates(cp_class, &eqhr);
ret = tt.transformCoordinates(cp_class, &eqhr_class);
ret = tt.parallacticAngle(cp, &pa);
ret = tt.parallacticAngle(cp, &pa_class);
ret = tt.parallacticAngle(cp_class, &pa);
ret = tt.parallacticAngle(cp_class, &pa_class);
ret = tt.refractionCorrection(cp, &dZ);
ret = tt.refractionCorrection(cp, &dZ_class);
ret = tt.refractionCorrection(cp_class, &dZ);
ret = tt.refractionCorrection(cp_class, &dZ_class);
return ret;
}(t);
// refractionCorrection(mcc_celestial_point_c auto pt, mcc_angle_c auto* dZ)
{ t.refractionCorrection(std::declval<MccCelestialPoint>(), std::declval<double*>()) } -> mcc_error_c;
{ t.refractionCorrection(std::declval<MccCelestialPoint>(), std::declval<MccAngle*>()) } -> mcc_error_c;
{
t.refractionCorrection(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<double*>())
} -> mcc_error_c;
{
t.refractionCorrection(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccAngle*>())
} -> mcc_error_c;
};
@@ -436,83 +448,181 @@ concept mcc_PCM_c = requires(T t) {
return val;
}(); // to ensure 'mountType' can be used in compile-time context
[](T tt) -> mcc_error_c auto {
MccPCMResult res;
MccGenericPCMResult<MccAngle> res_class;
/* there is no way to declare a concept for a class templated method, so mandatory implementation is only required
for default MCC library types */
MccCelestialPoint cp;
MccGenericCelestialPoint<MccAngle> cp_class;
MccEqtHrzCoords eqhr;
MccGenericEqtHrzCoords<MccAngle> eqhr_class;
// the templated method signature:
// computePCM(mcc_celestial_point_c auto pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto* app_pt)
// pt - hardware (encoder) coordinates for which PCM needs to be calculated (pt.pair_kind is ignored!),
// app_pt: app_pt->X = pt.X + result->pcmX
// app_pt->Y = pt.Y + result->pcmY
{
t.computePCM(std::declval<MccCelestialPoint>(), std::declval<MccPCMResult*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccCelestialPoint>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccPCMResult*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
// computePCM(mcc_celestial_point_c auto pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto* app_pt)
{
t.computePCM(std::declval<MccCelestialPoint>(), std::declval<MccPCMResult*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccCelestialPoint>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccPCMResult*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
auto ret = tt.computePCM(cp, &res, &cp);
ret = tt.computePCM(cp, &res_class, &cp);
ret = tt.computePCM(cp_class, &res, &cp);
ret = tt.computePCM(cp_class, &res_class, &cp);
ret = tt.computePCM(cp, &res, &cp_class);
ret = tt.computePCM(cp, &res_class, &cp_class);
ret = tt.computePCM(cp_class, &res, &cp_class);
ret = tt.computePCM(cp_class, &res_class, &cp_class);
// the templated method signature:
// computePCM(mcc_celestial_point_c auto pt, mcc_PCM_result_c auto* result, mcc_eqt_hrz_coord_c auto* app_pt)
//
// for equatorial mounts the method must compute (if app_pt is not nullptr):
// app_pt->HA = pt.X + result->pcmX
// app_pt->DEC_APP = pt.Y + result->pcmY
// for alt-azimuthal:
// app_pt->AZ = pt.X + result->pcmX
// app_pt->ZD = pt.Y + result->pcmY
{
t.computePCM(std::declval<MccCelestialPoint>(), std::declval<MccPCMResult*>(), std::declval<MccEqtHrzCoords*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccCelestialPoint>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccEqtHrzCoords*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccPCMResult*>(),
std::declval<MccEqtHrzCoords*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccEqtHrzCoords*>())
} -> mcc_error_c;
// computePCM(mcc_celestial_point_c auto pt, mcc_PCM_result_c auto* result, mcc_eqt_hrz_coord_c auto* app_pt)
//
// for equatorial mounts the method must compute:
// app_pt->HA = pt.X + result->pcmX
// app_pt->DEC_APP = pt.Y + result->pcmY
// for alt-azimuthal:
// app_pt->AZ = pt.X + result->pcmX
// app_pt->ZD = pt.Y + result->pcmY ret = tt.computePCM(cp, &res, &eqhr);
ret = tt.computePCM(cp, &res_class, &eqhr);
ret = tt.computePCM(cp_class, &res, &eqhr);
ret = tt.computePCM(cp_class, &res_class, &eqhr);
{
t.computePCM(std::declval<MccCelestialPoint>(), std::declval<MccPCMResult*>(),
std::declval<MccGenericEqtHrzCoords<MccAngle>*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccCelestialPoint>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccGenericEqtHrzCoords<MccAngle>*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccPCMResult*>(),
std::declval<MccGenericEqtHrzCoords<MccAngle>*>())
} -> mcc_error_c;
{
t.computePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccGenericEqtHrzCoords<MccAngle>*>())
} -> mcc_error_c;
ret = tt.computePCM(cp, &res, &eqhr_class);
ret = tt.computePCM(cp, &res_class, &eqhr_class);
ret = tt.computePCM(cp_class, &res, &eqhr_class);
ret = tt.computePCM(cp_class, &res_class, &eqhr_class);
// computeInversePCM(mcc_celestial_point_c auto pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto*
// app_pt)
// the templated method signature:
// computeInversePCM(mcc_celestial_point_c auto app_pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto*
// hw_pt)
//
// app_pt - apparent celestial coordinates for which inverted PCM needs to be calculated.
// app_pt.X and app_pt.Y are assumed to be HA and DEC for equatorial mounts; AZ and ZD for alt-azimuthal
// ones (app_pt.pair_kind is ignored!)
// hw_pt - computed hardware (encoder) coordinates
{
t.computeInversePCM(std::declval<MccCelestialPoint>(), std::declval<MccPCMResult*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccCelestialPoint>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccPCMResult*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(),
std::declval<MccGenericPCMResult<MccAngle>*>(), std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
ret = tt.computeInversePCM(cp, &res, &cp);
ret = tt.computeInversePCM(cp, &res_class, &cp);
ret = tt.computeInversePCM(cp_class, &res, &cp);
ret = tt.computeInversePCM(cp_class, &res_class, &cp);
{
t.computeInversePCM(std::declval<MccCelestialPoint>(), std::declval<MccPCMResult*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccCelestialPoint>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(), std::declval<MccPCMResult*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccGenericCelestialPoint<MccAngle>>(),
std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
ret = tt.computeInversePCM(cp, &res, &cp_class);
ret = tt.computeInversePCM(cp, &res_class, &cp_class);
ret = tt.computeInversePCM(cp_class, &res, &cp_class);
ret = tt.computeInversePCM(cp_class, &res_class, &cp_class);
// computeInversePCM(mcc_eqt_hrz_coord_c auto pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto*
// app_pt)
//
// NOTE: for computation of the corrections the method must use of app_pt.X and app_pt.Y
//
// for equatorial mounts the method must compute:
// hw_pt->X = app_pt.HA + inv_result.pcmX
// hw_pt->Y = app_pt.DEC_APP + inv_result.pcmY
// and inputs for the corrections computing are app_pt.HA and app_pt.DEC_APP
// for alt-azimuthal:
// hw_pt->X = app_pt.AZ + inv_result.pcmX
// hw_pt->Y = app_pt.ZD + inv_result.pcmY
// and inputs for the corrections computing are app_pt.ZA and app_pt.ZD ret =
// tt.computeInversePCM(eqhr, &res, &cp);
ret = tt.computeInversePCM(eqhr, &res_class, &cp);
ret = tt.computeInversePCM(eqhr_class, &res, &cp);
ret = tt.computeInversePCM(eqhr_class, &res_class, &cp);
// the templated method signature:
// computeInversePCM(mcc_eqt_hrz_coord_c auto app_pt, mcc_PCM_result_c auto* result, mcc_celestial_point_c auto*
// hw_pt)
//
// for equatorial mounts the method must compute:
// hw_pt->X = app_pt.HA + inv_result.pcmX
// hw_pt->Y = app_pt.DEC_APP + inv_result.pcmY
// for alt-azimuthal:
// hw_pt->X = app_pt.AZ + inv_result.pcmX
// hw_pt->Y = app_pt.ZD + inv_result.pcmY
{
t.computeInversePCM(std::declval<MccEqtHrzCoords>(), std::declval<MccPCMResult*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccEqtHrzCoords>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccGenericEqtHrzCoords<MccAngle>>(), std::declval<MccPCMResult*>(),
std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccGenericEqtHrzCoords<MccAngle>>(),
std::declval<MccGenericPCMResult<MccAngle>*>(), std::declval<MccCelestialPoint*>())
} -> mcc_error_c;
ret = tt.computeInversePCM(eqhr, &res, &cp_class);
ret = tt.computeInversePCM(eqhr, &res_class, &cp_class);
ret = tt.computeInversePCM(eqhr_class, &res, &cp_class);
ret = tt.computeInversePCM(eqhr_class, &res_class, &cp_class);
return ret;
}(t);
{
t.computeInversePCM(std::declval<MccEqtHrzCoords>(), std::declval<MccPCMResult*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccEqtHrzCoords>(), std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccGenericEqtHrzCoords<MccAngle>>(), std::declval<MccPCMResult*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
{
t.computeInversePCM(std::declval<MccGenericEqtHrzCoords<MccAngle>>(),
std::declval<MccGenericPCMResult<MccAngle>*>(),
std::declval<MccGenericCelestialPoint<MccAngle>*>())
} -> mcc_error_c;
};
/* MOUNT HARDWARE ABSTRACTION CLASS CONCEPT */
@@ -602,8 +712,9 @@ concept mcc_hardware_c = requires(T t, const T t_const) {
/* MOUNT TELEMETRY DATA CLASS CONCEPT */
/* MOUNT TELEMETRY DATA CLASS CONCEPT */
// target coordinates class concept
template <typename T>
concept mcc_pointing_target_coord_c = mcc_eqt_hrz_coord_c<T> && requires(T t) {
requires mcc_angle_c<decltype(t.RA_ICRS)>; // ICRS right ascention
@@ -611,6 +722,16 @@ concept mcc_pointing_target_coord_c = mcc_eqt_hrz_coord_c<T> && requires(T t) {
};
template <mcc_angle_c CoordT>
struct MccGenericPointingTarget : MccGenericEqtHrzCoords<CoordT> {
using typename MccGenericEqtHrzCoords<CoordT>::coord_t;
coord_t RA_ICRS{}, DEC_ICRS{};
};
typedef MccGenericPointingTarget<MccCelestialPoint::coord_t> MccPointingTarget;
static constexpr void mcc_copy_pointing_target_coord(mcc_pointing_target_coord_c auto const& from_pt,
mcc_pointing_target_coord_c auto* to_pt)
{
@@ -665,6 +786,28 @@ concept mcc_telemetry_data_c = mcc_eqt_hrz_coord_c<T> && std::default_initializa
};
/* DEFAULT TELEMETRY DATA CLASS */
template <mcc_angle_c CoordT>
struct MccGenericTelemetryData : MccGenericEqtHrzCoords<CoordT> {
using typename MccGenericEqtHrzCoords<CoordT>::coord_t;
MccJulianDay JD;
coord_t LST; // local apparent sideral time
MccGenericPointingTarget<coord_t> target{};
coord_t speedX, speedY;
coord_t pcmX, pcmY;
coord_t refCorr;
};
typedef MccGenericTelemetryData<MccCelestialPoint::coord_t> MccTelemetryData;
static constexpr void mcc_copy_telemetry_data(mcc_telemetry_data_c auto const& from_pt,
mcc_telemetry_data_c auto* to_pt)
{
@@ -707,7 +850,7 @@ static constexpr void mcc_copy_telemetry_data(mcc_telemetry_data_c auto const& f
/* MOUNT TELEMETRY MANAGER CLASS CONCEPT */
/* MOUNT TELEMETRY MANAGER CLASS CONCEPT */
template <mcc_error_c RetT>
struct mcc_telemetry_interface_t {
@@ -774,8 +917,34 @@ protected:
mcc_telemetry_interface_t() = default;
};
// template <typename T>
// concept mcc_telemetry_c = std::derived_from<T, mcc_telemetry_interface_t<typename T::error_t>>;
template <typename T>
concept mcc_telemetry_c = std::derived_from<T, mcc_telemetry_interface_t<typename T::error_t>>;
concept mcc_telemetry_c = requires(T t) {
// update telemetry data right now
{ t.updateTelemetryData() } -> mcc_error_c;
// get current data
// an implementation is expected to return current, possibly already expired, telemetry data
// one should call consistently both 'updateTelemetryData' + 'telemetryData' methods
// to ensure that the data is up-to-date
//
// templated method signature
// telemetryData(mcc_telemetry_data_c auto* data)
{ t.telemetryData(std::declval<MccTelemetryData*>()) } -> mcc_error_c;
{ t.telemetryData(std::declval<MccGenericTelemetryData<MccAngle>*>()) } -> mcc_error_c;
// waiting for updated data
// an implementation is expected to block the current thread waiting for
// telemetry data to be updated (internal synchronization)
//
// templated method signature
// waitForTelemetryData(mcc_telemetry_data_c auto* data)
{ t.waitForTelemetryData(std::declval<MccTelemetryData*>()) } -> mcc_error_c;
{ t.waitForTelemetryData(std::declval<MccGenericTelemetryData<MccAngle>*>()) } -> mcc_error_c;
};