213 lines
8.6 KiB
C++
213 lines
8.6 KiB
C++
/// @file axisunit.h
|
|
#pragma once
|
|
#include "../config/axis.h"
|
|
#include "pulse_gen.h"
|
|
|
|
namespace modules {
|
|
namespace motion {
|
|
|
|
// Import required types
|
|
using config::Axis;
|
|
using config::Idler;
|
|
using config::Pulley;
|
|
using config::Selector;
|
|
|
|
using config::Accel;
|
|
using config::Lenght;
|
|
using config::Speed;
|
|
|
|
using pulse_gen::pos_t;
|
|
using pulse_gen::steps_t;
|
|
|
|
/// Specialized axis unit type for compile-time conformability testing. Like for
|
|
/// unit::Unit this is done ensure quantities are not mixed between types, while also
|
|
/// providing convenience methods to convert from physical units to AxisUnits directly at
|
|
/// compile time. AxisUnits are just as efficient as the non-checked pulse_gen::pos_t and
|
|
/// pulse_gen::steps_t.
|
|
///
|
|
/// Each axis provides separate types for each quantity, since the low-level count is also
|
|
/// not directly comparable across each (depending on the configuration settings).
|
|
/// Quantities are normally defined through the literal operators. Types and base axes are
|
|
/// prefixed with a single letter identifier for the axis: P=pulley, S=selector, I=idler.
|
|
///
|
|
/// P_pos_t pulley_position = 10.0_P_mm;
|
|
/// auto pulley_zero = 0.0_P_mm; // implicit type
|
|
/// P_speed_ pulley_feedrate = 30.0_P_mm_s;
|
|
/// I_pos_t idler_position = 15.0_I_deg;
|
|
/// pulley_position + idler_position; // compile time error
|
|
///
|
|
/// motion::Motion.PlanMove (and related functions) support both physical and AxisUnit
|
|
/// natively. This is done by specifying the axis through the first template parameter,
|
|
/// which ensures related units are also conforming:
|
|
///
|
|
/// motion.PlanMoveTo<Pulley>(10.0_mm, 100._mm_s); // using physical units
|
|
/// motion.PlanMoveTo<Pulley>(10.0_P_mm, 100._P_mm_s); // using AxisUnit
|
|
///
|
|
/// Physical units are always represented with the largest floating point type, so they
|
|
/// should only preferably be used at compile-time only.
|
|
///
|
|
/// If runtime manipulation is necessary, AxisUnit should be used instead. Conversion from
|
|
/// physical to AxisUnit can be done through motion::unitToAxisUnit:
|
|
///
|
|
/// unitToAxisUnit<final_type>(physical_type)
|
|
///
|
|
/// Examples:
|
|
///
|
|
/// P_pos_t pulley_pos = unitToAxisUnit<P_pos_t>(10.0_mm);
|
|
/// P_speed_t pulley_speed = unitToAxisUnit<P_speed_t>(100.0_mm_s);
|
|
///
|
|
/// Conversion to pos_t or steps_t works the same using motion::unitToSteps instead.
|
|
///
|
|
/// The low-level step count can be accessed when necessary through AxisUnit::v, which
|
|
/// should be avoided as it bypasses all type checks. AxisUnit can also be constructed
|
|
/// without checks by providing a counter as the first initializer.
|
|
///
|
|
/// The scaling factor is stored with the pair config::AxisConfig::uSteps and
|
|
/// config::AxisConfig::stepsPerUnit.
|
|
template <typename T, Axis A, config::UnitType U>
|
|
struct AxisUnit {
|
|
T v;
|
|
|
|
static constexpr Axis axis = A;
|
|
static constexpr config::UnitType unit = U;
|
|
|
|
typedef T type_t;
|
|
typedef AxisUnit<T, A, U> self_t;
|
|
|
|
// same-type operations
|
|
constexpr self_t operator+(const self_t r) const { return { v + r.v }; }
|
|
constexpr self_t operator-(const self_t r) const { return { v - r.v }; }
|
|
constexpr self_t operator-() const { return { -v }; }
|
|
constexpr self_t operator*(const self_t r) const { return { v * r.v }; }
|
|
constexpr self_t operator/(const self_t r) const { return { v / r.v }; }
|
|
|
|
// allow an unitless multiplier to scale the quantity: AU * f => AU
|
|
constexpr self_t operator*(const long double f) const { return { (T)(v * f) }; }
|
|
constexpr self_t operator/(const long double f) const { return { (T)(v / f) }; }
|
|
};
|
|
|
|
// complementary f * AU => AU * f
|
|
template <typename T, Axis A, config::UnitType U>
|
|
constexpr AxisUnit<T, A, U> operator*(const long double f, const AxisUnit<T, A, U> u) { return u * f; }
|
|
|
|
/// Axis type conversion table for template expansion
|
|
struct AxisScale {
|
|
unit::UnitBase base;
|
|
long double stepsPerUnit;
|
|
long double stepsPerUnitReciprocal;
|
|
};
|
|
|
|
static constexpr AxisScale axisScale[config::NUM_AXIS] = {
|
|
{ config::pulleyLimits.base, config::pulley.stepsPerUnit, config::pulley.stepsPerUnitReciprocal },
|
|
{ config::selectorLimits.base, config::selector.stepsPerUnit, config::selector.stepsPerUnitReciprocal },
|
|
{ config::idlerLimits.base, config::idler.stepsPerUnit, config::idler.stepsPerUnitReciprocal },
|
|
};
|
|
|
|
/// Convert a unit::Unit to AxisUnit.
|
|
/// The scaling factor is stored with the pair config::AxisConfig::uSteps and
|
|
/// config::AxisConfig::stepsPerUnit (one per-axis).
|
|
template <typename AU, typename U>
|
|
static constexpr AU unitToAxisUnit(U v) {
|
|
static_assert(AU::unit == U::unit, "incorrect unit type conversion");
|
|
static_assert(U::base == axisScale[AU::axis].base, "incorrect unit base conversion");
|
|
return { (typename AU::type_t)(v.v * axisScale[AU::axis].stepsPerUnit) };
|
|
}
|
|
|
|
/// Convert an AxisUnit to a physical unit with a truncated integer type (normally int32_t).
|
|
/// Inverse of unitToAxisUnit, with the additional constraint that type casts are
|
|
/// performed earlier so that no floating point computation is required at runtime.
|
|
/// @tparam U Base unit type (normally U_mm)
|
|
/// @tparam AU AxisUnit type (implicit)
|
|
/// @tparam T Resulting integer type
|
|
/// @param v Value to truncate
|
|
/// @param mul Optional pre-multiplier
|
|
/// @returns Truncated unit (v * mul)
|
|
/// @see unitToAxisUnit
|
|
template <typename U, typename AU, typename T = int32_t>
|
|
static constexpr T axisUnitToTruncatedUnit(AU v, long double mul = 1.) {
|
|
static_assert(AU::unit == U::unit, "incorrect unit type conversion");
|
|
static_assert(U::base == axisScale[AU::axis].base, "incorrect unit base conversion");
|
|
return { ((T)(v.v * (axisScale[AU::axis].stepsPerUnitReciprocal / mul))) };
|
|
}
|
|
|
|
/// Truncate an Unit type to an integer (normally int32_t)
|
|
/// @param v Value to truncate
|
|
/// @param mul Optional pre-multiplier
|
|
/// @returns Truncated unit (v * mul)
|
|
template <typename U, typename T = int32_t>
|
|
static constexpr T truncatedUnit(U v, long double mul = 1.) {
|
|
return (T)(v.v * mul);
|
|
}
|
|
|
|
/// Convert a unit::Unit to a steps type (pos_t or steps_t).
|
|
/// Extract the raw step count from an AxisUnit with type checking.
|
|
template <typename AU, typename U>
|
|
static constexpr typename AU::type_t unitToSteps(U v) {
|
|
return unitToAxisUnit<AU>(v).v;
|
|
}
|
|
|
|
// Pulley
|
|
typedef AxisUnit<pos_t, Pulley, Lenght> P_pos_t; ///< Pulley position type (steps)
|
|
typedef AxisUnit<steps_t, Pulley, Speed> P_speed_t; ///< Pulley speed type (steps/s)
|
|
typedef AxisUnit<steps_t, Pulley, Accel> P_accel_t; ///< Pulley acceleration type (steps/s2)
|
|
|
|
static constexpr P_pos_t operator"" _P_mm(long double mm) {
|
|
return { unitToAxisUnit<P_pos_t>(config::U_mm { mm }) };
|
|
}
|
|
|
|
static constexpr P_speed_t operator"" _P_mm_s(long double mm_s) {
|
|
return { unitToAxisUnit<P_speed_t>(config::U_mm_s { mm_s }) };
|
|
}
|
|
|
|
static constexpr P_accel_t operator"" _P_mm_s2(long double mm_s2) {
|
|
return { unitToAxisUnit<P_accel_t>(config::U_mm_s2 { mm_s2 }) };
|
|
}
|
|
|
|
// Selector
|
|
typedef AxisUnit<pos_t, Selector, Lenght> S_pos_t; ///< Selector position type (steps)
|
|
typedef AxisUnit<steps_t, Selector, Speed> S_speed_t; ///< Selector speed type (steps/s)
|
|
typedef AxisUnit<steps_t, Selector, Accel> S_accel_t; ///< Selector acceleration type (steps/s2)
|
|
|
|
static constexpr S_pos_t operator"" _S_mm(long double mm) {
|
|
return { unitToAxisUnit<S_pos_t>(config::U_mm { mm }) };
|
|
}
|
|
|
|
static constexpr S_speed_t operator"" _S_mm_s(long double mm_s) {
|
|
return { unitToAxisUnit<S_speed_t>(config::U_mm_s { mm_s }) };
|
|
}
|
|
|
|
static constexpr S_accel_t operator"" _S_mm_s2(long double mm_s2) {
|
|
return { unitToAxisUnit<S_accel_t>(config::U_mm_s2 { mm_s2 }) };
|
|
}
|
|
|
|
// Idler
|
|
typedef AxisUnit<pos_t, Idler, Lenght> I_pos_t; ///< Idler position type (steps)
|
|
typedef AxisUnit<steps_t, Idler, Speed> I_speed_t; ///< Idler speed type (steps/s)
|
|
typedef AxisUnit<steps_t, Idler, Accel> I_accel_t; ///< Idler acceleration type (steps/s2)
|
|
|
|
static constexpr I_pos_t operator"" _I_deg(long double deg) {
|
|
return { unitToAxisUnit<I_pos_t>(config::U_deg { deg }) };
|
|
}
|
|
|
|
static constexpr I_speed_t operator"" _I_deg_s(long double deg_s) {
|
|
return { unitToAxisUnit<I_speed_t>(config::U_deg_s { deg_s }) };
|
|
}
|
|
|
|
static constexpr I_accel_t operator"" _I_deg_s2(long double deg_s2) {
|
|
return { unitToAxisUnit<I_accel_t>(config::U_deg_s2 { deg_s2 }) };
|
|
}
|
|
|
|
} // namespace motion
|
|
} // namespace modules
|
|
|
|
// Inject literal operators into the global namespace for convenience
|
|
using modules::motion::operator"" _P_mm;
|
|
using modules::motion::operator"" _P_mm_s;
|
|
using modules::motion::operator"" _P_mm_s2;
|
|
using modules::motion::operator"" _S_mm;
|
|
using modules::motion::operator"" _S_mm_s;
|
|
using modules::motion::operator"" _S_mm_s2;
|
|
using modules::motion::operator"" _I_deg;
|
|
using modules::motion::operator"" _I_deg_s;
|
|
using modules::motion::operator"" _I_deg_s2;
|