#pragma once12#include <AP_Common/AP_Common.h>3#include <AP_Param/AP_Param.h>4#include <AP_Math/AP_Math.h>5#include <AC_AttitudeControl/AC_PosControl.h> // Position control library67class AC_Circle8{9public:1011/// Constructor12AC_Circle(const AP_AHRS_View& ahrs, AC_PosControl& pos_control);1314// Initializes circle flight using a center position in centimeters relative to the EKF origin.15// See init_NED_m() for full details.16void init_NEU_cm(const Vector3p& center_neu_cm, bool is_terrain_alt, float rate_degs);1718// Initializes circle flight mode using a specified center position in meters.19// Parameters:20// - center_ned_m: Center of the circle in NED frame (meters, relative to EKF origin)21// - is_terrain_alt: If true, center_ned_m.z is interpreted as relative to terrain; otherwise, above EKF origin22// - rate_degs: Desired turn rate in degrees per second (positive = clockwise, negative = counter-clockwise)23// Caller must preconfigure the position controller's speed and acceleration settings before calling.24void init_NED_m(const Vector3p& center_ned_m, bool is_terrain_alt, float rate_degs);2526// Initializes the circle flight mode using the current stopping point as a reference.27// If the INIT_AT_CENTER option is not set, the circle center is projected one radius ahead along the vehicle's heading.28// Caller must configure position controller speeds and accelerations beforehand.29void init();3031// Sets the circle center using a Location object.32// Automatically determines whether the location uses terrain-relative or origin-relative altitude.33// If conversion fails, defaults to current position and logs a navigation error.34void set_center(const Location& center);3536// Sets the circle center using a NED position vector in meters from the EKF origin.37// - is_terrain_alt: If true, center_ned_m.z is interpreted as relative to terrain; otherwise, above EKF origin38void set_center_NED_m(const Vector3p& center_ned_m, bool is_terrain_alt) { _center_ned_m = center_ned_m; _is_terrain_alt = is_terrain_alt; }3940// Returns the circle center in centimeters from the EKF origin.41// See get_center_NED_m() for full details.42const Vector3p get_center_NEU_cm() const { return Vector3p(_center_ned_m.x, _center_ned_m.y, -_center_ned_m.z) * 100.0; }4344// Returns the circle center in meters from the EKF origin.45// Altitude frame is determined by `center_is_terrain_alt()`.46const Vector3p& get_center_NED_m() const { return _center_ned_m; }4748// Returns true if the circle center altitude is relative to terrain height.49bool center_is_terrain_alt() const { return _is_terrain_alt; }5051// Returns the circle radius in centimeters.52// See get_radius_m() for full details.53float get_radius_cm() const { return get_radius_m() * 100.0; }5455// Returns the circle radius in meters.56// If `_radius_m` is non-positive, falls back to the RADIUS parameter.57float get_radius_m() const { return is_positive(_radius_m) ? _radius_m : _radius_parm_m; }5859// Sets the circle radius in centimeters.60// See set_radius_m() for full details.61void set_radius_cm(float radius_cm);6263// Sets the circle radius in meters.64// Radius is constrained to AC_CIRCLE_RADIUS_MAX_M.65void set_radius_m(float radius_m);6667// Returns the configured circle turn rate in degrees per second from the RATE parameter.68float get_rate_degs() const { return _rate_parm_degs; }6970// Returns the current angular velocity in degrees per second.71// May be lower than the configured maximum due to ramp constraints.72float get_rate_current() const { return degrees(_angular_vel_rads); }7374// Sets the target circle rate in degrees per second.75// Positive values result in clockwise rotation; negative for counter-clockwise.76void set_rate_degs(float rate_degs);7778// Returns the total angle, in radians, that the vehicle has traveled along the circular path.79float get_angle_total_rad() const { return _angle_total_rad; }8081// Updates the circle controller using a climb rate in cm/s.82// See update_ms() for full implementation details.83bool update_cms(float climb_rate_cms = 0.0f) WARN_IF_UNUSED;8485// Updates the circle controller using a climb rate in m/s.86// Computes new angular position, yaw, and vertical trajectory, then updates the position controller.87// Returns false if terrain data is required but unavailable.88bool update_ms(float climb_rate_ms = 0.0f) WARN_IF_UNUSED;8990// Returns the desired roll angle in centidegrees from the position controller.91// Should be passed to the attitude controller as a stabilization input.92float get_roll_cd() const { return rad_to_cd(_pos_control.get_roll_rad()); }9394// Returns the desired pitch angle in centidegrees from the position controller.95// Should be passed to the attitude controller as a stabilization input.96float get_pitch_cd() const { return rad_to_cd(_pos_control.get_pitch_rad()); }9798// Returns the desired yaw angle in centidegrees computed by the circle controller.99// May be oriented toward the circle center or along the path depending on configuration.100float get_yaw_cd() const { return rad_to_cd(_yaw_rad); }101102// Returns the desired yaw angle in radians.103// Used for directional control based on circle configuration.104float get_yaw_rad() const { return _yaw_rad; }105106// Returns the desired thrust vector (unit vector in body frame) from the position controller.107// Can be used by the attitude controller to align thrust direction.108Vector3f get_thrust_vector() const { return _pos_control.get_thrust_vector(); }109110// Returns true if the circle controller's update() function has run recently.111// Used by vehicle code to determine if yaw and position outputs are valid.112bool is_active() const;113114// Returns the closest point on the circle to the vehicle's current position in centimeters.115// See get_closest_point_on_circle_NED_m() for full details.116void get_closest_point_on_circle_NEU_cm(Vector3f& result_neu_cm, float& dist_cm) const;117118// Returns the closest point on the circle to the vehicle's stopping point in meters.119// The result vector is updated with the NED position of the closest point on the circle.120// The altitude (z) is set to match the circle center's altitude.121// dist_m is updated with the 3D distance to the circle edge from the stopping point.122// If the vehicle is at the center, the point directly behind the vehicle (based on yaw) is returned.123void get_closest_point_on_circle_NED_m(Vector3p& result_ned_m, float& dist_m) const;124125// Returns the horizontal distance to the circle target in meters.126// Calculated using the position controller’s NE position error norm.127float get_distance_to_target_m() const { return _pos_control.get_pos_error_NE_m(); }128129// Returns the bearing from the vehicle to the circle target in radians.130// Bearing is measured clockwise from North (0 = North).131float get_bearing_to_target_rad() const { return _pos_control.get_bearing_to_target_rad(); }132133// Returns true if pilot stick control of circle radius and rate is enabled.134// See pilot_control_enabled() for flag logic.135bool pilot_control_enabled() const { return (_options.get() & CircleOptions::MANUAL_CONTROL) != 0; }136137// Returns true if the mount ROI is fixed at the circle center.138// See roi_at_center() for flag logic.139bool roi_at_center() const { return (_options.get() & CircleOptions::ROI_AT_CENTER) != 0; }140141// Sets rangefinder terrain offset (in centimeters) above EKF origin.142// See set_rangefinder_terrain_U_m() for full details.143void set_rangefinder_terrain_U_cm(bool use, bool healthy, float terrain_u_cm) { _rangefinder_available = use; _rangefinder_healthy = healthy; _rangefinder_terrain_u_m = terrain_u_cm * 0.01;}144145// Sets rangefinder terrain offset (in meters) above EKF origin.146// Used for terrain-relative altitude tracking during circular flight.147void set_rangefinder_terrain_U_m(bool use, bool healthy, float terrain_u_m) { _rangefinder_available = use; _rangefinder_healthy = healthy; _rangefinder_terrain_u_m = terrain_u_m;}148149// Checks if the circle radius parameter has changed.150// If so, updates internal `_radius_m` and stores the new parameter value.151void check_param_change();152153// perform any required parameter conversions154void convert_parameters();155156static const struct AP_Param::GroupInfo var_info[];157158private:159160// Calculates angular velocity and acceleration limits based on the configured radius and rate.161// Should be called whenever radius or rate changes.162// If `init_velocity` is true, resets angular velocity to zero (used on controller startup).163void calc_velocities(bool init_velocity);164165// Sets the initial angle around the circle and resets the accumulated angle.166// If `use_heading` is true, uses vehicle heading to initialize angle for minimal yaw motion.167// If false, uses position relative to circle center to set angle.168void init_start_angle(bool use_heading);169170// Returns the expected source of terrain data for the circle controller.171// Used to determine whether terrain offset comes from rangefinder, terrain database, or is unavailable.172enum class TerrainSource {173TERRAIN_UNAVAILABLE,174TERRAIN_FROM_RANGEFINDER,175TERRAIN_FROM_TERRAINDATABASE,176};177AC_Circle::TerrainSource get_terrain_source() const;178179// Returns terrain offset in meters above the EKF origin at the current position.180// Positive values indicate terrain is above the EKF origin altitude.181// Terrain source may be rangefinder or terrain database.182bool get_terrain_U_m(float& terrain_u_m);183184// references to inertial nav and ahrs libraries185const AP_AHRS_View& _ahrs;186AC_PosControl& _pos_control;187188enum CircleOptions {189MANUAL_CONTROL = 1U << 0, // Enables pilot stick input to adjust circle radius and turn rate.190FACE_DIRECTION_OF_TRAVEL = 1U << 1, // Yaw aligns with direction of travel (tangent to circle path).191INIT_AT_CENTER = 1U << 2, // Initializes circle with center at current position (instead of radius ahead).192ROI_AT_CENTER = 1U << 3, // Sets camera mount ROI to circle center during circle mode.193};194195// parameters196AP_Float _radius_parm_m; // Circle radius in meters, loaded from parameters.197AP_Float _rate_parm_degs; // Circle rotation rate in degrees per second, loaded from parameters.198AP_Int16 _options; // Bitmask of CircleOptions (e.g. manual control, ROI at center, etc.).199200// internal variables201Vector3p _center_ned_m; // Center of the circle in meters from EKF origin (NED frame).202float _radius_m; // Current circle radius in meters.203float _rotation_rate_max_rads; // Requested circle turn rate in rad/s (+ve = CW, -ve = CCW).204float _yaw_rad; // Desired yaw heading in radians (typically toward circle center or tangent).205float _angle_rad; // Current angular position around the circle in radians (0 = due north of center).206float _angle_total_rad; // Accumulated angle travelled in radians (used for full rotations).207float _angular_vel_rads; // Current angular velocity in rad/s.208float _angular_vel_max_rads; // Maximum allowed angular velocity in rad/s.209float _angular_accel_radss; // Angular acceleration limit in rad/s².210uint32_t _last_update_ms; // Timestamp (in milliseconds) of the last update() call.211float _last_radius_param_m; // Cached copy of radius parameter (m) to detect parameter changes.212213// terrain following variables214bool _is_terrain_alt; // True if _center_ned_m.z is relative to terrain height; false if relative to EKF origin.215bool _rangefinder_available; // True if rangefinder is available and enabled.216bool _rangefinder_healthy; // True if rangefinder reading is within valid operating range.217float _rangefinder_terrain_u_m; // Terrain height above EKF origin (meters), from rangefinder or terrain database.218};219220221