#pragma once namespace fgc { // Affine map between heading degrees and absolute encoder counts for one axis: // // counts = zero_count + deg * counts_per_deg // deg = (counts - zero_count) / counts_per_deg // // The firmware speaks only in encoder counts; this is how the host converts to // the heading/elevation degrees used by the MQTT contract and CamEvent. The // parameters are operator-calibrated against real `xenc` readings after homing // (counts_per_deg may be negative to flip the direction sense). struct AxisMap { double counts_per_deg = 1.0; // calibrate on the rig long zero_count = 0; // xenc value that corresponds to 0 deg double min_deg = -100000.0; // soft clamp applied by toCounts (degrees) double max_deg = 100000.0; // Degrees -> counts. Clamps `deg` to [min_deg, max_deg] and rounds. long toCounts(double deg) const; // Counts -> degrees. Returns 0 if counts_per_deg is ~0 (misconfigured). double toDeg(long counts) const; }; // Per-axis maps for the two-axis gimbal. struct Geometry { AxisMap yaw; AxisMap pitch; }; } // namespace fgc