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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
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
|
|
|
|
*/
|
|
|
|
static void adjust_altitude_target()
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
} else if (flight_stage == AP_SpdHgtControl::FLIGHT_LAND_APPROACH) {
|
2014-08-27 01:33:57 -03:00
|
|
|
setup_landing_glide_slope();
|
2015-01-01 00:17:45 -04:00
|
|
|
} else if (nav_controller->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);
|
|
|
|
} else if (mission.get_current_do_cmd().id != MAV_CMD_CONDITION_CHANGE_ALT) {
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
static void setup_glide_slope(void)
|
|
|
|
{
|
|
|
|
// 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);
|
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
|
|
|
|
https://github.com/diydrones/ardupilot/issues/39
|
|
|
|
*/
|
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.
|
2014-08-04 06:59:33 -03:00
|
|
|
if (relative_altitude() > 20 || 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
|
|
|
|
*/
|
|
|
|
static int32_t get_RTL_altitude()
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
*/
|
|
|
|
static float relative_altitude(void)
|
|
|
|
{
|
|
|
|
return (current_loc.alt - home.alt) * 0.01f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
return relative altitude in centimeters, absolute value
|
|
|
|
*/
|
|
|
|
static int32_t relative_altitude_abs_cm(void)
|
|
|
|
{
|
|
|
|
return labs(current_loc.alt - home.alt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
static void set_target_altitude_current(void)
|
|
|
|
{
|
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
|
|
|
|
*/
|
|
|
|
static void set_target_altitude_current_adjusted(void)
|
|
|
|
{
|
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
|
|
|
|
*/
|
|
|
|
static void set_target_altitude_location(const Location &loc)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
static int32_t relative_target_altitude_cm(void)
|
|
|
|
{
|
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;
|
|
|
|
relative_alt += int32_t(g.alt_offset)*100;
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
static void change_target_altitude(int32_t change_cm)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
static void set_target_altitude_proportion(const Location &loc, float proportion)
|
|
|
|
{
|
|
|
|
set_target_altitude_location(loc);
|
|
|
|
proportion = constrain_float(proportion, 0.0f, 1.0f);
|
|
|
|
change_target_altitude(-target_altitude.offset_cm*proportion);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
constrain target altitude to be between two locations. Used to
|
|
|
|
ensure we stay within two waypoints in altitude
|
|
|
|
*/
|
|
|
|
static void constrain_target_altitude_location(const Location &loc1, const Location &loc2)
|
|
|
|
{
|
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
|
|
|
|
*/
|
|
|
|
static int32_t calc_altitude_error_cm(void)
|
|
|
|
{
|
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
|
|
|
|
*/
|
|
|
|
static void check_minimum_altitude(void)
|
|
|
|
{
|
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
|
|
|
|
*/
|
|
|
|
static void reset_offset_altitude(void)
|
|
|
|
{
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
static void set_offset_altitude_location(const Location &loc)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2014-08-04 06:58:12 -03:00
|
|
|
if (flight_stage != AP_SpdHgtControl::FLIGHT_LAND_APPROACH &&
|
|
|
|
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
|
|
|
|
if (g.glide_slope_threshold <= 0 ||
|
|
|
|
labs(target_altitude.offset_cm)*0.01f < g.glide_slope_threshold) {
|
|
|
|
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
|
|
|
*/
|
2014-07-24 06:56:15 -03:00
|
|
|
static bool 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;
|
|
|
|
if (!loc.flags.relative_alt) {
|
|
|
|
loc_alt_cm -= home.alt;
|
|
|
|
}
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
static void setup_terrain_target_alt(Location &loc)
|
|
|
|
{
|
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
|
|
|
|
*/
|
|
|
|
static int32_t adjusted_altitude_cm(void)
|
2014-07-24 03:21:30 -03:00
|
|
|
{
|
2014-07-24 04:54:58 -03:00
|
|
|
return current_loc.alt - (g.alt_offset*100);
|
2014-07-24 03:21:30 -03:00
|
|
|
}
|
2014-08-06 20:31:03 -03:00
|
|
|
|
2014-08-11 03:46:08 -03:00
|
|
|
/*
|
|
|
|
return the height in meters above the next_WP_loc altitude
|
|
|
|
*/
|
|
|
|
static float height_above_target(void)
|
|
|
|
{
|
|
|
|
float target_alt = next_WP_loc.alt*0.01;
|
|
|
|
if (!next_WP_loc.flags.relative_alt) {
|
|
|
|
target_alt -= ahrs.get_home().alt*0.01;
|
|
|
|
}
|
|
|
|
|
|
|
|
#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
|
|
|
|
*/
|
|
|
|
static float lookahead_adjustment(void)
|
|
|
|
{
|
|
|
|
#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;
|
|
|
|
} else if (!nav_controller->reached_loiter_target()) {
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
static float rangefinder_correction(void)
|
|
|
|
{
|
2014-08-26 22:50:07 -03:00
|
|
|
if (hal.scheduler->millis() - rangefinder_state.last_correction_time_ms > 5000) {
|
|
|
|
// 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 ||
|
|
|
|
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;
|
|
|
|
}
|
2014-08-26 08:18:24 -03:00
|
|
|
|
2014-08-26 22:50:07 -03:00
|
|
|
|
|
|
|
/*
|
|
|
|
update the offset between rangefinder height and terrain height
|
|
|
|
*/
|
|
|
|
static void rangefinder_height_update(void)
|
|
|
|
{
|
|
|
|
uint16_t distance_cm = rangefinder.distance_cm();
|
|
|
|
int16_t max_distance_cm = rangefinder.max_distance_cm();
|
2014-08-27 05:25:17 -03:00
|
|
|
float height_estimate = 0;
|
|
|
|
if (rangefinder.healthy() && distance_cm < max_distance_cm && home_is_set) {
|
2014-08-26 22:50:07 -03:00
|
|
|
// correct the range for attitude (multiply by DCM.c.z, which
|
|
|
|
// is cos(roll)*cos(pitch))
|
2014-08-27 05:25:17 -03:00
|
|
|
height_estimate = distance_cm * 0.01f * ahrs.get_dcm_matrix().c.z;
|
|
|
|
|
2014-08-26 22:50:07 -03:00
|
|
|
// we consider ourselves to be fully in range when we have 10
|
|
|
|
// good samples (0.2s)
|
|
|
|
if (rangefinder_state.in_range_count < 10) {
|
|
|
|
rangefinder_state.in_range_count++;
|
|
|
|
} else {
|
|
|
|
rangefinder_state.in_range = true;
|
|
|
|
}
|
|
|
|
} 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
|
2014-08-27 05:25:17 -03:00
|
|
|
float correction = relative_altitude() - 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)) {
|
2014-08-27 05:25:17 -03:00
|
|
|
correction = terrain_altitude - 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
|
|
|
|
if (hal.scheduler->millis() - rangefinder_state.last_correction_time_ms > 5000) {
|
|
|
|
rangefinder_state.correction = correction;
|
|
|
|
} else {
|
|
|
|
rangefinder_state.correction = 0.8f*rangefinder_state.correction + 0.2f*correction;
|
|
|
|
}
|
2014-08-26 22:50:07 -03:00
|
|
|
rangefinder_state.last_correction_time_ms = hal.scheduler->millis();
|
|
|
|
}
|
2014-08-26 08:18:24 -03:00
|
|
|
}
|