LCOV - code coverage report
Current view: top level - AC_Fence - AC_Fence.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 421 498 84.5 %
Date: 2026-09-09 11:02:08 Functions: 38 41 92.7 %

          Line data    Source code
       1             : #include "AC_Fence.h"
       2             : 
       3             : #if AP_FENCE_ENABLED
       4             : 
       5             : #include <AP_Vehicle/AP_Vehicle_Type.h>
       6             : 
       7             : #ifndef AC_FENCE_DUMMY_METHODS_ENABLED
       8             : #define AC_FENCE_DUMMY_METHODS_ENABLED  (!(APM_BUILD_TYPE(APM_BUILD_Rover) | APM_BUILD_COPTER_OR_HELI | APM_BUILD_TYPE(APM_BUILD_ArduPlane) | APM_BUILD_TYPE(APM_BUILD_ArduSub) | (AP_FENCE_ENABLED == 1)))
       9             : #endif
      10             : 
      11             : #if !AC_FENCE_DUMMY_METHODS_ENABLED
      12             : 
      13             : #include <AP_AHRS/AP_AHRS.h>
      14             : #include <AP_HAL/AP_HAL.h>
      15             : #include <AP_Logger/AP_Logger.h>
      16             : #include <GCS_MAVLink/GCS.h>
      17             : #include <AP_Terrain/AP_Terrain.h>
      18             : 
      19             : extern const AP_HAL::HAL& hal;
      20             : 
      21             : #if APM_BUILD_TYPE(APM_BUILD_Rover)
      22             : #define AC_FENCE_TYPE_DEFAULT AC_FENCE_TYPE_CIRCLE | AC_FENCE_TYPE_POLYGON
      23             : #elif APM_BUILD_TYPE(APM_BUILD_ArduPlane)
      24             : #define AC_FENCE_TYPE_DEFAULT AC_FENCE_TYPE_POLYGON
      25             : #else
      26             : #define AC_FENCE_TYPE_DEFAULT AC_FENCE_TYPE_ALT_MAX | AC_FENCE_TYPE_CIRCLE | AC_FENCE_TYPE_POLYGON
      27             : #endif
      28             : 
      29             : // default boundaries
      30             : #define AC_FENCE_ALT_MAX_DEFAULT                    100.0f  // default max altitude is 100m
      31             : #define AC_FENCE_ALT_MIN_DEFAULT                    -10.0f  // default maximum depth in meters
      32             : #define AC_FENCE_CIRCLE_RADIUS_DEFAULT              300.0f  // default circular fence radius is 300m
      33             : #define AC_FENCE_ALT_MAX_BACKUP_DISTANCE            20.0f   // after fence is broken we recreate the fence 20m further up
      34             : #define AC_FENCE_ALT_MIN_BACKUP_DISTANCE            20.0f   // after fence is broken we recreate the fence 20m further down
      35             : #define AC_FENCE_MARGIN_DEFAULT                     2.0f    // default distance in meters that autopilot's should maintain from the fence to avoid a breach
      36             : #define AC_FENCE_MANUAL_RECOVERY_TIME_MIN           10000   // pilot has 10seconds to recover during which time the autopilot will not attempt to re-take control
      37             : 
      38             : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
      39             : #define AC_FENCE_CIRCLE_RADIUS_BACKUP_DISTANCE     100.0   // after fence is broken we recreate the fence 100m further out
      40             : #define AC_FENCE_OPTIONS_DEFAULT                   OPTIONS::DISABLE_MODE_CHANGE
      41             : #else
      42             : #define AC_FENCE_CIRCLE_RADIUS_BACKUP_DISTANCE      20.0   // after fence is broken we recreate the fence 20m further out
      43             : #define AC_FENCE_OPTIONS_DEFAULT                   0
      44             : #endif
      45             : 
      46             : #define AC_FENCE_NOTIFY_DEFAULT                     1.0f    // default to notifications at 1Hz
      47             : 
      48             : //#define AC_FENCE_DEBUG
      49             : 
      50             : const AP_Param::GroupInfo AC_Fence::var_info[] = {
      51             : 
      52             :     // @Param: ENABLE
      53             :     // @DisplayName: Fence enable/disable
      54             :     // @Description: Allows you to enable (1) or disable (0) the fence functionality. Fences can still be enabled and disabled via mavlink or an RC option, but these changes are not persisted.
      55             :     // @Values: 0:Disabled,1:Enabled
      56             :     // @User: Standard
      57             :     AP_GROUPINFO("ENABLE", 0, AC_Fence, _enabled, 0),
      58             : 
      59             :     // @Param: TYPE
      60             :     // @DisplayName: Fence Type
      61             :     // @Description: Configured fence types held as bitmask. Max altitude, Circle and Polygon fences will be immediately enabled if configured. Min altitude fence will only be enabled once the minimum altitude is reached.
      62             :     // @Bitmask{Rover}: 1:Circle Centered on Home,2:Inclusion/Exclusion Circles+Polygons
      63             :     // @Bitmask{Copter, Plane, Sub}: 0:Max altitude,1:Circle Centered on Home,2:Inclusion/Exclusion Circles+Polygons,3:Min altitude
      64             :     // @User: Standard
      65             :     AP_GROUPINFO("TYPE", 1, AC_Fence, _configured_fences, AC_FENCE_TYPE_DEFAULT),
      66             : 
      67             :     // @Param: ACTION
      68             :     // @DisplayName: Fence Action
      69             :     // @Description: What action should be taken when fence is breached
      70             :     // @Values{Copter}: 0:Report Only,1:RTL or Land,2:Always Land,3:SmartRTL or RTL or Land,4:Brake or Land,5:SmartRTL or Land
      71             :     // @Values{Rover}: 0:Report Only,1:RTL or Hold,2:Hold,3:SmartRTL or RTL or Hold,4:SmartRTL or Hold,5:Terminate,6:Loiter or Hold
      72             :     // @Values{Plane}: 0:Report Only,1:RTL,6:Guided,7:GuidedThrottlePass,8:AUTOLAND if possible else RTL
      73             :     // @Values: 0:Report Only,1:RTL or Land
      74             :     // @User: Standard
      75             :     AP_GROUPINFO("ACTION", 2,  AC_Fence, _action, float(Action::RTL_AND_LAND)),
      76             : 
      77             :     // @Param{Copter, Plane, Sub}: ALT_MAX
      78             :     // @DisplayName: Fence Maximum Altitude
      79             :     // @Description: Maximum altitude allowed before geofence triggers. See FENCE_ALT_MAX_TP for reference frame.
      80             :     // @Units: m
      81             :     // @Range: 10 1000
      82             :     // @Increment: 1
      83             :     // @User: Standard
      84             :     AP_GROUPINFO_FRAME("ALT_MAX",   3, AC_Fence, _alt_max_m,  AC_FENCE_ALT_MAX_DEFAULT, AP_PARAM_FRAME_COPTER | AP_PARAM_FRAME_SUB | AP_PARAM_FRAME_TRICOPTER | AP_PARAM_FRAME_HELI | AP_PARAM_FRAME_PLANE),
      85             : 
      86             :     // @Param: RADIUS
      87             :     // @DisplayName: Circular Fence Radius
      88             :     // @Description: Circle fence radius which when breached will cause an RTL
      89             :     // @Units: m
      90             :     // @Range: 30 10000
      91             :     // @User: Standard
      92             :     AP_GROUPINFO("RADIUS", 4,  AC_Fence, _circle_radius_m, AC_FENCE_CIRCLE_RADIUS_DEFAULT),
      93             : 
      94             :     // @Param: MARGIN
      95             :     // @DisplayName: Fence Margin
      96             :     // @Description: Distance that autopilot's should maintain from the fence to avoid a breach
      97             :     // @Units: m
      98             :     // @Range: 1 10
      99             :     // @User: Standard
     100             :     AP_GROUPINFO("MARGIN", 5, AC_Fence, _margin_m, AC_FENCE_MARGIN_DEFAULT),
     101             : 
     102             :     // @Param: TOTAL
     103             :     // @DisplayName: Fence polygon point total
     104             :     // @Description: Number of polygon points saved in eeprom (do not update manually)
     105             :     // @Range: 1 20
     106             :     // @User: Standard
     107             :     AP_GROUPINFO("TOTAL", 6, AC_Fence, _total, 0),
     108             : 
     109             :     // @Param{Copter, Plane, Sub}: ALT_MIN
     110             :     // @DisplayName: Fence Minimum Altitude
     111             :     // @Description: Minimum altitude allowed before geofence triggers. See FENCE_ALT_MIN_TP for reference frame.
     112             :     // @Units: m
     113             :     // @Range: -100 100
     114             :     // @Increment: 1
     115             :     // @User: Standard
     116             :     AP_GROUPINFO_FRAME("ALT_MIN", 7,  AC_Fence, _alt_min_m, AC_FENCE_ALT_MIN_DEFAULT, AP_PARAM_FRAME_COPTER | AP_PARAM_FRAME_SUB | AP_PARAM_FRAME_TRICOPTER | AP_PARAM_FRAME_HELI | AP_PARAM_FRAME_PLANE),
     117             : 
     118             :     // @Param{Plane}: RET_RALLY
     119             :     // @DisplayName: Fence Return to Rally
     120             :     // @Description: Should the vehicle return to fence return point or rally point
     121             :     // @Values: 0:Fence Return Point,1:Nearest Rally Point
     122             :     // @Range: 0 1
     123             :     // @Increment: 1
     124             :     // @User: Standard
     125             :     AP_GROUPINFO_FRAME("RET_RALLY", 8, AC_Fence, _ret_rally, 0, AP_PARAM_FRAME_PLANE),
     126             : 
     127             :     // @Param{Plane}: RET_ALT
     128             :     // @DisplayName: Fence Return Altitude
     129             :     // @Description: Altitude the vehicle will transit to when a fence breach occurs
     130             :     // @Units: m
     131             :     // @Range: 0 32767
     132             :     // @Increment: 1
     133             :     // @User: Standard
     134             :     AP_GROUPINFO_FRAME("RET_ALT", 9,  AC_Fence, _ret_altitude, 0.0f, AP_PARAM_FRAME_PLANE),
     135             : 
     136             :     // @Param{Plane, Copter}: AUTOENABLE
     137             :     // @DisplayName: Fence Auto-Enable
     138             :     // @Description: Auto-enable of fences. AutoEnableOnTakeoff enables all configured fences, except the minimum altitude fence (which is enabled when the minimum altitude is reached), after autotakeoffs reach altitude. During autolandings the fences will be disabled.  AutoEnableDisableFloorOnLanding enables all configured fences, except the minimum altitude fence (which is enabled when the minimum altitude is reached), after autotakeoffs reach altitude. During autolandings only the Minimum Altitude fence will be disabled. AutoEnableOnlyWhenArmed enables all configured fences on arming, except the minimum altitude fence (which is enabled when the minimum altitude is reached), but no fences are disabled during autolandings. However, fence breaches are ignored while executing prior breach recovery actions which may include autolandings.
     139             :     // @Values{Plane, Copter}: 0:AutoEnableOff,1:AutoEnableOnTakeoff,2:AutoEnableDisableFloorOnLanding,3:AutoEnableOnlyWhenArmed
     140             :     // @Range: 0 3
     141             :     // @Increment: 1
     142             :     // @User: Standard
     143             :     AP_GROUPINFO_FRAME("AUTOENABLE", 10, AC_Fence, _auto_enabled, static_cast<uint8_t>(AutoEnable::ALWAYS_DISABLED), AP_PARAM_FRAME_PLANE | AP_PARAM_FRAME_COPTER | AP_PARAM_FRAME_TRICOPTER | AP_PARAM_FRAME_HELI),
     144             : 
     145             :     // @Param: OPTIONS
     146             :     // @DisplayName: Fence options
     147             :     // @Description: When bit 0 is set disable mode change following fence action until fence breach is cleared. When bit 1 is set the allowable flight areas is the union of all polygon and circle fence areas instead of the intersection, which means a fence breach occurs only if you are outside all of the fence areas.
     148             :     // @Bitmask: 0:Disable mode change following fence action until fence breach is cleared, 1:Allow union of inclusion areas, 2:Notify on margin breaches
     149             :     // @User: Standard
     150             :     AP_GROUPINFO("OPTIONS", 11, AC_Fence, _options, static_cast<uint16_t>(AC_FENCE_OPTIONS_DEFAULT)),
     151             : 
     152             :     // @Param: NTF_FREQ
     153             :     // @DisplayName: Fence margin notification frequency in hz
     154             :     // @Description: When bit 2 of FENCE_OPTIONS is set this parameter controls the frequency of margin breach notifications. If set to 0 only new margin breaches are notified.
     155             :     // @Range: 0 10
     156             :     // @Units: Hz
     157             :     // @User: Advanced
     158             :     AP_GROUPINFO("NTF_FREQ", 12, AC_Fence, _notify_freq, static_cast<float>(AC_FENCE_NOTIFY_DEFAULT)),
     159             : 
     160             :     // @Param: MARGIN_XY
     161             :     // @DisplayName: Fence Horizontal Margin
     162             :     // @Description: Distance that autopilot's should maintain from the fence in the horizontal plane to avoid a breach. If set to 0 then FENCE_MARGIN is used.
     163             :     // @Units: m
     164             :     // @Range: 0 50
     165             :     // @User: Standard
     166             :     AP_GROUPINFO("MARGIN_XY", 13, AC_Fence, _margin_ne_m, 0),
     167             : 
     168             :     // @Param: ALT_MAX_TP
     169             :     // @DisplayName: Altitude max frame type
     170             :     // @Description: The altitude reference frame for the maximum altitude fence. Can be AMSL, home-relative, origin relative or AGL.
     171             :     // @User: Advanced
     172             :     // @Values: 0:Above sea level, 1:Above Home, 2: Above Origin, 3: Above Terrain
     173             :     AP_GROUPINFO_FRAME("ALT_MAX_TP", 14, AC_Fence, _alt_max_type, float(Location::AltFrame::ABOVE_HOME), AP_PARAM_FRAME_COPTER | AP_PARAM_FRAME_SUB | AP_PARAM_FRAME_TRICOPTER | AP_PARAM_FRAME_HELI | AP_PARAM_FRAME_PLANE),
     174             :     
     175             :     // @Param: ALT_MIN_TP
     176             :     // @DisplayName: Altitude min frame type
     177             :     // @Description: The altitude reference frame for the minimum altitude fence. Can be AMSL, home-relative, origin relative or AGL.
     178             :     // @User: Advanced
     179             :     // @Values: 0:Above sea level, 1:Above Home, 2: Above Origin, 3: Above Terrain
     180             :     AP_GROUPINFO_FRAME("ALT_MIN_TP", 15, AC_Fence, _alt_min_type, float(Location::AltFrame::ABOVE_HOME), AP_PARAM_FRAME_COPTER | AP_PARAM_FRAME_SUB | AP_PARAM_FRAME_TRICOPTER | AP_PARAM_FRAME_HELI | AP_PARAM_FRAME_PLANE),
     181             : 
     182             :     AP_GROUPEND
     183             : };
     184             : 
     185             : /// Default constructor.
     186        2061 : AC_Fence::AC_Fence()
     187             : {
     188             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     189        2061 :     if (_singleton != nullptr) {
     190           0 :         AP_HAL::panic("Fence must be singleton");
     191             :     }
     192             : #endif
     193        2061 :     _singleton = this;
     194        2061 :     AP_Param::setup_object_defaults(this, var_info);
     195        2061 :     if (_enabled) {
     196           0 :         _enabled_fences = _configured_fences.get() & ~AC_FENCE_TYPE_ALT_MIN;
     197             :     }
     198        2061 : }
     199             : 
     200             : // get a user-friendly list of fences
     201         494 : void AC_Fence::get_fence_names(uint8_t fences, ExpandingString& msg)
     202             : {
     203         494 :     if (!fences) {
     204           0 :         return;
     205             :     }
     206             :     static const char* FENCE_NAMES[] = {
     207             :         "Max Alt",
     208             :         "Circle",
     209             :         "Polygon",
     210             :         "Min Alt",
     211             :     };
     212         494 :     uint8_t i = 0;
     213         494 :     uint8_t nfences = 0;
     214        2019 :     while (fences !=0) {
     215        1525 :         if (fences & 0x1) {
     216         514 :             if (nfences > 0) {
     217          20 :                 if (!(fences & ~1U)) {
     218          18 :                     msg.printf(" and ");
     219             :                 } else {
     220           2 :                     msg.printf(", ");
     221             :                 }
     222             :             }
     223         514 :             msg.printf("%s", FENCE_NAMES[i]);
     224         514 :             nfences++;
     225             :         }
     226        1525 :         fences >>= 1;
     227        1525 :         i++;
     228             :     }
     229         494 :     msg.printf(" fence");
     230         494 :     if (nfences>1) {
     231          18 :         msg.printf("s");
     232             :     }
     233             : }
     234             : 
     235             : // print a message about the passed in fences
     236         153 : void AC_Fence::print_fence_message(const char* message, uint8_t fences) const
     237             : {
     238         153 :     if (!fences) {
     239           3 :         return;
     240             :     }
     241             : 
     242             :     char msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
     243         150 :     ExpandingString e(msg, MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1);
     244         150 :     AC_Fence::get_fence_names(fences, e);
     245         150 :     GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "%s %s", e.get_writeable_string(), message);
     246         150 : }
     247             : 
     248             : // should be called @10Hz to handle loading from eeprom
     249     1486795 : void AC_Fence::update()
     250             : {
     251     1486795 :     _poly_loader.update();
     252             :     // if someone changes the parameter we want to enable or disable everything
     253     1486795 :     if (_enabled != _last_enabled || _auto_enabled != _last_auto_enabled) {
     254             :         // reset the auto mask since we just reconfigured all of fencing
     255         141 :         _last_enabled = _enabled;
     256         141 :         _last_auto_enabled = _auto_enabled;
     257         141 :         if (_enabled) {
     258          73 :             _enabled_fences = _configured_fences.get() & ~AC_FENCE_TYPE_ALT_MIN;
     259             :         } else {
     260          68 :             _enabled_fences = 0;
     261             :         }
     262             :     }
     263             : #ifdef AC_FENCE_DEBUG
     264             :     static uint32_t last_msg_count = 0;
     265             :     if (get_enabled_fences() && last_msg_count++ % 10 == 0) {
     266             :         print_fence_message("active", get_enabled_fences());
     267             :         print_fence_message("breached", get_breaches());
     268             :     }
     269             : #endif
     270     1486795 : }
     271             : 
     272             : // enable or disable configured fences present in fence_types
     273             : // also updates the _min_alt_state enum if update_auto_enable is true
     274             : // returns a bitmask of fences that were changed
     275      158453 : uint8_t AC_Fence::enable(bool value, uint8_t fence_types, bool update_auto_enable)
     276             : {
     277      158453 :     uint8_t fences = _configured_fences.get() & fence_types;
     278      158453 :     uint8_t enabled_fences = _enabled_fences;
     279      158453 :     if (value) {
     280          96 :         enabled_fences |= fences;
     281             :     } else {
     282      158357 :         enabled_fences &= ~fences;
     283             :     }
     284             : 
     285      158453 :     if (update_auto_enable && (fences & AC_FENCE_TYPE_ALT_MIN) != 0) {
     286             :         // remember that min-alt fence was manually enabled/disabled
     287          47 :         _min_alt_state = value ? MinAltState::MANUALLY_ENABLED : MinAltState::MANUALLY_DISABLED;
     288             :     }
     289             : 
     290      158453 :     uint8_t fences_to_change = _enabled_fences ^ enabled_fences;
     291             : 
     292      158453 :     if (!fences_to_change) {
     293      158330 :         return 0;
     294             :     }
     295             : 
     296             : #if HAL_LOGGING_ENABLED
     297         123 :     AP::logger().Write_Event(value ? LogEvent::FENCE_ENABLE : LogEvent::FENCE_DISABLE);
     298         123 :     if (fences_to_change & AC_FENCE_TYPE_ALT_MAX) {
     299          18 :         AP::logger().Write_Event(value ? LogEvent::FENCE_ALT_MAX_ENABLE : LogEvent::FENCE_ALT_MAX_DISABLE);
     300             :     }
     301         123 :     if (fences_to_change & AC_FENCE_TYPE_CIRCLE) {
     302          30 :         AP::logger().Write_Event(value ? LogEvent::FENCE_CIRCLE_ENABLE : LogEvent::FENCE_CIRCLE_DISABLE);
     303             :     }
     304         123 :     if (fences_to_change & AC_FENCE_TYPE_ALT_MIN) {
     305          65 :         AP::logger().Write_Event(value ? LogEvent::FENCE_ALT_MIN_ENABLE : LogEvent::FENCE_ALT_MIN_DISABLE);
     306             :     }
     307         123 :     if (fences_to_change & AC_FENCE_TYPE_POLYGON) {
     308          49 :         AP::logger().Write_Event(value ? LogEvent::FENCE_POLYGON_ENABLE : LogEvent::FENCE_POLYGON_DISABLE);
     309             :     }
     310             : #endif
     311             : 
     312         123 :     _enabled_fences = enabled_fences;
     313             : 
     314         123 :     if (!value) {
     315          51 :         clear_breach(fences_to_change);
     316          51 :         if (update_auto_enable) {
     317             :             // explicit user disable resets the manual-recovery suppression
     318             :             // window so that a fresh fence enable will check immediately
     319          38 :             _manual_recovery_start_ms = 0;
     320             :         }
     321             :     }
     322             : 
     323         123 :     return fences_to_change;
     324             : }
     325             : 
     326             : /// enable/disable fence floor only
     327           0 : void AC_Fence::enable_floor()
     328             : {
     329           0 :     enable(true, AC_FENCE_TYPE_ALT_MIN);
     330           0 : }
     331             : 
     332           0 : void AC_Fence::disable_floor()
     333             : {
     334           0 :     enable(false, AC_FENCE_TYPE_ALT_MIN);
     335           0 : }
     336             : 
     337             : /*
     338             :   called on arming
     339             : */
     340        1127 : void AC_Fence::auto_enable_fence_on_arming(void)
     341             : {
     342        1127 :     if (auto_enabled() != AC_Fence::AutoEnable::ONLY_WHEN_ARMED) {
     343        1123 :         return;
     344             :     }
     345             : 
     346             :     // reset min alt state, after an auto-enable the min alt fence can be auto-enabled on
     347             :     // reaching altitude
     348           4 :     _min_alt_state = MinAltState::DEFAULT;
     349             : 
     350           4 :     const uint8_t fences = enable(true, AC_FENCE_ARMING_FENCES, false);
     351           4 :     print_fence_message("auto-enabled", fences);
     352             : }
     353             : 
     354             : /*
     355             :   called on disarming
     356             : */
     357        1103 : void AC_Fence::auto_disable_fence_on_disarming(void)
     358             : {
     359        1103 :     if (auto_enabled() != AC_Fence::AutoEnable::ONLY_WHEN_ARMED) {
     360        1099 :         return;
     361             :     }
     362             : 
     363           4 :     const uint8_t fences = enable(false, AC_FENCE_ALL_FENCES, false);
     364           4 :     print_fence_message("auto-disabled", fences);
     365             : }
     366             : 
     367             : /*
     368             :   called when an auto-takeoff is complete
     369             : */
     370         290 : void AC_Fence::auto_enable_fence_after_takeoff(void)
     371             : {
     372         579 :     if (auto_enabled() != AutoEnable::ENABLE_ON_AUTO_TAKEOFF &&
     373         289 :         auto_enabled() != AutoEnable::ENABLE_DISABLE_FLOOR_ONLY) {
     374         285 :         return;
     375             :     }
     376             : 
     377             :     // reset min-alt state
     378           5 :     _min_alt_state = MinAltState::DEFAULT;
     379             : 
     380           5 :     const uint8_t fences = enable(true, AC_FENCE_ALL_FENCES, false);
     381           5 :     print_fence_message("auto-enabled", fences);
     382             : }
     383             : 
     384             : // return fences that should be auto-disabled when requested
     385     1097236 : uint8_t AC_Fence::get_auto_disable_fences(void) const
     386             : {
     387     1097236 :     uint8_t auto_disable = 0;
     388     1097236 :     switch (auto_enabled()) {
     389        2605 :         case AC_Fence::AutoEnable::ENABLE_ON_AUTO_TAKEOFF:
     390        2605 :             auto_disable = AC_FENCE_ALL_FENCES;
     391        2605 :             break;
     392     1094631 :         case AC_Fence::AutoEnable::ENABLE_DISABLE_FLOOR_ONLY:
     393             :         case AC_Fence::AutoEnable::ONLY_WHEN_ARMED:
     394             :         default: // when auto disable is not set we still need to disable the altmin fence on landing
     395     1094631 :             auto_disable = AC_FENCE_TYPE_ALT_MIN;
     396     1094631 :             break;
     397             :     }
     398     1097236 :     if (_min_alt_state == MinAltState::MANUALLY_ENABLED) {
     399             :         // don't auto-disable min alt fence if manually enabled
     400        3789 :         auto_disable &= ~AC_FENCE_TYPE_ALT_MIN;
     401             :     }
     402     1097236 :     return auto_disable;
     403             : }
     404             : 
     405    11588490 : uint8_t AC_Fence::present() const
     406             : {
     407    11588490 :     uint8_t mask = AC_FENCE_TYPE_CIRCLE | AC_FENCE_TYPE_ALT_MIN | AC_FENCE_TYPE_ALT_MAX;
     408    11588490 :     if (_poly_loader.total_fence_count() > 0) {
     409      831219 :         mask |= AC_FENCE_TYPE_POLYGON;
     410             :     }
     411             : 
     412    11588490 :     return _configured_fences.get() & mask;
     413             : }
     414             : 
     415             : /// get_enabled_fences - returns bitmask of enabled fences
     416     8227814 : uint8_t AC_Fence::get_enabled_fences() const
     417             : {
     418     8227814 :     return _enabled_fences & present();
     419             : }
     420             : 
     421             : /// get_margin_ne_m - returns the fence margin in meters
     422      580468 : float AC_Fence::get_margin_ne_m() const
     423             : {
     424      580468 :     return is_positive(_margin_ne_m.get()) ? _margin_ne_m.get() : _margin_m.get();
     425             : }
     426             : 
     427             : // additional checks for the polygon fence:
     428        2149 : bool AC_Fence::pre_arm_check_polygon(char *failure_msg, const uint8_t failure_msg_len) const
     429             : {
     430        2149 :     if (!(_configured_fences & AC_FENCE_TYPE_POLYGON)) {
     431             :         // not enabled; all good
     432        1136 :         return true;
     433             :     }
     434             : 
     435        1013 :     if (! _poly_loader.loaded()) {
     436         139 :         hal.util->snprintf(failure_msg, failure_msg_len, "Polygon fence(s) invalid");
     437         139 :         return false;
     438             :     }
     439             : 
     440         874 :     if (!_poly_loader.check_inclusion_circle_margin(get_margin_ne_m())) {
     441           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "Polygon fence margin is greater than inclusion circle radius");
     442           0 :         return false;
     443             :     }
     444             : 
     445         874 :     return true;
     446             : }
     447             : 
     448             : // additional checks for the circle fence:
     449        2010 : bool AC_Fence::pre_arm_check_circle(char *failure_msg, const uint8_t failure_msg_len) const
     450             : {
     451        2010 :     if (_circle_radius_m < 0) {
     452           1 :         hal.util->snprintf(failure_msg, failure_msg_len, "Invalid Circle FENCE_RADIUS value");
     453           1 :         return false;
     454             :     }
     455        2009 :     if (_circle_radius_m < get_margin_ne_m()) {
     456           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "Circle FENCE_MARGIN is greater than FENCE_RADIUS");
     457           0 :         return false;
     458             :     }
     459             : 
     460        2009 :     return true;
     461             : }
     462             : 
     463             : // additional checks for the alt fence:
     464        2009 : bool AC_Fence::pre_arm_check_alt(char *failure_msg, const uint8_t failure_msg_len) const
     465             : {
     466        2009 :     if (_alt_max_m < 0.0f) {
     467           1 :         hal.util->snprintf(failure_msg, failure_msg_len, "Invalid FENCE_ALT_MAX value");
     468           1 :         return false;
     469             :     }
     470             : 
     471        2008 :     if (_alt_min_m < -100.0f) {
     472           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "Invalid FENCE_ALT_MIN value");
     473           0 :         return false;
     474             :     }
     475        2008 :     return true;
     476             : }
     477             : 
     478      128868 : bool AC_Fence::terrain_database_required() const
     479             : {
     480      128868 :     return _alt_min_type == Location::AltFrame::ABOVE_TERRAIN || _alt_max_type == Location::AltFrame::ABOVE_TERRAIN;
     481             : }
     482             : 
     483             : /// pre_arm_check - returns true if all pre-takeoff checks have completed successfully
     484      138793 : bool AC_Fence::pre_arm_check(char *failure_msg, const uint8_t failure_msg_len) const
     485             : {
     486             :     // if fences are enabled but none selected fail pre-arm check
     487      138793 :     if (_enabled && !present()) {
     488          45 :         hal.util->snprintf(failure_msg, failure_msg_len, "Fences enabled, but none selected");
     489          45 :         return false;
     490             :     }
     491             :     
     492             :     // if AUTOENABLE = 1 or 2 warn now, but fail in a later release
     493             :     // PARAMETER_CONVERSION - Added: Jul-2024 for ArduPilot-4.6
     494      138748 :     if (_auto_enabled == 1 || _auto_enabled == 2) {
     495             :         static uint32_t last_autoenable_warn_ms;
     496         155 :         const uint32_t now_ms = AP_HAL::millis();
     497         155 :         if (now_ms - last_autoenable_warn_ms > 60000) {
     498           6 :             GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "FENCE_AUTOENABLE is %u, will be removed in 4.7, use 3", unsigned(_auto_enabled));
     499           6 :             last_autoenable_warn_ms = now_ms;
     500             :         }
     501             :     }
     502             : 
     503             :     // if not enabled or not fence set-up always return true
     504      138748 :     if ((!enabled() && !_auto_enabled) || !_configured_fences) {
     505      136430 :         return true;
     506             :     }
     507             : 
     508             :     // if we have horizontal limits enabled, check we can get a
     509             :     // relative position from the AHRS
     510        3253 :     if ((_configured_fences & AC_FENCE_TYPE_CIRCLE) ||
     511         935 :         (_configured_fences & AC_FENCE_TYPE_POLYGON)) {
     512        1533 :         Vector2f position;
     513        1533 :         if (!AP::ahrs().get_relative_position_NE_home(position)) {
     514         169 :             hal.util->snprintf(failure_msg, failure_msg_len, "Fence requires position");
     515         169 :             return false;
     516             :         }
     517             :     }
     518             : 
     519        2149 :     if (!pre_arm_check_polygon(failure_msg, failure_msg_len)) {
     520         139 :         return false;
     521             :     }
     522             : 
     523        2010 :     if (!pre_arm_check_circle(failure_msg, failure_msg_len)) {
     524           1 :         return false;
     525             :     }
     526             : 
     527        2009 :     if (!pre_arm_check_alt(failure_msg, failure_msg_len)) {
     528           1 :         return false;
     529             :     }
     530             : 
     531        2008 :     auto breached_fences = _breached_fences;
     532        2008 :     if (auto_enabled() == AC_Fence::AutoEnable::ONLY_WHEN_ARMED) {
     533         217 :         Location loc;
     534         217 :         if (!AP::ahrs().get_location(loc)) {
     535           0 :             hal.util->snprintf(failure_msg, failure_msg_len, "Fence requires position");
     536           0 :             return false;
     537             :         }
     538         217 :         if (_poly_loader.breached(loc)) {
     539           0 :             breached_fences |= AC_FENCE_TYPE_POLYGON;
     540             :         }
     541             :     }
     542             : 
     543             :     // check no limits are currently breached
     544        2008 :     if (breached_fences) {
     545             :         char msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
     546         324 :         ExpandingString e(msg, MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1);
     547         324 :         AC_Fence::get_fence_names(_breached_fences, e);
     548         324 :         hal.util->snprintf(failure_msg, failure_msg_len, "Vehicle breaching %s", e.get_writeable_string());
     549         324 :         return false;
     550         324 :     }
     551             : 
     552             :     // validate FENCE_MARGIN parameter range
     553        1684 :     if (_margin_m < 0.0f) {
     554           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "Invalid FENCE_MARGIN value");
     555           0 :         return false;
     556             :     }
     557             : 
     558        1684 :     if (_margin_ne_m < 0.0f) {
     559           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "Invalid FENCE_MARGIN_XY value");
     560           0 :         return false;
     561             :     }
     562             : 
     563             :     // change min-alt into the same frame as max-alt to make them comparable:
     564        1684 :     const Location::AltFrame alt_max_frame = get_alt_max_frame();
     565        1684 :     float framed_alt_min_m = _alt_min_m;
     566        1684 :     if (get_alt_min_frame() != alt_max_frame) {
     567          29 :         Location loc;
     568          29 :         if (!AP::ahrs().get_location(loc)) {
     569           0 :             hal.util->snprintf(failure_msg, failure_msg_len, "Fence requires position");
     570           0 :             return false;
     571             :         }
     572          29 :         loc.set_alt_m(_alt_min_m, get_alt_min_frame());
     573          29 :         if (!loc.get_alt_m(alt_max_frame, framed_alt_min_m)) {
     574           0 :             hal.util->snprintf(failure_msg, failure_msg_len, "Fence requires terrain");
     575           0 :             return false;
     576             :         }
     577             :     }
     578             : 
     579        1684 :     if (_alt_max_m < framed_alt_min_m) {
     580           2 :         hal.util->snprintf(failure_msg, failure_msg_len, "FENCE_ALT_MAX < FENCE_ALT_MIN");
     581           2 :         return false;
     582             :     }
     583             : 
     584        1682 :     if (_alt_max_m - framed_alt_min_m <= 2.0f * _margin_m) {
     585           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "FENCE_MARGIN too big");
     586           0 :         return false;
     587             :     }
     588             : 
     589             :     // if we got this far everything must be ok
     590        1682 :     return true;
     591             : }
     592             : 
     593             : /*
     594             :   get our altitude in the supplied frame
     595             :   return false if not available
     596             :  */
     597      257133 : bool AC_Fence::get_alt_in_frame_m(Location::AltFrame alt_frame, float &alt) const
     598             : {
     599      257133 :     const auto &ahrs = AP::ahrs();
     600             : 
     601      257133 :     if (alt_frame == Location::AltFrame::ABOVE_ORIGIN) {
     602             :         // try and get it relative origin
     603       17357 :         if (!ahrs.get_relative_position_D_origin_float(alt)) {
     604             :             // treat as a breach if origin not known
     605          34 :             return false;
     606             :         }
     607       17323 :         alt = -alt;
     608       17323 :         return true;
     609             :     }
     610             : 
     611      239776 :     if (alt_frame == Location::AltFrame::ABOVE_HOME) {
     612             :         // try and get it relative origin
     613      220600 :         ahrs.get_relative_position_D_home(alt);
     614      220600 :         alt = -alt;
     615      220600 :         return true;
     616             :     }
     617             : 
     618       19176 :     Location loc;
     619       19176 :     if (AP::ahrs().get_location(loc)) {
     620       19065 :         if (loc.get_alt_m(alt_frame, alt)) {
     621       19065 :             return true;
     622             :         }
     623             :     }
     624             : 
     625         111 :     return false;
     626             : }
     627             : 
     628             : // update safe alt min - home *must* be set before this function is called with absolute reference frames
     629       27574 : void AC_Fence::update_safe_alt_min()
     630             : {
     631             :     // update safe alt min, failure to get home will have already breached above
     632       27574 :     if (_alt_min_type == Location::AltFrame::ABSOLUTE) {
     633             :         float home_alt;
     634        4075 :         UNUSED_RESULT(AP::ahrs().get_home().get_alt_m(Location::AltFrame::ABSOLUTE, home_alt));
     635        4075 :         _safe_relhome_alt_min_m  = _alt_min_m - home_alt - _margin_m;
     636             :     } else {
     637       23499 :         _safe_relhome_alt_min_m  = _alt_min_m - _margin_m;
     638             :     }
     639       27574 : }
     640             : 
     641             : /// returns true if we have freshly breached the maximum altitude
     642             : /// fence; also may set up a fallback fence which, if breached, will
     643             : /// cause the altitude fence to be freshly breached
     644      155704 : bool AC_Fence::check_fence_alt_max()
     645             : {
     646             :     // altitude fence check
     647      155704 :     if (!(get_enabled_fences() & AC_FENCE_TYPE_ALT_MAX)) {
     648             :         // not enabled; no breach
     649       90939 :         return false;
     650             :     }
     651             : 
     652             :     float _curr_alt_u_m;
     653       64765 :     if (!get_alt_in_alt_max_frame_m(_curr_alt_u_m)) {
     654             :         // if we can't get the alt then it is a breach
     655         145 :         return true;
     656             :     }
     657             : 
     658             :     // record distance above/below breach
     659       64620 :     _alt_max_breach_distance_m = _curr_alt_u_m - _alt_max_m;
     660             : 
     661             :     // update safe alt max, failure to get home will have already breached above
     662       64620 :     if (_alt_max_type == Location::AltFrame::ABSOLUTE) {
     663             :         float home_alt;
     664        5113 :         UNUSED_RESULT(AP::ahrs().get_home().get_alt_m(Location::AltFrame::ABSOLUTE, home_alt));
     665        5113 :         _safe_relhome_alt_max_m  = _alt_max_m - home_alt - _margin_m;
     666             :     } else {
     667       59507 :         _safe_relhome_alt_max_m = _alt_max_m - _margin_m;
     668             :     }
     669             : 
     670             :     // check if we are over the altitude fence
     671       64620 :     if (_curr_alt_u_m >= _alt_max_m) {
     672             :         // check for a new breach or a breach of the backup fence
     673        8876 :         if (!(_breached_fences & AC_FENCE_TYPE_ALT_MAX) ||
     674        4419 :             (!is_zero(_alt_max_backup_m) && _curr_alt_u_m >= _alt_max_backup_m)) {
     675             : 
     676             :             // new breach
     677          40 :             record_breach(AC_FENCE_TYPE_ALT_MAX);
     678             : 
     679             :             // create a backup fence 20m higher up
     680          40 :             _alt_max_backup_m = _curr_alt_u_m + AC_FENCE_ALT_MAX_BACKUP_DISTANCE;
     681             :             // new breach
     682          40 :             return true;
     683             :         }
     684             :         // old breach
     685        4417 :         return false;
     686       60163 :     } else if (_curr_alt_u_m >= get_safe_alt_max_m()) {
     687        1886 :         record_margin_breach(AC_FENCE_TYPE_ALT_MAX);
     688             :     } else {
     689       58277 :         clear_margin_breach(AC_FENCE_TYPE_ALT_MAX);
     690             :     }
     691             : 
     692             :     // not breached
     693             : 
     694             :     // clear max alt breach if present
     695       60163 :     if ((_breached_fences & AC_FENCE_TYPE_ALT_MAX) != 0) {
     696          35 :         clear_breach(AC_FENCE_TYPE_ALT_MAX);
     697          35 :         _alt_max_backup_m = 0.0f;
     698             :     }
     699             : 
     700       60163 :     return false;
     701             : }
     702             : 
     703             : /// returns true if we have freshly breached the minimum altitude
     704             : /// fence; also may set up a fallback fence which, if breached, will
     705             : /// cause the altitude fence to be freshly breached
     706       94796 : bool AC_Fence::check_fence_alt_min()
     707             : {
     708             :     // altitude fence check
     709       94796 :     if (!(get_enabled_fences() & AC_FENCE_TYPE_ALT_MIN)) {
     710             :         // not enabled; no breach
     711       72590 :         return false;
     712             :     }
     713             : 
     714             :     float _curr_alt_u_m;
     715       22206 :     if (!get_alt_in_alt_min_frame_m(_curr_alt_u_m)) {
     716             :         // if we can't get the alt then it is a breach
     717           0 :         return true;
     718             :     }
     719             : 
     720             :     // record distance above/below breach
     721       22206 :     _alt_min_breach_distance_m = _alt_min_m - _curr_alt_u_m;
     722             : 
     723             :     // update safe alt min, failure to get home will have already breached above
     724       22206 :     update_safe_alt_min();
     725             : 
     726             :     // check if we are under the altitude fence
     727       22206 :     if (_curr_alt_u_m <= _alt_min_m) {
     728             : 
     729             : 
     730             :         // check for a new breach or a breach of the backup fence
     731       11199 :         if (!(_breached_fences & AC_FENCE_TYPE_ALT_MIN) ||
     732        5580 :             (!is_zero(_alt_min_backup_m) && _curr_alt_u_m <= _alt_min_backup_m)) {
     733             : 
     734             :             // new breach
     735          42 :             record_breach(AC_FENCE_TYPE_ALT_MIN);
     736             : 
     737             :             // create a backup fence 20m lower down
     738          42 :             _alt_min_backup_m = _curr_alt_u_m - AC_FENCE_ALT_MIN_BACKUP_DISTANCE;
     739             :             // new breach
     740          42 :             return true;
     741             :         }
     742             :         // old breach
     743        5577 :         return false;
     744       16587 :     } else if (_curr_alt_u_m <= get_safe_alt_min_m()) {
     745        2465 :         record_margin_breach(AC_FENCE_TYPE_ALT_MIN);
     746             :     } else {
     747       14122 :         clear_margin_breach(AC_FENCE_TYPE_ALT_MIN);
     748             :     }
     749             : 
     750             :     // not breached
     751             : 
     752             :     // clear min alt breach if present
     753       16587 :     if ((_breached_fences & AC_FENCE_TYPE_ALT_MIN) != 0) {
     754          14 :         clear_breach(AC_FENCE_TYPE_ALT_MIN);
     755          14 :         _alt_min_backup_m = 0.0f;
     756             :     }
     757             : 
     758       16587 :     return false;
     759             : }
     760             : 
     761             : 
     762             : /// auto enable fence floor
     763       94796 : bool AC_Fence::auto_enable_fence_floor()
     764             : {
     765             :     // altitude fence check
     766       94796 :     if (!(_configured_fences & AC_FENCE_TYPE_ALT_MIN)       // not configured
     767       34135 :         || (get_enabled_fences() & AC_FENCE_TYPE_ALT_MIN)   // already enabled
     768       11929 :         || _min_alt_state == MinAltState::MANUALLY_DISABLED // user has manually disabled the fence
     769      129179 :         || (!_enabled && (auto_enabled() == AC_Fence::AutoEnable::ALWAYS_DISABLED
     770         248 :             || auto_enabled() == AutoEnable::ENABLE_ON_AUTO_TAKEOFF))) {
     771             :         // not enabled
     772       89428 :         return false;
     773             :     }
     774             : 
     775             :     float _curr_alt_u_m;
     776        5368 :     if (!get_alt_in_alt_min_frame_m(_curr_alt_u_m)) {
     777             :         // if we can't get the alt then don't enable yet
     778           0 :         return true;
     779             :     }
     780             : 
     781             :     // update safe alt min, failure to get home will have already breached above
     782        5368 :     update_safe_alt_min();
     783             : 
     784             :     // check if we are over the altitude fence
     785        5368 :     if (!floor_enabled() && _curr_alt_u_m >= get_safe_alt_min_m()) {
     786          22 :         enable(true, AC_FENCE_TYPE_ALT_MIN, false);
     787          22 :         GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Min Alt fence enabled (auto enable)");
     788          22 :         return true;
     789             :     } 
     790             : 
     791        5346 :     return false;
     792             : }
     793             : 
     794             : // check_fence_polygon - returns true if the poly fence is freshly
     795             : // breached.  That includes being inside exclusion zones and outside
     796             : // inclusions zones
     797      155704 : bool AC_Fence::check_fence_polygon()
     798             : {
     799      155704 :     if (!(get_enabled_fences() & AC_FENCE_TYPE_POLYGON)) {
     800             :         // not enabled; no breach
     801      120248 :         clear_breach(AC_FENCE_TYPE_POLYGON);
     802      120248 :         return false;
     803             :     }
     804             : 
     805       35456 :     const bool was_breached = _breached_fences & AC_FENCE_TYPE_POLYGON;
     806             : 
     807       35456 :     if (_last_fence_check_loc_valid && _poly_loader.breached(_last_fence_check_loc, _polygon_breach_distance_m, _polygon_nearest_point)) {
     808        3890 :         if (!was_breached) {
     809          40 :             record_breach(AC_FENCE_TYPE_POLYGON);
     810          40 :             return true;
     811             :         }
     812        3850 :         return false;
     813       31566 :     } else if (_polygon_breach_distance_m < 0 && fabsf(_polygon_breach_distance_m) < get_margin_ne_m()) {
     814        7027 :         record_margin_breach(AC_FENCE_TYPE_POLYGON);
     815             :     } else {
     816       24539 :         clear_margin_breach(AC_FENCE_TYPE_POLYGON);
     817             :     }
     818             : 
     819       31566 :     if (was_breached) {
     820          21 :         clear_breach(AC_FENCE_TYPE_POLYGON);
     821             :     }
     822       31566 :     return false;
     823             : }
     824             : 
     825             : /// check_fence_circle - returns true if the circle fence (defined via
     826             : /// parameters) has been freshly breached.  May also set up a backup
     827             : /// fence outside the fence and return a fresh breach if that backup
     828             : /// fence is breached.
     829      155704 : bool AC_Fence::check_fence_circle()
     830             : {
     831      155704 :     if (!(get_enabled_fences() & AC_FENCE_TYPE_CIRCLE)) {
     832             :         // not enabled; no breach
     833      110372 :         return false;
     834             :     }
     835             : 
     836       45332 :     Vector2f home;
     837       45332 :     if (AP::ahrs().get_relative_position_NE_home(home)) {
     838             :         // we (may) remain breached if we can't update home
     839       43442 :         _home_distance_m = home.length();
     840             : 
     841       43442 :         if (is_zero(home.length_squared())) {
     842        6438 :             _circle_breach_direction.x = _circle_radius_m;
     843        6438 :             _circle_breach_direction.y = 0.0f;
     844             :         } else {
     845       37004 :             _circle_breach_direction = home.normalized() * _circle_radius_m.get()  - home;
     846             :         }
     847             :     }
     848             : 
     849             :     // record distance outside/inside the fence
     850       45332 :     _circle_breach_distance_m = _home_distance_m - _circle_radius_m;
     851             : 
     852             :     // check if we are outside the fence
     853       45332 :     if (_home_distance_m >= _circle_radius_m) {
     854             : 
     855             :         // check for a new breach or a breach of the backup fence
     856        2035 :         if (!(_breached_fences & AC_FENCE_TYPE_CIRCLE) ||
     857        1015 :             (!is_zero(_circle_radius_backup_m) && _home_distance_m >= _circle_radius_backup_m)) {
     858             :             // new breach
     859             :             // create a backup fence 20m or 100m further out
     860           9 :             record_breach(AC_FENCE_TYPE_CIRCLE);
     861           9 :             _circle_radius_backup_m = _home_distance_m + AC_FENCE_CIRCLE_RADIUS_BACKUP_DISTANCE;
     862           9 :             return true;
     863             :         }
     864        1011 :         return false;
     865       44312 :     } else if (_home_distance_m >= _circle_radius_m - get_margin_ne_m()) {
     866        3151 :         record_margin_breach(AC_FENCE_TYPE_CIRCLE);
     867             :     } else {
     868       41161 :         clear_margin_breach(AC_FENCE_TYPE_CIRCLE);
     869             :     }
     870             : 
     871             :     // not currently breached
     872             : 
     873             :     // clear circle breach if present
     874       44312 :     if (_breached_fences & AC_FENCE_TYPE_CIRCLE) {
     875           5 :         clear_breach(AC_FENCE_TYPE_CIRCLE);
     876           5 :         _circle_radius_backup_m = 0.0f;
     877             :     }
     878             : 
     879       44312 :     return false;
     880             : }
     881             : 
     882             : 
     883             : /// check - returns bitmask of fence types breached (if any)
     884     2201372 : uint8_t AC_Fence::check(bool disable_auto_fences)
     885             : {
     886     2201372 :     uint8_t ret = 0;
     887     2201372 :     uint8_t disabled_fences = disable_auto_fences ? get_auto_disable_fences() : 0;
     888     2201372 :     uint8_t fences_to_disable = disabled_fences & _enabled_fences;
     889             : 
     890             :     // clear any breach from a non-enabled fence
     891     2201372 :     clear_breach(~_configured_fences);
     892             :     // clear any breach from disabled fences
     893     2201372 :     clear_breach(fences_to_disable);
     894             :     // clear any margin breach from a non-enabled fence
     895     2201372 :     clear_margin_breach(~_configured_fences);
     896             :     // clear any marginbreach from disabled fences
     897     2201372 :     clear_margin_breach(fences_to_disable);
     898             : 
     899     2201372 :     if (_min_alt_state == MinAltState::MANUALLY_ENABLED) {
     900             :         // if user has manually enabled the min-alt fence then don't auto-disable
     901       13674 :         fences_to_disable &= ~AC_FENCE_TYPE_ALT_MIN;
     902             :     }
     903             : 
     904             :     // report on any fences that were auto-disabled
     905     2201372 :     if (fences_to_disable) {
     906          60 :         print_fence_message("auto-disabled", fences_to_disable);
     907             :     }
     908             : 
     909             : #if 0
     910             :     /*
     911             :       this debug log message is very useful both when developing tests
     912             :       and doing manual SITL fence testing
     913             :      */
     914             :     {
     915             :         float _curr_alt_d_m;
     916             :         AP::ahrs().get_relative_position_D_home(_curr_alt_d_m);
     917             : 
     918             :         // @LoggerMessage: FENC
     919             :         // @Description: Fence status - development diagnostic message
     920             :         // @Field: TimeUS: Time since system startup
     921             :         // @Field: EN: bitmask of enabled fences
     922             :         // @Field: AE: bitmask of automatically enabled fences
     923             :         // @Field: CF: bitmask of configured-in-parameters fences
     924             :         // @Field: EF: bitmask of enabled fences
     925             :         // @Field: DF: bitmask of currently disabled fences
     926             :         // @Field: Alt: current vehicle altitude
     927             :         AP::logger().WriteStreaming("FENC", "TimeUS,EN,AE,CF,EF,DF,Alt", "QIIIIIf",
     928             :                                     AP_HAL::micros64(),
     929             :                                     enabled(),
     930             :                                     _auto_enabled,
     931             :                                     _configured_fences,
     932             :                                     get_enabled_fences(),
     933             :                                     disabled_fences,
     934             :                                     _curr_alt_d_m*-1);
     935             :     }
     936             : #endif
     937             :     
     938             :     // return immediately if disabled
     939     2201372 :     if ((!enabled() && !_auto_enabled && !(_configured_fences & AC_FENCE_TYPE_ALT_MIN)) || !_configured_fences) {
     940     2043060 :         return 0;
     941             :     }
     942             : 
     943             :     // take lock inside critical section
     944      158312 :     if (!_poly_loader.get_loaded_fence_semaphore().take_nonblocking()) {
     945           3 :         return 0;
     946             :     }
     947             : 
     948             :     // disable the (temporarily) disabled fences
     949      158309 :     enable(false, disabled_fences, false);
     950             : 
     951             :     // update the location at the time the check was made
     952      158309 :     _last_fence_check_loc_valid = AP::ahrs().get_location(_last_fence_check_loc);
     953             : 
     954             :     // maximum altitude fence check
     955      158309 :     if (!(disabled_fences & AC_FENCE_TYPE_ALT_MAX) && check_fence_alt_max()) {
     956         185 :         ret |= AC_FENCE_TYPE_ALT_MAX;
     957             :     }
     958             : 
     959             :     // minimum altitude fence check, do this before auto-disabling (e.g. because falling)
     960             :     // so that any action can be taken
     961      158309 :     if (!(disabled_fences & AC_FENCE_TYPE_ALT_MIN) && check_fence_alt_min()) {
     962          42 :         ret |= AC_FENCE_TYPE_ALT_MIN;
     963             :     }
     964             : 
     965             :     // auto enable floor unless auto enable on auto takeoff has been set (which means other behaviour is required)
     966      158309 :     if (!(disabled_fences & AC_FENCE_TYPE_ALT_MIN)) {
     967       94796 :         auto_enable_fence_floor();
     968             :     }
     969             : 
     970             :     // circle fence check
     971      158309 :     if (!(disabled_fences & AC_FENCE_TYPE_CIRCLE) && check_fence_circle()) {
     972           9 :         ret |= AC_FENCE_TYPE_CIRCLE;
     973             :     }
     974             : 
     975             :     // polygon fence check
     976      158309 :     if (!(disabled_fences & AC_FENCE_TYPE_POLYGON) && check_fence_polygon()) {
     977          40 :         ret |= AC_FENCE_TYPE_POLYGON;
     978             :     }
     979             : 
     980             :     // check if pilot is attempting to recover manually
     981             :     // this is done last so that _breached_fences is correct
     982      158309 :     if (_manual_recovery_start_ms != 0) {
     983             :         // we ignore any fence breaches during the manual recovery period which is about 10 seconds
     984        6965 :         if ((AP_HAL::millis() - _manual_recovery_start_ms) < AC_FENCE_MANUAL_RECOVERY_TIME_MIN) {
     985        6917 :             _poly_loader.get_loaded_fence_semaphore().give();
     986        6917 :             return 0;
     987             :         }
     988             :         // recovery period has passed so reset manual recovery time
     989             :         // and continue with fence breach checks
     990          48 :         _manual_recovery_start_ms = 0;
     991             :     }
     992             : 
     993      151392 :     _poly_loader.get_loaded_fence_semaphore().give();
     994             : 
     995             :     // return any new breaches that have occurred
     996      151392 :     return ret;
     997             : }
     998             : 
     999             : // returns true if the destination is within fence (used to reject waypoints outside the fence)
    1000        2138 : bool AC_Fence::check_location_within_fence(const Location& loc)
    1001             : {
    1002             :     // Altitude fence check - Fence Ceiling
    1003        2138 :     if ((get_enabled_fences() & AC_FENCE_TYPE_ALT_MAX)) {
    1004             :         float alt_m;
    1005           0 :         if (loc.get_alt_m(_alt_max_type, alt_m)) {
    1006           0 :             if (alt_m > _alt_max_m) {
    1007           0 :                 return false;
    1008             :             }
    1009             :         }
    1010             :     }
    1011             : 
    1012             :     // Altitude fence check - Fence Floor
    1013        2138 :     if ((get_enabled_fences() & AC_FENCE_TYPE_ALT_MIN)) {
    1014             :         float alt_m;
    1015           6 :         if (loc.get_alt_m(_alt_min_type, alt_m)) {
    1016           6 :             if (alt_m < _alt_min_m) {
    1017           0 :                 return false;
    1018             :             }
    1019             :         }
    1020             :     }
    1021             : 
    1022             :     // Circular fence check
    1023        2138 :     if ((get_enabled_fences() & AC_FENCE_TYPE_CIRCLE)) {
    1024           7 :         if (AP::ahrs().get_home().get_distance(loc) > _circle_radius_m) {
    1025           3 :             return false;
    1026             :         }
    1027             :     }
    1028             : 
    1029             :     // polygon fence check
    1030        2135 :     if ((get_enabled_fences() & AC_FENCE_TYPE_POLYGON)) {
    1031           1 :         if (_poly_loader.breached(loc)) {
    1032           0 :             return false;
    1033             :         }
    1034             :     }
    1035             : 
    1036        2135 :     return true;
    1037             : }
    1038             : 
    1039             : /// record_breach - update breach bitmask, time and count
    1040         131 : void AC_Fence::record_breach(uint8_t fence_type)
    1041             : {
    1042             :     // if we haven't already breached a limit, update the breach time
    1043         131 :     if (!_breached_fences) {
    1044         122 :         const uint32_t now = AP_HAL::millis();
    1045         122 :         _breach_time = now;
    1046             : 
    1047             :         // emit a message indicated we're newly-breached, but not too often
    1048         122 :         if (now - _last_breach_notify_sent_ms > 1000) {
    1049         120 :             _last_breach_notify_sent_ms = now;
    1050         120 :             GCS_SEND_MESSAGE(MSG_FENCE_STATUS);
    1051             :         }
    1052             :     }
    1053             : 
    1054             :     // update breach count
    1055         131 :     if (_breach_count < 65500) {
    1056         131 :         _breach_count++;
    1057             :     }
    1058             : 
    1059             :     // update bitmask
    1060         131 :     _breached_fences |= fence_type;
    1061         131 : }
    1062             : 
    1063             : /// record_margin_breach - update margin_breach bitmask, time and count
    1064       14529 : void AC_Fence::record_margin_breach(uint8_t fence_type)
    1065             : {
    1066             :     // if we haven't already breached a margin limit, update the breach time
    1067       14529 :     const uint32_t now = AP_HAL::millis();
    1068             : 
    1069       14529 :     bool notify_margin_breach = false;
    1070             : 
    1071       14529 :     if (option_enabled(OPTIONS::NOTIFY_MARGIN_BREACH)) {
    1072         540 :         if ((!is_zero(_notify_freq)
    1073         540 :             && AP_HAL::timeout_expired(_last_margin_breach_notify_sent_ms, now,
    1074         540 :                                        uint32_t(roundf(1000.0f / _notify_freq))))
    1075        1080 :             || !(_breached_fence_margins & fence_type)) {
    1076          20 :             notify_margin_breach = true;
    1077             :         }
    1078             :     }
    1079             : 
    1080       14529 :     if (!(_breached_fence_margins & fence_type)) {
    1081         171 :         _margin_breach_time = now;
    1082             :     }
    1083             : 
    1084             :     // update bitmask
    1085       14529 :     _breached_fence_margins |= fence_type;
    1086             : 
    1087             :     // emit a message indicated we're newly-breached, but not too often
    1088       14529 :     if (notify_margin_breach) {
    1089          20 :         _last_margin_breach_notify_sent_ms = now;
    1090             :         char msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    1091          20 :         ExpandingString e(msg, MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1);
    1092          20 :         AC_Fence::get_fence_names(_breached_fence_margins, e);
    1093          20 :         GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "%s in %.1fm", e.get_writeable_string(), fabsf(get_breach_distance(_breached_fence_margins)));
    1094          20 :     }
    1095       14529 : }
    1096             : 
    1097             : /// clear_breach - update breach bitmask
    1098     4523118 : void AC_Fence::clear_breach(uint8_t fence_type)
    1099             : {
    1100     4523118 :     _breached_fences &= ~fence_type;
    1101     4523118 : }
    1102             : 
    1103             : /// clear_margin_breach - update margin reach bitmask
    1104     4540843 : void AC_Fence::clear_margin_breach(uint8_t fence_type)
    1105             : {
    1106     4540843 :     if (_breached_fence_margins & fence_type) {
    1107         165 :         if (option_enabled(OPTIONS::NOTIFY_MARGIN_BREACH)) {
    1108           2 :             print_fence_message("cleared margin breach", fence_type);
    1109             :         }
    1110             :     }
    1111             : 
    1112     4540843 :     _breached_fence_margins &= ~fence_type;
    1113     4540843 : }
    1114             : 
    1115             : /// get_breach_distance - returns maximum distance in meters outside
    1116             : /// negative values mean distance in meters inside
    1117             : /// of the given fences.  fence_type is a bitmask here.
    1118          65 : float AC_Fence::get_breach_distance(uint8_t fence_type) const
    1119             : {
    1120          65 :     fence_type = fence_type & present();
    1121          65 :     float max_m = -FLT_MAX;
    1122             : 
    1123          65 :     if (fence_type & AC_FENCE_TYPE_ALT_MAX) {
    1124          13 :         max_m = MAX(_alt_max_breach_distance_m, max_m);
    1125             :     }
    1126          65 :     if (fence_type & AC_FENCE_TYPE_ALT_MIN) {
    1127          16 :         max_m = MAX(_alt_min_breach_distance_m, max_m);
    1128             :     }
    1129          65 :     if (fence_type & AC_FENCE_TYPE_CIRCLE) {
    1130          15 :         max_m = MAX(_circle_breach_distance_m, max_m);
    1131             :     }
    1132          65 :     if (fence_type & AC_FENCE_TYPE_POLYGON) {
    1133          33 :         max_m = MAX(_polygon_breach_distance_m, max_m);
    1134             :     }
    1135          65 :     return max_m;
    1136             : }
    1137             : 
    1138             : /// get_breach_direction - returns direction to the closest fence breach in NED frame.
    1139             : /// fence_check_pos is the absolute position when the check was made.
    1140             : ///  fence_type is a bitmask here.
    1141           0 : bool AC_Fence::get_breach_direction_NED(uint8_t fence_type, Vector3f& direction_to_closest, Location& fence_check_pos) const
    1142             : {
    1143           0 :     fence_type = fence_type & present();
    1144           0 :     if (_last_fence_check_loc_valid) {
    1145           0 :         fence_check_pos = _last_fence_check_loc;
    1146             :     }
    1147           0 :     float closest_m = FLT_MAX;
    1148             : 
    1149           0 :     if (fence_type & AC_FENCE_TYPE_ALT_MAX && fabsf(_alt_max_breach_distance_m) < closest_m) {
    1150           0 :         direction_to_closest = Vector3f{0, 0, _alt_max_breach_distance_m};
    1151           0 :         closest_m = fabsf(_alt_max_breach_distance_m);
    1152             :     }
    1153           0 :     if (fence_type & AC_FENCE_TYPE_ALT_MIN && fabsf(_alt_min_breach_distance_m) < closest_m) {
    1154           0 :         direction_to_closest = Vector3f{0, 0, -_alt_min_breach_distance_m};
    1155           0 :         closest_m = fabsf(_alt_min_breach_distance_m);
    1156             :     }
    1157           0 :     if (fence_type & AC_FENCE_TYPE_CIRCLE && fabsf(_circle_breach_distance_m) < closest_m) {
    1158           0 :         direction_to_closest = Vector3f{_circle_breach_direction.x, _circle_breach_direction.y, 0};
    1159           0 :         closest_m = fabsf(_circle_breach_distance_m);
    1160             :     }
    1161           0 :     if (fence_type & AC_FENCE_TYPE_POLYGON && fabsf(_polygon_breach_distance_m) < closest_m) {
    1162           0 :         direction_to_closest = Vector3f{_polygon_nearest_point.x, _polygon_nearest_point.y, 0};
    1163           0 :         closest_m = fabsf(_polygon_breach_distance_m);
    1164             :     }
    1165           0 :     return _last_fence_check_loc_valid;
    1166             : }
    1167             : 
    1168             : /// manual_recovery_start - caller indicates that pilot is re-taking manual control so fence should be disabled for 10 seconds
    1169             : ///     has no effect if no breaches have occurred
    1170        3709 : void AC_Fence::manual_recovery_start()
    1171             : {
    1172             :     // return immediate if we haven't breached a fence
    1173        3709 :     if (!_breached_fences) {
    1174        3624 :         return;
    1175             :     }
    1176             : 
    1177             :     // Not armed so return
    1178          85 :     if (!hal.util->get_soft_armed()) {
    1179           2 :         return;
    1180             :     }
    1181             : 
    1182             :     // record time pilot began manual recovery
    1183          83 :     _manual_recovery_start_ms = AP_HAL::millis();
    1184             : 
    1185          83 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Manual recovery started");
    1186             : }
    1187             : 
    1188             : // methods for mavlink SYS_STATUS message (send_sys_status)
    1189     3358005 : bool AC_Fence::sys_status_present() const
    1190             : {
    1191     3358005 :     return present();
    1192             : }
    1193             : 
    1194     1119335 : bool AC_Fence::sys_status_enabled() const
    1195             : {
    1196     1119335 :     if (!sys_status_present()) {
    1197      498842 :         return false;
    1198             :     }
    1199      620493 :     if (_action == Action::REPORT_ONLY) {
    1200        5887 :         return false;
    1201             :     }
    1202             :     // Fence is only enabled when the flag is enabled
    1203      614606 :     return enabled();
    1204             : }
    1205             : 
    1206     1119335 : bool AC_Fence::sys_status_failed() const
    1207             : {
    1208     1119335 :     if (!sys_status_present()) {
    1209             :         // not failed if not present; can fail if present but not enabled
    1210      498842 :         return false;
    1211             :     }
    1212      620493 :     if (get_breaches() != 0) {
    1213        7948 :         return true;
    1214             :     }
    1215      612545 :     return false;
    1216             : }
    1217             : 
    1218     3798185 : AC_PolyFence_loader &AC_Fence::polyfence()
    1219             : {
    1220     3798185 :     return _poly_loader;
    1221             : }
    1222     4975242 : const AC_PolyFence_loader &AC_Fence::polyfence() const
    1223             : {
    1224     4975242 :     return _poly_loader;
    1225             : }
    1226             : 
    1227             : 
    1228             : #else  // build type is not appropriate; provide a dummy implementation:
    1229             : const AP_Param::GroupInfo AC_Fence::var_info[] = { AP_GROUPEND };
    1230             : 
    1231          37 : AC_Fence::AC_Fence() {};
    1232             : 
    1233           0 : uint8_t AC_Fence::enable(bool value, uint8_t fence_types, bool update_auto_enable) { return 0; }
    1234             : 
    1235           0 : void AC_Fence::enable_floor() {}
    1236           0 : void AC_Fence::disable_floor() {}
    1237       17294 : void AC_Fence::update() {}
    1238             : 
    1239           0 : void AC_Fence::auto_enable_fence_after_takeoff() {}
    1240           0 : void AC_Fence::auto_enable_fence_on_arming() {}
    1241           0 : void AC_Fence::auto_disable_fence_on_disarming() {}
    1242             : 
    1243           0 : uint8_t AC_Fence::present() const { return 0; }
    1244             : 
    1245           0 : uint8_t AC_Fence::get_enabled_fences() const { return 0; }
    1246             : 
    1247           0 : bool AC_Fence::pre_arm_check(char *failure_msg, const uint8_t failure_msg_len) const  { return true; }
    1248           0 : bool AC_Fence::terrain_database_required() const { return false; }
    1249           0 : uint8_t AC_Fence::check(bool disable_auto_fences) { return 0; }
    1250           0 : bool AC_Fence::check_location_within_fence(const Location& loc) { return true; }
    1251           0 : float AC_Fence::get_breach_distance(uint8_t fence_type) const { return 0.0; }
    1252           0 : bool AC_Fence::get_breach_direction_NED(uint8_t fence_type, Vector3f& direction, Location& fence_check_pos) const { return false; }
    1253           0 : void AC_Fence::get_fence_names(uint8_t fences, ExpandingString& msg) { }
    1254           0 : void AC_Fence::print_fence_message(const char* msg, uint8_t fences) const {}
    1255             : 
    1256           0 : void AC_Fence::manual_recovery_start() {}
    1257             : 
    1258           0 : bool AC_Fence::sys_status_present() const { return false; }
    1259           0 : bool AC_Fence::sys_status_enabled() const { return false; }
    1260           0 : bool AC_Fence::sys_status_failed() const { return false; }
    1261             : 
    1262           0 : AC_PolyFence_loader &AC_Fence::polyfence()
    1263             : {
    1264           0 :     return _poly_loader;
    1265             : }
    1266           0 : const AC_PolyFence_loader &AC_Fence::polyfence() const
    1267             : {
    1268           0 :     return _poly_loader;
    1269             : }
    1270             : 
    1271             : #endif // #if AC_FENCE_DUMMY_METHODS_ENABLED
    1272             : 
    1273             : // singleton instance
    1274             : AC_Fence *AC_Fence::_singleton;
    1275             : 
    1276             : namespace AP
    1277             : {
    1278             : 
    1279    16388059 : AC_Fence *fence()
    1280             : {
    1281    16388059 :     return AC_Fence::get_singleton();
    1282             : }
    1283             : 
    1284             : }
    1285             : 
    1286             : #endif // AP_FENCE_ENABLED

Generated by: LCOV version 1.14