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/>.
21 //#define DEBUG_BATTERY
24 #include "sensors/battery.h"
26 #include "io/rc_controls.h"
27 #include "flight/lowpass.h"
28 #include "io/beeper.h"
31 #include "unittest_macros.h"
32 #include "gtest/gtest.h"
34 typedef struct batteryAdcToVoltageExpectation_s
{
36 uint16_t expectedVoltageInDeciVoltSteps
;
38 } batteryAdcToVoltageExpectation_t
;
40 #define ELEVEN_TO_ONE_VOLTAGE_DIVIDER 110 // (10k:1k) * 10 for 0.1V
42 TEST(BatteryTest
, BatteryADCToVoltage
)
44 // batteryInit() reads a bunch of fields including vbatscale, so set up the config with useful initial values:
45 batteryConfig_t batteryConfig
= {
46 .vbatscale
= VBAT_SCALE_DEFAULT
,
47 .vbatresdivval
= VBAT_RESDIVVAL_DEFAULT
,
48 .vbatresdivmultiplier
= VBAT_RESDIVMULTIPLIER_DEFAULT
,
49 .vbatmaxcellvoltage
= 43,
50 .vbatmincellvoltage
= 33,
51 .vbatwarningcellvoltage
= 35,
52 .currentMeterScale
= 400,
53 .currentMeterOffset
= 0,
54 .currentMeterType
= CURRENT_SENSOR_NONE
,
55 .multiwiiCurrentMeterOutput
= 0,
56 .batteryCapacity
= 2200,
59 batteryInit(&batteryConfig
);
61 batteryAdcToVoltageExpectation_t batteryAdcToVoltageExpectations
[] = {
62 {1420, 126 /*125.88*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
63 {1430, 127 /*126.76*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
64 {1440, 128 /*127.65*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
65 {1890, 168 /*167.54*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
66 {1900, 168 /*168.42*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
67 {1910, 169 /*169.31*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
68 { 0, 0 /* 0.00*/, VBAT_SCALE_MAX
},
69 {4096, 842 /*841.71*/, VBAT_SCALE_MAX
}
71 uint8_t testIterationCount
= sizeof(batteryAdcToVoltageExpectations
) / sizeof(batteryAdcToVoltageExpectation_t
);
75 for (uint8_t index
= 0; index
< testIterationCount
; index
++) {
76 batteryAdcToVoltageExpectation_t
*batteryAdcToVoltageExpectation
= &batteryAdcToVoltageExpectations
[index
];
77 batteryConfig
.vbatscale
= batteryAdcToVoltageExpectation
->scale
;
79 printf("adcReading: %d, vbatscale: %d\n",
80 batteryAdcToVoltageExpectation
->adcReading
,
81 batteryAdcToVoltageExpectation
->scale
84 uint16_t pointOneVoltSteps
= batteryAdcToVoltage(batteryAdcToVoltageExpectation
->adcReading
);
86 EXPECT_EQ(batteryAdcToVoltageExpectation
->expectedVoltageInDeciVoltSteps
, pointOneVoltSteps
);
90 uint16_t currentADCReading
;
93 typedef struct batteryAdcToBatteryStateExpectation_s
96 uint16_t expectedVoltageInDeciVoltSteps
;
97 batteryState_e expectedBatteryState
;
99 } batteryAdcToBatteryStateExpectation_t
;
101 /* Test the battery state and hysteresis code */
102 TEST(BatteryTest
, BatteryState
)
104 // batteryInit() reads a bunch of fields including vbatscale, so set up the config with useful initial values:
105 batteryConfig_t batteryConfig
= {
106 .vbatscale
= VBAT_SCALE_DEFAULT
,
107 .vbatresdivval
= VBAT_RESDIVVAL_DEFAULT
,
108 .vbatresdivmultiplier
= VBAT_RESDIVMULTIPLIER_DEFAULT
,
109 .vbatmaxcellvoltage
= 43,
110 .vbatmincellvoltage
= 33,
111 .vbatwarningcellvoltage
= 35,
112 .currentMeterScale
= 400,
113 .currentMeterOffset
= 0,
114 .currentMeterType
= CURRENT_SENSOR_NONE
,
115 .multiwiiCurrentMeterOutput
= 0,
116 .batteryCapacity
= 2200,
119 batteryInit(&batteryConfig
);
121 batteryAdcToBatteryStateExpectation_t batteryAdcToBatteryStateExpectations
[] = {
122 {1420, 126, BATTERY_OK
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
123 /* fall down to battery warning level */
124 {1185, 105, BATTERY_OK
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
125 {1175, 104, BATTERY_WARNING
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
126 /* creep back up to battery ok */
127 {1185, 105, BATTERY_WARNING
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
128 {1195, 106, BATTERY_WARNING
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
129 {1207, 107, BATTERY_OK
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
130 /* fall down to battery critical level */
131 {1175, 104, BATTERY_WARNING
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
132 {1108, 98, BATTERY_CRITICAL
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
133 /* creep back up to battery warning */
134 {1115, 99, BATTERY_CRITICAL
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
135 {1130, 100, BATTERY_CRITICAL
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
136 {1145, 101, BATTERY_WARNING
, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
},
139 uint8_t testIterationCount
= sizeof(batteryAdcToBatteryStateExpectations
) / sizeof(batteryAdcToBatteryStateExpectation_t
);
142 for (uint8_t index
= 0; index
< testIterationCount
; index
++) {
143 batteryAdcToBatteryStateExpectation_t
*batteryAdcToBatteryStateExpectation
= &batteryAdcToBatteryStateExpectations
[index
];
144 batteryConfig
.vbatscale
= batteryAdcToBatteryStateExpectation
->scale
;
145 currentADCReading
= batteryAdcToBatteryStateExpectation
->adcReading
;
147 batteryState_e batteryState
= getBatteryState();
148 EXPECT_EQ(batteryAdcToBatteryStateExpectation
->expectedBatteryState
, batteryState
);
152 typedef struct batteryAdcToCellCountExpectation_s
155 uint16_t expectedVoltageInDeciVoltSteps
;
158 } batteryAdcToCellCountExpectation_t
;
160 /* Test the cell count is correctly detected if we start at 0V */
161 TEST(BatteryTest
, CellCount
)
163 // batteryInit() reads a bunch of fields including vbatscale, so set up the config with useful initial values:
164 batteryConfig_t batteryConfig
= {
165 .vbatscale
= VBAT_SCALE_DEFAULT
,
166 .vbatresdivval
= VBAT_RESDIVVAL_DEFAULT
,
167 .vbatresdivmultiplier
= VBAT_RESDIVMULTIPLIER_DEFAULT
,
168 .vbatmaxcellvoltage
= 43,
169 .vbatmincellvoltage
= 33,
170 .vbatwarningcellvoltage
= 35,
171 .currentMeterScale
= 400,
172 .currentMeterOffset
= 0,
173 .currentMeterType
= CURRENT_SENSOR_NONE
,
174 .multiwiiCurrentMeterOutput
= 0,
175 .batteryCapacity
= 2200,
178 batteryInit(&batteryConfig
);
180 batteryAdcToCellCountExpectation_t batteryAdcToCellCountExpectations
[] = {
181 {0, 0, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
, 1},
182 {1420, 126, ELEVEN_TO_ONE_VOLTAGE_DIVIDER
, 3},
184 uint8_t testIterationCount
= sizeof(batteryAdcToCellCountExpectations
) / sizeof(batteryAdcToCellCountExpectation_t
);
187 for (uint8_t index
= 0; index
< testIterationCount
; index
++) {
188 batteryAdcToCellCountExpectation_t
*batteryAdcToCellCountExpectation
= &batteryAdcToCellCountExpectations
[index
];
189 batteryConfig
.vbatscale
= batteryAdcToCellCountExpectation
->scale
;
190 currentADCReading
= batteryAdcToCellCountExpectation
->adcReading
;
192 EXPECT_EQ(batteryAdcToCellCountExpectation
->cellCount
, batteryCellCount
);
196 //#define DEBUG_ROLLOVER_PATTERNS
198 * These next two tests do not test any production code (!) but serves as an example of how to use a signed variable for timing purposes.
200 * The 'signed diff timing' pattern is followed in a few places in the production codebase.
202 TEST(BatteryTest
, RollOverPattern1
)
205 uint16_t servicedAt
= 0;
206 uint16_t serviceInterval
= 5000;
207 int serviceCount
= 0;
208 int rolloverCount
= 0;
210 while(rolloverCount
< 3) {
212 int16_t diff
= (now
- servicedAt
);
213 if (diff
>= serviceInterval
) {
215 if (now
< servicedAt
) {
220 #ifdef DEBUG_ROLLOVER_PATTERNS
221 printf("servicedAt: %d, diff: %d\n", servicedAt
, diff
);
226 EXPECT_GE(diff
, serviceInterval
); // service interval must have passed
227 EXPECT_LT(diff
, serviceInterval
+ 95 + 10); // but never more than the service interval + ticks + jitter
230 now
+= 95 + (rand() % 10); // simulate 100 ticks +/- 5 ticks of jitter, this can rollover
232 EXPECT_GT(serviceCount
, 0);
235 TEST(BatteryTest
, RollOverPattern2
)
238 uint16_t serviceAt
= 0;
239 uint16_t serviceInterval
= 5000;
240 int serviceCount
= 0;
241 int rolloverCount
= 0;
243 while(rolloverCount
< 3) {
245 int16_t diff
= (now
- serviceAt
);
248 if (now
< serviceAt
) {
252 serviceAt
= now
+ serviceInterval
; // this can rollover
253 #ifdef DEBUG_ROLLOVER_PATTERNS
254 printf("servicedAt: %d, nextServiceAt: %d, diff: %d\n", now
, serviceAt
, diff
);
260 EXPECT_LE(diff
, serviceInterval
);
261 EXPECT_LT(diff
, 95 + 10); // never more than the ticks + jitter
264 now
+= 95 + (rand() % 10); // simulate 100 ticks +/- 5 ticks of jitter, this can rollover
266 EXPECT_GT(serviceCount
, 0);
274 uint8_t armingFlags
= 0;
275 int16_t rcCommand
[4] = {0,0,0,0};
278 bool feature(uint32_t mask
)
284 throttleStatus_e
calculateThrottleStatus(rxConfig_t
*rxConfig
, uint16_t deadband3d_throttle
)
287 UNUSED(deadband3d_throttle
);
288 return THROTTLE_HIGH
;
291 uint16_t adcGetChannel(uint8_t channel
)
294 return currentADCReading
;
297 void delay(uint32_t ms
)
303 int32_t lowpassFixed(lowpass_t
*filter
, int32_t in
, int16_t freq
)
310 void beeper(beeperMode_e mode
)