fwt_software/include/fgc/AxisSpeed.h

73 lines
3.3 KiB
C++

#pragma once
#include <string>
namespace fgc {
// Axis speed as a physical quantity, and its conversion to the TMC5160's VMAX.
//
// VMAX is a register value in internal units, and what it MEANS depends on the
// drivetrain: 30000 is 21.8 deg/s on the Delta fleet and 6.0 deg/s on the IOTA
// tower, because their gear ratios differ by 3.6x. Copying the number between
// towers therefore produces a silently different machine, which is exactly what
// happened - both carry the same literal.
//
// Declaring degrees per second instead makes the intent portable and checkable:
// it is the quantity that determines image smear and panorama period, and it can
// be reasoned about without knowing a gear ratio.
//
// The conversion needs only counts_per_deg, which the host already derives per
// tower from the homed travel. That works because the firmware keeps XACTUAL
// (step space) and XENC (encoder space) on the SAME scale - see
// firmware/docs/hardware-notes.md, "Encoders track 1:1 but must be aligned to
// XACTUAL". If that ever stops being true, this conversion breaks and the step
// scale becomes a separate calibration.
// The TMC5160 ramp generator: v[counts/s] = VMAX * f_CLK / 2^24.
//
// 12 MHz is the internal oscillator's nominal frequency. Confirmed on the rig
// rather than taken on faith: the predicted 0.7153 counts/s per VMAX unit is
// within 2% of the 0.73 measured across three homing runs on tower 121.
constexpr double kTmcClockHz = 12'000'000.0;
constexpr double kTmcVmaxDiv = 16'777'216.0; // 2^24
// VMAX is a 23-bit field.
constexpr long kTmcVmaxMax = 0x7FFFFF;
// The fleet default, in the unit that means the same thing on every tower.
constexpr double kDefaultScanSpeedDegPerSec = 8.0;
// Below this, counts_per_deg is not a drivetrain - it is an uncalibrated axis
// still carrying AxisMap's default of 1.0.
//
// The floor is physical, not arbitrary: the AS5047P in ABI mode emits 4000 steps
// per revolution (COMPONENTS.md section 7), so even an axis driven with NO gear
// reduction reads 11.1 counts/deg. Anything under that cannot have come from a
// real encoder on this rig. Converting a speed through such a scale yields a
// VMAX near zero - an axis that looks configured and never finishes a sweep -
// so it is refused rather than commanded.
constexpr double kMinPlausibleCountsPerDeg = 10.0;
struct AxisSpeedPlan {
bool ok = false;
long vmax = 0;
double requested_deg_s = 0.0;
// What `vmax` actually delivers. VMAX is an integer, so this differs from the
// request by the quantisation - negligible at production speeds, but it is
// what the startup log should report, because it is what the axis does.
double actual_deg_s = 0.0;
bool clamped = false;
std::string error; // why, when !ok
};
// `counts_per_deg` may be negative to flip the direction sense (see Geometry.h);
// only its magnitude matters to a speed, so the sign is ignored rather than
// producing a negative VMAX.
AxisSpeedPlan planAxisSpeed(double deg_per_s, double counts_per_deg,
double clock_hz = kTmcClockHz);
// The inverse, for reporting what a firmware default or an existing literal
// actually amounts to on this drivetrain.
double vmaxToDegPerSec(long vmax, double counts_per_deg, double clock_hz = kTmcClockHz);
} // namespace fgc