#include <AP_HAL/AP_HAL.h>
#include "AC_WPNav.h"
extern const AP_HAL::HAL& hal;
#define WPNAV_WP_SPEED_CMS 1000.0f
#define WPNAV_WP_SPEED_MIN_MS 0.01f
#define WPNAV_WP_RADIUS_CM 200.0f
#define WPNAV_WP_RADIUS_MIN_CM 5.0f
#define WPNAV_WP_SPEED_UP_CMS 250.0f
#define WPNAV_WP_SPEED_DOWN_CMS 150.0f
#define WPNAV_WP_ACCEL_Z_DEFAULT_CMSS 100.0f
const AP_Param::GroupInfo AC_WPNav::var_info[] = {
AP_GROUPINFO("SPEED", 0, AC_WPNav, _wp_speed_cms, WPNAV_WP_SPEED_CMS),
AP_GROUPINFO("RADIUS", 1, AC_WPNav, _wp_radius_cm, WPNAV_WP_RADIUS_CM),
AP_GROUPINFO("SPEED_UP", 2, AC_WPNav, _wp_speed_up_cms, WPNAV_WP_SPEED_UP_CMS),
AP_GROUPINFO("SPEED_DN", 3, AC_WPNav, _wp_speed_down_cms, WPNAV_WP_SPEED_DOWN_CMS),
AP_GROUPINFO("ACCEL", 5, AC_WPNav, _wp_accel_cmss, WPNAV_ACCELERATION_MS * 100.0),
AP_GROUPINFO("ACCEL_Z", 6, AC_WPNav, _wp_accel_z_cmss, WPNAV_WP_ACCEL_Z_DEFAULT_CMSS),
AP_GROUPINFO("RFND_USE", 10, AC_WPNav, _rangefinder_use, 1),
AP_GROUPINFO("JERK", 11, AC_WPNav, _wp_jerk_msss, 1.0f),
AP_GROUPINFO("TER_MARGIN", 12, AC_WPNav, _terrain_margin_m, 10.0),
AP_GROUPINFO("ACCEL_C", 13, AC_WPNav, _wp_accel_c_cmss, 0.0),
AP_GROUPEND
};
AC_WPNav::AC_WPNav(const AP_AHRS_View& ahrs, AC_PosControl& pos_control, const AC_AttitudeControl& attitude_control) :
_ahrs(ahrs),
_pos_control(pos_control),
_attitude_control(attitude_control)
{
AP_Param::setup_object_defaults(this, var_info);
_flags.reached_destination = false;
_flags.fast_waypoint = false;
_last_wp_speed_cms = _wp_speed_cms;
_last_wp_speed_up_cms = _wp_speed_up_cms;
_last_wp_speed_down_cms = get_default_speed_down_cms();
}
AC_WPNav::TerrainSource AC_WPNav::get_terrain_source() const
{
if (_rangefinder_available && _rangefinder_use) {
return AC_WPNav::TerrainSource::TERRAIN_FROM_RANGEFINDER;
}
#if AP_TERRAIN_AVAILABLE
const AP_Terrain *terrain = AP::terrain();
if (terrain != nullptr && terrain->enabled()) {
return AC_WPNav::TerrainSource::TERRAIN_FROM_TERRAINDATABASE;
} else {
return AC_WPNav::TerrainSource::TERRAIN_UNAVAILABLE;
}
#else
return AC_WPNav::TerrainSource::TERRAIN_UNAVAILABLE;
#endif
}
void AC_WPNav::wp_and_spline_init_m(float speed_ms, Vector3p stopping_point_ned_m)
{
_wp_radius_cm.set_and_save_ifchanged(MAX(_wp_radius_cm, WPNAV_WP_RADIUS_MIN_CM));
_wp_speed_cms.set_and_save_ifchanged(MAX(_wp_speed_cms, WPNAV_WP_SPEED_MIN_MS * 100.0));
_pos_control.D_init_controller_stopping_point();
_pos_control.NE_init_controller_stopping_point();
_check_wp_speed_change = !is_positive(speed_ms);
_wp_desired_speed_ne_ms = is_positive(speed_ms) ? speed_ms : get_default_speed_NE_ms();
_wp_desired_speed_ne_ms = MAX(_wp_desired_speed_ne_ms, WPNAV_WP_SPEED_MIN_MS);
_pos_control.NE_set_max_speed_accel_m(_wp_desired_speed_ne_ms, get_wp_acceleration_mss());
_pos_control.NE_set_correction_speed_accel_m(_wp_desired_speed_ne_ms, get_wp_acceleration_mss());
_pos_control.D_set_max_speed_accel_m(get_default_speed_down_ms(), get_default_speed_up_ms(), get_accel_D_mss());
_pos_control.D_set_correction_speed_accel_m(get_default_speed_down_ms(), get_default_speed_up_ms(), get_accel_D_mss());
if (!is_positive(_wp_jerk_msss)) {
_wp_jerk_msss.set(get_wp_acceleration_mss());
}
calc_scurve_jerk_and_snap();
_scurve_prev_leg.init();
_scurve_this_leg.init();
_scurve_next_leg.init();
_track_dt_scalar = 1.0f;
_flags.reached_destination = true;
_flags.fast_waypoint = false;
if (stopping_point_ned_m.is_zero()) {
get_wp_stopping_point_NED_m(stopping_point_ned_m);
}
_origin_ned_m = _destination_ned_m = stopping_point_ned_m;
_is_terrain_alt = false;
_this_leg_is_spline = false;
_offset_vel_ms = _wp_desired_speed_ne_ms;
_offset_accel_mss = 0.0;
_paused = false;
_wp_last_update_ms = AP_HAL::millis();
}
void AC_WPNav::set_speed_NE_cms(float speed_cms)
{
set_speed_NE_ms(speed_cms * 0.01);
}
void AC_WPNav::set_speed_NE_ms(float speed_ms)
{
if (speed_ms >= WPNAV_WP_SPEED_MIN_MS && is_positive(_wp_desired_speed_ne_ms)) {
_offset_vel_ms = speed_ms * _offset_vel_ms / _wp_desired_speed_ne_ms;
_wp_desired_speed_ne_ms = speed_ms;
_pos_control.NE_set_max_speed_accel_m(_wp_desired_speed_ne_ms, get_wp_acceleration_mss());
_pos_control.NE_set_correction_speed_accel_m(_wp_desired_speed_ne_ms, get_wp_acceleration_mss());
update_track_with_speed_accel_limits();
}
}
void AC_WPNav::set_speed_up_ms(float speed_up_ms)
{
_pos_control.D_set_max_speed_accel_m(_pos_control.get_max_speed_down_ms(), speed_up_ms, _pos_control.D_get_max_accel_mss());
update_track_with_speed_accel_limits();
}
void AC_WPNav::set_speed_down_ms(float speed_down_ms)
{
_pos_control.D_set_max_speed_accel_m(speed_down_ms, _pos_control.get_max_speed_up_ms(), _pos_control.D_get_max_accel_mss());
update_track_with_speed_accel_limits();
}
bool AC_WPNav::set_wp_destination_loc(const Location& destination, float arc_rad)
{
bool is_terrain_alt;
Vector3p dest_ned_m;
if (!get_vector_NED_m(destination, dest_ned_m, is_terrain_alt)) {
return false;
}
return set_wp_destination_NED_m(dest_ned_m, is_terrain_alt, arc_rad);
}
bool AC_WPNav::set_wp_destination_next_loc(const Location& destination, float arc_rad)
{
bool is_terrain_alt;
Vector3p dest_ned_m;
if (!get_vector_NED_m(destination, dest_ned_m, is_terrain_alt)) {
return false;
}
return set_wp_destination_next_NED_m(dest_ned_m, is_terrain_alt, arc_rad);
}
bool AC_WPNav::get_wp_destination_loc(Location& destination) const
{
if (!AP::ahrs().get_origin(destination)) {
return false;
}
destination = Location::from_ekf_offset_NED_m(get_wp_destination_NED_m(), _is_terrain_alt ? Location::AltFrame::ABOVE_TERRAIN : Location::AltFrame::ABOVE_ORIGIN);
return true;
}
bool AC_WPNav::set_wp_destination_NEU_cm(const Vector3f& destination_neu_cm, bool is_terrain_alt)
{
Vector3p destination_ned_m = Vector3p(destination_neu_cm.x, destination_neu_cm.y, -destination_neu_cm.z) * 0.01;
return set_wp_destination_NED_m(destination_ned_m, is_terrain_alt);
}
bool AC_WPNav::set_wp_destination_NED_m(const Vector3p& destination_ned_m, bool is_terrain_alt, float arc_rad)
{
if (!is_active() || !_flags.reached_destination) {
wp_and_spline_init_m(_wp_desired_speed_ne_ms);
}
_scurve_prev_leg.init();
float origin_speed_m = 0.0f;
_origin_ned_m = _destination_ned_m;
if (is_terrain_alt == _is_terrain_alt) {
if (_this_leg_is_spline) {
Vector3f curr_target_vel_ned_ms = _pos_control.get_vel_desired_NED_ms();
curr_target_vel_ned_ms.z -= _pos_control.get_vel_offset_D_ms();
origin_speed_m = curr_target_vel_ned_ms.length();
} else {
_scurve_prev_leg = _scurve_this_leg;
}
} else {
float terrain_d_m;
if (!get_terrain_D_m(terrain_d_m)) {
return false;
}
if (is_terrain_alt) {
_origin_ned_m.z -= terrain_d_m;
_pos_control.init_pos_terrain_D_m(terrain_d_m);
} else {
_origin_ned_m.z += terrain_d_m;
_pos_control.init_pos_terrain_D_m(0.0);
}
}
_destination_ned_m = destination_ned_m;
_is_terrain_alt = is_terrain_alt;
if (_flags.fast_waypoint && !_this_leg_is_spline && !_next_leg_is_spline && !_scurve_next_leg.finished()) {
_scurve_this_leg = _scurve_next_leg;
} else {
_scurve_this_leg.calculate_track(_origin_ned_m, _destination_ned_m, arc_rad,
_pos_control.NE_get_max_speed_ms(), _pos_control.get_max_speed_up_ms(), _pos_control.get_max_speed_down_ms(),
get_wp_acceleration_mss(), get_accel_D_mss(), get_corner_acceleration_mss(),
_scurve_snap_max_mssss, _scurve_jerk_max_msss);
if (!is_zero(origin_speed_m)) {
_scurve_this_leg.set_origin_speed_max(origin_speed_m);
}
}
_this_leg_is_spline = false;
_scurve_next_leg.init();
_next_destination_ned_m.zero();
_flags.fast_waypoint = false;
_flags.reached_destination = false;
return true;
}
bool AC_WPNav::set_wp_destination_next_NED_m(const Vector3p& destination_ned_m, bool is_terrain_alt, float arc_rad)
{
if (is_terrain_alt != _is_terrain_alt) {
return true;
}
_scurve_next_leg.calculate_track(_destination_ned_m, destination_ned_m, arc_rad,
_pos_control.NE_get_max_speed_ms(), _pos_control.get_max_speed_up_ms(), _pos_control.get_max_speed_down_ms(),
get_wp_acceleration_mss(), get_accel_D_mss(), get_corner_acceleration_mss(),
_scurve_snap_max_mssss, _scurve_jerk_max_msss);
if (_this_leg_is_spline) {
const float this_leg_dest_speed_max_ms = _spline_this_leg.get_destination_speed_max();
const float next_leg_origin_speed_max_ms = _scurve_next_leg.set_origin_speed_max(this_leg_dest_speed_max_ms);
_spline_this_leg.set_destination_speed_max(next_leg_origin_speed_max_ms);
}
_next_leg_is_spline = false;
_flags.fast_waypoint = true;
_next_destination_ned_m = destination_ned_m;
return true;
}
void AC_WPNav::get_wp_stopping_point_NE_cm(Vector2f& stopping_point_ne_cm) const
{
Vector2p stopping_point_ne_m = stopping_point_ne_cm.topostype() * 0.01;
get_wp_stopping_point_NE_m(stopping_point_ne_m);
stopping_point_ne_cm = stopping_point_ne_m.tofloat() * 100.0;
}
void AC_WPNav::get_wp_stopping_point_NE_m(Vector2p& stopping_point_ne_m) const
{
_pos_control.get_stopping_point_NE_m(stopping_point_ne_m);
}
void AC_WPNav::get_wp_stopping_point_NEU_cm(Vector3f& stopping_point_neu_cm) const
{
Vector3p stopping_point_ned_m = Vector3p(stopping_point_neu_cm.x, stopping_point_neu_cm.y, -stopping_point_neu_cm.z) * 0.01;
get_wp_stopping_point_NED_m(stopping_point_ned_m);
stopping_point_neu_cm = Vector3f(stopping_point_ned_m.x, stopping_point_ned_m.y, -stopping_point_ned_m.z) * 100.0;
}
void AC_WPNav::get_wp_stopping_point_NED_m(Vector3p& stopping_point_ned_m) const
{
_pos_control.get_stopping_point_NE_m(stopping_point_ned_m.xy());
_pos_control.get_stopping_point_D_m(stopping_point_ned_m.z);
}
bool AC_WPNav::advance_wp_target_along_track(float dt)
{
float terr_offset_d_m = 0.0f;
if (_is_terrain_alt && !get_terrain_D_m(terr_offset_d_m)) {
return false;
}
const float offset_d_scalar = _pos_control.terrain_scaler_D_m(terr_offset_d_m, get_terrain_margin_m());
_pos_control.set_pos_terrain_target_D_m(terr_offset_d_m);
const Vector3p& psc_pos_offset_ned_m = _pos_control.get_pos_offset_NED_m();
Vector3p curr_pos_ned_m = _pos_control.get_pos_estimate_NED_m() - psc_pos_offset_ned_m;
curr_pos_ned_m.z -= terr_offset_d_m;
Vector3f curr_target_vel_ned_ms = _pos_control.get_vel_desired_NED_ms();
curr_target_vel_ned_ms.z -= _pos_control.get_vel_offset_D_ms();
float track_dt_scalar = 1.0f;
if (is_positive(curr_target_vel_ned_ms.length_squared())) {
Vector3f track_direction_neu = curr_target_vel_ned_ms.normalized();
const float track_error_ned_m = _pos_control.get_pos_error_NED_m().dot(track_direction_neu);
const float track_velocity_ned_ms = _pos_control.get_vel_estimate_NED_ms().dot(track_direction_neu);
track_dt_scalar = constrain_float(0.05f + (track_velocity_ned_ms - _pos_control.NE_get_pos_p().kP() * track_error_ned_m) / curr_target_vel_ned_ms.length(), 0.0f, 1.0f);
}
float vel_dt_scalar = 1.0;
if (is_positive(_wp_desired_speed_ne_ms)) {
update_vel_accel(_offset_vel_ms, _offset_accel_mss, dt, 0.0, 0.0);
const float vel_input_ms = !_paused ? _wp_desired_speed_ne_ms * offset_d_scalar : 0.0;
shape_vel_accel(vel_input_ms, 0.0, _offset_vel_ms, _offset_accel_mss, -get_wp_acceleration_mss(), get_wp_acceleration_mss(),
_pos_control.get_shaping_jerk_NE_msss(), dt, true);
vel_dt_scalar = _offset_vel_ms / _wp_desired_speed_ne_ms;
}
float track_dt_scalar_tc = 1.0f;
if (!is_zero(_wp_jerk_msss)) {
track_dt_scalar_tc = get_wp_acceleration_mss()/_wp_jerk_msss;
}
_track_dt_scalar += (track_dt_scalar - _track_dt_scalar) * (dt / track_dt_scalar_tc);
Vector3p target_pos_ned_m;
Vector3f target_vel_ned_ms, target_accel_ned_mss;
bool s_finished;
if (!_this_leg_is_spline) {
target_pos_ned_m = _origin_ned_m;
s_finished = _scurve_this_leg.advance_target_along_track(_scurve_prev_leg, _scurve_next_leg, _wp_radius_cm * 0.01, get_corner_acceleration_mss(), _flags.fast_waypoint, _track_dt_scalar * vel_dt_scalar * dt, target_pos_ned_m, target_vel_ned_ms, target_accel_ned_mss);
} else {
target_vel_ned_ms = curr_target_vel_ned_ms;
_spline_this_leg.advance_target_along_track(_track_dt_scalar * vel_dt_scalar * dt, target_pos_ned_m, target_vel_ned_ms);
s_finished = _spline_this_leg.reached_destination();
}
Vector3f accel_offset_ned_mss;
if (is_positive(target_vel_ned_ms.length_squared())) {
Vector3f track_direction_neu = target_vel_ned_ms.normalized();
accel_offset_ned_mss = track_direction_neu * _offset_accel_mss * target_vel_ned_ms.length() / _wp_desired_speed_ne_ms;
}
target_vel_ned_ms *= vel_dt_scalar;
target_accel_ned_mss *= sq(vel_dt_scalar);
target_accel_ned_mss += accel_offset_ned_mss;
_pos_control.set_pos_vel_accel_NED_m(target_pos_ned_m, target_vel_ned_ms, target_accel_ned_mss);
if (!_flags.reached_destination) {
if (s_finished) {
if (_flags.fast_waypoint) {
_flags.reached_destination = true;
} else {
const Vector3f dist_to_dest_m = (curr_pos_ned_m - _destination_ned_m).tofloat();
if (dist_to_dest_m.length_squared() <= sq(_wp_radius_cm * 0.01)) {
_flags.reached_destination = true;
}
}
}
}
return true;
}
void AC_WPNav::update_track_with_speed_accel_limits()
{
if (_this_leg_is_spline) {
_spline_this_leg.set_speed_accel(_pos_control.NE_get_max_speed_ms(), _pos_control.get_max_speed_up_ms(), _pos_control.get_max_speed_down_ms(),
get_wp_acceleration_mss(), get_accel_D_mss());
} else {
_scurve_this_leg.set_speed_max(_pos_control.NE_get_max_speed_ms(), _pos_control.get_max_speed_up_ms(), _pos_control.get_max_speed_down_ms());
}
if (_next_leg_is_spline) {
_spline_next_leg.set_speed_accel(_pos_control.NE_get_max_speed_ms(), _pos_control.get_max_speed_up_ms(), _pos_control.get_max_speed_down_ms(),
get_wp_acceleration_mss(), get_accel_D_mss());
} else {
_scurve_next_leg.set_speed_max(_pos_control.NE_get_max_speed_ms(), _pos_control.get_max_speed_up_ms(), _pos_control.get_max_speed_down_ms());
}
}
float AC_WPNav::get_wp_distance_to_destination_cm() const
{
return get_wp_distance_to_destination_m() * 100.0;
}
float AC_WPNav::get_wp_distance_to_destination_m() const
{
return get_horizontal_distance(_pos_control.get_pos_estimate_NED_m().xy().tofloat(), _destination_ned_m.xy().tofloat());
}
int32_t AC_WPNav::get_wp_bearing_to_destination_cd() const
{
return get_bearing_cd(_pos_control.get_pos_estimate_NED_m().xy().tofloat(), _destination_ned_m.xy().tofloat());
}
float AC_WPNav::get_wp_bearing_to_destination_rad() const
{
return get_bearing_rad(_pos_control.get_pos_estimate_NED_m().xy().tofloat(), _destination_ned_m.xy().tofloat());
}
bool AC_WPNav::update_wpnav()
{
if (_check_wp_speed_change) {
if (!is_equal(_wp_speed_cms.get(), _last_wp_speed_cms)) {
set_speed_NE_ms(get_default_speed_NE_ms());
_last_wp_speed_cms = _wp_speed_cms;
}
}
if (!is_equal(_wp_speed_up_cms.get(), _last_wp_speed_up_cms)) {
set_speed_up_ms(get_default_speed_up_ms());
_last_wp_speed_up_cms = _wp_speed_up_cms;
}
if (!is_equal(_wp_speed_down_cms.get(), _last_wp_speed_down_cms)) {
set_speed_down_ms(get_default_speed_down_ms());
_last_wp_speed_down_cms = _wp_speed_down_cms;
}
bool ret = true;
if (!advance_wp_target_along_track(_pos_control.get_dt_s())) {
ret = false;
}
_pos_control.NE_update_controller();
_wp_last_update_ms = AP_HAL::millis();
return ret;
}
bool AC_WPNav::is_active() const
{
return (AP_HAL::millis() - _wp_last_update_ms) < 200;
}
bool AC_WPNav::force_stop_at_next_wp()
{
if (!_flags.fast_waypoint) {
return false;
}
_flags.fast_waypoint = false;
if (!_this_leg_is_spline) {
_scurve_this_leg.set_destination_speed_max(0);
}
if (!_next_leg_is_spline) {
_scurve_next_leg.init();
}
return true;
}
bool AC_WPNav::get_terrain_U_m(float& terrain_u_m)
{
switch (get_terrain_source()) {
case AC_WPNav::TerrainSource::TERRAIN_UNAVAILABLE:
return false;
case AC_WPNav::TerrainSource::TERRAIN_FROM_RANGEFINDER:
if (_rangefinder_healthy) {
terrain_u_m = _rangefinder_terrain_u_m;
return true;
}
return false;
case AC_WPNav::TerrainSource::TERRAIN_FROM_TERRAINDATABASE:
#if AP_TERRAIN_AVAILABLE
float height_above_terrain_m = 0.0f;
AP_Terrain *terrain = AP::terrain();
if (terrain != nullptr &&
terrain->height_above_terrain(height_above_terrain_m, true)) {
terrain_u_m = _pos_control.get_pos_estimate_U_m() - height_above_terrain_m;
return true;
}
#endif
return false;
}
return false;
}
bool AC_WPNav::get_terrain_D_m(float& terrain_d_m)
{
if (!get_terrain_U_m(terrain_d_m)) {
return false;
}
terrain_d_m *= -1.0;
return true;
}
bool AC_WPNav::set_spline_destination_loc(const Location& destination, const Location& next_destination, bool next_is_spline)
{
Vector3p dest_ned_m;
bool dest_is_terrain_alt;
if (!get_vector_NED_m(destination, dest_ned_m, dest_is_terrain_alt)) {
return false;
}
Vector3p next_dest_ned_m;
bool next_dest_is_terrain_alt;
if (!get_vector_NED_m(next_destination, next_dest_ned_m, next_dest_is_terrain_alt)) {
return false;
}
return set_spline_destination_NED_m(dest_ned_m, dest_is_terrain_alt, next_dest_ned_m, next_dest_is_terrain_alt, next_is_spline);
}
bool AC_WPNav::set_spline_destination_next_loc(const Location& next_destination, const Location& next_next_destination, bool next_next_is_spline)
{
Vector3p next_dest_ned_m;
bool next_dest_is_terrain_alt;
if (!get_vector_NED_m(next_destination, next_dest_ned_m, next_dest_is_terrain_alt)) {
return false;
}
Vector3p next_next_dest_ned_m;
bool next_next_dest_is_terr_alt;
if (!get_vector_NED_m(next_next_destination, next_next_dest_ned_m, next_next_dest_is_terr_alt)) {
return false;
}
return set_spline_destination_next_NED_m(next_dest_ned_m, next_dest_is_terrain_alt, next_next_dest_ned_m, next_next_dest_is_terr_alt, next_next_is_spline);
}
bool AC_WPNav::set_spline_destination_NED_m(const Vector3p& destination_ned_m, bool is_terrain_alt, const Vector3p& next_destination_ned_m, bool next_is_terrain_alt, bool next_is_spline)
{
if (!is_active() || !_flags.reached_destination) {
wp_and_spline_init_m(_wp_desired_speed_ne_ms);
}
_spline_this_leg.set_speed_accel(_pos_control.NE_get_max_speed_ms(), _pos_control.get_max_speed_up_ms(), _pos_control.get_max_speed_down_ms(),
_pos_control.NE_get_max_accel_mss(), _pos_control.D_get_max_accel_mss());
Vector3f origin_vector_ned_m;
if (is_terrain_alt == _is_terrain_alt) {
if (_flags.fast_waypoint) {
if (_this_leg_is_spline) {
origin_vector_ned_m = _spline_this_leg.get_destination_vel();
} else {
origin_vector_ned_m = (_destination_ned_m - _origin_ned_m).tofloat();
}
}
_origin_ned_m = _destination_ned_m;
} else {
_origin_ned_m = _destination_ned_m;
float terrain_d_m;
if (!get_terrain_D_m(terrain_d_m)) {
return false;
}
if (is_terrain_alt) {
_origin_ned_m.z -= terrain_d_m;
_pos_control.init_pos_terrain_D_m(terrain_d_m);
} else {
_origin_ned_m.z += terrain_d_m;
_pos_control.init_pos_terrain_D_m(0.0);
}
}
_destination_ned_m = destination_ned_m;
_is_terrain_alt = is_terrain_alt;
Vector3f destination_vector_ned_m;
if (is_terrain_alt == next_is_terrain_alt) {
if (next_is_spline) {
destination_vector_ned_m = (next_destination_ned_m - _origin_ned_m).tofloat();
} else {
destination_vector_ned_m = (next_destination_ned_m - _destination_ned_m).tofloat();
}
}
_flags.fast_waypoint = !destination_vector_ned_m.is_zero();
_spline_this_leg.set_origin_and_destination(_origin_ned_m, _destination_ned_m, origin_vector_ned_m, destination_vector_ned_m);
_this_leg_is_spline = true;
_flags.reached_destination = false;
return true;
}
bool AC_WPNav::set_spline_destination_next_NED_m(const Vector3p& next_destination_ned_m, bool next_is_terrain_alt, const Vector3p& next_next_destination_ned_m, bool next_next_is_terrain_alt, bool next_next_is_spline)
{
if (next_is_terrain_alt != _is_terrain_alt) {
return true;
}
Vector3f origin_vector_ned_m;
if (_this_leg_is_spline) {
origin_vector_ned_m = _spline_this_leg.get_destination_vel();
} else {
origin_vector_ned_m = (_destination_ned_m - _origin_ned_m).tofloat();
}
Vector3f destination_vector_ned_m;
if (next_is_terrain_alt == next_next_is_terrain_alt) {
if (next_next_is_spline) {
destination_vector_ned_m = (next_next_destination_ned_m - _destination_ned_m).tofloat();
} else {
destination_vector_ned_m = (next_next_destination_ned_m - next_destination_ned_m).tofloat();
}
}
_spline_next_leg.set_speed_accel(_pos_control.NE_get_max_speed_ms(), _pos_control.get_max_speed_up_ms(), _pos_control.get_max_speed_down_ms(),
_pos_control.NE_get_max_accel_mss(), _pos_control.D_get_max_accel_mss());
_spline_next_leg.set_origin_and_destination(_destination_ned_m, next_destination_ned_m, origin_vector_ned_m, destination_vector_ned_m);
_next_leg_is_spline = true;
_flags.fast_waypoint = true;
if (!_this_leg_is_spline) {
_scurve_this_leg.set_destination_speed_max(_spline_next_leg.get_origin_speed_max());
} else {
_spline_this_leg.set_destination_speed_max(_spline_next_leg.get_origin_speed_max());
}
return true;
}
bool AC_WPNav::get_vector_NED_m(const Location &loc, Vector3p &pos_from_origin_ned_m, bool &is_terrain_alt)
{
Vector2p loc_pos_from_origin_ned_m;
if (!loc.get_vector_xy_from_origin_NE_m(loc_pos_from_origin_ned_m)) {
return false;
}
if (loc.get_alt_frame() == Location::AltFrame::ABOVE_TERRAIN) {
float terrain_u_m;
if (!loc.get_alt_m(Location::AltFrame::ABOVE_TERRAIN, terrain_u_m)) {
return false;
}
pos_from_origin_ned_m.z = -terrain_u_m;
is_terrain_alt = true;
} else {
is_terrain_alt = false;
float origin_alt_m;
if (!loc.get_alt_m(Location::AltFrame::ABOVE_ORIGIN, origin_alt_m)) {
return false;
}
pos_from_origin_ned_m.z = -origin_alt_m;
is_terrain_alt = false;
}
pos_from_origin_ned_m.xy() = loc_pos_from_origin_ned_m;
return true;
}
void AC_WPNav::calc_scurve_jerk_and_snap()
{
_scurve_jerk_max_msss = MIN(_attitude_control.get_ang_vel_roll_max_rads() * GRAVITY_MSS, _attitude_control.get_ang_vel_pitch_max_rads() * GRAVITY_MSS);
if (is_zero(_scurve_jerk_max_msss)) {
_scurve_jerk_max_msss = _wp_jerk_msss;
} else {
_scurve_jerk_max_msss = MIN(_scurve_jerk_max_msss, _wp_jerk_msss);
}
_scurve_snap_max_mssss = (_scurve_jerk_max_msss * M_PI) / (2.0 * MAX(_attitude_control.get_input_tc(), 0.1f));
const float snap = MIN(_attitude_control.get_accel_roll_max_radss(), _attitude_control.get_accel_pitch_max_radss()) * GRAVITY_MSS;
if (is_positive(snap)) {
_scurve_snap_max_mssss = MIN(_scurve_snap_max_mssss, snap);
}
_scurve_snap_max_mssss *= 0.5;
}