Updated and Validated
[betaflight.git] / src / test / unit / battery_unittest.cc.txt
blobd38299be4ff8fbdbb35035507a186579fd6a2f9c
1 /*
2  * This file is part of Cleanflight.
3  *
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.
8  *
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.
13  *
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/>.
16  */
17 #include <stdint.h>
19 #include <limits.h>
21 //#define DEBUG_BATTERY
23 extern "C" {
24     #include "sensors/battery.h"
25     #include "fc/rc_controls.h"
26     #include "io/beeper.h"
29 #include "unittest_macros.h"
30 #include "gtest/gtest.h"
32 typedef struct batteryAdcToVoltageExpectation_s {
33     uint16_t adcReading;
34     uint16_t expectedVoltageInDeciVoltSteps;
35     uint8_t scale;
36 } batteryAdcToVoltageExpectation_t;
38 #define ELEVEN_TO_ONE_VOLTAGE_DIVIDER 110 // (10k:1k) * 10 for 0.1V
40 TEST(BatteryTest, BatteryADCToVoltage)
42     // batteryInit() reads a bunch of fields including vbatscale, so set up the config with useful initial values:
43     batteryConfig_t batteryConfig = {
44         .vbatscale = VBAT_SCALE_DEFAULT,
45         .vbatresdivval = VBAT_RESDIVVAL_DEFAULT,
46         .vbatresdivmultiplier = VBAT_RESDIVMULTIPLIER_DEFAULT,
47         .vbatmaxcellvoltage = 43,
48         .vbatmincellvoltage = 33,
49         .vbatwarningcellvoltage = 35,
50         .currentMeterScale = 400,
51         .currentMeterOffset = 0,
52         .currentMeterType = CURRENT_SENSOR_NONE,
53         .multiwiiCurrentMeterOutput = 0,
54         .batteryCapacity = 2200,
55     };
57     batteryInit(&batteryConfig);
59     batteryAdcToVoltageExpectation_t batteryAdcToVoltageExpectations[] = {
60             {1420, 126 /*125.88*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
61             {1430, 127 /*126.76*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
62             {1440, 128 /*127.65*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
63             {1890, 168 /*167.54*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
64             {1900, 168 /*168.42*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
65             {1910, 169 /*169.31*/, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
66             {   0,   0 /*  0.00*/, VBAT_SCALE_MAX},
67             {4096, 842 /*841.71*/, VBAT_SCALE_MAX}
68     };
69     uint8_t testIterationCount = sizeof(batteryAdcToVoltageExpectations) / sizeof(batteryAdcToVoltageExpectation_t);
71     // expect
73     for (uint8_t index = 0; index < testIterationCount; index ++) {
74         batteryAdcToVoltageExpectation_t *batteryAdcToVoltageExpectation = &batteryAdcToVoltageExpectations[index];
75         batteryConfig.vbatscale = batteryAdcToVoltageExpectation->scale;
76 #ifdef DEBUG_BATTERY
77         printf("adcReading: %d, vbatscale: %d\n",
78                 batteryAdcToVoltageExpectation->adcReading,
79                 batteryAdcToVoltageExpectation->scale
80         );
81 #endif
82         uint16_t pointOneVoltSteps = batteryAdcToVoltage(batteryAdcToVoltageExpectation->adcReading);
84         EXPECT_EQ(batteryAdcToVoltageExpectation->expectedVoltageInDeciVoltSteps, pointOneVoltSteps);
85     }
88 uint16_t currentADCReading;
91 typedef struct batteryAdcToBatteryStateExpectation_s
93     uint16_t adcReading;
94     uint16_t expectedVoltageInDeciVoltSteps;
95     batteryState_e expectedBatteryState;
96     uint8_t scale;
97 } batteryAdcToBatteryStateExpectation_t;
99 /* Test the battery state and hysteresis code */
100 TEST(BatteryTest, BatteryState)
102     // batteryInit() reads a bunch of fields including vbatscale, so set up the config with useful initial values:
103     batteryConfig_t batteryConfig = {
104         .vbatscale = VBAT_SCALE_DEFAULT,
105         .vbatresdivval = VBAT_RESDIVVAL_DEFAULT,
106         .vbatresdivmultiplier = VBAT_RESDIVMULTIPLIER_DEFAULT,
107         .vbatmaxcellvoltage = 43,
108         .vbatmincellvoltage = 33,
109         .vbatwarningcellvoltage = 35,
110         .currentMeterScale = 400,
111         .currentMeterOffset = 0,
112         .currentMeterType = CURRENT_SENSOR_NONE,
113         .multiwiiCurrentMeterOutput = 0,
114         .batteryCapacity = 2200,
115     };
117     batteryInit(&batteryConfig);
119     batteryAdcToBatteryStateExpectation_t batteryAdcToBatteryStateExpectations[] = {
120             {1420, 126, BATTERY_OK, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
121             /* fall down to battery warning level */
122             {1185, 105, BATTERY_OK, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
123             {1175, 104, BATTERY_WARNING, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
124             /* creep back up to battery ok */
125             {1185, 105, BATTERY_WARNING, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
126             {1195, 106, BATTERY_WARNING, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
127             {1207, 107, BATTERY_OK, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
128             /* fall down to battery critical level */
129             {1175, 104, BATTERY_WARNING, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
130             {1108, 98, BATTERY_CRITICAL, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
131             /* creep back up to battery warning */
132             {1115, 99, BATTERY_CRITICAL, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
133             {1130, 100, BATTERY_CRITICAL, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
134             {1145, 101, BATTERY_WARNING, ELEVEN_TO_ONE_VOLTAGE_DIVIDER},
136     };
137     uint8_t testIterationCount = sizeof(batteryAdcToBatteryStateExpectations) / sizeof(batteryAdcToBatteryStateExpectation_t);
139     // expect
140     for (uint8_t index = 0; index < testIterationCount; index ++) {
141         batteryAdcToBatteryStateExpectation_t *batteryAdcToBatteryStateExpectation = &batteryAdcToBatteryStateExpectations[index];
142         batteryConfig.vbatscale = batteryAdcToBatteryStateExpectation->scale;
143         currentADCReading = batteryAdcToBatteryStateExpectation->adcReading;
144         updateBattery( );
145         batteryState_e batteryState = getBatteryState();
146         EXPECT_EQ(batteryAdcToBatteryStateExpectation->expectedBatteryState, batteryState);
147     }
150 typedef struct batteryAdcToCellCountExpectation_s
152     uint16_t adcReading;
153     uint16_t expectedVoltageInDeciVoltSteps;
154     uint8_t scale;
155     uint8_t cellCount;
156 } batteryAdcToCellCountExpectation_t;
158 /* Test the cell count is correctly detected if we start at 0V */
159 TEST(BatteryTest, CellCount)
161     // batteryInit() reads a bunch of fields including vbatscale, so set up the config with useful initial values:
162     batteryConfig_t batteryConfig = {
163         .vbatscale = VBAT_SCALE_DEFAULT,
164         .vbatresdivval = VBAT_RESDIVVAL_DEFAULT,
165         .vbatresdivmultiplier = VBAT_RESDIVMULTIPLIER_DEFAULT,
166         .vbatmaxcellvoltage = 43,
167         .vbatmincellvoltage = 33,
168         .vbatwarningcellvoltage = 35,
169         .currentMeterScale = 400,
170         .currentMeterOffset = 0,
171         .currentMeterType = CURRENT_SENSOR_NONE,
172         .multiwiiCurrentMeterOutput = 0,
173         .batteryCapacity = 2200,
174     };
176     batteryInit(&batteryConfig);
178     batteryAdcToCellCountExpectation_t batteryAdcToCellCountExpectations[] = {
179             {0, 0, ELEVEN_TO_ONE_VOLTAGE_DIVIDER, 1},
180             {1420, 126, ELEVEN_TO_ONE_VOLTAGE_DIVIDER, 3},
181     };
182     uint8_t testIterationCount = sizeof(batteryAdcToCellCountExpectations) / sizeof(batteryAdcToCellCountExpectation_t);
184     // expect
185     for (uint8_t index = 0; index < testIterationCount; index ++) {
186         batteryAdcToCellCountExpectation_t *batteryAdcToCellCountExpectation = &batteryAdcToCellCountExpectations[index];
187         batteryConfig.vbatscale = batteryAdcToCellCountExpectation->scale;
188         currentADCReading = batteryAdcToCellCountExpectation->adcReading;
189         updateBattery( );
190         EXPECT_EQ(batteryAdcToCellCountExpectation->cellCount, batteryCellCount);
191     }
194 //#define DEBUG_ROLLOVER_PATTERNS
196  * 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.
198  * The 'signed diff timing' pattern is followed in a few places in the production codebase.
199  */
200 TEST(BatteryTest, RollOverPattern1)
202     uint16_t now = 0;
203     uint16_t servicedAt = 0;
204     uint16_t serviceInterval = 5000;
205     int serviceCount = 0;
206     int rolloverCount = 0;
208     while(rolloverCount < 3) {
210         int16_t diff = (now - servicedAt);
211         if (diff >= serviceInterval) {
213             if (now < servicedAt) {
214                 rolloverCount++;
215             }
217             servicedAt = now;
218 #ifdef DEBUG_ROLLOVER_PATTERNS
219             printf("servicedAt: %d, diff: %d\n", servicedAt, diff);
220 #endif
221             serviceCount++;
223             EXPECT_GT(diff, 0);
224             EXPECT_GE(diff, serviceInterval); // service interval must have passed
225             EXPECT_LT(diff, serviceInterval + 95 + 10); // but never more than the service interval + ticks + jitter
226         }
228         now += 95 + (rand() % 10); // simulate 100 ticks +/- 5 ticks of jitter, this can rollover
229     }
230     EXPECT_GT(serviceCount, 0);
233 TEST(BatteryTest, RollOverPattern2)
235     uint16_t now = 0;
236     uint16_t serviceAt = 0;
237     uint16_t serviceInterval = 5000;
238     int serviceCount = 0;
239     int rolloverCount = 0;
241     while(rolloverCount < 3) {
243         int16_t diff = (now - serviceAt);
244         if (diff >= 0) {
246             if (now < serviceAt) {
247                 rolloverCount++;
248             }
250             serviceAt = now + serviceInterval; // this can rollover
251 #ifdef DEBUG_ROLLOVER_PATTERNS
252             printf("servicedAt: %d, nextServiceAt: %d, diff: %d\n", now, serviceAt, diff);
253 #endif
255             serviceCount++;
257             EXPECT_GE(diff, 0);
258             EXPECT_LE(diff, serviceInterval);
259             EXPECT_LT(diff, 95 + 10); // never more than the ticks + jitter
260         }
262         now += 95 + (rand() % 10); // simulate 100 ticks +/- 5 ticks of jitter, this can rollover
263     }
264     EXPECT_GT(serviceCount, 0);
268 // STUBS
270 extern "C" {
272 uint8_t armingFlags = 0;
273 float rcCommand[4] = {0,0,0,0};
276 bool featureIsEnabled(uint32_t mask)
278     UNUSED(mask);
279     return false;
282 throttleStatus_e calculateThrottleStatus(rxConfig_t *rxConfig, uint16_t deadband3d_throttle)
284     UNUSED(*rxConfig);
285     UNUSED(deadband3d_throttle);
286     return THROTTLE_HIGH;
289 uint16_t adcGetChannel(uint8_t channel)
291     UNUSED(channel);
292     return currentADCReading;
295 void delay(uint32_t ms)
297     UNUSED(ms);
298     return;
301 int32_t lowpassFixed(lowpass_t *filter, int32_t in, int16_t freq)
303     UNUSED(filter);
304     UNUSED(freq);
305     return in;
308 void beeper(beeperMode_e mode)
310     UNUSED(mode);