2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
26 #include "build/debug.h"
28 #include "common/maths.h"
29 #include "common/filter.h"
31 #include "config/config_reset.h"
32 #include "config/parameter_group.h"
33 #include "config/parameter_group_ids.h"
35 #include "drivers/adc.h"
36 #include "drivers/time.h"
38 #include "fc/config.h"
39 #include "fc/controlrate_profile.h"
40 #include "fc/fc_core.h"
41 #include "fc/runtime_config.h"
43 #include "fc/settings.h"
45 #include "flight/imu.h"
46 #include "flight/mixer.h"
48 #include "navigation/navigation.h"
49 #include "navigation/navigation_private.h"
51 #include "config/feature.h"
53 #include "sensors/battery.h"
54 #include "sensors/esc_sensor.h"
58 #include "fc/rc_controls.h"
60 #include "io/beeper.h"
63 #define ADCVREF 3300 // in mV (3300 = 3.3V)
65 #define VBATT_CELL_FULL_MAX_DIFF 10 // Max difference with cell max voltage for the battery to be considered full (10mV steps)
66 #define VBATT_PRESENT_THRESHOLD 100 // Minimum voltage to consider battery present
67 #define VBATT_STABLE_DELAY 40 // Delay after connecting battery to begin monitoring
68 #define VBATT_HYSTERESIS 10 // Batt Hysteresis of +/-100mV for changing battery state
69 #define VBATT_LPF_FREQ 1 // Battery voltage filtering cutoff
70 #define AMPERAGE_LPF_FREQ 1 // Battery current filtering cutoff
71 #define IMPEDANCE_STABLE_SAMPLE_COUNT_THRESH 10 // Minimum sample count to consider calculated power supply impedance as stable
74 // Battery monitoring stuff
75 static uint8_t batteryCellCount
; // cell count
76 static uint16_t batteryFullVoltage
;
77 static uint16_t batteryWarningVoltage
;
78 static uint16_t batteryCriticalVoltage
;
79 static uint32_t batteryRemainingCapacity
= 0;
80 static bool batteryUseCapacityThresholds
= false;
81 static bool batteryFullWhenPluggedIn
= false;
82 static bool profileAutoswitchDisable
= false;
84 static uint16_t vbat
= 0; // battery voltage in 0.1V steps (filtered)
85 static uint16_t powerSupplyImpedance
= 0; // calculated impedance in milliohm
86 static uint16_t sagCompensatedVBat
= 0; // calculated no load vbat
87 static bool powerSupplyImpedanceIsValid
= false;
89 static int16_t amperage
= 0; // amperage read by current sensor in centiampere (1/100th A)
90 static int32_t power
= 0; // power draw in cW (0.01W resolution)
91 static int32_t mAhDrawn
= 0; // milliampere hours drawn from the battery since start
92 static int32_t mWhDrawn
= 0; // energy (milliWatt hours) drawn from the battery since start
94 batteryState_e batteryState
;
95 const batteryProfile_t
*currentBatteryProfile
;
97 PG_REGISTER_ARRAY_WITH_RESET_FN(batteryProfile_t
, MAX_BATTERY_PROFILE_COUNT
, batteryProfiles
, PG_BATTERY_PROFILES
, 2);
99 void pgResetFn_batteryProfiles(batteryProfile_t
*instance
)
101 for (int i
= 0; i
< MAX_BATTERY_PROFILE_COUNT
; i
++) {
102 RESET_CONFIG(batteryProfile_t
, &instance
[i
],
104 .cells
= SETTING_BAT_CELLS_DEFAULT
,
107 .cellDetect
= SETTING_VBAT_CELL_DETECT_VOLTAGE_DEFAULT
,
108 .cellMax
= SETTING_VBAT_MAX_CELL_VOLTAGE_DEFAULT
,
109 .cellMin
= SETTING_VBAT_MIN_CELL_VOLTAGE_DEFAULT
,
110 .cellWarning
= SETTING_VBAT_WARNING_CELL_VOLTAGE_DEFAULT
115 .value
= SETTING_BATTERY_CAPACITY_DEFAULT
,
116 .warning
= SETTING_BATTERY_CAPACITY_WARNING_DEFAULT
,
117 .critical
= SETTING_BATTERY_CAPACITY_CRITICAL_DEFAULT
,
118 .unit
= SETTING_BATTERY_CAPACITY_UNIT_DEFAULT
,
121 .controlRateProfile
= 0,
124 .throttleIdle
= SETTING_THROTTLE_IDLE_DEFAULT
,
125 .throttleScale
= SETTING_THROTTLE_SCALE_DEFAULT
,
127 .turtleModePowerFactor
= SETTING_TURTLE_MODE_POWER_FACTOR_DEFAULT
,
131 .failsafe_throttle
= SETTING_FAILSAFE_THROTTLE_DEFAULT
, // default throttle off.
136 .hover_throttle
= SETTING_NAV_MC_HOVER_THR_DEFAULT
,
140 .cruise_throttle
= SETTING_NAV_FW_CRUISE_THR_DEFAULT
,
141 .max_throttle
= SETTING_NAV_FW_MAX_THR_DEFAULT
,
142 .min_throttle
= SETTING_NAV_FW_MIN_THR_DEFAULT
,
143 .pitch_to_throttle
= SETTING_NAV_FW_PITCH2THR_DEFAULT
, // pwm units per degree of pitch (10pwm units ~ 1% throttle)
144 .launch_throttle
= SETTING_NAV_FW_LAUNCH_THR_DEFAULT
,
145 .launch_idle_throttle
= SETTING_NAV_FW_LAUNCH_IDLE_THR_DEFAULT
, // Motor idle or MOTOR_STOP
150 #if defined(USE_POWER_LIMITS)
152 .continuousCurrent
= SETTING_LIMIT_CONT_CURRENT_DEFAULT
, // dA
153 .burstCurrent
= SETTING_LIMIT_BURST_CURRENT_DEFAULT
, // dA
154 .burstCurrentTime
= SETTING_LIMIT_BURST_CURRENT_TIME_DEFAULT
, // dS
155 .burstCurrentFalldownTime
= SETTING_LIMIT_BURST_CURRENT_FALLDOWN_TIME_DEFAULT
, // dS
157 .continuousPower
= SETTING_LIMIT_CONT_POWER_DEFAULT
, // dW
158 .burstPower
= SETTING_LIMIT_BURST_POWER_DEFAULT
, // dW
159 .burstPowerTime
= SETTING_LIMIT_BURST_POWER_TIME_DEFAULT
, // dS
160 .burstPowerFalldownTime
= SETTING_LIMIT_BURST_POWER_FALLDOWN_TIME_DEFAULT
, // dS
163 #endif // USE_POWER_LIMITS
169 PG_REGISTER_WITH_RESET_TEMPLATE(batteryMetersConfig_t
, batteryMetersConfig
, PG_BATTERY_METERS_CONFIG
, 1);
171 PG_RESET_TEMPLATE(batteryMetersConfig_t
, batteryMetersConfig
,
175 .type
= SETTING_VBAT_METER_TYPE_DEFAULT
,
176 .scale
= VBAT_SCALE_DEFAULT
,
181 .type
= SETTING_CURRENT_METER_TYPE_DEFAULT
,
182 .scale
= CURRENT_METER_SCALE
,
183 .offset
= CURRENT_METER_OFFSET
186 .voltageSource
= SETTING_BAT_VOLTAGE_SRC_DEFAULT
,
188 .cruise_power
= SETTING_CRUISE_POWER_DEFAULT
,
189 .idle_power
= SETTING_IDLE_POWER_DEFAULT
,
190 .rth_energy_margin
= SETTING_RTH_ENERGY_MARGIN_DEFAULT
,
192 .throttle_compensation_weight
= SETTING_THR_COMP_WEIGHT_DEFAULT
196 void batteryInit(void)
198 batteryState
= BATTERY_NOT_PRESENT
;
199 batteryCellCount
= 1;
200 batteryFullVoltage
= 0;
201 batteryWarningVoltage
= 0;
202 batteryCriticalVoltage
= 0;
206 // profileDetect() profile sorting compare function
207 static int profile_compare(profile_comp_t
*a
, profile_comp_t
*b
) {
208 if (a
->max_voltage
< b
->max_voltage
)
210 else if (a
->max_voltage
> b
->max_voltage
)
216 // Find profile matching plugged battery for profile_autoselect
217 static int8_t profileDetect(void) {
218 profile_comp_t profile_comp_array
[MAX_BATTERY_PROFILE_COUNT
];
220 // Prepare profile sort
221 for (uint8_t i
= 0; i
< MAX_BATTERY_PROFILE_COUNT
; ++i
) {
222 const batteryProfile_t
*profile
= batteryProfiles(i
);
223 profile_comp_array
[i
].profile_index
= i
;
224 profile_comp_array
[i
].max_voltage
= profile
->cells
* profile
->voltage
.cellDetect
;
227 // Sort profiles by max voltage
228 qsort(profile_comp_array
, MAX_BATTERY_PROFILE_COUNT
, sizeof(*profile_comp_array
), (int (*)(const void *, const void *))profile_compare
);
230 // Return index of the first profile where vbat <= profile_max_voltage
231 for (uint8_t i
= 0; i
< MAX_BATTERY_PROFILE_COUNT
; ++i
)
232 if ((profile_comp_array
[i
].max_voltage
> 0) && (vbat
<= profile_comp_array
[i
].max_voltage
))
233 return profile_comp_array
[i
].profile_index
;
235 // No matching profile found
240 void setBatteryProfile(uint8_t profileIndex
)
242 if (profileIndex
>= MAX_BATTERY_PROFILE_COUNT
) {
245 currentBatteryProfile
= batteryProfiles(profileIndex
);
246 if ((currentBatteryProfile
->controlRateProfile
> 0) && (currentBatteryProfile
->controlRateProfile
< MAX_CONTROL_RATE_PROFILE_COUNT
)) {
247 setConfigProfile(currentBatteryProfile
->controlRateProfile
- 1);
251 void activateBatteryProfile(void)
253 static int8_t previous_battery_profile_index
= -1;
254 // Don't call batteryInit if the battery profile was not changed to prevent batteryCellCount to be reset while adjusting board alignment
255 // causing the beeper to be silent when it is disabled while the board is connected through USB (beeper -ON_USB)
256 if (systemConfig()->current_battery_profile_index
!= previous_battery_profile_index
) {
258 previous_battery_profile_index
= systemConfig()->current_battery_profile_index
;
263 static void updateBatteryVoltage(timeUs_t timeDelta
, bool justConnected
)
265 static pt1Filter_t vbatFilterState
;
267 switch (batteryMetersConfig()->voltage
.type
) {
268 case VOLTAGE_SENSOR_ADC
:
270 vbat
= getVBatSample();
273 #if defined(USE_ESC_SENSOR)
274 case VOLTAGE_SENSOR_ESC
:
276 escSensorData_t
* escSensor
= escSensorGetData();
277 if (escSensor
&& escSensor
->dataAge
<= ESC_DATA_MAX_AGE
) {
278 vbat
= escSensor
->voltage
;
286 case VOLTAGE_SENSOR_NONE
:
292 if (ARMING_FLAG(SIMULATOR_MODE
)) {
293 if (SIMULATOR_HAS_OPTION(HITL_SIMULATE_BATTERY
)) {
294 vbat
= ((uint16_t)simulatorData
.vbat
) * 10;
295 batteryFullVoltage
= 1260;
296 batteryWarningVoltage
= 1020;
297 batteryCriticalVoltage
= 960;
302 pt1FilterReset(&vbatFilterState
, vbat
);
304 vbat
= pt1FilterApply4(&vbatFilterState
, vbat
, VBATT_LPF_FREQ
, US2S(timeDelta
));
308 batteryState_e
checkBatteryVoltageState(void)
310 uint16_t stateVoltage
= getBatteryVoltage();
311 switch (batteryState
)
314 if (stateVoltage
<= (batteryWarningVoltage
- VBATT_HYSTERESIS
)) {
315 return BATTERY_WARNING
;
318 case BATTERY_WARNING
:
319 if (stateVoltage
<= (batteryCriticalVoltage
- VBATT_HYSTERESIS
)) {
320 return BATTERY_CRITICAL
;
321 } else if (stateVoltage
> (batteryWarningVoltage
+ VBATT_HYSTERESIS
)){
325 case BATTERY_CRITICAL
:
326 if (stateVoltage
> (batteryCriticalVoltage
+ VBATT_HYSTERESIS
)) {
327 return BATTERY_WARNING
;
337 static void checkBatteryCapacityState(void)
339 if (batteryRemainingCapacity
== 0) {
340 batteryState
= BATTERY_CRITICAL
;
341 } else if (batteryRemainingCapacity
<= currentBatteryProfile
->capacity
.warning
- currentBatteryProfile
->capacity
.critical
) {
342 batteryState
= BATTERY_WARNING
;
346 void batteryUpdate(timeUs_t timeDelta
)
348 /* battery has just been connected*/
349 if (batteryState
== BATTERY_NOT_PRESENT
&& vbat
> VBATT_PRESENT_THRESHOLD
) {
351 /* Actual battery state is calculated below, this is really BATTERY_PRESENT */
352 batteryState
= BATTERY_OK
;
353 /* wait for VBatt to stabilise then we can calc number of cells
354 (using the filtered value takes a long time to ramp up)
355 We only do this on the ground so don't care if we do block, not
356 worse than original code anyway*/
357 delay(VBATT_STABLE_DELAY
);
358 updateBatteryVoltage(timeDelta
, true);
360 int8_t detectedProfileIndex
= -1;
361 if (feature(FEATURE_BAT_PROFILE_AUTOSWITCH
) && (!profileAutoswitchDisable
))
362 detectedProfileIndex
= profileDetect();
364 if (detectedProfileIndex
!= -1) {
365 systemConfigMutable()->current_battery_profile_index
= detectedProfileIndex
;
366 setBatteryProfile(detectedProfileIndex
);
367 batteryCellCount
= currentBatteryProfile
->cells
;
368 } else if (currentBatteryProfile
->cells
> 0)
369 batteryCellCount
= currentBatteryProfile
->cells
;
371 batteryCellCount
= (vbat
/ currentBatteryProfile
->voltage
.cellDetect
) + 1;
372 // Assume there are no 7S, 9S and 11S batteries so round up to 8S, 10S and 12S respectively
373 if (batteryCellCount
== 7 || batteryCellCount
== 9 || batteryCellCount
== 11) {
374 batteryCellCount
+= 1;
376 batteryCellCount
= MIN(batteryCellCount
, 12);
379 batteryFullVoltage
= batteryCellCount
* currentBatteryProfile
->voltage
.cellMax
;
380 batteryWarningVoltage
= batteryCellCount
* currentBatteryProfile
->voltage
.cellWarning
;
381 batteryCriticalVoltage
= batteryCellCount
* currentBatteryProfile
->voltage
.cellMin
;
383 batteryFullWhenPluggedIn
= vbat
>= (batteryFullVoltage
- batteryCellCount
* VBATT_CELL_FULL_MAX_DIFF
);
384 batteryUseCapacityThresholds
= isAmperageConfigured() && batteryFullWhenPluggedIn
&& (currentBatteryProfile
->capacity
.value
> 0) &&
385 (currentBatteryProfile
->capacity
.warning
> 0) && (currentBatteryProfile
->capacity
.critical
> 0);
388 updateBatteryVoltage(timeDelta
, false);
390 /* battery has been disconnected - can take a while for filter cap to disharge so we use a threshold of VBATT_PRESENT_THRESHOLD */
391 if (batteryState
!= BATTERY_NOT_PRESENT
&& vbat
<= VBATT_PRESENT_THRESHOLD
) {
392 batteryState
= BATTERY_NOT_PRESENT
;
393 batteryCellCount
= 0;
394 batteryWarningVoltage
= 0;
395 batteryCriticalVoltage
= 0;
399 if (batteryState
!= BATTERY_NOT_PRESENT
) {
401 if ((currentBatteryProfile
->capacity
.value
> 0) && batteryFullWhenPluggedIn
) {
402 uint32_t capacityDiffBetweenFullAndEmpty
= currentBatteryProfile
->capacity
.value
- currentBatteryProfile
->capacity
.critical
;
403 int32_t drawn
= (currentBatteryProfile
->capacity
.unit
== BAT_CAPACITY_UNIT_MWH
? mWhDrawn
: mAhDrawn
);
404 batteryRemainingCapacity
= (drawn
> (int32_t)capacityDiffBetweenFullAndEmpty
? 0 : capacityDiffBetweenFullAndEmpty
- drawn
);
407 if (batteryUseCapacityThresholds
) {
408 checkBatteryCapacityState();
410 batteryState
= checkBatteryVoltageState();
414 if (ARMING_FLAG(ARMED
) || !ARMING_FLAG(WAS_EVER_ARMED
) || failsafeIsActive())
415 switch (batteryState
) {
416 case BATTERY_WARNING
:
417 beeper(BEEPER_BAT_LOW
);
419 case BATTERY_CRITICAL
:
420 beeper(BEEPER_BAT_CRIT_LOW
);
429 batteryState_e
getBatteryState(void)
434 bool batteryWasFullWhenPluggedIn(void)
436 return batteryFullWhenPluggedIn
;
439 bool batteryUsesCapacityThresholds(void)
441 return batteryUseCapacityThresholds
;
444 bool isBatteryVoltageConfigured(void)
446 return feature(FEATURE_VBAT
);
450 uint16_t getVBatSample(void) {
451 // calculate battery voltage based on ADC reading
452 // result is Vbatt in 0.01V steps. 3.3V = ADC Vref, 0xFFF = 12bit adc, 1100 = 11:1 voltage divider (10k:1k)
453 return (uint64_t)adcGetChannel(ADC_BATTERY
) * batteryMetersConfig()->voltage
.scale
* ADCVREF
/ (0xFFF * 1000);
457 uint16_t getBatteryVoltage(void)
459 if (batteryMetersConfig()->voltageSource
== BAT_VOLTAGE_SAG_COMP
) {
460 return sagCompensatedVBat
;
466 uint16_t getBatteryRawVoltage(void)
471 uint16_t getBatterySagCompensatedVoltage(void)
473 return sagCompensatedVBat
;
476 float calculateThrottleCompensationFactor(void)
478 return 1.0f
+ ((float)batteryFullVoltage
/ sagCompensatedVBat
- 1.0f
) * batteryMetersConfig()->throttle_compensation_weight
;
481 uint16_t getBatteryWarningVoltage(void)
483 return batteryWarningVoltage
;
486 uint8_t getBatteryCellCount(void)
488 return batteryCellCount
;
491 uint16_t getBatteryAverageCellVoltage(void)
493 if (batteryCellCount
> 0) {
494 return getBatteryVoltage() / batteryCellCount
;
499 uint16_t getBatteryRawAverageCellVoltage(void)
501 if (batteryCellCount
> 0) {
502 return vbat
/ batteryCellCount
;
507 uint16_t getBatterySagCompensatedAverageCellVoltage(void)
509 if (batteryCellCount
> 0) {
510 return sagCompensatedVBat
/ batteryCellCount
;
515 uint32_t getBatteryRemainingCapacity(void)
517 return batteryRemainingCapacity
;
520 bool isAmperageConfigured(void)
522 return feature(FEATURE_CURRENT_METER
) && batteryMetersConfig()->current
.type
!= CURRENT_SENSOR_NONE
;
525 int16_t getAmperage(void)
530 int16_t getAmperageSample(void)
532 int32_t microvolts
= ((uint32_t)adcGetChannel(ADC_CURRENT
) * ADCVREF
* 100) / 0xFFF * 10 - (int32_t)batteryMetersConfig()->current
.offset
* 100;
533 return microvolts
/ batteryMetersConfig()->current
.scale
; // current in 0.01A steps
536 int32_t getPower(void)
541 int32_t getMAhDrawn(void)
546 int32_t getMWhDrawn(void)
551 void currentMeterUpdate(timeUs_t timeDelta
)
553 static pt1Filter_t amperageFilterState
;
554 static int64_t mAhdrawnRaw
= 0;
556 switch (batteryMetersConfig()->current
.type
) {
557 case CURRENT_SENSOR_ADC
:
559 amperage
= pt1FilterApply4(&erageFilterState
, getAmperageSample(), AMPERAGE_LPF_FREQ
, US2S(timeDelta
));
562 case CURRENT_SENSOR_VIRTUAL
:
563 amperage
= batteryMetersConfig()->current
.offset
;
564 if (ARMING_FLAG(ARMED
)) {
565 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
566 bool allNav
= navConfig()->general
.flags
.nav_overrides_motor_stop
== NOMS_ALL_NAV
&& posControl
.navState
!= NAV_STATE_IDLE
;
567 bool autoNav
= navConfig()->general
.flags
.nav_overrides_motor_stop
== NOMS_AUTO_ONLY
&& (stateFlags
& (NAV_AUTO_RTH
| NAV_AUTO_WP
));
568 int32_t throttleOffset
;
570 if (allNav
|| autoNav
) { // account for motors running in Nav modes with throttle low + motor stop
571 throttleOffset
= (int32_t)rcCommand
[THROTTLE
] - 1000;
573 throttleOffset
= (throttleStickIsLow() && feature(FEATURE_MOTOR_STOP
)) ? 0 : (int32_t)rcCommand
[THROTTLE
] - 1000;
575 int32_t throttleFactor
= throttleOffset
+ (throttleOffset
* throttleOffset
/ 50);
576 amperage
+= throttleFactor
* batteryMetersConfig()->current
.scale
/ 1000;
579 #if defined(USE_ESC_SENSOR)
580 case CURRENT_SENSOR_ESC
:
582 escSensorData_t
* escSensor
= escSensorGetData();
583 if (escSensor
&& escSensor
->dataAge
<= ESC_DATA_MAX_AGE
) {
584 amperage
= pt1FilterApply4(&erageFilterState
, escSensor
->current
, AMPERAGE_LPF_FREQ
, US2S(timeDelta
));
592 case CURRENT_SENSOR_NONE
:
598 // Clamp amperage to positive values
599 amperage
= MAX(0, amperage
);
601 // Work around int64 math compiler bug, don't change it unless the bug has been fixed !
602 // should be: mAhdrawnRaw += (int64_t)amperage * timeDelta / 1000;
603 mAhdrawnRaw
+= (int64_t)((int32_t)amperage
* timeDelta
) / 1000;
605 mAhDrawn
= mAhdrawnRaw
/ (3600 * 100);
608 void powerMeterUpdate(timeUs_t timeDelta
)
610 static int64_t mWhDrawnRaw
= 0;
611 power
= (int32_t)amperage
* vbat
/ 100; // power unit is cW (0.01W resolution)
612 int32_t heatLossesCompensatedPower_mW
= (int32_t)amperage
* vbat
/ 10 + sq((int64_t)amperage
) * powerSupplyImpedance
/ 10000;
614 // Work around int64 math compiler bug, don't change it unless the bug has been fixed !
615 // should be: mWhDrawnRaw += (int64_t)heatLossesCompensatedPower_mW * timeDelta / 10000;
616 mWhDrawnRaw
+= (int64_t)((int64_t)heatLossesCompensatedPower_mW
* timeDelta
) / 10000;
618 mWhDrawn
= mWhDrawnRaw
/ (3600 * 100);
621 // calculate true power including heat losses in power supply (battery + wires)
622 // power is in cW (0.01W)
623 // batteryWarningVoltage is in cV (0.01V)
624 int32_t heatLossesCompensatedPower(int32_t power
)
626 return power
+ sq(power
* 100 / batteryWarningVoltage
) * powerSupplyImpedance
/ 100000;
629 void sagCompensatedVBatUpdate(timeUs_t currentTime
, timeUs_t timeDelta
)
631 static timeUs_t recordTimestamp
= 0;
632 static int16_t amperageRecord
;
633 static uint16_t vbatRecord
;
634 static uint8_t impedanceSampleCount
= 0;
635 static pt1Filter_t impedanceFilterState
;
636 static pt1Filter_t sagCompVBatFilterState
;
637 static batteryState_e last_battery_state
= BATTERY_NOT_PRESENT
;
639 if ((batteryState
!= BATTERY_NOT_PRESENT
) && (last_battery_state
== BATTERY_NOT_PRESENT
)) {
640 pt1FilterReset(&sagCompVBatFilterState
, vbat
);
641 pt1FilterReset(&impedanceFilterState
, 0);
644 if (batteryState
== BATTERY_NOT_PRESENT
) {
647 impedanceSampleCount
= 0;
648 powerSupplyImpedance
= 0;
649 powerSupplyImpedanceIsValid
= false;
650 sagCompensatedVBat
= vbat
;
654 if (cmpTimeUs(currentTime
, recordTimestamp
) > MS2US(500))
657 if (!recordTimestamp
) {
658 amperageRecord
= amperage
;
660 recordTimestamp
= currentTime
;
661 } else if ((amperage
- amperageRecord
>= 200) && ((int16_t)vbatRecord
- vbat
>= 4)) {
663 uint16_t impedanceSample
= (int32_t)(vbatRecord
- vbat
) * 1000 / (amperage
- amperageRecord
);
665 if (impedanceSampleCount
<= IMPEDANCE_STABLE_SAMPLE_COUNT_THRESH
) {
666 impedanceSampleCount
+= 1;
669 if (impedanceFilterState
.state
) {
670 pt1FilterSetTimeConstant(&impedanceFilterState
, impedanceSampleCount
> IMPEDANCE_STABLE_SAMPLE_COUNT_THRESH
? 1.2 : 0.5);
671 pt1FilterApply3(&impedanceFilterState
, impedanceSample
, US2S(timeDelta
));
673 pt1FilterReset(&impedanceFilterState
, impedanceSample
);
676 if (impedanceSampleCount
> IMPEDANCE_STABLE_SAMPLE_COUNT_THRESH
) {
677 powerSupplyImpedance
= lrintf(pt1FilterGetLastOutput(&impedanceFilterState
));
678 powerSupplyImpedanceIsValid
= true;
683 uint16_t sagCompensatedVBatSample
= MIN(batteryFullVoltage
, vbat
+ (int32_t)powerSupplyImpedance
* amperage
/ 1000);
684 pt1FilterSetTimeConstant(&sagCompVBatFilterState
, sagCompensatedVBatSample
< pt1FilterGetLastOutput(&sagCompVBatFilterState
) ? 40 : 500);
685 sagCompensatedVBat
= lrintf(pt1FilterApply3(&sagCompVBatFilterState
, sagCompensatedVBatSample
, US2S(timeDelta
)));
688 DEBUG_SET(DEBUG_SAG_COMP_VOLTAGE
, 0, powerSupplyImpedance
);
689 DEBUG_SET(DEBUG_SAG_COMP_VOLTAGE
, 1, sagCompensatedVBat
);
691 last_battery_state
= batteryState
;
694 uint8_t calculateBatteryPercentage(void)
696 if (batteryState
== BATTERY_NOT_PRESENT
)
699 if (batteryFullWhenPluggedIn
&& isAmperageConfigured() && (currentBatteryProfile
->capacity
.value
> 0) && (currentBatteryProfile
->capacity
.critical
> 0)) {
700 uint32_t capacityDiffBetweenFullAndEmpty
= currentBatteryProfile
->capacity
.value
- currentBatteryProfile
->capacity
.critical
;
701 return constrain(batteryRemainingCapacity
* 100 / capacityDiffBetweenFullAndEmpty
, 0, 100);
703 return constrain((getBatteryVoltage() - batteryCriticalVoltage
) * 100L / (batteryFullVoltage
- batteryCriticalVoltage
), 0, 100);
706 void batteryDisableProfileAutoswitch(void) {
707 profileAutoswitchDisable
= true;
710 bool isPowerSupplyImpedanceValid(void) {
711 return powerSupplyImpedanceIsValid
;
714 uint16_t getPowerSupplyImpedance(void) {
715 return powerSupplyImpedance
;
718 // returns cW (0.01W)
719 int32_t calculateAveragePower() {
720 return (int64_t)mWhDrawn
* 360 / getFlightTime();
723 // returns mWh / meter
724 int32_t calculateAverageEfficiency() {
725 return getFlyingEnergy() * 100 / getTotalTravelDistance();