// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
/// @file tecs.h
/// @brief Combined Total Energy Speed & Height Control.
/*
* Written by Paul Riseborough 2013 to provide:
* - Combined control of speed and height using throttle to control
* total energy and pitch angle to control exchange of energy between
* potential and kinetic.
* Selectable speed or height priority modes when calculating pitch angle
* - Fallback mode when no airspeed measurement is available that
* sets throttle based on height rate demand and switches pitch angle control to
* height priority
* - Underspeed protection that demands maximum throttle switches pitch angle control
* to speed priority mode
* - Relative ease of tuning through use of intuitive time constant, trim rate and damping parameters and the use
* of easy to measure aircraft performance data
*/
#ifndef TECS_H
#define TECS_H
#include <mathlib/mathlib.h>
#include <stdint.h>
class __EXPORT TECS
{
public:
TECS() :
_airspeed_enabled(false),
_throttle_slewrate(0.0f),
_climbOutDem(false),
_hgt_dem_prev(0.0f),
_hgt_dem_adj_last(0.0f),
_hgt_dem_in_old(0.0f),
_TAS_dem_last(0.0f),
_TAS_dem_adj(0.0f),
_TAS_dem(0.0f),
_integ1_state(0.0f),
_integ2_state(0.0f),
_integ3_state(0.0f),
_integ4_state(0.0f),
_integ5_state(0.0f),
_integ6_state(0.0f),
_integ7_state(0.0f),
_pitch_dem(0.0f),
_last_pitch_dem(0.0f),
_SPE_dem(0.0f),
_SKE_dem(0.0f),
_SPEdot_dem(0.0f),
_SKEdot_dem(0.0f),
_SPE_est(0.0f),
_SKE_est(0.0f),
_SPEdot(0.0f),
_SKEdot(0.0f),
_vel_dot(0.0f),
_STEdotErrLast(0.0f) {
}
bool airspeed_sensor_enabled() {
return _airspeed_enabled;
}
void enable_airspeed(bool enabled) {
_airspeed_enabled = enabled;
}
// Update of the estimated height and height rate internal state
// Update of the inertial speed rate internal state
// Should be called at 50Hz or greater
void update_50hz(float baro_altitude, float airspeed, const math::Dcm &rotMat, const math::Vector3 &accel_body, const math::Vector3 &accel_earth);
// Update the control loop calculations
void update_pitch_throttle(const math::Dcm &rotMat, float pitch, float baro_altitude, float hgt_dem, float EAS_dem, float indicated_airspeed, float EAS2TAS, bool climbOutDem, float ptchMinCO,
float throttle_min, float throttle_max, float throttle_cruise,
float pitch_limit_min, float pitch_limit_max);
// demanded throttle in percentage
// should return 0 to 100
float get_throttle_demand(void) {
return _throttle_dem;
}
int32_t get_throttle_demand_percent(void) {
return get_throttle_demand();
}
float get_pitch_demand() { return _pitch_dem; }
// demanded pitch angle in centi-degrees
// should return between -9000 to +9000
int32_t get_pitch_demand_cd() { return int32_t(get_pitch_demand() * 5729.5781f);}
// Rate of change of velocity along X body axis in m/s^2
float get_VXdot(void) { return _vel_dot; }
float get_speed_weight() {
return _spdWeight;
}
// log data on internal state of the controller. Called at 10Hz
// void log_data(DataFlash_Class &dataflash, uint8_t msgid);
// struct PACKED log_TECS_Tuning {
// LOG_PACKET_HEADER;
// float hgt;
// float dhgt;
// float hgt_dem;
// float dhgt_dem;
// float spd_dem;
// float spd;
// float dspd;
// float ithr;
// float iptch;
// float thr;
// float ptch;
// float dspd_dem;
// } log_tuning;
void set_time_const(float time_const) {
_timeConst = time_const;
}
void set_min_sink_rate(float rate) {
_minSinkRate = rate;
}
void set_max_sink_rate(float sink_rate) {
_maxSinkRate = sink_rate;
}
void set_max_climb_rate(float climb_rate) {
_maxClimbRate = climb_rate;
}
void set_throttle_damp(float throttle_damp) {
_thrDamp = throttle_damp;
}
void set_integrator_gain(float gain) {
_integGain = gain;
}
void set_vertical_accel_limit(float limit) {
_vertAccLim = limit;
}
void set_height_comp_filter_omega(float omega) {
_hgtCompFiltOmega = omega;
}
void set_speed_comp_filter_omega(float omega) {
_spdCompFiltOmega = omega;
}
void set_roll_throttle_compensation(float compensation) {
_rollComp = compensation;
}
void set_speed_weight(float weight) {
_spdWeight = weight;
}
void set_pitch_damping(float damping) {
_ptchDamp = damping;
}
void set_throttle_slewrate(float slewrate) {
_throttle_slewrate = slewrate;
}
void set_indicated_airspeed_min(float airspeed) {
_indicated_airspeed_min = airspeed;
}
void set_indicated_airspeed_max(float airspeed) {
_indicated_airspeed_max = airspeed;
}
void set_heightrate_p(float heightrate_p) {
_heightrate_p = heightrate_p;
}
private:
// Last time update_50Hz was called
uint64_t _update_50hz_last_usec;
// Last time update_speed was called
uint64_t _update_speed_last_usec;
// Last time update_pitch_throttle was called
uint64_t _update_pitch_throttle_last_usec;
// TECS tuning parameters
float _hgtCompFiltOmega;
float _spdCompFiltOmega;
float _maxClimbRate;
float _minSinkRate;
float _maxSinkRate;
float _timeConst;
float _ptchDamp;
float _thrDamp;
float _integGain;
float _vertAccLim;
float _rollComp;
float _spdWeight;
float _heightrate_p;
// throttle demand in the range from 0.0 to 1.0
float _throttle_dem;
// pitch angle demand in radians
float _pitch_dem;
// Integrator state 1 - height filter second derivative
float _integ1_state;
// Integrator state 2 - height rate
float _integ2_state;
// Integrator state 3 - height
float _integ3_state;
// Integrator state 4 - airspeed filter first derivative
float _integ4_state;
// Integrator state 5 - true airspeed
float _integ5_state;
// Integrator state 6 - throttle integrator
float _integ6_state;
// Integrator state 6 - pitch integrator
float _integ7_state;
// throttle demand rate limiter state
float _last_throttle_dem;
// pitch demand rate limiter state
float _last_pitch_dem;
// Rate of change of speed along X axis
float _vel_dot;
// Equivalent airspeed
float _EAS;
// True airspeed limits
float _TASmax;
float _TASmin;
// Current and last true airspeed demand
float _TAS_dem;
float _TAS_dem_last;
// Equivalent airspeed demand
float _EAS_dem;
// height demands
float _hgt_dem;
float _hgt_dem_in_old;
float _hgt_dem_adj;
float _hgt_dem_adj_last;
float _hgt_rate_dem;
float _hgt_dem_prev;
// Speed demand after application of rate limiting
// This is the demand tracked by the TECS control loops
float _TAS_dem_adj;
// Speed rate demand after application of rate limiting
// This is the demand tracked by the TECS control loops
float _TAS_rate_dem;
// Total energy rate filter state
float _STEdotErrLast;
// Underspeed condition
bool _underspeed;
// Bad descent condition caused by unachievable airspeed demand
bool _badDescent;
// climbout mode
bool _climbOutDem;
// throttle demand before limiting
float _throttle_dem_unc;
// pitch demand before limiting
float _pitch_dem_unc;
// Maximum and minimum specific total energy rate limits
float _STEdot_max;
float _STEdot_min;
// Maximum and minimum floating point throttle limits
float _THRmaxf;
float _THRminf;
// Maximum and minimum floating point pitch limits
float _PITCHmaxf;
float _PITCHminf;
// Specific energy quantities
float _SPE_dem;
float _SKE_dem;
float _SPEdot_dem;
float _SKEdot_dem;
float _SPE_est;
float _SKE_est;
float _SPEdot;
float _SKEdot;
// Specific energy error quantities
float _STE_error;
// Time since last update of main TECS loop (seconds)
float _DT;
bool _airspeed_enabled;
float _throttle_slewrate;
float _indicated_airspeed_min;
float _indicated_airspeed_max;
// Update the airspeed internal state using a second order complementary filter
void _update_speed(float airspeed_demand, float indicated_airspeed,
float indicated_airspeed_min, float indicated_airspeed_max, float EAS2TAS);
// Update the demanded airspeed
void _update_speed_demand(void);
// Update the demanded height
void _update_height_demand(float demand, float state);
// Detect an underspeed condition
void _detect_underspeed(void);
// Update Specific Energy Quantities
void _update_energies(void);
// Update Demanded Throttle
void _update_throttle(float throttle_cruise, const math::Dcm &rotMat);
// Detect Bad Descent
void _detect_bad_descent(void);
// Update Demanded Pitch Angle
void _update_pitch(void);
// Initialise states and variables
void _initialise_states(float pitch, float throttle_cruise, float baro_altitude, float ptchMinCO_rad);
// Calculate specific total energy rate limits
void _update_STE_rate_lim(void);
};
#endif //TECS_H