// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- /// @file limits.cpp /// @brief Imposes limits on location (geofence), altitude and other parameters. /// Each limit breach will trigger an action or set of actions to recover. Adapted from geofence. /// @author Andrew Tridgell /// Andreas Antonopoulos #include const AP_Param::GroupInfo AP_Limit_Geofence::var_info[] PROGMEM = { // @Param: FNC_ON // @DisplayName: Enable Geofence // @Description: Setting this to Enabled(1) will enable the geofence. Setting this to Disabled(0) will disable the geofence // @Values: 0:Disabled,1:Enabled // @User: Standard AP_GROUPINFO("FNC_ON", 0, AP_Limit_Geofence, _enabled), // @Param: FNC_REQ // @DisplayName: Require Geofence // @Description: Setting this to Enabled(1) will make being inside the geofence a required check before arming the vehicle. // @Values: 0:Disabled,1:Enabled // @User: Standard AP_GROUPINFO("FNC_REQ", 1, AP_Limit_Geofence, _required), // @Param: FNC_SMPL // @DisplayName: Require Geofence // @Description: "Simple" geofence (enabled - 1) is based on a radius from the home position, "Complex" (disabled - 0) define a complex fence by lat/long positions // @Values: 0:Disabled,1:Enabled // @User: Standard AP_GROUPINFO("FNC_SMPL", 2, AP_Limit_Geofence, _simple), // @Param: FNC_RAD // @DisplayName: Require Geofence // @Description: Radius of fenced area in meters. A value of 20 creates a 20-meter radius circle (40-meter diameter) from the home point. // @Units: Meters // @Range: 0 32767 // @Increment: 1 // @User: Standard AP_GROUPINFO("FNC_RAD", 3, AP_Limit_Geofence, _radius), AP_GROUPINFO("FNC_TOT", 4, AP_Limit_Geofence, _fence_total), AP_GROUPEND }; AP_Limit_Geofence::AP_Limit_Geofence(uint32_t efs, uint8_t f_wp_s, uint8_t max_fp, GPS *&gps, struct Location *h_loc, struct Location *c_loc) : AP_Limit_Module(AP_LIMITS_GEOFENCE), _eeprom_fence_start(efs), _fence_wp_size(f_wp_s), _max_fence_points(max_fp), _boundary_uptodate(false), _current_loc(c_loc), _home(h_loc), _gps(gps) { update_boundary(); } bool AP_Limit_Geofence::triggered() { // reset trigger before checking _triggered = false; // never trigger while disabled if (!_enabled) return false; // if Geofence is required and we don't know where we are, trigger. if (_required && (!_gps || !_gps->fix || !_home || !_current_loc)) { // TRIGGER _triggered = true; } uint32_t distance; if (_simple && _current_loc && _home) { // simple mode, pointers to current and home exist. distance = (uint32_t) get_distance_meters(_current_loc, _home); if (distance > 0 && distance > (uint16_t) _radius) { // TRIGGER _triggered = true; } } else { // COMPLEX GEOFENCE mode // check boundary and update if necessary if (!_boundary_uptodate) { update_boundary(); } // if boundary is correct, and current_loc exists check if we are inside the fence. if (boundary_correct() && _current_loc) { Vector2l location; location.x = _current_loc->lat; location.y = _current_loc->lng; if (Polygon_outside(location, &_boundary[1], _fence_total-1)) {// trigger if outside // TRIGGER _triggered = true; } } else { // boundary incorrect // If geofence is required and our boundary fence is incorrect, we trigger. if (_required) { // TRIGGER _triggered = true; } } } return _triggered; } uint32_t get_distance_meters(struct Location *loc1, struct Location *loc2) // distance in meters between two locations { if (!loc1 || !loc2) return -1; // pointers missing (dangling) if(loc1->lat == 0 || loc1->lng == 0) return -1; // do not trigger a false positive on erroneous location data if(loc2->lat == 0 || loc2->lng == 0) return -1; // do not trigger a false positive on erroneous location data float dlat = (float)(loc2->lat - loc1->lat); float dlong = (float)(loc2->lng - loc1->lng); return (sqrt(sq(dlat) + sq(dlong)) * .01113195); } AP_Int8 AP_Limit_Geofence::fence_total() { return _fence_total; } // save a fence point void AP_Limit_Geofence::set_fence_point_with_index(Vector2l &point, uint8_t i) { uint32_t mem; if (i >= (unsigned)fence_total()) { // not allowed return; } mem = _eeprom_fence_start + (i * _fence_wp_size); eeprom_write_dword((uint32_t *)mem, point.x); mem += sizeof(uint32_t); eeprom_write_dword((uint32_t *)mem, point.y); _boundary_uptodate = false; } /* fence boundaries fetch/store */ Vector2l AP_Limit_Geofence::get_fence_point_with_index(uint8_t i) { uint32_t mem; Vector2l ret; if (i > (unsigned) fence_total()) { return Vector2l(0,0); } // read fence point mem = _eeprom_fence_start + (i * _fence_wp_size); ret.x = eeprom_read_dword((uint32_t *)mem); mem += sizeof(uint32_t); ret.y = eeprom_read_dword((uint32_t *)mem); return ret; } void AP_Limit_Geofence::update_boundary() { if (!_simple && _fence_total > 0) { for (uint8_t i = 0; i < (uint8_t) _fence_total; i++) { _boundary[i] = get_fence_point_with_index(i); } _boundary_uptodate = true; } } bool AP_Limit_Geofence::boundary_correct() { if (Polygon_complete(&_boundary[1], _fence_total - 1) && !Polygon_outside(_boundary[0], &_boundary[1], _fence_total - 1)) { return true; } else return false; }