LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduPlane - fence.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 100 107 93.5 %
Date: 2026-09-09 11:02:08 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "Plane.h"
       2             : 
       3             : // Code to integrate AC_Fence library with main ArduPlane code
       4             : 
       5             : #if AP_FENCE_ENABLED
       6             : 
       7             : // async fence checking io callback at 1Khz
       8    79445325 : void Plane::fence_checks_async()
       9             : {
      10    79445325 :     const uint32_t now = AP_HAL::millis();
      11             : 
      12    79445325 :     if (!AP_HAL::timeout_expired(fence_breaches.last_check_ms, now, 333U)) { // 3Hz update rate
      13    79053987 :         return;
      14             :     }
      15             : 
      16      391338 :     if (fence_breaches.have_updates) {
      17      226522 :         return; // wait for the main loop to pick up the new breaches before checking again
      18             :     }
      19             : 
      20      164816 :     fence_breaches.last_check_ms = now;
      21      164816 :     orig_breaches = fence.get_breaches();
      22      164816 :     const bool armed = arming.is_armed();
      23             : 
      24      164816 :     uint16_t mission_id = plane.mission.get_current_nav_cmd().id;
      25      164816 :     bool landing_or_landed = plane.flight_stage == AP_FixedWing::FlightStage::LAND
      26      153961 :                          || !armed
      27             : #if HAL_QUADPLANE_ENABLED
      28      113489 :                          || control_mode->mode_number() == Mode::Number::QLAND
      29      110870 :                          || quadplane.in_vtol_land_descent()
      30             : #endif
      31      318777 :                          || (plane.is_land_command(mission_id) && plane.mission.state() == AP_Mission::MISSION_RUNNING);
      32             : 
      33             :     // check for new breaches; new_breaches is bitmask of fence types breached
      34      164816 :     fence_breaches.new_breaches = fence.check(landing_or_landed);
      35      164816 :     fence_breaches.have_updates = true; // new breach status latched so main loop will now pick it up
      36             : }
      37             : 
      38             : // fence_check - ask fence library to check for breaches and initiate the response
      39      167812 : void Plane::fence_check()
      40             : {
      41      167812 :     if (!fence_breaches.have_updates) {
      42        3625 :         return;
      43             :     }
      44             :     /*
      45             :       if we are either disarmed or we are currently not in breach and
      46             :       we are not flying then clear the state associated with the
      47             :       previous mode breach handling. This allows the fence state
      48             :       machine to reset at the end of a fence breach action such as an
      49             :       RTL and autoland
      50             :      */
      51      164187 :     const bool armed = arming.is_armed();
      52             : 
      53      164187 :     if (plane.previous_mode_reason == ModeReason::FENCE_BREACHED) {
      54        1400 :         if (!armed || ((fence_breaches.new_breaches == 0 && orig_breaches == 0) && !plane.is_flying())) {
      55           5 :             plane.previous_mode_reason = ModeReason::FENCE_REENABLE;
      56             :         }
      57             :     }
      58             : 
      59      164187 :     if (!fence.enabled()) {
      60             :         // Switch back to the chosen control mode if still in
      61             :         // GUIDED to the return point
      62      157972 :         switch(fence.get_action()) {
      63      157600 :             case AC_Fence::Action::GUIDED:
      64             :             case AC_Fence::Action::GUIDED_THROTTLE_PASS:
      65             :             case AC_Fence::Action::RTL_AND_LAND:
      66             :             case AC_Fence::Action::AUTOLAND_OR_RTL:
      67      157619 :                 if (plane.control_mode_reason == ModeReason::FENCE_BREACHED &&
      68          19 :                     control_mode->is_guided_mode()) {
      69           6 :                     set_mode(*previous_mode, ModeReason::FENCE_RETURN_PREVIOUS_MODE);
      70             :                 }
      71      157600 :                 break;
      72         372 :             default:
      73             :                 // No returning to a previous mode, unless our action allows it
      74         372 :                 break;
      75             :         }
      76             :         /*
      77             :           clear mode reasons if they are FENCE_BREACHED to allow AUX
      78             :           switch fence disable/enable to re-enable the fence after a breach
      79             :          */
      80      157972 :         if (plane.previous_mode_reason == ModeReason::FENCE_BREACHED) {
      81          10 :             plane.previous_mode_reason = ModeReason::FENCE_REENABLE;
      82             :         }
      83      157972 :         if (plane.control_mode_reason == ModeReason::FENCE_BREACHED) {
      84          13 :             plane.control_mode_reason = ModeReason::FENCE_REENABLE;
      85             :         }
      86      157972 :         goto fence_check_complete;
      87             :     }
      88             : 
      89             :     // we still don't do anything when disarmed, but we do check for fence breaches.
      90             :     // fence pre-arm check actually checks if any fence has been breached
      91             :     // that's not ever going to be true if we don't call check on AP_Fence while disarmed
      92        6215 :     if (!armed) {
      93         527 :         goto fence_check_complete;
      94             :     }
      95             : 
      96             :     // Never trigger a fence breach in the final stage of landing
      97        5688 :     if (landing.is_expecting_impact()) {
      98         488 :         goto fence_check_complete;
      99             :     }
     100             : 
     101        5200 :     if (in_fence_recovery()) {
     102             :         // we have already triggered, don't trigger again until the
     103             :         // user disables/re-enables using the fence channel switch
     104        1149 :         goto fence_check_complete;
     105             :     }
     106             : 
     107        4051 :     if (fence_breaches.new_breaches) {
     108          40 :         fence.print_fence_message("breached", fence_breaches.new_breaches);
     109             : 
     110             :         // if the user wants some kind of response and motors are armed
     111          40 :         const auto fence_act = fence.get_action();
     112          40 :         switch (fence_act) {
     113           3 :         case AC_Fence::Action::REPORT_ONLY:
     114          40 :             break;
     115             : 
     116           0 :         case AC_Fence::Action::ALWAYS_LAND:
     117             :         case AC_Fence::Action::SMART_RTL:
     118             :         case AC_Fence::Action::SMART_RTL_OR_LAND:
     119             :         case AC_Fence::Action::BRAKE:
     120             :             // invalid enumeration value for Plane
     121           0 :             break;
     122             : 
     123          30 :         case AC_Fence::Action::AUTOLAND_OR_RTL:
     124             :         case AC_Fence::Action::RTL_AND_LAND:
     125           2 :             if (control_mode == &mode_auto &&
     126          32 :                 mission.get_in_landing_sequence_flag() &&
     127           0 :                 (g.rtl_autoland == RtlAutoland::RTL_THEN_DO_LAND_START ||
     128           0 :                     g.rtl_autoland == RtlAutoland::RTL_IMMEDIATE_DO_LAND_START)) {
     129             :                 // already landing
     130           0 :                 goto fence_check_complete;
     131             :             }
     132             : #if MODE_AUTOLAND_ENABLED
     133          30 :             if (control_mode == &mode_autoland) {
     134             :                 // Already landing
     135           0 :                 return;
     136             :             }
     137          30 :             if ((fence_act == AC_Fence::Action::AUTOLAND_OR_RTL) && set_mode(mode_autoland, ModeReason::FENCE_BREACHED)) {
     138           1 :                 break;
     139             :             }
     140             : #endif
     141          29 :             set_mode(mode_rtl, ModeReason::FENCE_BREACHED);
     142          29 :             break;
     143             : 
     144           7 :         case AC_Fence::Action::GUIDED:
     145             :         case AC_Fence::Action::GUIDED_THROTTLE_PASS:
     146           7 :             set_mode(mode_guided, ModeReason::FENCE_BREACHED);
     147             : 
     148           7 :             Location loc;
     149           7 :             if (fence.get_return_rally() != 0) {
     150           1 :                 loc = calc_best_rally_or_home_location(current_loc, get_RTL_altitude_cm());
     151             :             } else {
     152             :                 //return to fence return point, not a rally point
     153           6 :                 if (fence.get_return_altitude() > 0) {
     154             :                     // fly to the return point using _retalt
     155           1 :                     loc.set_alt_cm(home.alt + 100.0 * fence.get_return_altitude(),
     156             :                                    Location::AltFrame::ABSOLUTE);
     157           5 :                 } else if (fence.get_safe_alt_min_m() >= fence.get_safe_alt_max_m()) {
     158             :                     // invalid min/max, use RTL_altitude
     159           0 :                     loc.set_alt_cm(home.alt + g.RTL_altitude*100,
     160             :                                    Location::AltFrame::ABSOLUTE);
     161             :                 } else {
     162             :                     // fly to the return point, with an altitude half way between
     163             :                     // min and max
     164           5 :                     loc.set_alt_cm(home.alt + 100.0f * (fence.get_safe_alt_min_m() + fence.get_safe_alt_max_m()) / 2,
     165             :                                    Location::AltFrame::ABSOLUTE);
     166             :                 }
     167             : 
     168           6 :                 Vector2l return_point;
     169           6 :                 if(fence.polyfence().get_return_point(return_point)) {
     170           3 :                     loc.lat = return_point[0];
     171           3 :                     loc.lng = return_point[1];
     172             :                 } else {
     173             :                     // When no fence return point is found (ie. no inclusion fence uploaded, but exclusion is)
     174             :                     // we fail to obtain a valid fence return point. In this case, home is considered a safe
     175             :                     // return point.
     176           3 :                     loc.lat = home.lat;
     177           3 :                     loc.lng = home.lng;
     178             :                 }
     179             :             }
     180             : 
     181           7 :             setup_terrain_target_alt(loc);
     182           7 :             set_guided_WP(loc);
     183             : 
     184           7 :             if (fence.get_action() == AC_Fence::Action::GUIDED_THROTTLE_PASS) {
     185           1 :                 guided_throttle_passthru = true;
     186             :             }
     187           7 :             break;
     188             :         }
     189             : 
     190          40 :         LOGGER_WRITE_ERROR(LogErrorSubsystem::FAILSAFE_FENCE, LogErrorCode(fence_breaches.new_breaches));
     191        4011 :     } else if (orig_breaches && fence.get_breaches() == 0) {
     192           3 :         GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Fence breach cleared");
     193             :         // record clearing of breach
     194           3 :         LOGGER_WRITE_ERROR(LogErrorSubsystem::FAILSAFE_FENCE, LogErrorCode::ERROR_RESOLVED);
     195             :     }
     196             : 
     197        4008 : fence_check_complete:
     198      164187 :     fence_breaches.have_updates = false;
     199             : }
     200             : 
     201    15396868 : bool Plane::fence_stickmixing(void) const
     202             : {
     203    15712574 :     if (fence.enabled() &&
     204    15712574 :         fence.get_breaches() &&
     205       53102 :         in_fence_recovery())
     206             :     {
     207             :         // don't mix in user input
     208       38590 :         return false;
     209             :     }
     210             :     // normal mixing rules
     211    15358278 :     return true;
     212             : }
     213             : 
     214       58366 : bool Plane::in_fence_recovery() const
     215             : {
     216       58366 :     if (control_mode == &mode_auto && !mission.get_in_landing_sequence_flag()) {
     217             :         // the user may have changed target WP to be outside the
     218             :         // landing sequence
     219        3683 :         return false;
     220             :     }
     221       54683 :     const bool current_mode_breach = plane.control_mode_reason == ModeReason::FENCE_BREACHED;
     222       54683 :     const bool previous_mode_breach = plane.previous_mode_reason ==  ModeReason::FENCE_BREACHED;
     223      164049 :     const bool previous_mode_complete = (plane.control_mode_reason == ModeReason::RTL_COMPLETE_SWITCHING_TO_VTOL_LAND_RTL) ||
     224       54683 :                                         (plane.control_mode_reason == ModeReason::RTL_COMPLETE_SWITCHING_TO_FIXEDWING_AUTOLAND) ||
     225      162306 :                                         (plane.control_mode_reason == ModeReason::QRTL_INSTEAD_OF_RTL) ||
     226       52940 :                                         (plane.control_mode_reason == ModeReason::QLAND_INSTEAD_OF_RTL);
     227             : 
     228       54683 :     return current_mode_breach || (previous_mode_breach && previous_mode_complete);
     229             : }
     230             : 
     231             : #endif

Generated by: LCOV version 1.14