mirror of
https://github.com/iNavFlight/inav.git
synced 2025-07-19 14:25:16 +03:00
Add remaining flight time/distance calculations and OSD items
This commit is contained in:
parent
46ef27db85
commit
83148a04d2
19 changed files with 401 additions and 37 deletions
|
@ -74,6 +74,7 @@ COMMON_SRC = \
|
||||||
flight/mixer.c \
|
flight/mixer.c \
|
||||||
flight/pid.c \
|
flight/pid.c \
|
||||||
flight/pid_autotune.c \
|
flight/pid_autotune.c \
|
||||||
|
flight/rth_estimator.c \
|
||||||
flight/servos.c \
|
flight/servos.c \
|
||||||
flight/wind_estimator.c \
|
flight/wind_estimator.c \
|
||||||
io/beeper.c \
|
io/beeper.c \
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define DEBUG16_VALUE_COUNT 4
|
#define DEBUG16_VALUE_COUNT 4
|
||||||
extern int16_t debug[DEBUG16_VALUE_COUNT];
|
extern int16_t debug[DEBUG16_VALUE_COUNT];
|
||||||
|
@ -63,6 +65,7 @@ typedef enum {
|
||||||
DEBUG_SAG_COMP_VOLTAGE,
|
DEBUG_SAG_COMP_VOLTAGE,
|
||||||
DEBUG_VIBE,
|
DEBUG_VIBE,
|
||||||
DEBUG_CRUISE,
|
DEBUG_CRUISE,
|
||||||
|
DEBUG_REM_FLIGHT_TIME,
|
||||||
DEBUG_COUNT
|
DEBUG_COUNT
|
||||||
} debugType_e;
|
} debugType_e;
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,8 @@ static const OSD_Entry menuOsdElemsEntries[] =
|
||||||
OSD_ELEMENT_ENTRY("ON TIME", OSD_ONTIME),
|
OSD_ELEMENT_ENTRY("ON TIME", OSD_ONTIME),
|
||||||
OSD_ELEMENT_ENTRY("FLY TIME", OSD_FLYTIME),
|
OSD_ELEMENT_ENTRY("FLY TIME", OSD_FLYTIME),
|
||||||
OSD_ELEMENT_ENTRY("ON/FLY TIME", OSD_ONTIME_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("TIME (HOUR)", OSD_RTC_TIME),
|
||||||
OSD_ELEMENT_ENTRY("FLY MODE", OSD_FLYMODE),
|
OSD_ELEMENT_ENTRY("FLY MODE", OSD_FLYMODE),
|
||||||
OSD_ELEMENT_ENTRY("NAME", OSD_CRAFT_NAME),
|
OSD_ELEMENT_ENTRY("NAME", OSD_CRAFT_NAME),
|
||||||
|
|
|
@ -153,7 +153,9 @@ float cos_approx(float x);
|
||||||
float atan2_approx(float y, float x);
|
float atan2_approx(float y, float x);
|
||||||
float acos_approx(float x);
|
float acos_approx(float x);
|
||||||
#define tan_approx(x) (sin_approx(x) / cos_approx(x))
|
#define tan_approx(x) (sin_approx(x) / cos_approx(x))
|
||||||
|
#define asin_approx(x) (M_PIf / 2 - acos_approx(x))
|
||||||
#else
|
#else
|
||||||
|
#define asin_approx(x) asinf(x)
|
||||||
#define sin_approx(x) sinf(x)
|
#define sin_approx(x) sinf(x)
|
||||||
#define cos_approx(x) cosf(x)
|
#define cos_approx(x) cosf(x)
|
||||||
#define atan2_approx(y,x) atan2f(y,x)
|
#define atan2_approx(y,x) atan2f(y,x)
|
||||||
|
|
|
@ -94,6 +94,7 @@ enum {
|
||||||
#define GYRO_WATCHDOG_DELAY 100 // Watchdog for boards without interrupt for gyro
|
#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
|
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;
|
float dT;
|
||||||
|
|
||||||
|
@ -704,6 +705,10 @@ void taskMainPidLoop(timeUs_t currentTimeUs)
|
||||||
cycleTime = getTaskDeltaTime(TASK_SELF);
|
cycleTime = getTaskDeltaTime(TASK_SELF);
|
||||||
dT = (float)cycleTime * 0.000001f;
|
dT = (float)cycleTime * 0.000001f;
|
||||||
|
|
||||||
|
if (ARMING_FLAG(ARMED) && ((!STATE(FIXED_WING)) || (isNavLaunchEnabled() && isFixedWingLaunchDetected()))) {
|
||||||
|
flightTime += cycleTime;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_ASYNC_GYRO_PROCESSING
|
#ifdef USE_ASYNC_GYRO_PROCESSING
|
||||||
if (getAsyncMode() == ASYNC_MODE_NONE) {
|
if (getAsyncMode() == ASYNC_MODE_NONE) {
|
||||||
taskGyro(currentTimeUs);
|
taskGyro(currentTimeUs);
|
||||||
|
@ -825,3 +830,8 @@ void taskUpdateRxMain(timeUs_t currentTimeUs)
|
||||||
processRx(currentTimeUs);
|
processRx(currentTimeUs);
|
||||||
isRXDataNew = true;
|
isRXDataNew = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns seconds
|
||||||
|
float getFlightTime() {
|
||||||
|
return (float)(flightTime / 1000) / 1000;
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/time.h"
|
||||||
|
|
||||||
typedef enum disarmReason_e {
|
typedef enum disarmReason_e {
|
||||||
DISARM_NONE = 0,
|
DISARM_NONE = 0,
|
||||||
DISARM_TIMEOUT = 1,
|
DISARM_TIMEOUT = 1,
|
||||||
|
@ -29,6 +31,7 @@ typedef enum disarmReason_e {
|
||||||
DISARM_REASON_COUNT
|
DISARM_REASON_COUNT
|
||||||
} disarmReason_t;
|
} disarmReason_t;
|
||||||
|
|
||||||
|
|
||||||
void handleInflightCalibrationStickPosition(void);
|
void handleInflightCalibrationStickPosition(void);
|
||||||
|
|
||||||
void disarm(disarmReason_t disarmReason);
|
void disarm(disarmReason_t disarmReason);
|
||||||
|
@ -36,3 +39,4 @@ void tryArm(void);
|
||||||
disarmReason_t getDisarmReason(void);
|
disarmReason_t getDisarmReason(void);
|
||||||
|
|
||||||
bool isCalibrating(void);
|
bool isCalibrating(void);
|
||||||
|
float getFlightTime();
|
||||||
|
|
|
@ -67,7 +67,7 @@ tables:
|
||||||
- name: i2c_speed
|
- name: i2c_speed
|
||||||
values: ["400KHZ", "800KHZ", "100KHZ", "200KHZ"]
|
values: ["400KHZ", "800KHZ", "100KHZ", "200KHZ"]
|
||||||
- name: debug_modes
|
- name: debug_modes
|
||||||
values: ["NONE", "GYRO", "NOTCH", "NAV_LANDING", "FW_ALTITUDE", "AGL", "FLOW_RAW", "FLOW", "SBUS", "FPORT", "ALWAYS", "STAGE2", "WIND_ESTIMATOR", "SAG_COMP_VOLTAGE", "VIBE", "CRUISE"]
|
values: ["NONE", "GYRO", "NOTCH", "NAV_LANDING", "FW_ALTITUDE", "AGL", "FLOW_RAW", "FLOW", "SBUS", "FPORT", "ALWAYS", "STAGE2", "WIND_ESTIMATOR", "SAG_COMP_VOLTAGE", "VIBE", "CRUISE", "REM_FLIGHT_TIME"]
|
||||||
- name: async_mode
|
- name: async_mode
|
||||||
values: ["NONE", "GYRO", "ALL"]
|
values: ["NONE", "GYRO", "ALL"]
|
||||||
- name: aux_operator
|
- name: aux_operator
|
||||||
|
@ -536,6 +536,17 @@ groups:
|
||||||
field: voltageSource
|
field: voltageSource
|
||||||
table: bat_voltage_source
|
table: bat_voltage_source
|
||||||
type: uint8_t
|
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_BATTERY_PROFILES
|
- name: PG_BATTERY_PROFILES
|
||||||
type: batteryProfile_t
|
type: batteryProfile_t
|
||||||
|
@ -1311,6 +1322,10 @@ groups:
|
||||||
field: fw.loiter_radius
|
field: fw.loiter_radius
|
||||||
min: 0
|
min: 0
|
||||||
max: 10000
|
max: 10000
|
||||||
|
- name: nav_fw_cruise_speed
|
||||||
|
field: fw.cruise_speed
|
||||||
|
min: 0
|
||||||
|
max: 65535
|
||||||
|
|
||||||
- name: nav_fw_land_dive_angle
|
- name: nav_fw_land_dive_angle
|
||||||
field: fw.land_dive_angle
|
field: fw.land_dive_angle
|
||||||
|
@ -1531,6 +1546,10 @@ groups:
|
||||||
min: 0
|
min: 0
|
||||||
max: 1
|
max: 1
|
||||||
|
|
||||||
|
- name: osd_estimations_wind_compensation
|
||||||
|
field: estimations_wind_compensation
|
||||||
|
type: bool
|
||||||
|
|
||||||
- name: PG_SYSTEM_CONFIG
|
- name: PG_SYSTEM_CONFIG
|
||||||
type: systemConfig_t
|
type: systemConfig_t
|
||||||
headers: ["fc/config.h"]
|
headers: ["fc/config.h"]
|
||||||
|
|
|
@ -30,8 +30,14 @@ PG_RESET_TEMPLATE(statsConfig_t, statsConfig,
|
||||||
|
|
||||||
static uint32_t arm_millis;
|
static uint32_t arm_millis;
|
||||||
static uint32_t arm_distance_cm;
|
static uint32_t arm_distance_cm;
|
||||||
|
|
||||||
#ifdef USE_ADC
|
#ifdef USE_ADC
|
||||||
static uint32_t arm_mWhDrawn;
|
static uint32_t arm_mWhDrawn;
|
||||||
|
static uint32_t flyingEnergy; // energy drawn during flying up to last disarm (ARMED) mWh
|
||||||
|
|
||||||
|
uint32_t getFlyingEnergy() {
|
||||||
|
return flyingEnergy;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void statsOnArm(void)
|
void statsOnArm(void)
|
||||||
|
@ -51,8 +57,11 @@ void statsOnDisarm(void)
|
||||||
statsConfigMutable()->stats_total_time += dt; //[s]
|
statsConfigMutable()->stats_total_time += dt; //[s]
|
||||||
statsConfigMutable()->stats_total_dist += (getTotalTravelDistance() - arm_distance_cm) / 100; //[m]
|
statsConfigMutable()->stats_total_dist += (getTotalTravelDistance() - arm_distance_cm) / 100; //[m]
|
||||||
#ifdef USE_ADC
|
#ifdef USE_ADC
|
||||||
if (feature(FEATURE_VBAT) && feature(FEATURE_CURRENT_METER))
|
if (feature(FEATURE_VBAT) && feature(FEATURE_CURRENT_METER)) {
|
||||||
statsConfigMutable()->stats_total_energy += getMWhDrawn() - arm_mWhDrawn;
|
const uint32_t energy = getMWhDrawn() - arm_mWhDrawn;
|
||||||
|
statsConfigMutable()->stats_total_energy += energy;
|
||||||
|
flyingEnergy += energy;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
writeEEPROM();
|
writeEEPROM();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ typedef struct statsConfig_s {
|
||||||
uint8_t stats_enabled;
|
uint8_t stats_enabled;
|
||||||
} statsConfig_t;
|
} statsConfig_t;
|
||||||
|
|
||||||
|
uint32_t getFlyingEnergy();
|
||||||
void statsOnArm(void);
|
void statsOnArm(void);
|
||||||
void statsOnDisarm(void);
|
void statsOnDisarm(void);
|
||||||
|
|
||||||
|
|
201
src/main/flight/rth_estimator.c
Normal file
201
src/main/flight/rth_estimator.c
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
|
||||||
|
#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 "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
|
||||||
|
*/
|
||||||
|
static 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)
|
||||||
|
*/
|
||||||
|
static 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
|
||||||
|
*/
|
||||||
|
static 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)
|
||||||
|
*/
|
||||||
|
static float windCompensatedForwardSpeed(float forwardSpeed, float heading, float horizontalWindSpeed, float windHeading) {
|
||||||
|
return windDriftCorrectedForwardSpeed(forwardSpeed, heading, horizontalWindSpeed, windHeading) + forwardWindSpeed(heading, horizontalWindSpeed, windHeading);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns degrees
|
||||||
|
static 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
|
||||||
|
static 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)heatLossesCompensatedPower(batteryMetersConfig()->idle_power + batteryMetersConfig()->cruise_power * altitudeChangeThrToCruiseThrRatio) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// altitudeChange is in m
|
||||||
|
// verticalWindSpeed is in m/s
|
||||||
|
// cruise_speed is in cm/s
|
||||||
|
// output is in seconds
|
||||||
|
static 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
|
||||||
|
static 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
|
||||||
|
static uint16_t estimateRTHAltitudeChangeEnergy(float altitudeChange, float verticalWindSpeed) {
|
||||||
|
return estimateRTHAltitudeChangePower(altitudeChange) * estimateRTHAltitudeChangeTime(altitudeChange, verticalWindSpeed) / 3600;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns distance in m
|
||||||
|
// *heading is in degrees
|
||||||
|
static 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 mWh
|
||||||
|
static int32_t calculateRemainingEnergyBeforeRTH(bool takeWindIntoAccount) {
|
||||||
|
// Fixed wing only for now
|
||||||
|
if (!STATE(FIXED_WING))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!(feature(FEATURE_VBAT) && feature(FEATURE_CURRENT_METER) && navigationPositionEstimateIsHealthy() && (batteryMetersConfig()->cruise_power > 0) && (ARMING_FLAG(ARMED)) && ((!STATE(FIXED_WING)) || (isNavLaunchEnabled() && isFixedWingLaunchDetected())) && (navConfig()->fw.cruise_speed > 0) && (currentBatteryProfile->capacity.unit == BAT_CAPACITY_UNIT_MWH) && (currentBatteryProfile->capacity.value > 0) && batteryWasFullWhenPluggedIn() && isEstimatedWindSpeedValid() && isImuHeadingValid()))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint16_t windHeading; // centidegrees
|
||||||
|
const float horizontalWindSpeed = takeWindIntoAccount ? 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 + heatLossesCompensatedPower(batteryMetersConfig()->idle_power + batteryMetersConfig()->cruise_power) * time_to_home / 360; // mWh
|
||||||
|
const uint32_t energy_margin_abs = (currentBatteryProfile->capacity.value - currentBatteryProfile->capacity.critical) * batteryMetersConfig()->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(bool takeWindIntoAccount) {
|
||||||
|
|
||||||
|
const int32_t remainingEnergyBeforeRTH = calculateRemainingEnergyBeforeRTH(takeWindIntoAccount);
|
||||||
|
|
||||||
|
// 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(bool takeWindIntoAccount) {
|
||||||
|
|
||||||
|
const int32_t remainingFlightTimeBeforeRTH = calculateRemainingFlightTimeBeforeRTH(takeWindIntoAccount);
|
||||||
|
|
||||||
|
// error: return error code directly
|
||||||
|
if (remainingFlightTimeBeforeRTH < 0)
|
||||||
|
return remainingFlightTimeBeforeRTH;
|
||||||
|
|
||||||
|
return remainingFlightTimeBeforeRTH * calculateAverageSpeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
5
src/main/flight/rth_estimator.h
Normal file
5
src/main/flight/rth_estimator.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
#if defined(USE_ADC) && defined(USE_GPS)
|
||||||
|
int32_t calculateRemainingFlightTimeBeforeRTH(bool takeWindIntoAccount);
|
||||||
|
int32_t calculateRemainingDistanceBeforeRTH(bool takeWindIntoAccount);
|
||||||
|
#endif
|
|
@ -173,4 +173,4 @@ void updateWindEstimator(timeUs_t currentTimeUs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "common/axis.h"
|
||||||
#include "common/time.h"
|
#include "common/time.h"
|
||||||
|
|
||||||
bool isEstimatedWindSpeedValid(void);
|
bool isEstimatedWindSpeedValid(void);
|
||||||
|
|
|
@ -67,9 +67,11 @@
|
||||||
#include "fc/rc_controls.h"
|
#include "fc/rc_controls.h"
|
||||||
#include "fc/rc_modes.h"
|
#include "fc/rc_modes.h"
|
||||||
#include "fc/runtime_config.h"
|
#include "fc/runtime_config.h"
|
||||||
|
#include "fc/fc_tasks.h"
|
||||||
|
|
||||||
#include "flight/imu.h"
|
#include "flight/imu.h"
|
||||||
#include "flight/pid.h"
|
#include "flight/pid.h"
|
||||||
|
#include "flight/rth_estimator.h"
|
||||||
#include "flight/wind_estimator.h"
|
#include "flight/wind_estimator.h"
|
||||||
|
|
||||||
#include "navigation/navigation.h"
|
#include "navigation/navigation.h"
|
||||||
|
@ -116,7 +118,6 @@
|
||||||
x; \
|
x; \
|
||||||
})
|
})
|
||||||
|
|
||||||
static timeUs_t flyTime = 0;
|
|
||||||
static unsigned currentLayout = 0;
|
static unsigned currentLayout = 0;
|
||||||
static int layoutOverride = -1;
|
static int layoutOverride = -1;
|
||||||
|
|
||||||
|
@ -160,7 +161,7 @@ static displayPort_t *osdDisplayPort;
|
||||||
#define AH_SIDEBAR_WIDTH_POS 7
|
#define AH_SIDEBAR_WIDTH_POS 7
|
||||||
#define AH_SIDEBAR_HEIGHT_POS 3
|
#define AH_SIDEBAR_HEIGHT_POS 3
|
||||||
|
|
||||||
PG_REGISTER_WITH_RESET_FN(osdConfig_t, osdConfig, PG_OSD_CONFIG, 2);
|
PG_REGISTER_WITH_RESET_FN(osdConfig_t, osdConfig, PG_OSD_CONFIG, 3);
|
||||||
|
|
||||||
static int digitCount(int32_t value)
|
static int digitCount(int32_t value)
|
||||||
{
|
{
|
||||||
|
@ -460,7 +461,7 @@ static inline void osdFormatOnTime(char *buff)
|
||||||
|
|
||||||
static inline void osdFormatFlyTime(char *buff, textAttributes_t *attr)
|
static inline void osdFormatFlyTime(char *buff, textAttributes_t *attr)
|
||||||
{
|
{
|
||||||
uint32_t seconds = flyTime / 1000000;
|
uint32_t seconds = getFlightTime();
|
||||||
osdFormatTime(buff, seconds, SYM_FLY_M, SYM_FLY_H);
|
osdFormatTime(buff, seconds, SYM_FLY_M, SYM_FLY_H);
|
||||||
if (attr && osdConfig()->time_alarm > 0) {
|
if (attr && osdConfig()->time_alarm > 0) {
|
||||||
if (seconds / 60 >= osdConfig()->time_alarm && ARMING_FLAG(ARMED)) {
|
if (seconds / 60 >= osdConfig()->time_alarm && ARMING_FLAG(ARMED)) {
|
||||||
|
@ -1233,6 +1234,60 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH:
|
||||||
|
{
|
||||||
|
static timeUs_t updatedTimestamp = 0;
|
||||||
|
/*static int32_t updatedTimeSeconds = 0;*/
|
||||||
|
timeUs_t currentTimeUs = micros();
|
||||||
|
static int32_t timeSeconds = -1;
|
||||||
|
if (cmpTimeUs(currentTimeUs, updatedTimestamp) >= 1000000) {
|
||||||
|
timeSeconds = calculateRemainingFlightTimeBeforeRTH(osdConfig()->estimations_wind_compensation);
|
||||||
|
updatedTimestamp = currentTimeUs;
|
||||||
|
}
|
||||||
|
if ((!ARMING_FLAG(ARMED)) || (timeSeconds == -1)) {
|
||||||
|
buff[0] = SYM_FLY_M;
|
||||||
|
strcpy(buff + 1, "--:--");
|
||||||
|
updatedTimestamp = 0;
|
||||||
|
} else if (timeSeconds == -2) {
|
||||||
|
// Wind is too strong to come back with cruise throttle
|
||||||
|
buff[0] = SYM_FLY_M;
|
||||||
|
buff[1] = buff[2] = buff[4] = buff[5] = SYM_WIND_HORIZONTAL;
|
||||||
|
buff[3] = ':';
|
||||||
|
buff[6] = '\0';
|
||||||
|
TEXT_ATTRIBUTES_ADD_BLINK(elemAttr);
|
||||||
|
} else {
|
||||||
|
osdFormatTime(buff, timeSeconds, SYM_FLY_M, SYM_FLY_H);
|
||||||
|
if (timeSeconds == 0)
|
||||||
|
TEXT_ATTRIBUTES_ADD_BLINK(elemAttr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OSD_REMAINING_DISTANCE_BEFORE_RTH:;
|
||||||
|
static timeUs_t updatedTimestamp = 0;
|
||||||
|
timeUs_t currentTimeUs = micros();
|
||||||
|
static int32_t distanceMeters = -1;
|
||||||
|
if (cmpTimeUs(currentTimeUs, updatedTimestamp) >= 1000000) {
|
||||||
|
distanceMeters = calculateRemainingDistanceBeforeRTH(osdConfig()->estimations_wind_compensation);
|
||||||
|
updatedTimestamp = currentTimeUs;
|
||||||
|
}
|
||||||
|
buff[0] = SYM_TRIP_DIST;
|
||||||
|
if ((!ARMING_FLAG(ARMED)) || (distanceMeters == -1)) {
|
||||||
|
buff[1] = SYM_DIST_M;
|
||||||
|
strcpy(buff + 2, "---");
|
||||||
|
} else if (distanceMeters == -2) {
|
||||||
|
// Wind is too strong to come back with cruise throttle
|
||||||
|
buff[0] = SYM_DIST_M;
|
||||||
|
buff[2] = buff[3] = buff[4] = SYM_WIND_HORIZONTAL;
|
||||||
|
buff[5] = '\0';
|
||||||
|
TEXT_ATTRIBUTES_ADD_BLINK(elemAttr);
|
||||||
|
} else {
|
||||||
|
osdFormatDistanceSymbol(buff + 1, distanceMeters * 100);
|
||||||
|
if (distanceMeters == 0)
|
||||||
|
TEXT_ATTRIBUTES_ADD_BLINK(elemAttr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case OSD_FLYMODE:
|
case OSD_FLYMODE:
|
||||||
{
|
{
|
||||||
char *p = "ACRO";
|
char *p = "ACRO";
|
||||||
|
@ -1875,6 +1930,9 @@ static uint8_t osdIncElementIndex(uint8_t elementIndex)
|
||||||
if (elementIndex == OSD_EFFICIENCY_MAH_PER_KM) {
|
if (elementIndex == OSD_EFFICIENCY_MAH_PER_KM) {
|
||||||
elementIndex = OSD_TRIP_DIST;
|
elementIndex = OSD_TRIP_DIST;
|
||||||
}
|
}
|
||||||
|
if (elementIndex == OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH) {
|
||||||
|
elementIndex = OSD_ITEM_COUNT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!feature(FEATURE_GPS)) {
|
if (!feature(FEATURE_GPS)) {
|
||||||
if (elementIndex == OSD_GPS_SPEED) {
|
if (elementIndex == OSD_GPS_SPEED) {
|
||||||
|
@ -1952,6 +2010,8 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig)
|
||||||
osdConfig->item_pos[0][OSD_FLYTIME] = OSD_POS(23, 9);
|
osdConfig->item_pos[0][OSD_FLYTIME] = OSD_POS(23, 9);
|
||||||
osdConfig->item_pos[0][OSD_ONTIME_FLYTIME] = OSD_POS(23, 11) | OSD_VISIBLE_FLAG;
|
osdConfig->item_pos[0][OSD_ONTIME_FLYTIME] = OSD_POS(23, 11) | OSD_VISIBLE_FLAG;
|
||||||
osdConfig->item_pos[0][OSD_RTC_TIME] = OSD_POS(23, 12);
|
osdConfig->item_pos[0][OSD_RTC_TIME] = OSD_POS(23, 12);
|
||||||
|
osdConfig->item_pos[0][OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH] = OSD_POS(23, 7);
|
||||||
|
osdConfig->item_pos[0][OSD_REMAINING_DISTANCE_BEFORE_RTH] = OSD_POS(23, 6);
|
||||||
|
|
||||||
osdConfig->item_pos[0][OSD_GPS_SATS] = OSD_POS(0, 11) | OSD_VISIBLE_FLAG;
|
osdConfig->item_pos[0][OSD_GPS_SATS] = OSD_POS(0, 11) | OSD_VISIBLE_FLAG;
|
||||||
osdConfig->item_pos[0][OSD_GPS_HDOP] = OSD_POS(0, 10);
|
osdConfig->item_pos[0][OSD_GPS_HDOP] = OSD_POS(0, 10);
|
||||||
|
@ -1995,6 +2055,8 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig)
|
||||||
osdConfig->units = OSD_UNIT_METRIC;
|
osdConfig->units = OSD_UNIT_METRIC;
|
||||||
osdConfig->main_voltage_decimals = 1;
|
osdConfig->main_voltage_decimals = 1;
|
||||||
osdConfig->attitude_angle_decimals = 0;
|
osdConfig->attitude_angle_decimals = 0;
|
||||||
|
|
||||||
|
osdConfig->estimations_wind_compensation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void osdSetNextRefreshIn(uint32_t timeMs) {
|
static void osdSetNextRefreshIn(uint32_t timeMs) {
|
||||||
|
@ -2196,7 +2258,7 @@ static void osdShowStats(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
displayWrite(osdDisplayPort, statNameX, top, "FLY TIME :");
|
displayWrite(osdDisplayPort, statNameX, top, "FLY TIME :");
|
||||||
uint32_t flySeconds = flyTime / 1000000;
|
uint16_t flySeconds = getFlightTime();
|
||||||
uint16_t flyMinutes = flySeconds / 60;
|
uint16_t flyMinutes = flySeconds / 60;
|
||||||
flySeconds %= 60;
|
flySeconds %= 60;
|
||||||
uint16_t flyHours = flyMinutes / 60;
|
uint16_t flyHours = flyMinutes / 60;
|
||||||
|
@ -2241,8 +2303,6 @@ static void osdShowArmed(void)
|
||||||
|
|
||||||
static void osdRefresh(timeUs_t currentTimeUs)
|
static void osdRefresh(timeUs_t currentTimeUs)
|
||||||
{
|
{
|
||||||
static timeUs_t lastTimeUs = 0;
|
|
||||||
|
|
||||||
if (IS_RC_MODE_ACTIVE(BOXOSD) && (!cmsInMenu)) {
|
if (IS_RC_MODE_ACTIVE(BOXOSD) && (!cmsInMenu)) {
|
||||||
displayClearScreen(osdDisplayPort);
|
displayClearScreen(osdDisplayPort);
|
||||||
armState = ARMING_FLAG(ARMED);
|
armState = ARMING_FLAG(ARMED);
|
||||||
|
@ -2263,13 +2323,6 @@ static void osdRefresh(timeUs_t currentTimeUs)
|
||||||
armState = ARMING_FLAG(ARMED);
|
armState = ARMING_FLAG(ARMED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ARMING_FLAG(ARMED)) {
|
|
||||||
timeUs_t deltaT = currentTimeUs - lastTimeUs;
|
|
||||||
flyTime += deltaT;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTimeUs = currentTimeUs;
|
|
||||||
|
|
||||||
if (resumeRefreshAt) {
|
if (resumeRefreshAt) {
|
||||||
// If we already reached he time for the next refresh,
|
// If we already reached he time for the next refresh,
|
||||||
// or THR is high or PITCH is high, resume refreshing.
|
// or THR is high or PITCH is high, resume refreshing.
|
||||||
|
|
|
@ -85,6 +85,8 @@ typedef enum {
|
||||||
OSD_WIND_SPEED_VERTICAL,
|
OSD_WIND_SPEED_VERTICAL,
|
||||||
OSD_SAG_COMPENSATED_MAIN_BATT_VOLTAGE,
|
OSD_SAG_COMPENSATED_MAIN_BATT_VOLTAGE,
|
||||||
OSD_MAIN_BATT_SAG_COMPENSATED_CELL_VOLTAGE,
|
OSD_MAIN_BATT_SAG_COMPENSATED_CELL_VOLTAGE,
|
||||||
|
OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH,
|
||||||
|
OSD_REMAINING_DISTANCE_BEFORE_RTH,
|
||||||
OSD_ITEM_COUNT // MUST BE LAST
|
OSD_ITEM_COUNT // MUST BE LAST
|
||||||
} osd_items_e;
|
} osd_items_e;
|
||||||
|
|
||||||
|
@ -136,6 +138,8 @@ typedef struct osdConfig_s {
|
||||||
|
|
||||||
uint8_t units; // from osd_unit_e
|
uint8_t units; // from osd_unit_e
|
||||||
uint8_t stats_energy_unit; // from osd_stats_energy_unit_e
|
uint8_t stats_energy_unit; // from osd_stats_energy_unit_e
|
||||||
|
|
||||||
|
bool estimations_wind_compensation; // use wind compensation for estimated remaining flight/distance
|
||||||
} osdConfig_t;
|
} osdConfig_t;
|
||||||
|
|
||||||
PG_DECLARE(osdConfig_t, osdConfig);
|
PG_DECLARE(osdConfig_t, osdConfig);
|
||||||
|
|
|
@ -124,6 +124,7 @@ PG_RESET_TEMPLATE(navConfig_t, navConfig,
|
||||||
.max_climb_angle = 20,
|
.max_climb_angle = 20,
|
||||||
.max_dive_angle = 15,
|
.max_dive_angle = 15,
|
||||||
.cruise_throttle = 1400,
|
.cruise_throttle = 1400,
|
||||||
|
.cruise_speed = 0, // cm/s
|
||||||
.max_throttle = 1700,
|
.max_throttle = 1700,
|
||||||
.min_throttle = 1200,
|
.min_throttle = 1200,
|
||||||
.pitch_to_throttle = 10, // pwm units per degree of pitch (10pwm units ~ 1% throttle)
|
.pitch_to_throttle = 10, // pwm units per degree of pitch (10pwm units ~ 1% throttle)
|
||||||
|
@ -1893,6 +1894,22 @@ static void updateHomePositionCompatibility(void)
|
||||||
GPS_directionToHome = posControl.homeDirection / 100;
|
GPS_directionToHome = posControl.homeDirection / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float RTHAltitude() {
|
||||||
|
switch (navConfig()->general.flags.rth_alt_control_mode) {
|
||||||
|
case NAV_RTH_NO_ALT:
|
||||||
|
return(posControl.actualState.abs.pos.z);
|
||||||
|
case NAV_RTH_EXTRA_ALT: // Maintain current altitude + predefined safety margin
|
||||||
|
return(posControl.actualState.abs.pos.z + navConfig()->general.rth_altitude);
|
||||||
|
case NAV_RTH_MAX_ALT:
|
||||||
|
return(MAX(posControl.homeWaypointAbove.pos.z, posControl.actualState.abs.pos.z));
|
||||||
|
case NAV_RTH_AT_LEAST_ALT: // Climb to at least some predefined altitude above home
|
||||||
|
return(MAX(posControl.homePosition.pos.z + navConfig()->general.rth_altitude, posControl.actualState.abs.pos.z));
|
||||||
|
case NAV_RTH_CONST_ALT: // Climb/descend to predefined altitude above home
|
||||||
|
default:
|
||||||
|
return(posControl.homePosition.pos.z + navConfig()->general.rth_altitude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
* Reset home position to current position
|
* Reset home position to current position
|
||||||
*-----------------------------------------------------------*/
|
*-----------------------------------------------------------*/
|
||||||
|
@ -1900,24 +1917,7 @@ static void updateDesiredRTHAltitude(void)
|
||||||
{
|
{
|
||||||
if (ARMING_FLAG(ARMED)) {
|
if (ARMING_FLAG(ARMED)) {
|
||||||
if (!(navGetStateFlags(posControl.navState) & NAV_AUTO_RTH)) {
|
if (!(navGetStateFlags(posControl.navState) & NAV_AUTO_RTH)) {
|
||||||
switch (navConfig()->general.flags.rth_alt_control_mode) {
|
posControl.homeWaypointAbove.pos.z = RTHAltitude();
|
||||||
case NAV_RTH_NO_ALT:
|
|
||||||
posControl.homeWaypointAbove.pos.z = posControl.actualState.abs.pos.z;
|
|
||||||
break;
|
|
||||||
case NAV_RTH_EXTRA_ALT: // Maintain current altitude + predefined safety margin
|
|
||||||
posControl.homeWaypointAbove.pos.z = posControl.actualState.abs.pos.z + navConfig()->general.rth_altitude;
|
|
||||||
break;
|
|
||||||
case NAV_RTH_MAX_ALT:
|
|
||||||
posControl.homeWaypointAbove.pos.z = MAX(posControl.homeWaypointAbove.pos.z, posControl.actualState.abs.pos.z);
|
|
||||||
break;
|
|
||||||
case NAV_RTH_AT_LEAST_ALT: // Climb to at least some predefined altitude above home
|
|
||||||
posControl.homeWaypointAbove.pos.z = MAX(posControl.homePosition.pos.z + navConfig()->general.rth_altitude, posControl.actualState.abs.pos.z);
|
|
||||||
break;
|
|
||||||
case NAV_RTH_CONST_ALT: // Climb/descend to predefined altitude above home
|
|
||||||
default:
|
|
||||||
posControl.homeWaypointAbove.pos.z = posControl.homePosition.pos.z + navConfig()->general.rth_altitude;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -3053,6 +3053,11 @@ int32_t navigationGetHomeHeading(void)
|
||||||
return posControl.homePosition.yaw;
|
return posControl.homePosition.yaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns m/s
|
||||||
|
float calculateAverageSpeed() {
|
||||||
|
return (float)getTotalTravelDistance() / (getFlightTime() * 100);
|
||||||
|
}
|
||||||
|
|
||||||
#else // NAV
|
#else // NAV
|
||||||
|
|
||||||
#ifdef USE_GPS
|
#ifdef USE_GPS
|
||||||
|
@ -3115,6 +3120,7 @@ int32_t getTotalTravelDistance(void)
|
||||||
{
|
{
|
||||||
return lrintf(GPS_totalTravelDistance);
|
return lrintf(GPS_totalTravelDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // NAV
|
#endif // NAV
|
||||||
|
|
|
@ -152,6 +152,7 @@ typedef struct navConfig_s {
|
||||||
uint8_t max_climb_angle; // Fixed wing max banking angle (deg)
|
uint8_t max_climb_angle; // Fixed wing max banking angle (deg)
|
||||||
uint8_t max_dive_angle; // Fixed wing max banking angle (deg)
|
uint8_t max_dive_angle; // Fixed wing max banking angle (deg)
|
||||||
uint16_t cruise_throttle; // Cruise throttle
|
uint16_t cruise_throttle; // Cruise throttle
|
||||||
|
uint16_t cruise_speed; // Speed at cruise throttle (cm/s), used for time/distance left before RTH
|
||||||
uint16_t min_throttle; // Minimum allowed throttle in auto mode
|
uint16_t min_throttle; // Minimum allowed throttle in auto mode
|
||||||
uint16_t max_throttle; // Maximum allowed throttle in auto mode
|
uint16_t max_throttle; // Maximum allowed throttle in auto mode
|
||||||
uint8_t pitch_to_throttle; // Pitch angle (in deg) to throttle gain (in 1/1000's of throttle) (*10)
|
uint8_t pitch_to_throttle; // Pitch angle (in deg) to throttle gain (in 1/1000's of throttle) (*10)
|
||||||
|
@ -309,6 +310,8 @@ void resetWaypointList(void);
|
||||||
bool loadNonVolatileWaypointList(void);
|
bool loadNonVolatileWaypointList(void);
|
||||||
bool saveNonVolatileWaypointList(void);
|
bool saveNonVolatileWaypointList(void);
|
||||||
|
|
||||||
|
float RTHAltitude();
|
||||||
|
|
||||||
/* Geodetic functions */
|
/* Geodetic functions */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GEO_ALT_ABSOLUTE,
|
GEO_ALT_ABSOLUTE,
|
||||||
|
@ -339,6 +342,9 @@ bool navigationIsFlyingAutonomousMode(void);
|
||||||
bool navigationRTHAllowsLanding(void);
|
bool navigationRTHAllowsLanding(void);
|
||||||
|
|
||||||
bool isNavLaunchEnabled(void);
|
bool isNavLaunchEnabled(void);
|
||||||
|
bool isFixedWingLaunchDetected(void);
|
||||||
|
|
||||||
|
float calculateAverageSpeed();
|
||||||
|
|
||||||
/* Returns the heading recorded when home position was acquired.
|
/* Returns the heading recorded when home position was acquired.
|
||||||
* Note that the navigation system uses deg*100 as unit and angles
|
* Note that the navigation system uses deg*100 as unit and angles
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include "build/debug.h"
|
||||||
|
|
||||||
#include "common/maths.h"
|
#include "common/maths.h"
|
||||||
#include "common/filter.h"
|
#include "common/filter.h"
|
||||||
|
|
||||||
|
@ -32,7 +34,14 @@
|
||||||
#include "drivers/time.h"
|
#include "drivers/time.h"
|
||||||
|
|
||||||
#include "fc/config.h"
|
#include "fc/config.h"
|
||||||
|
#include "fc/fc_core.h"
|
||||||
#include "fc/runtime_config.h"
|
#include "fc/runtime_config.h"
|
||||||
|
#include "fc/stats.h"
|
||||||
|
|
||||||
|
#include "flight/imu.h"
|
||||||
|
#include "flight/mixer.h"
|
||||||
|
|
||||||
|
#include "navigation/navigation.h"
|
||||||
|
|
||||||
#include "config/feature.h"
|
#include "config/feature.h"
|
||||||
|
|
||||||
|
@ -44,7 +53,6 @@
|
||||||
|
|
||||||
#include "io/beeper.h"
|
#include "io/beeper.h"
|
||||||
|
|
||||||
#include "build/debug.h"
|
|
||||||
|
|
||||||
#define ADCVREF 3300 // in mV (3300 = 3.3V)
|
#define ADCVREF 3300 // in mV (3300 = 3.3V)
|
||||||
|
|
||||||
|
@ -119,7 +127,11 @@ PG_RESET_TEMPLATE(batteryMetersConfig_t, batteryMetersConfig,
|
||||||
.offset = CURRENT_METER_OFFSET
|
.offset = CURRENT_METER_OFFSET
|
||||||
},
|
},
|
||||||
|
|
||||||
.voltageSource = BAT_VOLTAGE_RAW
|
.voltageSource = BAT_VOLTAGE_RAW,
|
||||||
|
|
||||||
|
.cruise_power = 0,
|
||||||
|
.idle_power = 0,
|
||||||
|
.rth_energy_margin = 5
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -459,6 +471,14 @@ void powerMeterUpdate(timeUs_t timeDelta)
|
||||||
mWhDrawn = mWhDrawnRaw / (3600 * 100);
|
mWhDrawn = mWhDrawnRaw / (3600 * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calculate true power including heat losses in power supply (battery + wires)
|
||||||
|
// power is in cW (0.01W)
|
||||||
|
// batteryWarningVoltage is in cV (0.01V)
|
||||||
|
int32_t heatLossesCompensatedPower(int32_t power)
|
||||||
|
{
|
||||||
|
return power + sq(power * 100 / batteryWarningVoltage) * powerSupplyImpedance / 100000;
|
||||||
|
}
|
||||||
|
|
||||||
void sagCompensatedVBatUpdate(timeUs_t currentTime)
|
void sagCompensatedVBatUpdate(timeUs_t currentTime)
|
||||||
{
|
{
|
||||||
static timeUs_t recordTimestamp = 0;
|
static timeUs_t recordTimestamp = 0;
|
||||||
|
@ -510,3 +530,13 @@ uint8_t calculateBatteryPercentage(void)
|
||||||
void batteryDisableProfileAutoswitch(void) {
|
void batteryDisableProfileAutoswitch(void) {
|
||||||
profileAutoswitchDisable = true;
|
profileAutoswitchDisable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns cW (0.01W)
|
||||||
|
int32_t calculateAveragePower() {
|
||||||
|
return (int64_t)mWhDrawn * 360 / getFlightTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns mWh / meter
|
||||||
|
int32_t calculateAverageEfficiency() {
|
||||||
|
return getFlyingEnergy() * 100 / getTotalTravelDistance();
|
||||||
|
}
|
||||||
|
|
|
@ -74,6 +74,10 @@ typedef struct batteryMetersConfig_s {
|
||||||
|
|
||||||
batVoltageSource_e voltageSource;
|
batVoltageSource_e voltageSource;
|
||||||
|
|
||||||
|
uint32_t cruise_power; // power drawn by the motor(s) at cruise throttle/speed (cW)
|
||||||
|
uint16_t idle_power; // power drawn by the system when the motor(s) are stopped (cW)
|
||||||
|
uint8_t rth_energy_margin; // Energy that should be left after RTH (%), used for remaining time/distance before RTH
|
||||||
|
|
||||||
} batteryMetersConfig_t;
|
} batteryMetersConfig_t;
|
||||||
|
|
||||||
typedef struct batteryProfile_s {
|
typedef struct batteryProfile_s {
|
||||||
|
@ -144,3 +148,6 @@ void powerMeterUpdate(timeUs_t timeDelta);
|
||||||
|
|
||||||
uint8_t calculateBatteryPercentage(void);
|
uint8_t calculateBatteryPercentage(void);
|
||||||
float calculateThrottleCompensationFactor(void);
|
float calculateThrottleCompensationFactor(void);
|
||||||
|
int32_t calculateAveragePower();
|
||||||
|
int32_t calculateAverageEfficiency();
|
||||||
|
int32_t heatLossesCompensatedPower(int32_t power);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue