2015-08-11 03:28:41 -03:00
# include "AC_Fence.h"
2019-02-14 02:40:09 -04:00
2022-07-19 08:32:28 -03:00
# if AP_FENCE_ENABLED
2022-03-04 12:42:09 -04:00
2022-03-04 16:25:54 -04:00
# include <AP_Vehicle/AP_Vehicle_Type.h>
# ifndef AC_FENCE_DUMMY_METHODS_ENABLED
2022-07-25 13:51:32 -03:00
# 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)))
2022-03-04 16:25:54 -04:00
# endif
# if !AC_FENCE_DUMMY_METHODS_ENABLED
2019-02-14 02:40:09 -04:00
# include <AP_AHRS/AP_AHRS.h>
# include <AP_HAL/AP_HAL.h>
2020-06-01 23:27:28 -03:00
# include <AP_Logger/AP_Logger.h>
2022-08-14 23:17:34 -03:00
# include <GCS_MAVLink/GCS.h>
2013-04-26 06:47:07 -03:00
extern const AP_HAL : : HAL & hal ;
2020-03-26 21:51:14 -03:00
# if APM_BUILD_TYPE(APM_BUILD_Rover)
2018-10-26 00:29:02 -03:00
# define AC_FENCE_TYPE_DEFAULT AC_FENCE_TYPE_CIRCLE | AC_FENCE_TYPE_POLYGON
2021-03-03 07:49:47 -04:00
# elif APM_BUILD_TYPE(APM_BUILD_ArduPlane)
# define AC_FENCE_TYPE_DEFAULT AC_FENCE_TYPE_POLYGON
2018-10-26 00:29:02 -03:00
# else
# define AC_FENCE_TYPE_DEFAULT AC_FENCE_TYPE_ALT_MAX | AC_FENCE_TYPE_CIRCLE | AC_FENCE_TYPE_POLYGON
# endif
2022-08-06 17:43:38 -03:00
// default boundaries
# define AC_FENCE_ALT_MAX_DEFAULT 100.0f // default max altitude is 100m
# define AC_FENCE_ALT_MIN_DEFAULT -10.0f // default maximum depth in meters
# define AC_FENCE_CIRCLE_RADIUS_DEFAULT 300.0f // default circular fence radius is 300m
# define AC_FENCE_ALT_MAX_BACKUP_DISTANCE 20.0f // after fence is broken we recreate the fence 20m further up
# define AC_FENCE_ALT_MIN_BACKUP_DISTANCE 20.0f // after fence is broken we recreate the fence 20m further down
# define AC_FENCE_MARGIN_DEFAULT 2.0f // default distance in meters that autopilot's should maintain from the fence to avoid a breach
# 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
# if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
# define AC_FENCE_CIRCLE_RADIUS_BACKUP_DISTANCE 100.0 // after fence is broken we recreate the fence 50m further out
# else
# define AC_FENCE_CIRCLE_RADIUS_BACKUP_DISTANCE 20.0 // after fence is broken we recreate the fence 20m further out
# endif
2015-10-25 14:03:46 -03:00
const AP_Param : : GroupInfo AC_Fence : : var_info [ ] = {
2013-04-26 06:47:07 -03:00
// @Param: ENABLE
// @DisplayName: Fence enable/disable
// @Description: Allows you to enable (1) or disable (0) the fence functionality
// @Values: 0:Disabled,1:Enabled
// @User: Standard
2021-01-06 08:13:57 -04:00
AP_GROUPINFO ( " ENABLE " , 0 , AC_Fence , _enabled , 0 ) ,
2013-04-26 06:47:07 -03:00
// @Param: TYPE
// @DisplayName: Fence Type
// @Description: Enabled fence types held as bitmask
2023-07-22 03:40:57 -03:00
// @Bitmask{Rover}: 1:Circle Centered on Home,2:Inclusion/Exclusion Circles+Polygons
// @Bitmask{Copter, Plane, Sub}: 0:Max altitude,1:Circle Centered on Home,2:Inclusion/Exclusion Circles+Polygons,3:Min altitude
2013-04-26 06:47:07 -03:00
// @User: Standard
2018-10-26 00:29:02 -03:00
AP_GROUPINFO ( " TYPE " , 1 , AC_Fence , _enabled_fences , AC_FENCE_TYPE_DEFAULT ) ,
2013-04-26 06:47:07 -03:00
// @Param: ACTION
2013-11-26 09:18:05 -04:00
// @DisplayName: Fence Action
// @Description: What action should be taken when fence is breached
2021-01-30 22:34:40 -04:00
// @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
2021-04-26 00:23:33 -03:00
// @Values{Rover}: 0:Report Only,1:RTL or Hold,2:Hold,3:SmartRTL or RTL or Hold,4:SmartRTL or Hold
2020-09-10 03:28:09 -03:00
// @Values{Plane}: 0:Report Only,1:RTL,6:Guided,7:GuidedThrottlePass
2019-01-24 06:00:42 -04:00
// @Values: 0:Report Only,1:RTL or Land
2013-04-26 06:47:07 -03:00
// @User: Standard
AP_GROUPINFO ( " ACTION " , 2 , AC_Fence , _action , AC_FENCE_ACTION_RTL_AND_LAND ) ,
2020-09-10 03:28:09 -03:00
// @Param{Copter, Plane, Sub}: ALT_MAX
2013-04-26 06:47:07 -03:00
// @DisplayName: Fence Maximum Altitude
// @Description: Maximum altitude allowed before geofence triggers
2017-05-02 10:38:01 -03:00
// @Units: m
2013-05-01 05:05:04 -03:00
// @Range: 10 1000
// @Increment: 1
2013-04-26 06:47:07 -03:00
// @User: Standard
2020-09-10 03:28:09 -03:00
AP_GROUPINFO_FRAME ( " ALT_MAX " , 3 , AC_Fence , _alt_max , 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 ) ,
2013-04-26 06:47:07 -03:00
// @Param: RADIUS
// @DisplayName: Circular Fence Radius
2013-05-01 05:05:04 -03:00
// @Description: Circle fence radius which when breached will cause an RTL
2017-05-02 10:38:01 -03:00
// @Units: m
2013-07-26 22:28:33 -03:00
// @Range: 30 10000
2013-04-26 06:47:07 -03:00
// @User: Standard
2013-05-01 05:05:04 -03:00
AP_GROUPINFO ( " RADIUS " , 4 , AC_Fence , _circle_radius , AC_FENCE_CIRCLE_RADIUS_DEFAULT ) ,
2013-08-15 04:05:38 -03:00
// @Param: MARGIN
// @DisplayName: Fence Margin
// @Description: Distance that autopilot's should maintain from the fence to avoid a breach
2017-05-02 10:38:01 -03:00
// @Units: m
2013-08-15 04:05:38 -03:00
// @Range: 1 10
// @User: Standard
AP_GROUPINFO ( " MARGIN " , 5 , AC_Fence , _margin , AC_FENCE_MARGIN_DEFAULT ) ,
2016-06-23 03:24:48 -03:00
// @Param: TOTAL
// @DisplayName: Fence polygon point total
// @Description: Number of polygon points saved in eeprom (do not update manually)
// @Range: 1 20
// @User: Standard
AP_GROUPINFO ( " TOTAL " , 6 , AC_Fence , _total , 0 ) ,
2020-09-10 03:28:09 -03:00
// @Param{Copter, Plane, Sub}: ALT_MIN
2017-02-03 00:18:25 -04:00
// @DisplayName: Fence Minimum Altitude
// @Description: Minimum altitude allowed before geofence triggers
2017-05-02 10:38:01 -03:00
// @Units: m
2017-02-03 00:18:25 -04:00
// @Range: -100 100
// @Increment: 1
// @User: Standard
2017-07-17 05:39:26 -03:00
AP_GROUPINFO_FRAME ( " ALT_MIN " , 7 , AC_Fence , _alt_min , 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 ) ,
2020-09-10 03:28:09 -03:00
// @Param{Plane}: RET_RALLY
// @DisplayName: Fence Return to Rally
2021-01-06 08:15:59 -04:00
// @Description: Should the vehicle return to fence return point or rally point
2020-09-10 03:28:09 -03:00
// @Values: 0:Fence Return Point,1:Nearest Rally Point
// @Range: 0 1
// @Increment: 1
// @User: Standard
AP_GROUPINFO_FRAME ( " RET_RALLY " , 8 , AC_Fence , _ret_rally , 0 , AP_PARAM_FRAME_PLANE ) ,
// @Param{Plane}: RET_ALT
// @DisplayName: Fence Return Altitude
// @Description: Altitude the vehicle will transit to when a fence breach occurs
// @Units: m
// @Range: 0 32767
// @Increment: 1
// @User: Standard
AP_GROUPINFO_FRAME ( " RET_ALT " , 9 , AC_Fence , _ret_altitude , 0.0f , AP_PARAM_FRAME_PLANE ) ,
2022-04-27 05:06:25 -03:00
// @Param{Plane}: AUTOENABLE
2020-09-10 03:28:09 -03:00
// @DisplayName: Fence Auto-Enable
// @Description: Auto-enable of fence
2022-04-27 05:06:25 -03:00
// @Values: 0:AutoEnableOff,1:AutoEnableOnTakeoff,2:AutoEnableDisableFloorOnLanding,3:AutoEnableOnlyWhenArmed
2020-09-10 03:28:09 -03:00
// @Range: 0 3
// @Increment: 1
// @User: Standard
2021-03-03 07:49:47 -04:00
AP_GROUPINFO_FRAME ( " AUTOENABLE " , 10 , AC_Fence , _auto_enabled , static_cast < uint8_t > ( AutoEnable : : ALWAYS_DISABLED ) , AP_PARAM_FRAME_PLANE ) ,
2017-02-03 00:18:25 -04:00
2022-08-12 16:32:28 -03:00
// @Param{Plane}: OPTIONS
// @DisplayName: Fence options
2023-07-22 19:03:08 -03:00
// @Description: 0: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.
// @Bitmask: 0:Disable mode change following fence action until fence breach is cleared, 1:Allow union of inclusion areas
2022-08-12 16:32:28 -03:00
// @User: Standard
AP_GROUPINFO_FRAME ( " OPTIONS " , 11 , AC_Fence , _options , static_cast < uint16_t > ( OPTIONS : : DISABLE_MODE_CHANGE ) , AP_PARAM_FRAME_PLANE ) ,
2013-04-26 06:47:07 -03:00
AP_GROUPEND
} ;
/// Default constructor.
2019-01-30 20:37:22 -04:00
AC_Fence : : AC_Fence ( )
2013-04-26 06:47:07 -03:00
{
2019-01-30 21:54:13 -04:00
# if CONFIG_HAL_BOARD == HAL_BOARD_SITL
if ( _singleton ! = nullptr ) {
AP_HAL : : panic ( " Fence must be singleton " ) ;
}
# endif
_singleton = this ;
2013-04-26 06:47:07 -03:00
AP_Param : : setup_object_defaults ( this , var_info ) ;
}
2019-08-28 04:22:16 -03:00
/// enable the Fence code generally; a master switch for all fences
2017-07-20 13:42:28 -03:00
void AC_Fence : : enable ( bool value )
{
2020-06-01 23:27:28 -03:00
if ( _enabled & & ! value ) {
AP : : logger ( ) . Write_Event ( LogEvent : : FENCE_DISABLE ) ;
} else if ( ! _enabled & & value ) {
AP : : logger ( ) . Write_Event ( LogEvent : : FENCE_ENABLE ) ;
}
2022-07-05 00:08:55 -03:00
_enabled . set ( value ) ;
2017-07-20 13:42:28 -03:00
if ( ! value ) {
2020-09-10 03:28:09 -03:00
clear_breach ( AC_FENCE_TYPE_ALT_MIN | AC_FENCE_TYPE_ALT_MAX | AC_FENCE_TYPE_CIRCLE | AC_FENCE_TYPE_POLYGON ) ;
2021-01-10 23:08:51 -04:00
disable_floor ( ) ;
2020-09-10 03:28:09 -03:00
} else {
enable_floor ( ) ;
}
}
/// enable/disable fence floor only
void AC_Fence : : enable_floor ( )
{
if ( ! _floor_enabled ) {
// Floor is currently disabled, enable it
AP : : logger ( ) . Write_Event ( LogEvent : : FENCE_FLOOR_ENABLE ) ;
}
_floor_enabled = true ;
}
void AC_Fence : : disable_floor ( )
{
if ( _floor_enabled ) {
// Floor is currently enabled, disable it
AP : : logger ( ) . Write_Event ( LogEvent : : FENCE_FLOOR_DISABLE ) ;
2017-07-20 13:42:28 -03:00
}
2021-01-06 08:15:59 -04:00
_floor_enabled = false ;
2020-09-10 03:28:09 -03:00
clear_breach ( AC_FENCE_TYPE_ALT_MIN ) ;
}
2021-01-13 19:35:48 -04:00
/*
called when an auto - takeoff is complete
*/
void AC_Fence : : auto_enable_fence_after_takeoff ( void )
{
switch ( auto_enabled ( ) ) {
case AC_Fence : : AutoEnable : : ALWAYS_ENABLED :
case AC_Fence : : AutoEnable : : ENABLE_DISABLE_FLOOR_ONLY :
enable ( true ) ;
2021-03-03 08:25:09 -04:00
gcs ( ) . send_text ( MAV_SEVERITY_NOTICE , " Fence enabled (auto enabled) " ) ;
2021-01-13 19:35:48 -04:00
break ;
default :
// fence does not auto-enable in other takeoff conditions
break ;
}
}
/*
called when performing an auto landing
*/
void AC_Fence : : auto_disable_fence_for_landing ( void )
{
switch ( auto_enabled ( ) ) {
case AC_Fence : : AutoEnable : : ALWAYS_ENABLED :
enable ( false ) ;
gcs ( ) . send_text ( MAV_SEVERITY_NOTICE , " Fence disabled (auto disable) " ) ;
break ;
case AC_Fence : : AutoEnable : : ENABLE_DISABLE_FLOOR_ONLY :
disable_floor ( ) ;
gcs ( ) . send_text ( MAV_SEVERITY_NOTICE , " Fence floor disabled (auto disable) " ) ;
break ;
default :
// fence does not auto-disable in other landing conditions
break ;
}
}
2020-09-10 03:28:09 -03:00
bool AC_Fence : : present ( ) const
{
2021-01-06 08:15:59 -04:00
const auto enabled_fences = _enabled_fences . get ( ) ;
2020-12-18 05:08:56 -04:00
// A fence is present if any of the conditions are true.
// * tin can (circle) is enabled
// * min or max alt is enabled
// * polygon fences are enabled and any fence has been uploaded
2021-01-06 08:15:59 -04:00
if ( enabled_fences & AC_FENCE_TYPE_CIRCLE | |
enabled_fences & AC_FENCE_TYPE_ALT_MIN | |
enabled_fences & AC_FENCE_TYPE_ALT_MAX | |
( ( enabled_fences & AC_FENCE_TYPE_POLYGON ) & & _poly_loader . total_fence_count ( ) > 0 ) ) {
2020-12-18 05:08:56 -04:00
return true ;
2020-09-10 03:28:09 -03:00
}
2020-12-18 05:08:56 -04:00
return false ;
2017-07-20 13:42:28 -03:00
}
2013-04-26 06:47:07 -03:00
/// get_enabled_fences - returns bitmask of enabled fences
2013-05-01 05:05:04 -03:00
uint8_t AC_Fence : : get_enabled_fences ( ) const
2013-04-26 06:47:07 -03:00
{
2020-09-10 03:28:09 -03:00
if ( ! _enabled & & ! _auto_enabled ) {
2017-12-15 06:06:56 -04:00
return 0 ;
2013-04-26 06:47:07 -03:00
}
2017-12-15 06:06:56 -04:00
return _enabled_fences ;
2013-04-26 06:47:07 -03:00
}
2017-12-13 22:54:03 -04:00
// additional checks for the polygon fence:
bool AC_Fence : : pre_arm_check_polygon ( const char * & fail_msg ) const
{
if ( ! ( _enabled_fences & AC_FENCE_TYPE_POLYGON ) ) {
// not enabled; all good
return true ;
}
2019-08-28 04:22:16 -03:00
if ( ! _poly_loader . loaded ( ) ) {
fail_msg = " Fences invalid " ;
2017-12-13 22:54:03 -04:00
return false ;
}
2020-07-21 14:00:57 -03:00
if ( ! _poly_loader . check_inclusion_circle_margin ( _margin ) ) {
fail_msg = " Margin is less than inclusion circle radius " ;
return false ;
}
2017-12-13 22:54:03 -04:00
return true ;
}
2017-12-26 22:53:39 -04:00
// additional checks for the circle fence:
bool AC_Fence : : pre_arm_check_circle ( const char * & fail_msg ) const
{
if ( _circle_radius < 0 ) {
fail_msg = " Invalid FENCE_RADIUS value " ;
return false ;
}
2020-07-21 14:00:57 -03:00
if ( _circle_radius < _margin ) {
fail_msg = " FENCE_MARGIN is less than FENCE_RADIUS " ;
return false ;
}
2017-12-26 22:53:39 -04:00
return true ;
}
// additional checks for the alt fence:
bool AC_Fence : : pre_arm_check_alt ( const char * & fail_msg ) const
{
if ( _alt_max < 0.0f ) {
fail_msg = " Invalid FENCE_ALT_MAX value " ;
return false ;
}
2020-09-10 03:28:09 -03:00
if ( _alt_min < - 100.0f ) {
fail_msg = " Invalid FENCE_ALT_MIN value " ;
return false ;
}
2017-12-26 22:53:39 -04:00
return true ;
}
2017-12-13 22:54:03 -04:00
2013-04-26 06:47:07 -03:00
/// pre_arm_check - returns true if all pre-takeoff checks have completed successfully
2017-03-24 01:38:19 -03:00
bool AC_Fence : : pre_arm_check ( const char * & fail_msg ) const
2013-04-26 06:47:07 -03:00
{
2017-03-24 01:38:19 -03:00
fail_msg = nullptr ;
2021-05-18 12:20:49 -03:00
// if fences are enabled but none selected fail pre-arm check
if ( enabled ( ) & & ! present ( ) ) {
fail_msg = " Fences enabled, but none selected " ;
return false ;
}
2013-04-26 06:47:07 -03:00
// if not enabled or not fence set-up always return true
2020-09-10 03:28:09 -03:00
if ( ( ! _enabled & & ! _auto_enabled ) | | ! _enabled_fences ) {
2013-04-26 06:47:07 -03:00
return true ;
}
2017-12-07 19:25:52 -04:00
// if we have horizontal limits enabled, check we can get a
2019-01-30 20:37:22 -04:00
// relative position from the AHRS
2017-12-07 19:25:52 -04:00
if ( ( _enabled_fences & AC_FENCE_TYPE_CIRCLE ) | |
( _enabled_fences & AC_FENCE_TYPE_POLYGON ) ) {
Vector2f position ;
2019-03-05 23:04:58 -04:00
if ( ! AP : : ahrs ( ) . get_relative_position_NE_home ( position ) ) {
2020-07-03 20:29:45 -03:00
fail_msg = " Fence requires position " ;
2017-12-07 19:25:52 -04:00
return false ;
}
2013-04-26 06:47:07 -03:00
}
2017-12-13 22:54:03 -04:00
if ( ! pre_arm_check_polygon ( fail_msg ) ) {
return false ;
}
2017-12-26 22:53:39 -04:00
if ( ! pre_arm_check_circle ( fail_msg ) ) {
return false ;
}
if ( ! pre_arm_check_alt ( fail_msg ) ) {
return false ;
}
2019-08-28 04:22:16 -03:00
// check no limits are currently breached
if ( _breached_fences ) {
fail_msg = " vehicle outside fence " ;
return false ;
}
2017-08-24 06:48:17 -03:00
// validate FENCE_MARGIN parameter range
if ( _margin < 0.0f ) {
fail_msg = " Invalid FENCE_MARGIN value " ;
return false ;
}
if ( _alt_max < _alt_min ) {
fail_msg = " FENCE_ALT_MAX < FENCE_ALT_MIN " ;
return false ;
}
if ( _alt_max - _alt_min < = 2.0f * _margin ) {
fail_msg = " FENCE_MARGIN too big " ;
return false ;
}
2013-04-26 06:47:07 -03:00
// if we got this far everything must be ok
return true ;
}
2019-08-28 04:22:16 -03:00
/// returns true if we have freshly breached the maximum altitude
/// fence; also may set up a fallback fence which, if breached, will
/// cause the altitude fence to be freshly breached
2017-12-15 05:35:22 -04:00
bool AC_Fence : : check_fence_alt_max ( )
2017-12-07 20:53:18 -04:00
{
// altitude fence check
if ( ! ( _enabled_fences & AC_FENCE_TYPE_ALT_MAX ) ) {
// not enabled; no breach
return false ;
}
2019-01-30 20:37:22 -04:00
AP : : ahrs ( ) . get_relative_position_D_home ( _curr_alt ) ;
2017-12-15 05:35:22 -04:00
_curr_alt = - _curr_alt ; // translate Down to Up
2017-12-07 20:53:18 -04:00
// check if we are over the altitude fence
2020-09-10 03:28:09 -03:00
if ( _curr_alt > = _alt_max ) {
2017-12-07 20:53:18 -04:00
// record distance above breach
2017-12-15 05:35:22 -04:00
_alt_max_breach_distance = _curr_alt - _alt_max ;
2017-12-07 20:53:18 -04:00
// check for a new breach or a breach of the backup fence
if ( ! ( _breached_fences & AC_FENCE_TYPE_ALT_MAX ) | |
2017-12-15 05:35:22 -04:00
( ! is_zero ( _alt_max_backup ) & & _curr_alt > = _alt_max_backup ) ) {
2017-12-07 20:53:18 -04:00
// new breach
record_breach ( AC_FENCE_TYPE_ALT_MAX ) ;
// create a backup fence 20m higher up
2017-12-15 05:35:22 -04:00
_alt_max_backup = _curr_alt + AC_FENCE_ALT_MAX_BACKUP_DISTANCE ;
2021-01-06 08:15:59 -04:00
// new breach
2017-12-07 20:53:18 -04:00
return true ;
}
2021-01-06 08:15:59 -04:00
// old breach
2017-12-07 20:53:18 -04:00
return false ;
}
// not breached
2021-01-06 08:15:59 -04:00
// clear max alt breach if present
2017-12-07 20:53:18 -04:00
if ( ( _breached_fences & AC_FENCE_TYPE_ALT_MAX ) ! = 0 ) {
clear_breach ( AC_FENCE_TYPE_ALT_MAX ) ;
_alt_max_backup = 0.0f ;
_alt_max_breach_distance = 0.0f ;
}
return false ;
}
2020-09-10 03:28:09 -03:00
/// returns true if we have freshly breached the minimum altitude
/// fence; also may set up a fallback fence which, if breached, will
/// cause the altitude fence to be freshly breached
bool AC_Fence : : check_fence_alt_min ( )
{
// altitude fence check
if ( ! ( _enabled_fences & AC_FENCE_TYPE_ALT_MIN ) ) {
2021-01-06 08:15:59 -04:00
// not enabled; no breach
2020-09-10 03:28:09 -03:00
return false ;
}
AP : : ahrs ( ) . get_relative_position_D_home ( _curr_alt ) ;
_curr_alt = - _curr_alt ; // translate Down to Up
// check if we are under the altitude fence
2017-08-24 06:48:17 -03:00
if ( _curr_alt < = _alt_min ) {
2020-09-10 03:28:09 -03:00
// record distance below breach
_alt_min_breach_distance = _alt_min - _curr_alt ;
// check for a new breach or a breach of the backup fence
if ( ! ( _breached_fences & AC_FENCE_TYPE_ALT_MIN ) | |
2017-08-24 06:48:17 -03:00
( ! is_zero ( _alt_min_backup ) & & _curr_alt < = _alt_min_backup ) ) {
2020-09-10 03:28:09 -03:00
// new breach
record_breach ( AC_FENCE_TYPE_ALT_MIN ) ;
// create a backup fence 20m lower down
_alt_min_backup = _curr_alt - AC_FENCE_ALT_MIN_BACKUP_DISTANCE ;
// new breach
return true ;
}
// old breach
return false ;
}
// not breached
2021-01-06 08:15:59 -04:00
// clear min alt breach if present
2020-09-10 03:28:09 -03:00
if ( ( _breached_fences & AC_FENCE_TYPE_ALT_MIN ) ! = 0 ) {
clear_breach ( AC_FENCE_TYPE_ALT_MIN ) ;
_alt_min_backup = 0.0f ;
_alt_min_breach_distance = 0.0f ;
}
return false ;
}
2019-08-28 04:22:16 -03:00
// check_fence_polygon - returns true if the poly fence is freshly
// breached. That includes being inside exclusion zones and outside
// inclusions zones
2017-12-07 20:10:16 -04:00
bool AC_Fence : : check_fence_polygon ( )
2019-03-06 00:45:00 -04:00
{
const bool was_breached = _breached_fences & AC_FENCE_TYPE_POLYGON ;
2019-08-28 04:22:16 -03:00
const bool breached = ( ( _enabled_fences & AC_FENCE_TYPE_POLYGON ) & &
_poly_loader . breached ( ) ) ;
2019-03-06 00:45:00 -04:00
if ( breached ) {
if ( ! was_breached ) {
record_breach ( AC_FENCE_TYPE_POLYGON ) ;
return true ;
}
return false ;
}
if ( was_breached ) {
clear_breach ( AC_FENCE_TYPE_POLYGON ) ;
}
return false ;
}
2019-08-28 04:22:16 -03:00
/// check_fence_circle - returns true if the circle fence (defined via
/// parameters) has been freshly breached. May also set up a backup
/// fence outside the fence and return a fresh breach if that backup
/// fence is breaced.
2017-12-07 20:18:29 -04:00
bool AC_Fence : : check_fence_circle ( )
{
if ( ! ( _enabled_fences & AC_FENCE_TYPE_CIRCLE ) ) {
// not enabled; no breach
return false ;
}
2017-12-13 23:46:48 -04:00
Vector2f home ;
2019-01-30 20:37:22 -04:00
if ( AP : : ahrs ( ) . get_relative_position_NE_home ( home ) ) {
2017-12-13 23:46:48 -04:00
// we (may) remain breached if we can't update home
_home_distance = home . length ( ) ;
}
2017-12-07 20:18:29 -04:00
// check if we are outside the fence
if ( _home_distance > = _circle_radius ) {
// record distance outside the fence
_circle_breach_distance = _home_distance - _circle_radius ;
// check for a new breach or a breach of the backup fence
if ( ! ( _breached_fences & AC_FENCE_TYPE_CIRCLE ) | |
( ! is_zero ( _circle_radius_backup ) & & _home_distance > = _circle_radius_backup ) ) {
// new breach
// create a backup fence 20m further out
record_breach ( AC_FENCE_TYPE_CIRCLE ) ;
_circle_radius_backup = _home_distance + AC_FENCE_CIRCLE_RADIUS_BACKUP_DISTANCE ;
return true ;
}
return false ;
}
// not currently breached
// clear circle breach if present
if ( _breached_fences & AC_FENCE_TYPE_CIRCLE ) {
clear_breach ( AC_FENCE_TYPE_CIRCLE ) ;
_circle_radius_backup = 0.0f ;
_circle_breach_distance = 0.0f ;
}
return false ;
}
2017-12-07 20:10:16 -04:00
2017-12-15 05:35:22 -04:00
/// check - returns bitmask of fence types breached (if any)
uint8_t AC_Fence : : check ( )
2013-04-26 06:47:07 -03:00
{
2017-12-15 06:06:56 -04:00
uint8_t ret = 0 ;
2013-04-26 06:47:07 -03:00
2022-09-10 18:03:02 -03:00
// clear any breach from a non-enabled fence
clear_breach ( ~ _enabled_fences ) ;
2013-04-26 06:47:07 -03:00
// return immediately if disabled
2020-09-10 03:28:09 -03:00
if ( ( ! _enabled & & ! _auto_enabled ) | | ! _enabled_fences ) {
2017-12-15 06:06:56 -04:00
return 0 ;
2013-04-26 06:47:07 -03:00
}
2014-04-26 23:08:37 -03:00
// check if pilot is attempting to recover manually
if ( _manual_recovery_start_ms ! = 0 ) {
// we ignore any fence breaches during the manual recovery period which is about 10 seconds
2015-11-19 23:05:31 -04:00
if ( ( AP_HAL : : millis ( ) - _manual_recovery_start_ms ) < AC_FENCE_MANUAL_RECOVERY_TIME_MIN ) {
2017-12-15 06:06:56 -04:00
return 0 ;
2014-04-26 23:08:37 -03:00
}
2017-12-15 06:06:56 -04:00
// recovery period has passed so reset manual recovery time
// and continue with fence breach checks
_manual_recovery_start_ms = 0 ;
2014-04-26 23:08:37 -03:00
}
2017-12-07 20:53:18 -04:00
// maximum altitude fence check
2017-12-15 05:35:22 -04:00
if ( check_fence_alt_max ( ) ) {
2017-12-07 20:53:18 -04:00
ret | = AC_FENCE_TYPE_ALT_MAX ;
2013-04-26 06:47:07 -03:00
}
2020-09-10 03:28:09 -03:00
// minimum altitude fence check
if ( _floor_enabled & & check_fence_alt_min ( ) ) {
ret | = AC_FENCE_TYPE_ALT_MIN ;
}
2013-04-26 06:47:07 -03:00
// circle fence check
2017-12-07 20:18:29 -04:00
if ( check_fence_circle ( ) ) {
ret | = AC_FENCE_TYPE_CIRCLE ;
2013-04-26 06:47:07 -03:00
}
2016-06-23 03:24:48 -03:00
// polygon fence check
2017-12-07 20:10:16 -04:00
if ( check_fence_polygon ( ) ) {
ret | = AC_FENCE_TYPE_POLYGON ;
2016-06-23 03:24:48 -03:00
}
2013-04-26 06:47:07 -03:00
// return any new breaches that have occurred
return ret ;
}
2016-05-19 05:31:36 -03:00
// returns true if the destination is within fence (used to reject waypoints outside the fence)
2019-01-01 22:54:09 -04:00
bool AC_Fence : : check_destination_within_fence ( const Location & loc )
2016-04-28 04:29:35 -03:00
{
2020-09-10 03:28:09 -03:00
// Altitude fence check - Fence Ceiling
2016-07-02 05:14:17 -03:00
if ( ( get_enabled_fences ( ) & AC_FENCE_TYPE_ALT_MAX ) ) {
int32_t alt_above_home_cm ;
2019-03-14 22:45:12 -03:00
if ( loc . get_alt_cm ( Location : : AltFrame : : ABOVE_HOME , alt_above_home_cm ) ) {
2016-07-02 05:14:17 -03:00
if ( ( alt_above_home_cm * 0.01f ) > _alt_max ) {
return false ;
}
}
2016-04-28 04:29:35 -03:00
}
2020-09-10 03:28:09 -03:00
// Altitude fence check - Fence Floor
if ( ( get_enabled_fences ( ) & AC_FENCE_TYPE_ALT_MIN ) ) {
int32_t alt_above_home_cm ;
if ( loc . get_alt_cm ( Location : : AltFrame : : ABOVE_HOME , alt_above_home_cm ) ) {
if ( ( alt_above_home_cm * 0.01f ) < _alt_min ) {
return false ;
}
}
}
2016-04-28 04:29:35 -03:00
// Circular fence check
2016-07-02 05:14:17 -03:00
if ( ( get_enabled_fences ( ) & AC_FENCE_TYPE_CIRCLE ) ) {
2019-02-24 20:12:59 -04:00
if ( AP : : ahrs ( ) . get_home ( ) . get_distance ( loc ) > _circle_radius ) {
2016-07-02 05:14:17 -03:00
return false ;
}
}
// polygon fence check
2019-05-29 10:02:04 -03:00
if ( ( get_enabled_fences ( ) & AC_FENCE_TYPE_POLYGON ) ) {
if ( _poly_loader . breached ( loc ) ) {
return false ;
2016-07-02 05:14:17 -03:00
}
2016-04-28 04:29:35 -03:00
}
return true ;
}
2013-04-26 06:47:07 -03:00
/// record_breach - update breach bitmask, time and count
void AC_Fence : : record_breach ( uint8_t fence_type )
{
// if we haven't already breached a limit, update the breach time
2017-12-15 06:06:56 -04:00
if ( ! _breached_fences ) {
2021-02-12 23:01:25 -04:00
const uint32_t now = AP_HAL : : millis ( ) ;
_breach_time = now ;
// emit a message indicated we're newly-breached, but not too often
if ( now - _last_breach_notify_sent_ms > 1000 ) {
_last_breach_notify_sent_ms = now ;
gcs ( ) . send_message ( MSG_FENCE_STATUS ) ;
}
2013-04-26 06:47:07 -03:00
}
// update breach count
2013-05-01 05:05:04 -03:00
if ( _breach_count < 65500 ) {
2013-04-26 06:47:07 -03:00
_breach_count + + ;
}
// update bitmask
2013-05-01 05:05:04 -03:00
_breached_fences | = fence_type ;
2013-04-26 06:47:07 -03:00
}
/// clear_breach - update breach bitmask, time and count
void AC_Fence : : clear_breach ( uint8_t fence_type )
{
2013-05-01 05:05:04 -03:00
_breached_fences & = ~ fence_type ;
}
2013-04-26 06:47:07 -03:00
2019-08-08 00:16:54 -03:00
/// get_breach_distance - returns maximum distance in meters outside
/// of the given fences. fence_type is a bitmask here.
2013-05-01 05:05:04 -03:00
float AC_Fence : : get_breach_distance ( uint8_t fence_type ) const
{
2019-08-08 00:16:54 -03:00
float max = 0.0f ;
2020-09-10 03:28:09 -03:00
2019-08-08 00:16:54 -03:00
if ( fence_type & AC_FENCE_TYPE_ALT_MAX ) {
max = MAX ( _alt_max_breach_distance , max ) ;
}
2020-09-10 03:28:09 -03:00
if ( fence_type & AC_FENCE_TYPE_ALT_MIN ) {
2021-01-06 08:15:59 -04:00
max = MAX ( _alt_min_breach_distance , max ) ;
2020-09-10 03:28:09 -03:00
}
2019-08-08 00:16:54 -03:00
if ( fence_type & AC_FENCE_TYPE_CIRCLE ) {
max = MAX ( _circle_breach_distance , max ) ;
}
return max ;
2013-07-24 11:05:21 -03:00
}
2014-04-26 23:08:37 -03:00
/// manual_recovery_start - caller indicates that pilot is re-taking manual control so fence should be disabled for 10 seconds
/// has no effect if no breaches have occurred
void AC_Fence : : manual_recovery_start ( )
{
// return immediate if we haven't breached a fence
2017-12-15 06:06:56 -04:00
if ( ! _breached_fences ) {
2014-04-26 23:08:37 -03:00
return ;
}
// record time pilot began manual recovery
2015-11-19 23:05:31 -04:00
_manual_recovery_start_ms = AP_HAL : : millis ( ) ;
2014-04-26 23:08:37 -03:00
}
2016-06-23 03:24:48 -03:00
2018-12-04 00:32:42 -04:00
// methods for mavlink SYS_STATUS message (send_sys_status)
2017-12-27 00:31:32 -04:00
bool AC_Fence : : sys_status_present ( ) const
2017-12-12 01:49:14 -04:00
{
2020-09-10 03:28:09 -03:00
return present ( ) ;
2017-12-12 01:49:14 -04:00
}
2017-12-27 00:31:32 -04:00
bool AC_Fence : : sys_status_enabled ( ) const
2017-12-12 01:49:14 -04:00
{
2017-12-27 00:31:32 -04:00
if ( ! sys_status_present ( ) ) {
2017-12-12 01:49:14 -04:00
return false ;
}
if ( _action = = AC_FENCE_ACTION_REPORT_ONLY ) {
return false ;
}
2020-12-18 05:08:56 -04:00
// Fence is only enabled when the flag is enabled
return _enabled ;
2017-12-12 01:49:14 -04:00
}
2017-12-27 00:31:32 -04:00
bool AC_Fence : : sys_status_failed ( ) const
2017-12-12 01:49:14 -04:00
{
2017-12-27 00:31:32 -04:00
if ( ! sys_status_present ( ) ) {
2017-12-12 01:49:14 -04:00
// not failed if not present; can fail if present but not enabled
return false ;
}
if ( get_breaches ( ) ! = 0 ) {
return true ;
}
return false ;
}
2019-01-30 21:54:13 -04:00
2020-09-10 03:28:09 -03:00
AC_PolyFence_loader & AC_Fence : : polyfence ( )
{
2019-08-28 04:22:16 -03:00
return _poly_loader ;
}
2020-09-10 03:28:09 -03:00
const AC_PolyFence_loader & AC_Fence : : polyfence ( ) const
{
2019-08-28 04:22:16 -03:00
return _poly_loader ;
}
2022-03-04 16:25:54 -04:00
# else // build type is not appropriate; provide a dummy implementation:
const AP_Param : : GroupInfo AC_Fence : : var_info [ ] = { AP_GROUPEND } ;
AC_Fence : : AC_Fence ( ) { } ;
void AC_Fence : : enable ( bool value ) { } ;
void AC_Fence : : disable_floor ( ) { } ;
void AC_Fence : : auto_enable_fence_after_takeoff ( ) { } ;
void AC_Fence : : auto_disable_fence_for_landing ( ) { } ;
bool AC_Fence : : present ( ) const { return false ; }
uint8_t AC_Fence : : get_enabled_fences ( ) const { return 0 ; }
bool AC_Fence : : pre_arm_check ( const char * & fail_msg ) const { return true ; }
uint8_t AC_Fence : : check ( ) { return 0 ; }
bool AC_Fence : : check_destination_within_fence ( const Location & loc ) { return true ; }
float AC_Fence : : get_breach_distance ( uint8_t fence_type ) const { return 0.0 ; }
void AC_Fence : : manual_recovery_start ( ) { }
bool AC_Fence : : sys_status_present ( ) const { return false ; }
bool AC_Fence : : sys_status_enabled ( ) const { return false ; }
bool AC_Fence : : sys_status_failed ( ) const { return false ; }
AC_PolyFence_loader & AC_Fence : : polyfence ( )
{
return _poly_loader ;
}
const AC_PolyFence_loader & AC_Fence : : polyfence ( ) const
{
return _poly_loader ;
}
# endif // #if AC_FENCE_DUMMY_METHODS_ENABLED
2019-01-30 21:54:13 -04:00
// singleton instance
AC_Fence * AC_Fence : : _singleton ;
2020-09-10 03:28:09 -03:00
namespace AP
{
2019-01-30 21:54:13 -04:00
AC_Fence * fence ( )
{
return AC_Fence : : get_singleton ( ) ;
}
}
2022-03-04 12:42:09 -04:00
2022-07-19 08:32:28 -03:00
# endif // AP_FENCE_ENABLED