Line data Source code
1 : #include <AP_HAL/AP_HAL.h>
2 : #include <AP_Math/AP_Math.h>
3 : #include <AP_Terrain/AP_Terrain.h>
4 : #include "AC_Circle.h"
5 : #include <AP_Logger/AP_Logger.h>
6 : #include <AP_Vehicle/AP_Vehicle_Type.h>
7 :
8 : extern const AP_HAL::HAL& hal;
9 :
10 : // Circle mode default and limit constants
11 : #define AC_CIRCLE_RADIUS_M_DEFAULT 10.0f // Default circle radius in meters
12 : #define AC_CIRCLE_RATE_DEFAULT 20.0f // Default circle turn rate in degrees per second. Positive = clockwise, negative = counter-clockwise.
13 : #define AC_CIRCLE_ANGULAR_ACCEL_MIN 2.0f // Minimum angular acceleration in deg/s² (used to avoid sluggish yaw transitions).
14 : #define AC_CIRCLE_RADIUS_MAX_M 2000.0 // Maximum allowed circle radius in meters (2000 m = 2 km).
15 :
16 : const AP_Param::GroupInfo AC_Circle::var_info[] = {
17 : // 0 was RADIUS (cm)
18 :
19 : // @Param: RATE
20 : // @DisplayName: Circle rate
21 : // @Description: Circle mode's maximum turn rate in deg/sec. Positive to turn clockwise, negative for counter clockwise. Final circle rate is also limited by speed and acceleration settings.
22 : // @Units: deg/s
23 : // @Range: -90 90
24 : // @Increment: 1
25 : // @User: Standard
26 : AP_GROUPINFO("RATE", 1, AC_Circle, _rate_parm_degs, AC_CIRCLE_RATE_DEFAULT),
27 :
28 : // @Param: OPTIONS
29 : // @DisplayName: Circle options
30 : // @Description: Circle behaviour options
31 : // @Bitmask: 0:RC pitch and roll control radius and rate, 1:Face direction of travel, 2:Start at center rather than on perimeter, 3:Make Mount ROI the center of the circle
32 : // @User: Standard
33 : AP_GROUPINFO("OPTIONS", 2, AC_Circle, _options, 1),
34 :
35 : // @Param: RADIUS_M
36 : // @DisplayName: Circle Radius
37 : // @Description: Radius of the circle the vehicle will fly when in Circle flight mode
38 : // @Units: m
39 : // @Range: 0 2000
40 : // @Increment: 1
41 : // @User: Standard
42 : AP_GROUPINFO("RADIUS_M", 3, AC_Circle, _radius_parm_m, AC_CIRCLE_RADIUS_M_DEFAULT),
43 :
44 : AP_GROUPEND
45 : };
46 :
47 : // Default constructor.
48 : // Note that the Vector/Matrix constructors already implicitly zero
49 : // their values.
50 1084 : AC_Circle::AC_Circle(const AP_AHRS_View& ahrs, AC_PosControl& pos_control) :
51 1084 : _ahrs(ahrs),
52 1084 : _pos_control(pos_control)
53 : {
54 1084 : AP_Param::setup_object_defaults(this, var_info);
55 :
56 1084 : _rotation_rate_max_rads = radians(_rate_parm_degs);
57 1084 : }
58 :
59 : // Initializes circle flight using a center position in centimeters relative to the EKF origin.
60 : // See init_NED_m() for full details.
61 2 : void AC_Circle::init_NEU_cm(const Vector3p& center_neu_cm, bool is_terrain_alt, float rate_degs)
62 : {
63 : // Convert input from NEU cm to NED meters and delegate to meter-based initializer
64 2 : Vector3p center_ned_m = Vector3p{center_neu_cm.x, center_neu_cm.y, -center_neu_cm.z} * 0.01;
65 2 : init_NED_m(center_ned_m, is_terrain_alt, rate_degs);
66 2 : }
67 :
68 : // Initializes circle flight mode using a specified center position in meters.
69 : // Parameters:
70 : // - center_ned_m: Center of the circle in NED frame (meters, relative to EKF origin)
71 : // - is_terrain_alt: If true, center_ned_m.z is interpreted as relative to terrain; otherwise, above EKF origin
72 : // - rate_degs: Desired turn rate in degrees per second (positive = clockwise, negative = counter-clockwise)
73 : // Caller must preconfigure the position controller's speed and acceleration settings before calling.
74 2 : void AC_Circle::init_NED_m(const Vector3p& center_ned_m, bool is_terrain_alt, float rate_degs)
75 : {
76 : // Store circle center and frame reference
77 2 : _center_ned_m = center_ned_m;
78 2 : _is_terrain_alt = is_terrain_alt;
79 :
80 : // Convert desired turn rate from degrees to radians
81 2 : _rotation_rate_max_rads = radians(rate_degs);
82 :
83 : // Initialise position controller using current lean angles
84 2 : _pos_control.NE_init_controller_stopping_point();
85 2 : _pos_control.D_init_controller_stopping_point();
86 :
87 : // Calculate velocity and acceleration limits based on circle configuration
88 2 : calc_velocities(true);
89 :
90 : // Use position-based angle initialization (avoids sharp yaw discontinuity)
91 2 : init_start_angle(false);
92 2 : }
93 :
94 : // Initializes the circle flight mode using the current stopping point as a reference.
95 : // If the INIT_AT_CENTER option is not set, the circle center is projected one radius ahead along the vehicle's heading.
96 : // Caller must configure position controller speeds and accelerations beforehand.
97 2270 : void AC_Circle::init()
98 : {
99 : // Load radius and rate from parameters
100 2270 : _radius_m = _radius_parm_m.get();
101 2270 : _last_radius_param_m = _radius_m;
102 2270 : _rotation_rate_max_rads = radians(_rate_parm_degs);
103 :
104 : // Initialise position controller using current lean angles
105 2270 : _pos_control.NE_init_controller_stopping_point();
106 2270 : _pos_control.D_init_controller_stopping_point();
107 :
108 : // Get stopping point as initial reference for center
109 2270 : const Vector3p& stopping_point_ned_m = _pos_control.get_pos_desired_NED_m();
110 :
111 : // By default, set center to stopping point
112 2270 : _center_ned_m = stopping_point_ned_m;
113 :
114 : // If INIT_AT_CENTER is not set, project center forward by radius in heading direction
115 2270 : if ((_options.get() & CircleOptions::INIT_AT_CENTER) == 0) {
116 2270 : _center_ned_m.x += _radius_m * _ahrs.cos_yaw();
117 2270 : _center_ned_m.y += _radius_m * _ahrs.sin_yaw();
118 : }
119 :
120 : // Circle altitude is relative to EKF origin by default
121 2270 : _is_terrain_alt = false;
122 :
123 : // Calculate velocity and acceleration constraints
124 2270 : calc_velocities(true);
125 :
126 : // Initialize angle using vehicle heading
127 2270 : init_start_angle(true);
128 2270 : }
129 :
130 : // Sets the circle center using a Location object.
131 : // Automatically determines whether the location uses terrain-relative or origin-relative altitude.
132 : // If conversion fails, defaults to current position and logs a navigation error.
133 7 : void AC_Circle::set_center(const Location& center)
134 : {
135 : // Check if the location uses terrain-relative altitude
136 7 : if (center.get_alt_frame() == Location::AltFrame::ABOVE_TERRAIN) {
137 : // convert Location with terrain altitude
138 0 : Vector2p center_ne_m;
139 : float terr_alt_m;
140 :
141 : // Attempt to convert XY and Z to NEU frame with terrain altitude
142 0 : if (center.get_vector_xy_from_origin_NE_m(center_ne_m) && center.get_alt_m(Location::AltFrame::ABOVE_TERRAIN, terr_alt_m)) {
143 0 : set_center_NED_m(Vector3p{center_ne_m.x, center_ne_m.y, -terr_alt_m}, true);
144 : } else {
145 : // Conversion failed: fall back to current position and log error
146 0 : set_center_NED_m(_pos_control.get_pos_estimate_NED_m(), false);
147 0 : LOGGER_WRITE_ERROR(LogErrorSubsystem::NAVIGATION, LogErrorCode::FAILED_CIRCLE_INIT);
148 : }
149 : } else {
150 : // Handle alt-above-origin, alt-above-home, or absolute altitudes
151 7 : Vector3p circle_center_ned_m;
152 7 : if (!center.get_vector_from_origin_NED_m(circle_center_ned_m)) {
153 : // Conversion failed: fall back to current position and log error
154 0 : circle_center_ned_m = _pos_control.get_pos_estimate_NED_m();
155 0 : LOGGER_WRITE_ERROR(LogErrorSubsystem::NAVIGATION, LogErrorCode::FAILED_CIRCLE_INIT);
156 : }
157 :
158 : // Apply converted center and mark it as origin-relative
159 7 : set_center_NED_m(circle_center_ned_m, false);
160 : }
161 7 : }
162 :
163 : // Sets the target circle rate in degrees per second.
164 : // Positive values result in clockwise rotation; negative for counter-clockwise.
165 16175 : void AC_Circle::set_rate_degs(float rate_degs)
166 : {
167 : // Convert the rate from degrees/sec to radians/sec
168 16175 : _rotation_rate_max_rads = radians(rate_degs);
169 16175 : }
170 :
171 : // Sets the circle radius in centimeters.
172 : // See set_radius_m() for full details.
173 2 : void AC_Circle::set_radius_cm(float radius_cm)
174 : {
175 : // Convert input from cm to meters and call set_radius_m()
176 2 : set_radius_m(radius_cm * 0.01);
177 2 : }
178 :
179 : // Sets the circle radius in meters.
180 : // Radius is constrained to AC_CIRCLE_RADIUS_MAX_M.
181 6415 : void AC_Circle::set_radius_m(float radius_m)
182 : {
183 : // Constrain the radius to prevent unsafe or invalid circle sizes
184 6415 : _radius_m = constrain_float(radius_m, 0, AC_CIRCLE_RADIUS_MAX_M);
185 6415 : }
186 :
187 : // Returns true if the circle controller's update() function has run recently.
188 : // Used by vehicle code to determine if yaw and position outputs are valid.
189 156551 : bool AC_Circle::is_active() const
190 : {
191 : // Consider the controller active if the last update occurred within the past 200 milliseconds
192 156551 : return (AP_HAL::millis() - _last_update_ms < 200);
193 : }
194 :
195 : // Updates the circle controller using a climb rate in cm/s.
196 : // See update_ms() for full implementation details.
197 127956 : bool AC_Circle::update_cms(float climb_rate_cms)
198 : {
199 : // Convert climb rate from cm/s to m/s and call the meter-based update
200 127956 : return update_ms(climb_rate_cms * 0.01);
201 : }
202 :
203 : // Updates the circle controller using a climb rate in m/s.
204 : // Computes new angular position, yaw, and vertical trajectory, then updates the position controller.
205 : // Returns false if terrain data is required but unavailable.
206 237837 : bool AC_Circle::update_ms(float climb_rate_ms)
207 : {
208 : // Recalculate angular velocities based on the current radius and rate
209 237837 : calc_velocities(false);
210 :
211 : // calculate dt
212 237837 : const float dt = _pos_control.get_dt_s();
213 :
214 : // ramp angular velocity to maximum
215 237837 : if (_angular_vel_rads < _angular_vel_max_rads) {
216 8296 : _angular_vel_rads += fabsf(_angular_accel_radss) * dt;
217 8296 : _angular_vel_rads = MIN(_angular_vel_rads, _angular_vel_max_rads);
218 : }
219 237837 : if (_angular_vel_rads > _angular_vel_max_rads) {
220 12424 : _angular_vel_rads -= fabsf(_angular_accel_radss) * dt;
221 12424 : _angular_vel_rads = MAX(_angular_vel_rads, _angular_vel_max_rads);
222 : }
223 :
224 : // update the target angle and total angle travelled
225 237837 : float angle_change_rad = _angular_vel_rads * dt;
226 237837 : _angle_rad += angle_change_rad;
227 237837 : _angle_rad = wrap_PI(_angle_rad);
228 237837 : _angle_total_rad += angle_change_rad;
229 :
230 : // calculate terrain adjustments
231 237837 : float terrain_u_m = 0.0f;
232 237837 : if (_is_terrain_alt && !get_terrain_U_m(terrain_u_m)) {
233 0 : return false;
234 : }
235 :
236 : // calculate z-axis target
237 : float target_d_m;
238 237837 : if (_is_terrain_alt) {
239 0 : target_d_m = _center_ned_m.z - terrain_u_m;
240 : } else {
241 237837 : target_d_m = -_pos_control.get_pos_desired_U_m();
242 : }
243 :
244 : // Construct target position centered on the circle center
245 : Vector3p target_ned_m {
246 : _center_ned_m.x,
247 : _center_ned_m.y,
248 : target_d_m
249 237837 : };
250 237837 : if (!is_zero(_radius_m)) {
251 : // Calculate position on the circle edge based on current angle
252 237837 : target_ned_m.x += _radius_m * cosf(-_angle_rad);
253 237837 : target_ned_m.y += - _radius_m * sinf(-_angle_rad);
254 :
255 : // Compute yaw toward the circle center
256 237837 : _yaw_rad = get_bearing_rad(_pos_control.get_pos_desired_NED_m().xy().tofloat(), _center_ned_m.xy().tofloat());
257 :
258 : // Optionally adjust yaw to face direction of travel
259 237837 : if ((_options.get() & CircleOptions::FACE_DIRECTION_OF_TRAVEL) != 0) {
260 0 : _yaw_rad += is_positive(_rotation_rate_max_rads) ? -radians(90.0) : radians(90.0);
261 0 : _yaw_rad = wrap_2PI(_yaw_rad);
262 : }
263 : } else {
264 : // set heading be the same as the angle for zero radius
265 0 : _yaw_rad = _angle_rad;
266 : }
267 :
268 : // update position controller target
269 237837 : Vector2f zero_ne;
270 237837 : _pos_control.input_pos_vel_accel_NE_m(target_ned_m.xy(), zero_ne, zero_ne);
271 237837 : if (_is_terrain_alt) {
272 0 : float vel_zero = 0;
273 0 : float target_pos_d_m = target_ned_m.z;
274 0 : _pos_control.input_pos_vel_accel_D_m(target_pos_d_m, vel_zero, 0);
275 : } else {
276 237837 : _pos_control.D_set_pos_target_from_climb_rate_ms(climb_rate_ms);
277 : }
278 :
279 : // update position controller
280 237837 : _pos_control.NE_update_controller();
281 :
282 : // set update time
283 237837 : _last_update_ms = AP_HAL::millis();
284 :
285 237837 : return true;
286 : }
287 :
288 : // Returns the closest point on the circle to the vehicle's current position in centimeters.
289 : // See get_closest_point_on_circle_NED_m() for full details.
290 2 : void AC_Circle::get_closest_point_on_circle_NEU_cm(Vector3f& result_neu_cm, float& dist_cm) const
291 : {
292 2 : Vector3p result_ned_m;
293 : float dist_m;
294 :
295 : // Compute closest point in meters
296 2 : get_closest_point_on_circle_NED_m(result_ned_m, dist_m);
297 :
298 : // Convert results back to neu centimeters
299 2 : result_neu_cm = Vector3f(result_ned_m.x, result_ned_m.y, -result_ned_m.z) * 100.0;
300 2 : dist_cm = dist_m * 100.0;
301 2 : }
302 :
303 : // Returns the closest point on the circle to the vehicle's stopping point in meters.
304 : // The result vector is updated with the NEU position of the closest point on the circle.
305 : // The altitude (z) is set to match the circle center's altitude.
306 : // dist_m is updated with the 3D distance to the circle edge from the stopping point.
307 : // If the vehicle is at the center, the point directly behind the vehicle (based on yaw) is returned.
308 7 : void AC_Circle::get_closest_point_on_circle_NED_m(Vector3p& result_ned_m, float& dist_to_edge_m) const
309 : {
310 : // Get vehicle stopping point from the position controller (in NEU frame, meters)
311 7 : Vector3p stopping_point_ned_m;
312 7 : _pos_control.get_stopping_point_NE_m(stopping_point_ned_m.xy());
313 7 : _pos_control.get_stopping_point_D_m(stopping_point_ned_m.z);
314 :
315 : // Compute vector from stopping point to the circle center
316 7 : Vector3f vec_from_center_ned_m = (stopping_point_ned_m - _center_ned_m).tofloat();
317 : // Return circle center if radius is zero (vehicle orbits in place)
318 7 : if (!is_positive(_radius_m)) {
319 1 : result_ned_m = _center_ned_m;
320 1 : dist_to_edge_m = 0;
321 1 : return;
322 : }
323 :
324 6 : const float dist_to_center_m_sq = vec_from_center_ned_m.length_squared();
325 : // Handle edge case: vehicle is at the exact center of the circle
326 6 : if (dist_to_center_m_sq < sq(0.5)) {
327 0 : result_ned_m.x = _center_ned_m.x - _radius_m * _ahrs.cos_yaw();
328 0 : result_ned_m.y = _center_ned_m.y - _radius_m * _ahrs.sin_yaw();
329 0 : result_ned_m.z = _center_ned_m.z;
330 0 : dist_to_edge_m = (stopping_point_ned_m - result_ned_m).length();
331 0 : return;
332 : }
333 :
334 : // Calculate the closest point on the circle's edge by projecting out from center
335 6 : const float dist_to_center_m_xy = vec_from_center_ned_m.xy().length();
336 6 : result_ned_m.x = _center_ned_m.x + vec_from_center_ned_m.x / dist_to_center_m_xy * _radius_m;
337 6 : result_ned_m.y = _center_ned_m.y + vec_from_center_ned_m.y / dist_to_center_m_xy * _radius_m;
338 6 : result_ned_m.z = _center_ned_m.z;
339 6 : dist_to_edge_m = (stopping_point_ned_m - result_ned_m).length();
340 : }
341 :
342 : // Calculates angular velocity and acceleration limits based on the configured radius and rate.
343 : // Should be called whenever radius or rate changes.
344 : // If `init_velocity` is true, resets angular velocity to zero (used on controller startup).
345 240109 : void AC_Circle::calc_velocities(bool init_velocity)
346 : {
347 : // If performing a panorama (radius is zero), use current yaw rate and enforce minimum acceleration
348 240109 : if (_radius_m <= 0) {
349 0 : _angular_vel_max_rads = _rotation_rate_max_rads;
350 0 : _angular_accel_radss = MAX(fabsf(_angular_vel_max_rads), radians(AC_CIRCLE_ANGULAR_ACCEL_MIN)); // reach maximum yaw velocity in 1 second
351 : }else{
352 : // Limit max horizontal speed based on radius and available acceleration
353 240109 : float vel_max_ms = MIN(_pos_control.NE_get_max_speed_ms(), safe_sqrt(0.5f*_pos_control.NE_get_max_accel_mss()*_radius_m));
354 :
355 : // Convert linear speed to angular velocity (rad/s)
356 240109 : _angular_vel_max_rads = vel_max_ms/_radius_m;
357 :
358 : // Constrain to requested circle rate
359 240109 : _angular_vel_max_rads = constrain_float(_rotation_rate_max_rads, -_angular_vel_max_rads, _angular_vel_max_rads);
360 :
361 : // Derive maximum angular acceleration
362 240109 : _angular_accel_radss = MAX(_pos_control.NE_get_max_accel_mss() / _radius_m, radians(AC_CIRCLE_ANGULAR_ACCEL_MIN));
363 : }
364 :
365 : // Reset angular velocity to zero at initialization if requested
366 240109 : if (init_velocity) {
367 2272 : _angular_vel_rads = 0.0;
368 : }
369 240109 : }
370 :
371 : // Sets the initial angle around the circle and resets the accumulated angle.
372 : // If `use_heading` is true, uses vehicle heading to initialize angle for minimal yaw motion.
373 : // If false, uses position relative to circle center to set angle.
374 2272 : void AC_Circle::init_start_angle(bool use_heading)
375 : {
376 : // Reset the accumulated angle traveled around the circle
377 2272 : _angle_total_rad = 0.0;
378 :
379 : // If radius is zero, we're doing a panorama—set angle to current yaw heading
380 2272 : if (_radius_m <= 0) {
381 0 : _angle_rad = _ahrs.yaw;
382 0 : return;
383 : }
384 :
385 : // if use_heading is true
386 2272 : if (use_heading) {
387 : // Initialize angle directly behind current heading to minimize yaw motion
388 2270 : _angle_rad = wrap_PI(_ahrs.yaw - M_PI);
389 : } else {
390 : // If vehicle is exactly at the center, init angle behind vehicle (prevent undefined bearing)
391 2 : const Vector3p &curr_pos_desired_ned_m = _pos_control.get_pos_desired_NED_m();
392 2 : if (is_equal(curr_pos_desired_ned_m.x, _center_ned_m.x) && is_equal(curr_pos_desired_ned_m.y, _center_ned_m.y)) {
393 0 : _angle_rad = wrap_PI(_ahrs.yaw - M_PI);
394 : } else {
395 : // Calculate bearing from circle center to current position
396 2 : float bearing_rad = atan2f(curr_pos_desired_ned_m.y - _center_ned_m.y, curr_pos_desired_ned_m.x - _center_ned_m.x);
397 2 : _angle_rad = wrap_PI(bearing_rad);
398 : }
399 : }
400 : }
401 :
402 : // Returns the expected source of terrain data for the circle controller.
403 : // Used to determine whether terrain offset comes from rangefinder, terrain database, or is unavailable.
404 0 : AC_Circle::TerrainSource AC_Circle::get_terrain_source() const
405 : {
406 : // Use rangefinder if available and enabled
407 0 : if (_rangefinder_available) {
408 0 : return AC_Circle::TerrainSource::TERRAIN_FROM_RANGEFINDER;
409 : }
410 : #if AP_TERRAIN_AVAILABLE
411 : // Fallback to terrain database if available and enabled
412 0 : const AP_Terrain *terrain = AP_Terrain::get_singleton();
413 0 : if ((terrain != nullptr) && terrain->enabled()) {
414 0 : return AC_Circle::TerrainSource::TERRAIN_FROM_TERRAINDATABASE;
415 : } else {
416 0 : return AC_Circle::TerrainSource::TERRAIN_UNAVAILABLE;
417 : }
418 : #else
419 : // Terrain unavailable if no valid source
420 : return AC_Circle::TerrainSource::TERRAIN_UNAVAILABLE;
421 : #endif
422 : }
423 :
424 : // Returns terrain offset in meters above the EKF origin at the current position.
425 : // Positive values indicate terrain is above the EKF origin altitude.
426 : // Terrain source may be rangefinder or terrain database.
427 0 : bool AC_Circle::get_terrain_U_m(float& terrain_u_m)
428 : {
429 : // Determine terrain source and calculate offset accordingly
430 0 : switch (get_terrain_source()) {
431 0 : case AC_Circle::TerrainSource::TERRAIN_UNAVAILABLE:
432 0 : return false;
433 0 : case AC_Circle::TerrainSource::TERRAIN_FROM_RANGEFINDER:
434 0 : if (_rangefinder_healthy) {
435 : // Use last known healthy rangefinder terrain offset
436 0 : terrain_u_m = _rangefinder_terrain_u_m;
437 0 : return true;
438 : }
439 0 : return false;
440 0 : case AC_Circle::TerrainSource::TERRAIN_FROM_TERRAINDATABASE:
441 : #if AP_TERRAIN_AVAILABLE
442 0 : float terr_alt_m = 0.0f;
443 0 : AP_Terrain *terrain = AP_Terrain::get_singleton();
444 0 : if (terrain != nullptr && terrain->height_above_terrain(terr_alt_m, true)) {
445 : // Calculate offset from EKF origin altitude to terrain altitude
446 0 : terrain_u_m = _pos_control.get_pos_estimate_U_m() - terr_alt_m;
447 0 : return true;
448 : }
449 : #endif
450 0 : return false;
451 : }
452 :
453 : // This fallback should never be reached
454 0 : return false;
455 : }
456 :
457 : // Checks if the circle radius parameter has changed.
458 : // If so, updates internal `_radius_m` and stores the new parameter value.
459 118591 : void AC_Circle::check_param_change()
460 : {
461 : // Check if the stored radius param has changed
462 118591 : if (!is_equal(_last_radius_param_m, _radius_parm_m.get())) {
463 : // update internal radius and store new parameter value to detect future changes
464 1 : _radius_m = _radius_parm_m.get();
465 1 : _last_radius_param_m = _radius_m;
466 : }
467 118591 : }
468 :
469 : // perform any required parameter conversions
470 1084 : void AC_Circle::convert_parameters()
471 : {
472 : // PARAMETER_CONVERSION - Added: Jan-2026 for ArduPilot-4.7
473 :
474 : // exit immediately if radius_m parameter is already configured
475 1084 : if (_radius_parm_m.configured()) {
476 32 : return;
477 : }
478 :
479 : // convert parameters
480 : static const AP_Param::ConversionInfo conversion_info[] = {
481 : #if APM_BUILD_TYPE(APM_BUILD_ArduSub)
482 : { 60, 0, AP_PARAM_FLOAT, "CIRCLE_RADIUS_M" }, // CIRCLE_RADIUS moved to CIRCLE_RADIUS_M
483 : #else
484 : { 104, 0, AP_PARAM_FLOAT, "CIRCLE_RADIUS_M" }, // CIRCLE_RADIUS moved to CIRCLE_RADIUS_M
485 : #endif
486 : };
487 1052 : AP_Param::convert_old_parameters_scaled(conversion_info, ARRAY_SIZE(conversion_info), 0.01, 0);
488 : }
|