Skip to content

Commit

Permalink
Add remaining flight time/distance calculations and OSD items
Browse files Browse the repository at this point in the history
  • Loading branch information
shellixyz committed Jun 4, 2018
1 parent 4374471 commit 7624a4c
Show file tree
Hide file tree
Showing 19 changed files with 401 additions and 39 deletions.
1 change: 1 addition & 0 deletions make/source.mk
Expand Up @@ -74,6 +74,7 @@ COMMON_SRC = \
flight/mixer.c \
flight/pid.c \
flight/pid_autotune.c \
flight/rth_estimator.c \
flight/servos.c \
flight/wind_estimator.c \
io/beeper.c \
Expand Down
5 changes: 4 additions & 1 deletion src/main/build/debug.h
Expand Up @@ -16,6 +16,8 @@
*/

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#define DEBUG16_VALUE_COUNT 4
extern int16_t debug[DEBUG16_VALUE_COUNT];
Expand Down Expand Up @@ -59,6 +61,7 @@ typedef enum {
DEBUG_ALWAYS,
DEBUG_STAGE2,
DEBUG_WIND_ESTIMATOR,
DEBUG_REM_FLIGHT_TIME,
DEBUG_COUNT
} debugType_e;

Expand All @@ -75,4 +78,4 @@ void debugTracePrintBufferHex(bool synchronous, const void *buffer, size_t size)
#define DEBUG_TRACE_SYNC(fmt, ...)
#define DEBUG_TRACE_BUFFER_HEX(buf, size)
#define DEBUG_TRACE_BUFFER_HEX_SYNC(buf, size)
#endif
#endif
2 changes: 2 additions & 0 deletions src/main/cms/cms_menu_osd.c
Expand Up @@ -155,6 +155,8 @@ static const OSD_Entry menuOsdElemsEntries[] =
OSD_ELEMENT_ENTRY("ON TIME", OSD_ONTIME),
OSD_ELEMENT_ENTRY("FLY TIME", OSD_FLYTIME),
OSD_ELEMENT_ENTRY("ON/FLY TIME", OSD_ONTIME_FLYTIME),
OSD_ELEMENT_ENTRY("REM. TIME", OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH),
OSD_ELEMENT_ENTRY("REM. DIST", OSD_REMAINING_DISTANCE_BEFORE_RTH),
OSD_ELEMENT_ENTRY("TIME (HOUR)", OSD_RTC_TIME),
OSD_ELEMENT_ENTRY("FLY MODE", OSD_FLYMODE),
OSD_ELEMENT_ENTRY("NAME", OSD_CRAFT_NAME),
Expand Down
2 changes: 2 additions & 0 deletions src/main/common/maths.h
Expand Up @@ -153,7 +153,9 @@ float cos_approx(float x);
float atan2_approx(float y, float x);
float acos_approx(float x);
#define tan_approx(x) (sin_approx(x) / cos_approx(x))
#define asin_approx(x) (M_PIf / 2 - acos_approx(x))
#else
#define asin_approx(x) asinf(x)
#define sin_approx(x) sinf(x)
#define cos_approx(x) cosf(x)
#define atan2_approx(y,x) atan2f(y,x)
Expand Down
10 changes: 10 additions & 0 deletions src/main/fc/fc_core.c
Expand Up @@ -94,6 +94,7 @@ enum {
#define GYRO_WATCHDOG_DELAY 100 // Watchdog for boards without interrupt for gyro

timeDelta_t cycleTime = 0; // this is the number in micro second to achieve a full loop, it can differ a little and is taken into account in the PID loop
static timeUs_t flightTime = 0;

float dT;

Expand Down Expand Up @@ -710,6 +711,10 @@ void taskMainPidLoop(timeUs_t currentTimeUs)
cycleTime = getTaskDeltaTime(TASK_SELF);
dT = (float)cycleTime * 0.000001f;

if (ARMING_FLAG(ARMED) && ((!STATE(FIXED_WING)) || (isNavLaunchEnabled() && isFixedWingLaunchDetected()))) {
flightTime += cycleTime;
}

#ifdef USE_ASYNC_GYRO_PROCESSING
if (getAsyncMode() == ASYNC_MODE_NONE) {
taskGyro(currentTimeUs);
Expand Down Expand Up @@ -831,3 +836,8 @@ void taskUpdateRxMain(timeUs_t currentTimeUs)
processRx(currentTimeUs);
isRXDataNew = true;
}

// returns seconds
float getFlightTime() {
return (float)(flightTime / 1000) / 1000;
}
4 changes: 4 additions & 0 deletions src/main/fc/fc_core.h
Expand Up @@ -17,6 +17,8 @@

#pragma once

#include "common/time.h"

typedef enum disarmReason_e {
DISARM_NONE = 0,
DISARM_TIMEOUT = 1,
Expand All @@ -29,10 +31,12 @@ typedef enum disarmReason_e {
DISARM_REASON_COUNT
} disarmReason_t;


void handleInflightCalibrationStickPosition(void);

void disarm(disarmReason_t disarmReason);
void tryArm(void);
disarmReason_t getDisarmReason(void);

bool isCalibrating(void);
float getFlightTime();
21 changes: 20 additions & 1 deletion src/main/fc/settings.yaml
Expand Up @@ -67,7 +67,7 @@ tables:
- name: i2c_speed
values: ["400KHZ", "800KHZ", "100KHZ", "200KHZ"]
- name: debug_modes
values: ["NONE", "GYRO", "NOTCH", "NAV_LANDING", "FW_ALTITUDE", "AGL", "FLOW_RAW", "SBUS", "FPORT", "ALWAYS", "STAGE2", "WIND_ESTIMATOR"]
values: ["NONE", "GYRO", "NOTCH", "NAV_LANDING", "FW_ALTITUDE", "AGL", "FLOW_RAW", "SBUS", "FPORT", "ALWAYS", "STAGE2", "WIND_ESTIMATOR", "REM_FLIGHT_TIME"]
- name: async_mode
values: ["NONE", "GYRO", "ALL"]
- name: aux_operator
Expand Down Expand Up @@ -559,6 +559,17 @@ groups:
field: current.type
table: current_sensor
type: uint8_t
- name: cruise_power
field: cruise_power
min: 0
max: 4294967295
- name: idle_power
field: idle_power
min: 0
max: 65535
- name: rth_energy_margin
min: 0
max: 100

- name: PG_MIXER_CONFIG
type: mixerConfig_t
Expand Down Expand Up @@ -1276,6 +1287,10 @@ groups:
field: fw.loiter_radius
min: 0
max: 10000
- name: nav_fw_cruise_speed
field: fw.cruise_speed
min: 0
max: 65535

- name: nav_fw_land_dive_angle
field: fw.land_dive_angle
Expand Down Expand Up @@ -1492,6 +1507,10 @@ groups:
min: 0
max: 1

- name: osd_use_wind_compensation
field: use_wind_compensation
type: bool

- name: PG_SYSTEM_CONFIG
type: systemConfig_t
headers: ["fc/config.h"]
Expand Down
12 changes: 10 additions & 2 deletions src/main/fc/stats.c
Expand Up @@ -32,8 +32,13 @@ static uint32_t arm_millis;
static uint32_t arm_distance_cm;
#ifdef USE_ADC
static uint32_t arm_mWhDrawn;
static uint32_t flyingEnergy; // energy drawn during flying up to last disarm (ARMED) mWh
#endif

uint32_t getFlyingEnergy() {
return flyingEnergy;
}

void statsOnArm(void)
{
arm_millis = millis();
Expand All @@ -51,8 +56,11 @@ void statsOnDisarm(void)
statsConfigMutable()->stats_total_time += dt; //[s]
statsConfigMutable()->stats_total_dist += (getTotalTravelDistance() - arm_distance_cm) / 100; //[m]
#ifdef USE_ADC
if (feature(FEATURE_VBAT) && feature(FEATURE_CURRENT_METER))
statsConfigMutable()->stats_total_energy += getMWhDrawn() - arm_mWhDrawn;
if (feature(FEATURE_VBAT) && feature(FEATURE_CURRENT_METER)) {
const uint32_t energy = getMWhDrawn() - arm_mWhDrawn;
statsConfigMutable()->stats_total_energy += energy;
flyingEnergy += energy;
}
#endif
writeEEPROM();
}
Expand Down
1 change: 1 addition & 0 deletions src/main/fc/stats.h
Expand Up @@ -11,6 +11,7 @@ typedef struct statsConfig_s {
uint8_t stats_enabled;
} statsConfig_t;

uint32_t getFlyingEnergy();
void statsOnArm(void);
void statsOnDisarm(void);

Expand Down
208 changes: 208 additions & 0 deletions src/main/flight/rth_estimator.c
@@ -0,0 +1,208 @@

#include "build/debug.h"

#include "common/maths.h"

#include "fc/config.h"
#include "fc/fc_core.h"
#include "fc/runtime_config.h"

#include "flight/imu.h"
#include "flight/mixer.h"
#include "flight/wind_estimator.h"

#include "io/osd.h"

#include "navigation/navigation.h"

#include "sensors/battery.h"

#include <stdint.h>

#if defined(USE_ADC) && defined(USE_GPS)

/* INPUTS:
* - forwardSpeed (same unit as horizontalWindSpeed)
* - heading degrees
* - horizontalWindSpeed (same unit as forwardSpeed)
* - windHeading degrees
* OUTPUT:
* returns degrees
*/
float windDriftCompensationAngle(float forwardSpeed, float heading, float horizontalWindSpeed, float windHeading) {
return RADIANS_TO_DEGREES(asin_approx(-horizontalWindSpeed * sin_approx(DEGREES_TO_RADIANS(windHeading - heading)) / forwardSpeed));
}

/* INPUTS:
* - forwardSpeed (same unit as horizontalWindSpeed)
* - heading degrees
* - horizontalWindSpeed (same unit as forwardSpeed)
* - windHeading degrees
* OUTPUT:
* returns (same unit as forwardSpeed and horizontalWindSpeed)
*/
float windDriftCorrectedForwardSpeed(float forwardSpeed, float heading, float horizontalWindSpeed, float windHeading) {
return forwardSpeed * cos_approx(DEGREES_TO_RADIANS(windDriftCompensationAngle(forwardSpeed, heading, horizontalWindSpeed, windHeading)));
}

/* INPUTS:
* - heading degrees
* - horizontalWindSpeed
* - windHeading degrees
* OUTPUT:
* returns same unit as horizontalWindSpeed
*/
float forwardWindSpeed(float heading, float horizontalWindSpeed, float windHeading) {
return horizontalWindSpeed * cos_approx(DEGREES_TO_RADIANS(windHeading - heading));
}

/* INPUTS:
* - forwardSpeed (same unit as horizontalWindSpeed)
* - heading degrees
* - horizontalWindSpeed (same unit as forwardSpeed)
* - windHeading degrees
* OUTPUT:
* returns (same unit as forwardSpeed and horizontalWindSpeed)
*/
float windCompensatedForwardSpeed(float forwardSpeed, float heading, float horizontalWindSpeed, float windHeading) {
return windDriftCorrectedForwardSpeed(forwardSpeed, heading, horizontalWindSpeed, windHeading) + forwardWindSpeed(heading, horizontalWindSpeed, windHeading);
}

// returns degrees
int8_t RTHAltitudeChangePitchAngle(float altitudeChange) {
return altitudeChange < 0 ? navConfig()->fw.max_dive_angle : -navConfig()->fw.max_climb_angle;
}

// altitudeChange is in meters
// idle_power and cruise_power are in deciWatt
// output is in Watt
float estimateRTHAltitudeChangePower(float altitudeChange) {
uint16_t altitudeChangeThrottle = navConfig()->fw.cruise_throttle - RTHAltitudeChangePitchAngle(altitudeChange) * navConfig()->fw.pitch_to_throttle;
altitudeChangeThrottle = constrain(altitudeChangeThrottle, navConfig()->fw.min_throttle, navConfig()->fw.max_throttle);
const float altitudeChangeThrToCruiseThrRatio = (float)(altitudeChangeThrottle - motorConfig()->minthrottle) / (navConfig()->fw.cruise_throttle - motorConfig()->minthrottle);
return (float)(batteryConfig()->idle_power + batteryConfig()->cruise_power * altitudeChangeThrToCruiseThrRatio) / 100;
}

// altitudeChange is in m
// verticalWindSpeed is in m/s
// cruise_speed is in cm/s
// output is in seconds
float estimateRTHAltitudeChangeTime(float altitudeChange, float verticalWindSpeed) {
// Assuming increase in throttle keeps air speed at cruise speed
const float estimatedVerticalSpeed = (float)navConfig()->fw.cruise_speed / 100 * sin_approx(DEGREES_TO_RADIANS(RTHAltitudeChangePitchAngle(altitudeChange))) + verticalWindSpeed;
return altitudeChange / estimatedVerticalSpeed;
}

// altitudeChange is in m
// horizontalWindSpeed is in m/s
// windHeading is in degrees
// verticalWindSpeed is in m/s
// cruise_speed is in cm/s
// output is in meters
float estimateRTHAltitudeChangeGroundDistance(float altitudeChange, float horizontalWindSpeed, float windHeading, float verticalWindSpeed) {
// Assuming increase in throttle keeps air speed at cruise speed
float estimatedHorizontalSpeed = (float)navConfig()->fw.cruise_speed / 100 * cos_approx(DEGREES_TO_RADIANS(RTHAltitudeChangePitchAngle(altitudeChange))) + forwardWindSpeed(DECIDEGREES_TO_DEGREES((float)attitude.values.yaw), horizontalWindSpeed, windHeading);
return estimateRTHAltitudeChangeTime(altitudeChange, verticalWindSpeed) * estimatedHorizontalSpeed;
}

// altitudeChange is in m
// verticalWindSpeed is in m/s
// output is in Wh
uint16_t estimateRTHAltitudeChangeEnergy(float altitudeChange, float verticalWindSpeed) {
return estimateRTHAltitudeChangePower(altitudeChange) * estimateRTHAltitudeChangeTime(altitudeChange, verticalWindSpeed) / 3600;
}

// returns distance in m
// *heading is in degrees
float estimateRTHDistanceAndHeadingAfterAltitudeChange(float altitudeChange, float horizontalWindSpeed, float windHeading, float verticalWindSpeed, float *heading) {
float estimatedAltitudeChangeGroundDistance = estimateRTHAltitudeChangeGroundDistance(altitudeChange, horizontalWindSpeed, windHeading, verticalWindSpeed);
if (altitudeChange > 0) {
float headingDiff = DEGREES_TO_RADIANS(DECIDEGREES_TO_DEGREES((float)attitude.values.yaw) - GPS_directionToHome);
float triangleAltitude = GPS_distanceToHome * sin_approx(headingDiff);
float triangleAltitudeToReturnStart = estimatedAltitudeChangeGroundDistance - GPS_distanceToHome * cos_approx(headingDiff);
*heading = RADIANS_TO_DEGREES(atan2_approx(triangleAltitude, triangleAltitudeToReturnStart));
return sqrt(sq(triangleAltitude) + sq(triangleAltitudeToReturnStart));
} else {
*heading = GPS_directionToHome;
return GPS_distanceToHome - estimatedAltitudeChangeGroundDistance;
}
}

// returns height change during RTH in meters
float RTHAltitudeChange() {
return (RTHAltitude() - getEstimatedActualPosition(Z)) / 100;
}

// returns mWh
int32_t calculateRemainingEnergyBeforeRTH() {
// Fixed wing only for now
if (!STATE(FIXED_WING))
return -1;

if (!(feature(FEATURE_VBAT) && feature(FEATURE_CURRENT_METER) && navigationPositionEstimateIsHealthy() && (batteryConfig()->cruise_power > 0) && (ARMING_FLAG(ARMED)) && ((!STATE(FIXED_WING)) || (isNavLaunchEnabled() && isFixedWingLaunchDetected())) && (navConfig()->fw.cruise_speed > 0) && (batteryConfig()->capacity.unit == BAT_CAPACITY_UNIT_MWH) && (batteryConfig()->capacity.value > 0) && batteryWasFullWhenPluggedIn() && isEstimatedWindSpeedValid() && isImuHeadingValid()))
return -1;

uint16_t windHeading; // centidegrees
const float horizontalWindSpeed = osdConfig()->use_wind_compensation ? getEstimatedHorizontalWindSpeed(&windHeading) / 100 : 0; // m/s
const float windHeadingDegrees = CENTIDEGREES_TO_DEGREES((float)windHeading);
const float verticalWindSpeed = getEstimatedWindSpeed(Z) / 100;

const float RTH_altitude_change = (RTHAltitude() - getEstimatedActualPosition(Z)) / 100;
float RTH_heading; // degrees
const float RTH_distance = estimateRTHDistanceAndHeadingAfterAltitudeChange(RTH_altitude_change, horizontalWindSpeed, windHeadingDegrees, verticalWindSpeed, &RTH_heading);
const float RTH_speed = windCompensatedForwardSpeed((float)navConfig()->fw.cruise_speed / 100, DECIDEGREES_TO_DEGREES(attitude.values.yaw), horizontalWindSpeed, windHeadingDegrees);

DEBUG_SET(DEBUG_REM_FLIGHT_TIME, 0, lrintf(RTH_altitude_change * 100));
DEBUG_SET(DEBUG_REM_FLIGHT_TIME, 1, lrintf(RTH_distance * 100));
DEBUG_SET(DEBUG_REM_FLIGHT_TIME, 2, lrintf(RTH_speed * 100));
DEBUG_SET(DEBUG_REM_FLIGHT_TIME, 3, lrintf(horizontalWindSpeed * 100));

if (RTH_speed <= 0)
return -2; // wind is too strong

const uint32_t time_to_home = RTH_distance / RTH_speed; // seconds
const uint32_t energy_to_home = estimateRTHAltitudeChangeEnergy(RTH_altitude_change, verticalWindSpeed) * 1000 + batteryConfig()->cruise_power * time_to_home / 360; // mWh
const uint32_t energy_margin_abs = (batteryConfig()->capacity.value - batteryConfig()->capacity.critical) * batteryConfig()->rth_energy_margin / 100; // mWh
const int32_t remaining_energy_before_rth = getBatteryRemainingCapacity() - energy_margin_abs - energy_to_home; // mWh

if (remaining_energy_before_rth < 0) // No energy left = No time left
return 0;

return remaining_energy_before_rth;
}

// returns seconds
int32_t calculateRemainingFlightTimeBeforeRTH() {

const int32_t remainingEnergyBeforeRTH = calculateRemainingEnergyBeforeRTH();

// error: return error code directly
if (remainingEnergyBeforeRTH < 0)
return remainingEnergyBeforeRTH;

const int32_t averagePower = calculateAveragePower();

if (averagePower == 0)
return -1;

const uint32_t time_before_rth = remainingEnergyBeforeRTH * 360 / averagePower;

if (time_before_rth > 0x7FFFFFFF) // int32 overflow
return -1;

return time_before_rth;
}

// returns meters
int32_t calculateRemainingDistanceBeforeRTH() {

const int32_t remainingFlightTimeBeforeRTH = calculateRemainingFlightTimeBeforeRTH();

// error: return error code directly
if (remainingFlightTimeBeforeRTH < 0)
return remainingFlightTimeBeforeRTH;

return remainingFlightTimeBeforeRTH * calculateAverageSpeed();
}

#endif

0 comments on commit 7624a4c

Please sign in to comment.