2014-07-24 03:21:30 -03:00
|
|
|
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
|
|
|
/*
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2015-05-13 03:09:36 -03:00
|
|
|
#include "Plane.h"
|
|
|
|
|
2014-07-24 03:21:30 -03:00
|
|
|
/*
|
2014-07-24 04:54:58 -03:00
|
|
|
altitude handling routines. These cope with both barometric control
|
|
|
|
and terrain following control
|
2014-07-24 03:21:30 -03:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
adjust altitude target depending on mode
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::adjust_altitude_target()
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
if (control_mode == FLY_BY_WIRE_B ||
|
|
|
|
control_mode == CRUISE) {
|
|
|
|
return;
|
|
|
|
}
|
2014-08-11 03:46:08 -03:00
|
|
|
if (flight_stage == AP_SpdHgtControl::FLIGHT_LAND_FINAL) {
|
|
|
|
// in land final TECS uses TECS_LAND_SINK as a target sink
|
|
|
|
// rate, and ignores the target altitude
|
|
|
|
set_target_altitude_location(next_WP_loc);
|
2016-01-30 02:32:42 -04:00
|
|
|
} else if (flight_stage == AP_SpdHgtControl::FLIGHT_LAND_APPROACH ||
|
|
|
|
flight_stage == AP_SpdHgtControl::FLIGHT_LAND_PREFLARE) {
|
2014-08-27 01:33:57 -03:00
|
|
|
setup_landing_glide_slope();
|
2016-01-06 22:30:41 -04:00
|
|
|
adjust_landing_slope_for_rangefinder_bump();
|
2016-05-07 21:40:42 -03:00
|
|
|
} else if (reached_loiter_target()) {
|
2014-07-24 03:21:30 -03:00
|
|
|
// once we reach a loiter target then lock to the final
|
|
|
|
// altitude target
|
|
|
|
set_target_altitude_location(next_WP_loc);
|
2014-08-13 20:49:52 -03:00
|
|
|
} else if (target_altitude.offset_cm != 0 &&
|
|
|
|
!location_passed_point(current_loc, prev_WP_loc, next_WP_loc)) {
|
2014-07-24 03:21:30 -03:00
|
|
|
// control climb/descent rate
|
2015-01-01 00:17:45 -04:00
|
|
|
set_target_altitude_proportion(next_WP_loc, 1.0f-auto_state.wp_proportion);
|
2014-07-24 03:21:30 -03:00
|
|
|
|
|
|
|
// stay within the range of the start and end locations in altitude
|
|
|
|
constrain_target_altitude_location(next_WP_loc, prev_WP_loc);
|
2016-04-26 08:27:12 -03:00
|
|
|
} else {
|
2014-07-24 03:21:30 -03:00
|
|
|
set_target_altitude_location(next_WP_loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
altitude_error_cm = calc_altitude_error_cm();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
setup for a gradual glide slope to the next waypoint, if appropriate
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::setup_glide_slope(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
// establish the distance we are travelling to the next waypoint,
|
|
|
|
// for calculating out rate of change of altitude
|
2015-01-01 00:17:45 -04:00
|
|
|
auto_state.wp_distance = get_distance(current_loc, next_WP_loc);
|
|
|
|
auto_state.wp_proportion = location_path_proportion(current_loc,
|
|
|
|
prev_WP_loc, next_WP_loc);
|
2016-01-08 20:12:03 -04:00
|
|
|
SpdHgt_Controller->set_path_proportion(auto_state.wp_proportion);
|
2016-04-19 19:41:02 -03:00
|
|
|
update_flight_stage();
|
2014-07-24 03:21:30 -03:00
|
|
|
|
|
|
|
/*
|
|
|
|
work out if we will gradually change altitude, or try to get to
|
|
|
|
the new altitude as quickly as possible.
|
|
|
|
*/
|
|
|
|
switch (control_mode) {
|
|
|
|
case RTL:
|
|
|
|
case GUIDED:
|
|
|
|
/* glide down slowly if above target altitude, but ascend more
|
|
|
|
rapidly if below it. See
|
2016-03-25 06:47:03 -03:00
|
|
|
https://github.com/ArduPilot/ardupilot/issues/39
|
2014-07-24 03:21:30 -03:00
|
|
|
*/
|
2014-07-24 04:54:58 -03:00
|
|
|
if (above_location_current(next_WP_loc)) {
|
2014-07-24 03:21:30 -03:00
|
|
|
set_offset_altitude_location(next_WP_loc);
|
|
|
|
} else {
|
|
|
|
reset_offset_altitude();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AUTO:
|
2014-08-04 06:59:33 -03:00
|
|
|
// we only do glide slide handling in AUTO when above 20m or
|
|
|
|
// when descending. The 20 meter threshold is arbitrary, and
|
2014-07-24 03:21:30 -03:00
|
|
|
// is basically to prevent situations where we try to slowly
|
|
|
|
// gain height at low altitudes, potentially hitting
|
|
|
|
// obstacles.
|
2015-04-13 02:17:41 -03:00
|
|
|
if (adjusted_relative_altitude_cm() > 2000 || above_location_current(next_WP_loc)) {
|
2014-07-24 03:21:30 -03:00
|
|
|
set_offset_altitude_location(next_WP_loc);
|
|
|
|
} else {
|
|
|
|
reset_offset_altitude();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
reset_offset_altitude();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
return RTL altitude as AMSL altitude
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
int32_t Plane::get_RTL_altitude()
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
if (g.RTL_altitude_cm < 0) {
|
|
|
|
return current_loc.alt;
|
|
|
|
}
|
|
|
|
return g.RTL_altitude_cm + home.alt;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
return relative altitude in meters (relative to home)
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
float Plane::relative_altitude(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
return (current_loc.alt - home.alt) * 0.01f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
return relative altitude in centimeters, absolute value
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
int32_t Plane::relative_altitude_abs_cm(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
return labs(current_loc.alt - home.alt);
|
|
|
|
}
|
|
|
|
|
2016-05-05 08:01:28 -03:00
|
|
|
/*
|
|
|
|
return relative altitude in meters (relative to terrain, if available,
|
|
|
|
or home otherwise)
|
|
|
|
*/
|
2016-06-03 18:10:26 -03:00
|
|
|
float Plane::relative_ground_altitude(bool use_rangefinder_if_available)
|
2016-05-05 08:01:28 -03:00
|
|
|
{
|
2016-06-16 18:19:10 -03:00
|
|
|
#if RANGEFINDER_ENABLED == ENABLED
|
|
|
|
if (use_rangefinder_if_available && rangefinder_state.in_range) {
|
2016-06-03 18:10:26 -03:00
|
|
|
return rangefinder_state.height_estimate;
|
|
|
|
}
|
2016-06-16 18:19:10 -03:00
|
|
|
#endif
|
2016-06-03 18:10:26 -03:00
|
|
|
|
2016-05-05 08:01:28 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
|
|
|
float altitude;
|
2016-06-10 04:47:32 -03:00
|
|
|
if (target_altitude.terrain_following &&
|
|
|
|
terrain.status() == AP_Terrain::TerrainStatusOK &&
|
|
|
|
terrain.height_above_terrain(altitude, true)) {
|
2016-05-05 08:01:28 -03:00
|
|
|
return altitude;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return relative_altitude();
|
|
|
|
}
|
2014-07-24 03:21:30 -03:00
|
|
|
|
|
|
|
/*
|
|
|
|
set the target altitude to the current altitude. This is used when
|
|
|
|
setting up for altitude hold, such as when releasing elevator in
|
|
|
|
CRUISE mode.
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::set_target_altitude_current(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
2014-07-24 04:54:58 -03:00
|
|
|
// record altitude above sea level at the current time as our
|
|
|
|
// target altitude
|
2014-07-24 03:21:30 -03:00
|
|
|
target_altitude.amsl_cm = current_loc.alt;
|
2014-07-24 04:54:58 -03:00
|
|
|
|
|
|
|
// reset any glide slope offset
|
|
|
|
reset_offset_altitude();
|
|
|
|
|
2014-07-24 18:58:39 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 04:54:58 -03:00
|
|
|
// also record the terrain altitude if possible
|
|
|
|
float terrain_altitude;
|
2014-08-05 22:44:06 -03:00
|
|
|
if (g.terrain_follow && terrain.height_above_terrain(terrain_altitude, true)) {
|
2014-07-24 04:54:58 -03:00
|
|
|
target_altitude.terrain_following = true;
|
|
|
|
target_altitude.terrain_alt_cm = terrain_altitude*100;
|
|
|
|
} else {
|
|
|
|
// if terrain following is disabled, or we don't know our
|
|
|
|
// terrain altitude when we set the altitude then don't
|
|
|
|
// terrain follow
|
|
|
|
target_altitude.terrain_following = false;
|
|
|
|
}
|
|
|
|
#endif
|
2014-07-24 03:21:30 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
set the target altitude to the current altitude, with ALT_OFFSET adjustment
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::set_target_altitude_current_adjusted(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
2014-07-24 04:54:58 -03:00
|
|
|
set_target_altitude_current();
|
|
|
|
|
|
|
|
// use adjusted_altitude_cm() to take account of ALTITUDE_OFFSET
|
2014-07-24 03:21:30 -03:00
|
|
|
target_altitude.amsl_cm = adjusted_altitude_cm();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
set target altitude based on a location structure
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::set_target_altitude_location(const Location &loc)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
target_altitude.amsl_cm = loc.alt;
|
2014-07-24 04:54:58 -03:00
|
|
|
if (loc.flags.relative_alt) {
|
|
|
|
target_altitude.amsl_cm += home.alt;
|
|
|
|
}
|
2014-07-24 18:58:39 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 04:54:58 -03:00
|
|
|
/*
|
|
|
|
if this location has the terrain_alt flag set and we know the
|
|
|
|
terrain altitude of our current location then treat it as a
|
|
|
|
terrain altitude
|
|
|
|
*/
|
|
|
|
float height;
|
2014-08-05 22:44:06 -03:00
|
|
|
if (loc.flags.terrain_alt && terrain.height_above_terrain(height, true)) {
|
2014-07-24 04:54:58 -03:00
|
|
|
target_altitude.terrain_following = true;
|
|
|
|
target_altitude.terrain_alt_cm = loc.alt;
|
|
|
|
if (!loc.flags.relative_alt) {
|
|
|
|
// it has home added, remove it
|
|
|
|
target_altitude.terrain_alt_cm -= home.alt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
target_altitude.terrain_following = false;
|
|
|
|
}
|
|
|
|
#endif
|
2014-07-24 03:21:30 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
return relative to home target altitude in centimeters. Used for
|
|
|
|
altitude control libraries
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
int32_t Plane::relative_target_altitude_cm(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
2014-07-24 18:58:39 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 04:54:58 -03:00
|
|
|
float relative_home_height;
|
|
|
|
if (target_altitude.terrain_following &&
|
2014-08-05 22:44:06 -03:00
|
|
|
terrain.height_relative_home_equivalent(target_altitude.terrain_alt_cm*0.01f,
|
|
|
|
relative_home_height, true)) {
|
2014-08-06 20:31:03 -03:00
|
|
|
// add lookahead adjustment the target altitude
|
|
|
|
target_altitude.lookahead = lookahead_adjustment();
|
|
|
|
relative_home_height += target_altitude.lookahead;
|
2014-08-26 08:18:24 -03:00
|
|
|
|
|
|
|
// correct for rangefinder data
|
|
|
|
relative_home_height += rangefinder_correction();
|
|
|
|
|
2014-07-24 04:54:58 -03:00
|
|
|
// we are following terrain, and have terrain data for the
|
|
|
|
// current location. Use it.
|
|
|
|
return relative_home_height*100;
|
|
|
|
}
|
|
|
|
#endif
|
2014-08-26 08:18:24 -03:00
|
|
|
int32_t relative_alt = target_altitude.amsl_cm - home.alt;
|
2016-07-19 20:09:21 -03:00
|
|
|
relative_alt += mission_alt_offset()*100;
|
2014-08-26 08:18:24 -03:00
|
|
|
relative_alt += rangefinder_correction() * 100;
|
|
|
|
return relative_alt;
|
2014-07-24 03:21:30 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
change the current target altitude by an amount in centimeters. Used
|
|
|
|
to cope with changes due to elevator in CRUISE or FBWB
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::change_target_altitude(int32_t change_cm)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
target_altitude.amsl_cm += change_cm;
|
2014-07-24 18:58:39 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 04:54:58 -03:00
|
|
|
if (target_altitude.terrain_following) {
|
|
|
|
target_altitude.terrain_alt_cm += change_cm;
|
|
|
|
}
|
|
|
|
#endif
|
2014-07-24 03:21:30 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
change target altitude by a proportion of the target altitude offset
|
|
|
|
(difference in height to next WP from previous WP). proportion
|
|
|
|
should be between 0 and 1.
|
|
|
|
|
|
|
|
When proportion is zero we have reached the destination. When
|
|
|
|
proportion is 1 we are at the starting waypoint.
|
|
|
|
|
|
|
|
Note that target_altitude is setup initially based on the
|
|
|
|
destination waypoint
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::set_target_altitude_proportion(const Location &loc, float proportion)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
set_target_altitude_location(loc);
|
|
|
|
proportion = constrain_float(proportion, 0.0f, 1.0f);
|
|
|
|
change_target_altitude(-target_altitude.offset_cm*proportion);
|
2015-04-12 23:20:01 -03:00
|
|
|
//rebuild the glide slope if we are above it and supposed to be climbing
|
|
|
|
if(g.glide_slope_threshold > 0) {
|
|
|
|
if(target_altitude.offset_cm > 0 && calc_altitude_error_cm() < -100 * g.glide_slope_threshold) {
|
|
|
|
set_target_altitude_location(loc);
|
|
|
|
set_offset_altitude_location(loc);
|
|
|
|
change_target_altitude(-target_altitude.offset_cm*proportion);
|
|
|
|
//adjust the new target offset altitude to reflect that we are partially already done
|
|
|
|
if(proportion > 0.0f)
|
|
|
|
target_altitude.offset_cm = ((float)target_altitude.offset_cm)/proportion;
|
|
|
|
}
|
|
|
|
}
|
2014-07-24 03:21:30 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
constrain target altitude to be between two locations. Used to
|
|
|
|
ensure we stay within two waypoints in altitude
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::constrain_target_altitude_location(const Location &loc1, const Location &loc2)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
2014-07-24 06:56:15 -03:00
|
|
|
if (loc1.alt > loc2.alt) {
|
2014-07-24 03:21:30 -03:00
|
|
|
target_altitude.amsl_cm = constrain_int32(target_altitude.amsl_cm, loc2.alt, loc1.alt);
|
|
|
|
} else {
|
|
|
|
target_altitude.amsl_cm = constrain_int32(target_altitude.amsl_cm, loc1.alt, loc2.alt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
return error between target altitude and current altitude
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
int32_t Plane::calc_altitude_error_cm(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
2014-07-24 18:58:39 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 04:54:58 -03:00
|
|
|
float terrain_height;
|
|
|
|
if (target_altitude.terrain_following &&
|
2014-08-05 22:44:06 -03:00
|
|
|
terrain.height_above_terrain(terrain_height, true)) {
|
2014-08-06 20:31:03 -03:00
|
|
|
return target_altitude.lookahead*100 + target_altitude.terrain_alt_cm - (terrain_height*100);
|
2014-07-24 04:54:58 -03:00
|
|
|
}
|
|
|
|
#endif
|
2014-07-24 03:21:30 -03:00
|
|
|
return target_altitude.amsl_cm - adjusted_altitude_cm();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
check for FBWB_min_altitude_cm violation
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::check_minimum_altitude(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
2014-07-24 04:54:58 -03:00
|
|
|
if (g.FBWB_min_altitude_cm == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-24 18:58:39 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 04:54:58 -03:00
|
|
|
if (target_altitude.terrain_following) {
|
|
|
|
// set our target terrain height to be at least the min set
|
|
|
|
if (target_altitude.terrain_alt_cm < g.FBWB_min_altitude_cm) {
|
|
|
|
target_altitude.terrain_alt_cm = g.FBWB_min_altitude_cm;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (target_altitude.amsl_cm < home.alt + g.FBWB_min_altitude_cm) {
|
2014-07-24 03:21:30 -03:00
|
|
|
target_altitude.amsl_cm = home.alt + g.FBWB_min_altitude_cm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
reset the altitude offset used for glide slopes
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::reset_offset_altitude(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
target_altitude.offset_cm = 0;
|
|
|
|
}
|
|
|
|
|
2014-08-27 01:33:57 -03:00
|
|
|
|
2014-07-24 03:21:30 -03:00
|
|
|
/*
|
|
|
|
reset the altitude offset used for glide slopes, based on difference
|
|
|
|
between altitude at a destination and current altitude. If
|
|
|
|
destination is above the current altitude then the result is
|
|
|
|
positive.
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::set_offset_altitude_location(const Location &loc)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
|
|
|
target_altitude.offset_cm = loc.alt - current_loc.alt;
|
2014-07-24 04:54:58 -03:00
|
|
|
|
2014-07-24 18:58:39 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 20:07:45 -03:00
|
|
|
/*
|
|
|
|
if this location has the terrain_alt flag set and we know the
|
|
|
|
terrain altitude of our current location then treat it as a
|
|
|
|
terrain altitude
|
|
|
|
*/
|
|
|
|
float height;
|
2014-07-24 04:54:58 -03:00
|
|
|
if (loc.flags.terrain_alt &&
|
2014-07-24 20:07:45 -03:00
|
|
|
target_altitude.terrain_following &&
|
2014-08-05 22:44:06 -03:00
|
|
|
terrain.height_above_terrain(height, true)) {
|
2014-07-24 20:07:45 -03:00
|
|
|
target_altitude.offset_cm = target_altitude.terrain_alt_cm - (height * 100);
|
2014-07-24 04:54:58 -03:00
|
|
|
}
|
|
|
|
#endif
|
2014-08-04 04:13:30 -03:00
|
|
|
|
2016-01-30 02:32:42 -04:00
|
|
|
if (flight_stage != AP_SpdHgtControl::FLIGHT_LAND_PREFLARE &&
|
|
|
|
flight_stage != AP_SpdHgtControl::FLIGHT_LAND_APPROACH &&
|
2014-08-04 06:58:12 -03:00
|
|
|
flight_stage != AP_SpdHgtControl::FLIGHT_LAND_FINAL) {
|
|
|
|
// if we are within GLIDE_SLOPE_MIN meters of the target altitude
|
|
|
|
// then reset the offset to not use a glide slope. This allows for
|
|
|
|
// more accurate flight of missions where the aircraft may lose or
|
|
|
|
// gain a bit of altitude near waypoint turn points due to local
|
|
|
|
// terrain changes
|
2015-04-12 23:20:01 -03:00
|
|
|
if (g.glide_slope_min <= 0 ||
|
|
|
|
labs(target_altitude.offset_cm)*0.01f < g.glide_slope_min) {
|
2014-08-04 06:58:12 -03:00
|
|
|
target_altitude.offset_cm = 0;
|
|
|
|
}
|
2014-08-04 04:13:30 -03:00
|
|
|
}
|
2014-07-24 04:54:58 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-07-24 06:56:15 -03:00
|
|
|
return true if current_loc is above loc. Used for glide slope
|
|
|
|
calculations.
|
|
|
|
|
|
|
|
"above" is simple if we are not terrain following, as it just means
|
|
|
|
the pressure altitude of one is above the other.
|
|
|
|
|
|
|
|
When in terrain following mode "above" means the over-the-terrain
|
|
|
|
current altitude is above the over-the-terrain alt of loc. It is
|
|
|
|
quite possible for current_loc to be "above" loc when it is at a
|
|
|
|
lower pressure altitude, if current_loc is in a low part of the
|
|
|
|
terrain
|
2014-07-24 04:54:58 -03:00
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
bool Plane::above_location_current(const Location &loc)
|
2014-07-24 04:54:58 -03:00
|
|
|
{
|
2014-07-24 18:58:39 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 06:56:15 -03:00
|
|
|
float terrain_alt;
|
|
|
|
if (loc.flags.terrain_alt &&
|
2014-08-05 22:44:06 -03:00
|
|
|
terrain.height_above_terrain(terrain_alt, true)) {
|
2014-07-24 06:56:15 -03:00
|
|
|
float loc_alt = loc.alt*0.01f;
|
|
|
|
if (!loc.flags.relative_alt) {
|
|
|
|
loc_alt -= home.alt*0.01f;
|
|
|
|
}
|
2014-08-04 03:55:15 -03:00
|
|
|
return terrain_alt > loc_alt;
|
2014-07-24 04:54:58 -03:00
|
|
|
}
|
|
|
|
#endif
|
2014-07-24 03:21:30 -03:00
|
|
|
|
2014-08-04 03:55:15 -03:00
|
|
|
float loc_alt_cm = loc.alt;
|
2015-07-28 19:20:23 -03:00
|
|
|
if (loc.flags.relative_alt) {
|
|
|
|
loc_alt_cm += home.alt;
|
2014-08-04 03:55:15 -03:00
|
|
|
}
|
|
|
|
return current_loc.alt > loc_alt_cm;
|
2014-07-24 04:54:58 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
modify a destination to be setup for terrain following if
|
|
|
|
TERRAIN_FOLLOW is enabled
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::setup_terrain_target_alt(Location &loc)
|
2014-07-24 04:54:58 -03:00
|
|
|
{
|
2014-07-24 19:02:19 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
2014-07-24 04:54:58 -03:00
|
|
|
if (g.terrain_follow) {
|
|
|
|
loc.flags.terrain_alt = true;
|
|
|
|
}
|
2014-07-24 19:02:19 -03:00
|
|
|
#endif
|
2014-07-24 04:54:58 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
return current_loc.alt adjusted for ALT_OFFSET
|
|
|
|
This is useful during long flights to account for barometer changes
|
|
|
|
from the GCS, or to adjust the flying height of a long mission
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
int32_t Plane::adjusted_altitude_cm(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
2016-07-19 20:09:21 -03:00
|
|
|
return current_loc.alt - (mission_alt_offset()*100);
|
2014-07-24 03:21:30 -03:00
|
|
|
}
|
2014-08-06 20:31:03 -03:00
|
|
|
|
2015-02-25 07:54:52 -04:00
|
|
|
/*
|
|
|
|
return home-relative altitude adjusted for ALT_OFFSET This is useful
|
|
|
|
during long flights to account for barometer changes from the GCS,
|
|
|
|
or to adjust the flying height of a long mission
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
int32_t Plane::adjusted_relative_altitude_cm(void)
|
2015-02-25 07:54:52 -04:00
|
|
|
{
|
|
|
|
return adjusted_altitude_cm() - home.alt;
|
|
|
|
}
|
|
|
|
|
2016-07-19 20:09:21 -03:00
|
|
|
|
|
|
|
/*
|
|
|
|
return the mission altitude offset. This raises or lowers all
|
|
|
|
mission items. It is primarily set using the ALT_OFFSET parameter,
|
|
|
|
but can also be adjusted by the rangefinder landing code for a
|
|
|
|
NAV_LAND command if we have aborted a steep landing
|
|
|
|
*/
|
|
|
|
float Plane::mission_alt_offset(void)
|
|
|
|
{
|
|
|
|
float ret = g.alt_offset;
|
2016-07-21 02:29:31 -03:00
|
|
|
if (control_mode == AUTO &&
|
|
|
|
(auto_state.land_in_progress || auto_state.wp_is_land_approach)) {
|
2016-07-19 20:09:21 -03:00
|
|
|
// when landing after an aborted landing due to too high glide
|
|
|
|
// slope we use an offset from the last landing attempt
|
|
|
|
ret += auto_state.land_alt_offset;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-08-11 03:46:08 -03:00
|
|
|
/*
|
|
|
|
return the height in meters above the next_WP_loc altitude
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
float Plane::height_above_target(void)
|
2014-08-11 03:46:08 -03:00
|
|
|
{
|
|
|
|
float target_alt = next_WP_loc.alt*0.01;
|
|
|
|
if (!next_WP_loc.flags.relative_alt) {
|
2015-04-11 06:43:13 -03:00
|
|
|
target_alt -= ahrs.get_home().alt*0.01f;
|
2014-08-11 03:46:08 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
#if AP_TERRAIN_AVAILABLE
|
|
|
|
// also record the terrain altitude if possible
|
|
|
|
float terrain_altitude;
|
|
|
|
if (next_WP_loc.flags.terrain_alt &&
|
|
|
|
terrain.height_above_terrain(terrain_altitude, true)) {
|
|
|
|
return terrain_altitude - target_alt;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-08-13 19:04:06 -03:00
|
|
|
return (adjusted_altitude_cm()*0.01f - ahrs.get_home().alt*0.01f) - target_alt;
|
2014-08-11 03:46:08 -03:00
|
|
|
}
|
|
|
|
|
2014-08-06 20:31:03 -03:00
|
|
|
/*
|
|
|
|
work out target altitude adjustment from terrain lookahead
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
float Plane::lookahead_adjustment(void)
|
2014-08-06 20:31:03 -03:00
|
|
|
{
|
|
|
|
#if AP_TERRAIN_AVAILABLE
|
|
|
|
int32_t bearing_cd;
|
|
|
|
int16_t distance;
|
|
|
|
// work out distance and bearing to target
|
|
|
|
if (control_mode == FLY_BY_WIRE_B) {
|
|
|
|
// there is no target waypoint in FBWB, so use yaw as an approximation
|
|
|
|
bearing_cd = ahrs.yaw_sensor;
|
|
|
|
distance = g.terrain_lookahead;
|
2016-05-07 21:40:42 -03:00
|
|
|
} else if (!reached_loiter_target()) {
|
2014-08-06 20:31:03 -03:00
|
|
|
bearing_cd = nav_controller->target_bearing_cd();
|
2015-01-01 00:17:45 -04:00
|
|
|
distance = constrain_float(auto_state.wp_distance, 0, g.terrain_lookahead);
|
2014-08-06 20:31:03 -03:00
|
|
|
} else {
|
|
|
|
// no lookahead when loitering
|
|
|
|
bearing_cd = 0;
|
|
|
|
distance = 0;
|
|
|
|
}
|
|
|
|
if (distance <= 0) {
|
|
|
|
// no lookahead
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float groundspeed = ahrs.groundspeed();
|
|
|
|
if (groundspeed < 1) {
|
|
|
|
// we're not moving
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// we need to know the climb ratio. We use 50% of the maximum
|
|
|
|
// climb rate so we are not constantly at 100% throttle and to
|
|
|
|
// give a bit more margin on terrain
|
|
|
|
float climb_ratio = 0.5f * SpdHgt_Controller->get_max_climbrate() / groundspeed;
|
|
|
|
|
|
|
|
if (climb_ratio <= 0) {
|
|
|
|
// lookahead makes no sense for negative climb rates
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ask the terrain code for the lookahead altitude change
|
|
|
|
float lookahead = terrain.lookahead(bearing_cd*0.01f, distance, climb_ratio);
|
|
|
|
|
|
|
|
if (target_altitude.offset_cm < 0) {
|
|
|
|
// we are heading down to the waypoint, so we don't need to
|
|
|
|
// climb as much
|
|
|
|
lookahead += target_altitude.offset_cm*0.01f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// constrain lookahead to a reasonable limit
|
|
|
|
return constrain_float(lookahead, 0, 1000.0f);
|
2014-08-08 00:54:05 -03:00
|
|
|
#else
|
|
|
|
return 0;
|
2014-08-06 20:31:03 -03:00
|
|
|
#endif
|
|
|
|
}
|
2014-08-26 08:18:24 -03:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
correct target altitude using rangefinder data. Returns offset in
|
|
|
|
meters to correct target altitude. A positive number means we need
|
|
|
|
to ask the speed/height controller to fly higher
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
float Plane::rangefinder_correction(void)
|
2014-08-26 08:18:24 -03:00
|
|
|
{
|
2015-05-13 20:00:11 -03:00
|
|
|
#if RANGEFINDER_ENABLED == ENABLED
|
2015-05-13 19:05:32 -03:00
|
|
|
if (millis() - rangefinder_state.last_correction_time_ms > 5000) {
|
2014-08-26 22:50:07 -03:00
|
|
|
// we haven't had any rangefinder data for 5s - don't use it
|
2014-08-26 08:18:24 -03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// for now we only support the rangefinder for landing
|
|
|
|
bool using_rangefinder = (g.rangefinder_landing &&
|
|
|
|
control_mode == AUTO &&
|
|
|
|
(flight_stage == AP_SpdHgtControl::FLIGHT_LAND_APPROACH ||
|
2016-01-30 02:32:42 -04:00
|
|
|
flight_stage == AP_SpdHgtControl::FLIGHT_LAND_PREFLARE ||
|
2014-08-26 08:18:24 -03:00
|
|
|
flight_stage == AP_SpdHgtControl::FLIGHT_LAND_FINAL));
|
|
|
|
if (!using_rangefinder) {
|
2014-08-26 22:50:07 -03:00
|
|
|
return 0;
|
2014-08-26 08:18:24 -03:00
|
|
|
}
|
|
|
|
|
2014-08-26 22:50:07 -03:00
|
|
|
return rangefinder_state.correction;
|
2015-05-13 20:00:11 -03:00
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
2014-08-26 22:50:07 -03:00
|
|
|
}
|
2014-08-26 08:18:24 -03:00
|
|
|
|
2015-05-13 20:00:11 -03:00
|
|
|
#if RANGEFINDER_ENABLED == ENABLED
|
2014-08-26 22:50:07 -03:00
|
|
|
/*
|
|
|
|
update the offset between rangefinder height and terrain height
|
|
|
|
*/
|
2015-05-13 03:09:36 -03:00
|
|
|
void Plane::rangefinder_height_update(void)
|
2014-08-26 22:50:07 -03:00
|
|
|
{
|
2015-09-15 22:14:26 -03:00
|
|
|
float distance = rangefinder.distance_cm()*0.01f;
|
2015-04-17 03:37:00 -03:00
|
|
|
if ((rangefinder.status() == RangeFinder::RangeFinder_Good) && home_is_set != HOME_UNSET) {
|
2015-09-15 22:14:26 -03:00
|
|
|
if (!rangefinder_state.have_initial_reading) {
|
|
|
|
rangefinder_state.have_initial_reading = true;
|
|
|
|
rangefinder_state.initial_range = distance;
|
|
|
|
}
|
2014-08-26 22:50:07 -03:00
|
|
|
// correct the range for attitude (multiply by DCM.c.z, which
|
|
|
|
// is cos(roll)*cos(pitch))
|
2016-03-09 03:20:41 -04:00
|
|
|
rangefinder_state.height_estimate = distance * ahrs.get_rotation_body_to_ned().c.z;
|
2014-08-27 05:25:17 -03:00
|
|
|
|
2014-08-26 22:50:07 -03:00
|
|
|
// we consider ourselves to be fully in range when we have 10
|
2015-09-15 22:14:26 -03:00
|
|
|
// good samples (0.2s) that are different by 5% of the maximum
|
|
|
|
// range from the initial range we see. The 5% change is to
|
|
|
|
// catch Lidars that are giving a constant range, either due
|
|
|
|
// to misconfiguration or a faulty sensor
|
2014-08-26 22:50:07 -03:00
|
|
|
if (rangefinder_state.in_range_count < 10) {
|
2015-09-15 22:14:26 -03:00
|
|
|
if (fabsf(rangefinder_state.initial_range - distance) > 0.05f * rangefinder.max_distance_cm()*0.01f) {
|
|
|
|
rangefinder_state.in_range_count++;
|
|
|
|
}
|
2014-08-26 22:50:07 -03:00
|
|
|
} else {
|
|
|
|
rangefinder_state.in_range = true;
|
2015-09-15 22:14:26 -03:00
|
|
|
if (!rangefinder_state.in_use &&
|
2016-01-30 02:32:42 -04:00
|
|
|
(flight_stage == AP_SpdHgtControl::FLIGHT_LAND_APPROACH ||
|
2016-03-09 03:20:41 -04:00
|
|
|
flight_stage == AP_SpdHgtControl::FLIGHT_LAND_PREFLARE ||
|
|
|
|
flight_stage == AP_SpdHgtControl::FLIGHT_LAND_FINAL ||
|
2016-04-29 02:31:08 -03:00
|
|
|
control_mode == QLAND ||
|
|
|
|
control_mode == QRTL ||
|
|
|
|
(control_mode == AUTO && plane.mission.get_current_nav_cmd().id == MAV_CMD_NAV_VTOL_LAND)) &&
|
2015-09-15 22:14:26 -03:00
|
|
|
g.rangefinder_landing) {
|
|
|
|
rangefinder_state.in_use = true;
|
2016-03-09 03:20:41 -04:00
|
|
|
gcs_send_text_fmt(MAV_SEVERITY_INFO, "Rangefinder engaged at %.2fm", (double)rangefinder_state.height_estimate);
|
2015-09-15 22:14:26 -03:00
|
|
|
}
|
2014-08-26 22:50:07 -03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rangefinder_state.in_range_count = 0;
|
|
|
|
rangefinder_state.in_range = false;
|
2014-08-26 08:18:24 -03:00
|
|
|
}
|
2014-08-26 22:50:07 -03:00
|
|
|
|
|
|
|
if (rangefinder_state.in_range) {
|
|
|
|
// base correction is the difference between baro altitude and
|
|
|
|
// rangefinder estimate
|
2016-03-25 20:06:07 -03:00
|
|
|
float correction = relative_altitude() - rangefinder_state.height_estimate;
|
2014-08-26 22:50:07 -03:00
|
|
|
|
|
|
|
#if AP_TERRAIN_AVAILABLE
|
|
|
|
// if we are terrain following then correction is based on terrain data
|
|
|
|
float terrain_altitude;
|
|
|
|
if ((target_altitude.terrain_following || g.terrain_follow) &&
|
|
|
|
terrain.height_above_terrain(terrain_altitude, true)) {
|
2016-03-25 20:06:07 -03:00
|
|
|
correction = terrain_altitude - rangefinder_state.height_estimate;
|
2014-08-26 22:50:07 -03:00
|
|
|
}
|
2014-08-26 08:18:24 -03:00
|
|
|
#endif
|
|
|
|
|
2014-08-27 05:25:17 -03:00
|
|
|
// remember the last correction. Use a low pass filter unless
|
|
|
|
// the old data is more than 5 seconds old
|
2016-05-17 22:21:21 -03:00
|
|
|
uint32_t now = millis();
|
|
|
|
if (now - rangefinder_state.last_correction_time_ms > 5000) {
|
2014-08-27 05:25:17 -03:00
|
|
|
rangefinder_state.correction = correction;
|
2015-09-15 22:14:26 -03:00
|
|
|
rangefinder_state.initial_correction = correction;
|
2016-05-14 03:09:58 -03:00
|
|
|
auto_state.initial_land_slope = auto_state.land_slope;
|
2014-08-27 05:25:17 -03:00
|
|
|
} else {
|
|
|
|
rangefinder_state.correction = 0.8f*rangefinder_state.correction + 0.2f*correction;
|
2015-09-15 22:14:26 -03:00
|
|
|
if (fabsf(rangefinder_state.correction - rangefinder_state.initial_correction) > 30) {
|
|
|
|
// the correction has changed by more than 30m, reset use of Lidar. We may have a bad lidar
|
|
|
|
if (rangefinder_state.in_use) {
|
2016-03-25 20:06:07 -03:00
|
|
|
gcs_send_text_fmt(MAV_SEVERITY_INFO, "Rangefinder disengaged at %.2fm", (double)rangefinder_state.height_estimate);
|
2015-09-15 22:14:26 -03:00
|
|
|
}
|
|
|
|
memset(&rangefinder_state, 0, sizeof(rangefinder_state));
|
|
|
|
}
|
2014-08-27 05:25:17 -03:00
|
|
|
}
|
2016-05-17 22:21:21 -03:00
|
|
|
rangefinder_state.last_correction_time_ms = now;
|
2014-08-26 22:50:07 -03:00
|
|
|
}
|
2014-08-26 08:18:24 -03:00
|
|
|
}
|
2015-05-13 20:00:11 -03:00
|
|
|
#endif
|