632 lines
21 KiB
C
632 lines
21 KiB
C
/*
|
|
* This file is part of Betaflight.
|
|
*
|
|
* Betaflight 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.
|
|
*
|
|
* Betaflight 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 Betaflight. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
|
|
#include "platform.h"
|
|
|
|
#ifdef USE_GPS_RESCUE
|
|
|
|
#include "build/debug.h"
|
|
|
|
#include "common/axis.h"
|
|
#include "common/maths.h"
|
|
#include "common/utils.h"
|
|
|
|
#include "drivers/time.h"
|
|
|
|
#include "io/gps.h"
|
|
|
|
#include "fc/config.h"
|
|
#include "fc/core.h"
|
|
#include "fc/rc_controls.h"
|
|
#include "fc/rc_modes.h"
|
|
#include "fc/runtime_config.h"
|
|
|
|
#include "flight/failsafe.h"
|
|
#include "flight/imu.h"
|
|
#include "flight/pid.h"
|
|
#include "flight/position.h"
|
|
|
|
#include "pg/pg.h"
|
|
#include "pg/pg_ids.h"
|
|
|
|
#include "rx/rx.h"
|
|
|
|
#include "sensors/acceleration.h"
|
|
|
|
#include "gps_rescue.h"
|
|
|
|
typedef enum {
|
|
RESCUE_SANITY_OFF = 0,
|
|
RESCUE_SANITY_ON,
|
|
RESCUE_SANITY_FS_ONLY
|
|
} gpsRescueSanity_e;
|
|
|
|
typedef enum {
|
|
RESCUE_IDLE,
|
|
RESCUE_INITIALIZE,
|
|
RESCUE_ATTAIN_ALT,
|
|
RESCUE_CROSSTRACK,
|
|
RESCUE_LANDING_APPROACH,
|
|
RESCUE_LANDING,
|
|
RESCUE_ABORT,
|
|
RESCUE_COMPLETE
|
|
} rescuePhase_e;
|
|
|
|
typedef enum {
|
|
RESCUE_HEALTHY,
|
|
RESCUE_FLYAWAY,
|
|
RESCUE_GPSLOST,
|
|
RESCUE_LOWSATS,
|
|
RESCUE_CRASH_FLIP_DETECTED,
|
|
RESCUE_STALLED,
|
|
RESCUE_TOO_CLOSE
|
|
} rescueFailureState_e;
|
|
|
|
typedef struct {
|
|
int32_t targetAltitudeCm;
|
|
int32_t targetGroundspeed;
|
|
uint8_t minAngleDeg;
|
|
uint8_t maxAngleDeg;
|
|
bool crosstrack;
|
|
} rescueIntent_s;
|
|
|
|
typedef struct {
|
|
int32_t maxAltitudeCm;
|
|
int32_t currentAltitudeCm;
|
|
uint16_t distanceToHomeM;
|
|
uint16_t maxDistanceToHomeM;
|
|
int16_t directionToHome;
|
|
uint16_t groundSpeed;
|
|
uint8_t numSat;
|
|
float zVelocity; // Up/down movement in cm/s
|
|
float zVelocityAvg; // Up/down average in cm/s
|
|
float accMagnitude;
|
|
float accMagnitudeAvg;
|
|
bool healthy;
|
|
} rescueSensorData_s;
|
|
|
|
typedef struct {
|
|
bool bumpDetection;
|
|
bool convergenceDetection;
|
|
} rescueSanityFlags;
|
|
|
|
typedef struct {
|
|
rescuePhase_e phase;
|
|
rescueFailureState_e failure;
|
|
rescueSensorData_s sensor;
|
|
rescueIntent_s intent;
|
|
bool isFailsafe;
|
|
bool isAvailable;
|
|
} rescueState_s;
|
|
|
|
#define GPS_RESCUE_MAX_YAW_RATE 180 // deg/sec max yaw rate
|
|
#define GPS_RESCUE_RATE_SCALE_DEGREES 45 // Scale the commanded yaw rate when the error is less then this angle
|
|
|
|
PG_REGISTER_WITH_RESET_TEMPLATE(gpsRescueConfig_t, gpsRescueConfig, PG_GPS_RESCUE, 1);
|
|
|
|
PG_RESET_TEMPLATE(gpsRescueConfig_t, gpsRescueConfig,
|
|
.angle = 32,
|
|
.initialAltitudeM = 50,
|
|
.descentDistanceM = 200,
|
|
.rescueGroundspeed = 2000,
|
|
.throttleP = 150,
|
|
.throttleI = 20,
|
|
.throttleD = 50,
|
|
.velP = 80,
|
|
.velI = 20,
|
|
.velD = 15,
|
|
.yawP = 40,
|
|
.throttleMin = 1200,
|
|
.throttleMax = 1600,
|
|
.throttleHover = 1280,
|
|
.sanityChecks = RESCUE_SANITY_ON,
|
|
.minSats = 8,
|
|
.minRescueDth = 100,
|
|
.allowArmingWithoutFix = false,
|
|
.useMag = true
|
|
);
|
|
|
|
static uint16_t rescueThrottle;
|
|
static float rescueYaw;
|
|
|
|
int32_t gpsRescueAngle[ANGLE_INDEX_COUNT] = { 0, 0 };
|
|
uint16_t hoverThrottle = 0;
|
|
float averageThrottle = 0.0;
|
|
float altitudeError = 0.0;
|
|
uint32_t throttleSamples = 0;
|
|
bool magForceDisable = false;
|
|
|
|
static bool newGPSData = false;
|
|
|
|
rescueState_s rescueState;
|
|
|
|
/*
|
|
If we have new GPS data, update home heading
|
|
if possible and applicable.
|
|
*/
|
|
void rescueNewGpsData(void)
|
|
{
|
|
newGPSData = true;
|
|
}
|
|
|
|
static void rescueStart()
|
|
{
|
|
rescueState.phase = RESCUE_INITIALIZE;
|
|
}
|
|
|
|
static void rescueStop()
|
|
{
|
|
rescueState.phase = RESCUE_IDLE;
|
|
}
|
|
|
|
// Things that need to run regardless of GPS rescue mode being enabled or not
|
|
static void idleTasks()
|
|
{
|
|
// Do not calculate any of the idle task values when we are not flying
|
|
if (!ARMING_FLAG(ARMED)) {
|
|
rescueState.sensor.maxAltitudeCm = 0;
|
|
rescueState.sensor.maxDistanceToHomeM = 0;
|
|
return;
|
|
}
|
|
|
|
// Don't update any rescue flight statistics if we haven't applied a proper altitude offset yet
|
|
if (!isAltitudeOffset()) {
|
|
return;
|
|
}
|
|
|
|
gpsRescueAngle[AI_PITCH] = 0;
|
|
gpsRescueAngle[AI_ROLL] = 0;
|
|
|
|
// Store the max altitude we see not during RTH so we know our fly-back minimum alt
|
|
rescueState.sensor.maxAltitudeCm = MAX(rescueState.sensor.currentAltitudeCm, rescueState.sensor.maxAltitudeCm);
|
|
// Store the max distance to home during normal flight so we know if a flyaway is happening
|
|
rescueState.sensor.maxDistanceToHomeM = MAX(rescueState.sensor.distanceToHomeM, rescueState.sensor.maxDistanceToHomeM);
|
|
|
|
rescueThrottle = rcCommand[THROTTLE];
|
|
|
|
//to do: have a default value for hoverThrottle
|
|
|
|
// FIXME: GPS Rescue throttle handling should take into account min_check as the
|
|
// active throttle is from min_check through PWM_RANGE_MAX. Currently adjusting for this
|
|
// in gpsRescueGetThrottle() but it would be better handled here.
|
|
|
|
const float ct = getCosTiltAngle();
|
|
if (ct > 0.5 && ct < 0.96 && throttleSamples < 1E6 && rescueThrottle > 1070) { //5 to 45 degrees tilt
|
|
//TO DO: only sample when acceleration is low
|
|
uint16_t adjustedThrottle = 1000 + (rescueThrottle - PWM_RANGE_MIN) * ct;
|
|
if (throttleSamples == 0) {
|
|
averageThrottle = adjustedThrottle;
|
|
} else {
|
|
averageThrottle += (adjustedThrottle - averageThrottle) / (throttleSamples + 1);
|
|
}
|
|
hoverThrottle = lrintf(averageThrottle);
|
|
throttleSamples++;
|
|
}
|
|
}
|
|
|
|
// Very similar to maghold function on betaflight/cleanflight
|
|
static void setBearing(int16_t desiredHeading)
|
|
{
|
|
float errorAngle = (attitude.values.yaw / 10.0f) - desiredHeading;
|
|
|
|
// Determine the most efficient direction to rotate
|
|
if (errorAngle <= -180) {
|
|
errorAngle += 360;
|
|
} else if (errorAngle > 180) {
|
|
errorAngle -= 360;
|
|
}
|
|
|
|
errorAngle *= -GET_DIRECTION(rcControlsConfig()->yaw_control_reversed);
|
|
|
|
// Calculate a desired yaw rate based on a maximum limit beyond
|
|
// an error window and then scale the requested rate down inside
|
|
// the window as error approaches 0.
|
|
rescueYaw = -constrainf(errorAngle / GPS_RESCUE_RATE_SCALE_DEGREES * GPS_RESCUE_MAX_YAW_RATE, -GPS_RESCUE_MAX_YAW_RATE, GPS_RESCUE_MAX_YAW_RATE);
|
|
}
|
|
|
|
static void rescueAttainPosition()
|
|
{
|
|
// Speed and altitude controller internal variables
|
|
static float previousSpeedError = 0;
|
|
static int16_t speedIntegral = 0;
|
|
static float previousAltitudeError = 0;
|
|
static int16_t altitudeIntegral = 0;
|
|
|
|
if (rescueState.phase == RESCUE_INITIALIZE) {
|
|
// Initialize internal variables each time GPS Rescue is started
|
|
previousSpeedError = 0;
|
|
speedIntegral = 0;
|
|
previousAltitudeError = 0;
|
|
altitudeIntegral = 0;
|
|
}
|
|
|
|
// Point to home if that is in our intent
|
|
if (rescueState.intent.crosstrack) {
|
|
setBearing(rescueState.sensor.directionToHome);
|
|
}
|
|
|
|
DEBUG_SET(DEBUG_RTH, 3, rescueState.failure); //Failure can change with no new GPS Data
|
|
|
|
if (!newGPSData) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Speed controller
|
|
*/
|
|
const int16_t speedError = (rescueState.intent.targetGroundspeed - rescueState.sensor.groundSpeed) / 100;
|
|
const int16_t speedDerivative = speedError - previousSpeedError;
|
|
|
|
speedIntegral = constrain(speedIntegral + speedError, -100, 100);
|
|
|
|
previousSpeedError = speedError;
|
|
|
|
const int16_t angleAdjustment = gpsRescueConfig()->velP * speedError + (gpsRescueConfig()->velI * speedIntegral) / 100 + gpsRescueConfig()->velD * speedDerivative;
|
|
|
|
gpsRescueAngle[AI_PITCH] = constrain(gpsRescueAngle[AI_PITCH] + MIN(angleAdjustment, 80), rescueState.intent.minAngleDeg * 100, rescueState.intent.maxAngleDeg * 100);
|
|
|
|
const float ct = cos(DECIDEGREES_TO_RADIANS(gpsRescueAngle[AI_PITCH] / 10));
|
|
|
|
/**
|
|
Altitude controller
|
|
*/
|
|
const int16_t altitudeError = (rescueState.intent.targetAltitudeCm - rescueState.sensor.currentAltitudeCm) / 100; // Error in meters
|
|
const int16_t altitudeDerivative = altitudeError - previousAltitudeError;
|
|
|
|
// Only allow integral windup within +-15m absolute altitude error
|
|
if (ABS(altitudeError) < 25) {
|
|
altitudeIntegral = constrain(altitudeIntegral + altitudeError, -250, 250);
|
|
} else {
|
|
altitudeIntegral = 0;
|
|
}
|
|
|
|
previousAltitudeError = altitudeError;
|
|
|
|
const int16_t altitudeAdjustment = (gpsRescueConfig()->throttleP * altitudeError + (gpsRescueConfig()->throttleI * altitudeIntegral) / 10 * + gpsRescueConfig()->throttleD * altitudeDerivative) / ct / 20;
|
|
const int16_t hoverAdjustment = (hoverThrottle - 1000) / ct;
|
|
|
|
rescueThrottle = constrain(1000 + altitudeAdjustment + hoverAdjustment, gpsRescueConfig()->throttleMin, gpsRescueConfig()->throttleMax);
|
|
|
|
DEBUG_SET(DEBUG_RTH, 0, rescueThrottle);
|
|
DEBUG_SET(DEBUG_RTH, 1, gpsRescueAngle[AI_PITCH]);
|
|
DEBUG_SET(DEBUG_RTH, 2, altitudeAdjustment);
|
|
}
|
|
|
|
static void performSanityChecks()
|
|
{
|
|
static uint32_t previousTimeUs = 0; // Last time Stalled/LowSat was checked
|
|
static int8_t secondsStalled = 0; // Stalled movement detection
|
|
static uint16_t lastDistanceToHomeM = 0; // Fly Away detection
|
|
static int8_t secondsFlyingAway = 0;
|
|
static int8_t secondsLowSats = 0; // Minimum sat detection
|
|
|
|
const uint32_t currentTimeUs = micros();
|
|
|
|
if (rescueState.phase == RESCUE_IDLE) {
|
|
rescueState.failure = RESCUE_HEALTHY;
|
|
return;
|
|
} else if (rescueState.phase == RESCUE_INITIALIZE) {
|
|
// Initialize internal variables each time GPS Rescue is started
|
|
previousTimeUs = currentTimeUs;
|
|
secondsStalled = 10; // Start the count at 10 to be less forgiving at the beginning
|
|
lastDistanceToHomeM = rescueState.sensor.distanceToHomeM;
|
|
secondsFlyingAway = 0;
|
|
secondsLowSats = 5; // Start the count at 5 to be less forgiving at the beginning
|
|
return;
|
|
}
|
|
|
|
// Do not abort until each of these items is fully tested
|
|
if (rescueState.failure != RESCUE_HEALTHY) {
|
|
if (gpsRescueConfig()->sanityChecks == RESCUE_SANITY_ON
|
|
|| (gpsRescueConfig()->sanityChecks == RESCUE_SANITY_FS_ONLY && rescueState.isFailsafe == true)) {
|
|
rescueState.phase = RESCUE_ABORT;
|
|
}
|
|
}
|
|
|
|
// Check if crash recovery mode is active, disarm if so.
|
|
if (crashRecoveryModeActive()) {
|
|
rescueState.failure = RESCUE_CRASH_FLIP_DETECTED;
|
|
}
|
|
|
|
// Check if GPS comms are healthy
|
|
if (!rescueState.sensor.healthy) {
|
|
rescueState.failure = RESCUE_GPSLOST;
|
|
}
|
|
|
|
// Things that should run at a low refresh rate (such as flyaway detection, etc)
|
|
// This runs at ~1hz
|
|
const uint32_t dTime = currentTimeUs - previousTimeUs;
|
|
if (dTime < 1000000) { //1hz
|
|
return;
|
|
}
|
|
|
|
previousTimeUs = currentTimeUs;
|
|
|
|
if (rescueState.phase == RESCUE_CROSSTRACK) {
|
|
secondsStalled = constrain(secondsStalled + ((rescueState.sensor.groundSpeed < 150) ? 1 : -1), 0, 20);
|
|
|
|
if (secondsStalled == 20) {
|
|
rescueState.failure = RESCUE_STALLED;
|
|
}
|
|
|
|
secondsFlyingAway = constrain(secondsFlyingAway + ((lastDistanceToHomeM < rescueState.sensor.distanceToHomeM) ? 1 : -1), 0, 10);
|
|
lastDistanceToHomeM = rescueState.sensor.distanceToHomeM;
|
|
|
|
if (secondsFlyingAway == 10) {
|
|
//If there is a mag and has not been disabled, we have to assume is healthy and has been used in imu.c
|
|
if (sensors(SENSOR_MAG) && gpsRescueConfig()->useMag && !magForceDisable) {
|
|
//Try again with mag disabled
|
|
magForceDisable = true;
|
|
secondsFlyingAway = 0;
|
|
} else {
|
|
rescueState.failure = RESCUE_FLYAWAY;
|
|
}
|
|
}
|
|
}
|
|
|
|
secondsLowSats = constrain(secondsLowSats + ((rescueState.sensor.numSat < gpsRescueConfig()->minSats) ? 1 : -1), 0, 10);
|
|
|
|
if (secondsLowSats == 10) {
|
|
rescueState.failure = RESCUE_LOWSATS;
|
|
}
|
|
}
|
|
|
|
static void sensorUpdate()
|
|
{
|
|
rescueState.sensor.currentAltitudeCm = getEstimatedAltitudeCm();
|
|
rescueState.sensor.healthy = gpsIsHealthy();
|
|
|
|
// Calculate altitude velocity
|
|
static uint32_t previousTimeUs;
|
|
static int32_t previousAltitudeCm;
|
|
|
|
const uint32_t currentTimeUs = micros();
|
|
const float dTime = currentTimeUs - previousTimeUs;
|
|
|
|
if (newGPSData) { // Calculate velocity at lowest common denominator
|
|
rescueState.sensor.distanceToHomeM = GPS_distanceToHome;
|
|
rescueState.sensor.directionToHome = GPS_directionToHome;
|
|
rescueState.sensor.numSat = gpsSol.numSat;
|
|
rescueState.sensor.groundSpeed = gpsSol.groundSpeed;
|
|
|
|
rescueState.sensor.zVelocity = (rescueState.sensor.currentAltitudeCm - previousAltitudeCm) * 1000000.0f / dTime;
|
|
rescueState.sensor.zVelocityAvg = 0.8f * rescueState.sensor.zVelocityAvg + rescueState.sensor.zVelocity * 0.2f;
|
|
|
|
rescueState.sensor.accMagnitude = (float) sqrtf(sq(acc.accADC[Z]) + sq(acc.accADC[X]) + sq(acc.accADC[Y])) * acc.dev.acc_1G_rec;
|
|
rescueState.sensor.accMagnitudeAvg = (rescueState.sensor.accMagnitudeAvg * 0.8f) + (rescueState.sensor.accMagnitude * 0.2f);
|
|
|
|
previousAltitudeCm = rescueState.sensor.currentAltitudeCm;
|
|
previousTimeUs = currentTimeUs;
|
|
}
|
|
}
|
|
|
|
// This function checks the following conditions to determine if GPS rescue is available:
|
|
// 1. sensor healthy - GPS data is being received.
|
|
// 2. GPS has a valid fix.
|
|
// 3. GPS number of satellites is less than the minimum configured for GPS rescue.
|
|
// Note: this function does not take into account the distance from homepoint etc. (gps_rescue_min_dth) and
|
|
// is also independent of the gps_rescue_sanity_checks configuration
|
|
static bool checkGPSRescueIsAvailable(void)
|
|
{
|
|
static uint32_t previousTimeUs = 0; // Last time LowSat was checked
|
|
const uint32_t currentTimeUs = micros();
|
|
static int8_t secondsLowSats = 0; // Minimum sat detection
|
|
static bool lowsats = false;
|
|
static bool noGPSfix = false;
|
|
bool result = true;
|
|
|
|
if (!gpsIsHealthy() || !STATE(GPS_FIX_HOME)) {
|
|
return false;
|
|
}
|
|
|
|
// Things that should run at a low refresh rate >> ~1hz
|
|
const uint32_t dTime = currentTimeUs - previousTimeUs;
|
|
if (dTime < 1000000) { //1hz
|
|
if (noGPSfix || lowsats) {
|
|
result = false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
previousTimeUs = currentTimeUs;
|
|
|
|
if (!STATE(GPS_FIX)) {
|
|
result = false;
|
|
noGPSfix = true;
|
|
} else {
|
|
noGPSfix = false;
|
|
}
|
|
|
|
secondsLowSats = constrain(secondsLowSats + ((gpsSol.numSat < gpsRescueConfig()->minSats) ? 1 : -1), 0, 2);
|
|
if (secondsLowSats == 2) {
|
|
lowsats = true;
|
|
result = false;
|
|
} else {
|
|
lowsats = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Determine what phase we are in, determine if all criteria are met to move to the next phase
|
|
*/
|
|
void updateGPSRescueState(void)
|
|
{
|
|
if (!FLIGHT_MODE(GPS_RESCUE_MODE)) {
|
|
rescueStop();
|
|
} else if (FLIGHT_MODE(GPS_RESCUE_MODE) && rescueState.phase == RESCUE_IDLE) {
|
|
rescueStart();
|
|
rescueAttainPosition(); // Initialize
|
|
performSanityChecks(); // Initialize
|
|
}
|
|
|
|
rescueState.isFailsafe = failsafeIsActive();
|
|
|
|
sensorUpdate();
|
|
|
|
rescueState.isAvailable = checkGPSRescueIsAvailable();
|
|
|
|
switch (rescueState.phase) {
|
|
case RESCUE_IDLE:
|
|
idleTasks();
|
|
break;
|
|
case RESCUE_INITIALIZE:
|
|
if (hoverThrottle == 0) { //no actual throttle data yet, let's use the default.
|
|
hoverThrottle = gpsRescueConfig()->throttleHover;
|
|
}
|
|
|
|
if (!STATE(GPS_FIX_HOME)) {
|
|
setArmingDisabled(ARMING_DISABLED_ARM_SWITCH);
|
|
disarm();
|
|
}
|
|
|
|
// Minimum distance detection.
|
|
if (rescueState.sensor.distanceToHomeM < gpsRescueConfig()->minRescueDth) {
|
|
rescueState.failure = RESCUE_TOO_CLOSE;
|
|
|
|
// Never allow rescue mode to engage as a failsafe when too close.
|
|
if (rescueState.isFailsafe) {
|
|
setArmingDisabled(ARMING_DISABLED_ARM_SWITCH);
|
|
disarm();
|
|
}
|
|
|
|
// When not in failsafe mode: leave it up to the sanity check setting.
|
|
}
|
|
|
|
rescueState.phase = RESCUE_ATTAIN_ALT;
|
|
FALLTHROUGH;
|
|
case RESCUE_ATTAIN_ALT:
|
|
// Get to a safe altitude at a low velocity ASAP
|
|
if (ABS(rescueState.intent.targetAltitudeCm - rescueState.sensor.currentAltitudeCm) < 1000) {
|
|
rescueState.phase = RESCUE_CROSSTRACK;
|
|
}
|
|
|
|
rescueState.intent.targetGroundspeed = 500;
|
|
rescueState.intent.targetAltitudeCm = MAX(gpsRescueConfig()->initialAltitudeM * 100, rescueState.sensor.maxAltitudeCm + 1500);
|
|
rescueState.intent.crosstrack = true;
|
|
rescueState.intent.minAngleDeg = 10;
|
|
rescueState.intent.maxAngleDeg = 15;
|
|
break;
|
|
case RESCUE_CROSSTRACK:
|
|
if (rescueState.sensor.distanceToHomeM < gpsRescueConfig()->descentDistanceM) {
|
|
rescueState.phase = RESCUE_LANDING_APPROACH;
|
|
}
|
|
|
|
// We can assume at this point that we are at or above our RTH height, so we need to try and point to home and tilt while maintaining alt
|
|
// Is our altitude way off? We should probably kick back to phase RESCUE_ATTAIN_ALT
|
|
rescueState.intent.targetGroundspeed = gpsRescueConfig()->rescueGroundspeed;
|
|
rescueState.intent.targetAltitudeCm = MAX(gpsRescueConfig()->initialAltitudeM * 100, rescueState.sensor.maxAltitudeCm + 1500);
|
|
rescueState.intent.crosstrack = true;
|
|
rescueState.intent.minAngleDeg = 15;
|
|
rescueState.intent.maxAngleDeg = gpsRescueConfig()->angle;
|
|
break;
|
|
case RESCUE_LANDING_APPROACH:
|
|
// We are getting close to home in the XY plane, get Z where it needs to be to move to landing phase
|
|
if (rescueState.sensor.distanceToHomeM < 10 && rescueState.sensor.currentAltitudeCm <= 1000) {
|
|
rescueState.phase = RESCUE_LANDING;
|
|
}
|
|
|
|
// Only allow new altitude and new speed to be equal or lower than the current values (to prevent parabolic movement on overshoot)
|
|
const int32_t newAlt = gpsRescueConfig()->initialAltitudeM * 100 * rescueState.sensor.distanceToHomeM / gpsRescueConfig()->descentDistanceM;
|
|
const int32_t newSpeed = gpsRescueConfig()->rescueGroundspeed * rescueState.sensor.distanceToHomeM / gpsRescueConfig()->descentDistanceM;
|
|
|
|
rescueState.intent.targetAltitudeCm = constrain(newAlt, 100, rescueState.intent.targetAltitudeCm);
|
|
rescueState.intent.targetGroundspeed = constrain(newSpeed, 100, rescueState.intent.targetGroundspeed);
|
|
rescueState.intent.crosstrack = true;
|
|
rescueState.intent.minAngleDeg = 10;
|
|
rescueState.intent.maxAngleDeg = 20;
|
|
break;
|
|
case RESCUE_LANDING:
|
|
// We have reached the XYZ envelope to be considered at "home". We need to land gently and check our accelerometer for abnormal data.
|
|
// At this point, do not let the target altitude go up anymore, so if we overshoot, we dont' move in a parabolic trajectory
|
|
|
|
// If we are over 150% of average magnitude, just disarm since we're pretty much home
|
|
if (rescueState.sensor.accMagnitude > rescueState.sensor.accMagnitudeAvg * 1.5) {
|
|
setArmingDisabled(ARMING_DISABLED_ARM_SWITCH);
|
|
disarm();
|
|
rescueState.phase = RESCUE_COMPLETE;
|
|
}
|
|
|
|
rescueState.intent.targetGroundspeed = 0;
|
|
rescueState.intent.targetAltitudeCm = 0;
|
|
rescueState.intent.crosstrack = true;
|
|
rescueState.intent.minAngleDeg = 0;
|
|
rescueState.intent.maxAngleDeg = 15;
|
|
break;
|
|
case RESCUE_COMPLETE:
|
|
rescueStop();
|
|
break;
|
|
case RESCUE_ABORT:
|
|
setArmingDisabled(ARMING_DISABLED_ARM_SWITCH);
|
|
disarm();
|
|
rescueStop();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
performSanityChecks();
|
|
|
|
if (rescueState.phase != RESCUE_IDLE) {
|
|
rescueAttainPosition();
|
|
}
|
|
|
|
newGPSData = false;
|
|
}
|
|
|
|
float gpsRescueGetYawRate(void)
|
|
{
|
|
return rescueYaw;
|
|
}
|
|
|
|
float gpsRescueGetThrottle(void)
|
|
{
|
|
// Calculated a desired commanded throttle scaled from 0.0 to 1.0 for use in the mixer.
|
|
// We need to compensate for min_check since the throttle value set by gps rescue
|
|
// is based on the raw rcCommand value commanded by the pilot.
|
|
float commandedThrottle = scaleRangef(rescueThrottle, MAX(rxConfig()->mincheck, PWM_RANGE_MIN), PWM_RANGE_MAX, 0.0f, 1.0f);
|
|
commandedThrottle = constrainf(commandedThrottle, 0.0f, 1.0f);
|
|
|
|
return commandedThrottle;
|
|
}
|
|
|
|
bool gpsRescueIsConfigured(void)
|
|
{
|
|
return failsafeConfig()->failsafe_procedure == FAILSAFE_PROCEDURE_GPS_RESCUE || isModeActivationConditionPresent(BOXGPSRESCUE);
|
|
}
|
|
|
|
bool gpsRescueIsAvailable(void)
|
|
{
|
|
return rescueState.isAvailable;
|
|
}
|
|
|
|
bool gpsRescueIsDisabled(void)
|
|
{
|
|
return (!STATE(GPS_FIX_HOME));
|
|
}
|
|
|
|
bool gpsRescueDisableMag(void)
|
|
{
|
|
return ((!gpsRescueConfig()->useMag || magForceDisable) && (rescueState.phase >= RESCUE_INITIALIZE) && (rescueState.phase <= RESCUE_LANDING));
|
|
}
|
|
#endif
|
|
|