#include "battery.h" // 3rdparty lib includes #include #include // local includes #include "drivingstatistics.h" #include "globals.h" #include "newsettings.h" #define CURVE(higherVoltage,lowerVoltage,fromAh,toAh) \ if (cellVoltage >= lowerVoltage && cellVoltage <= higherVoltage) \ return 100 * (expected_ah - cpputils::mapValue(cellVoltage, higherVoltage, lowerVoltage, fromAh, toAh)) / expected_ah; float getBatteryPercentage(float batVoltage, BatteryCellType cellType) { const float cellVoltage = batVoltage / configs.battery.cellsSeries.value; switch (cellType) { case BatteryCellType::_22P: { const float expected_ah = 2.2; if (cellVoltage > 4.15) return 100; CURVE(4.15, 4.04, 0, 0.25) CURVE(4.04, 3.95, 0.25, 0.5) CURVE(3.95, 3.86, 0.5, 0.75) CURVE(3.86, 3.74, 0.75, 1.0) CURVE(3.74, 3.64, 1.0, 1.25) CURVE(3.64, 3.59, 1.25, 1.5) CURVE(3.59, 3.54, 1.5, 1.75) CURVE(3.54, 3.43, 1.75, 2.0) CURVE(3.43, 3.35, 2.0, 2.1) CURVE(3.35, 2.50, 2.1, 2.2) break; } case BatteryCellType::MH1: { const float expected_ah = 3.2; if (cellVoltage > 4.15) return 100; CURVE(4.15, 4.09, 0, 0.25) CURVE(4.09, 4.04, 0.25, 0.5) CURVE(4.04, 3.95, 0.5, 0.75) CURVE(3.95, 3.88, 0.75, 1.0) CURVE(3.88, 3.79, 1.0, 1.25) CURVE(3.79, 3.70, 1.25, 1.5) CURVE(3.70, 3.65, 1.5, 1.75) CURVE(3.65, 3.60, 1.75, 2.0) CURVE(3.60, 3.56, 2.0, 2.25) CURVE(3.56, 3.50, 2.25, 2.5) CURVE(3.50, 3.40, 2.5, 2.75) CURVE(3.40, 3.30, 2.75, 3.0) CURVE(3.30, 2.5, 3.0, 3.2) break; } case BatteryCellType::HG2: { const float expected_ah = 3.0; if (cellVoltage > 4.15) return 100; CURVE(4.15, 4.08, 0, 0.25) CURVE(4.08, 4.01, 0.25, 0.5) CURVE(4.01, 3.92, 0.5, 0.75) CURVE(3.92, 3.84, 0.75, 1.0) CURVE(3.84, 3.75, 1.0, 1.25) CURVE(3.75, 3.67, 1.25, 1.5) CURVE(3.67, 3.62, 1.5, 1.75) CURVE(3.62, 3.55, 1.75, 2.0) CURVE(3.55, 3.44, 2.0, 2.25) CURVE(3.44, 3.30, 2.25, 2.5) CURVE(3.30, 3.05, 2.5, 2.75) CURVE(3.05, 2.50, 2.75, 3.0) break; } case BatteryCellType::VTC5: { const float expected_ah = 2.6; if (cellVoltage > 4.15) return 100; CURVE(4.15, 4.08, 0, 0.25) CURVE(4.08, 3.98, 0.25, 0.5) CURVE(3.98, 3.89, 0.5, 0.75) CURVE(3.89, 3.79, 0.75, 1.0) CURVE(3.79, 3.71, 1.0, 1.25) CURVE(3.71, 3.64, 1.25, 1.5) CURVE(3.64, 3.53, 1.5, 1.75) CURVE(3.53, 3.44, 1.75, 2.0) CURVE(3.44, 3.20, 2.0, 2.25) CURVE(3.20, 2.80, 2.25, 2.5) CURVE(2.80, 2.50, 2.5, 2.60) break; } case BatteryCellType::BAK_25R: { const float expected_ah = 2.5; if(cellVoltage > 4.15){ return 100; } CURVE(4.15, 4.06, 0, 0.25) CURVE(4.06, 3.96, 0.25, 0.5) CURVE(3.96, 3.88, 0.5, 0.75) CURVE(3.88, 3.77, 0.75, 1) CURVE(3.77, 3.68, 1, 1.25) CURVE(3.68, 3.62, 1.25, 1.5) CURVE(3.62, 3.56, 1.5, 1.75) CURVE(3.56, 3.47, 1.75, 2) CURVE(3.47, 3.31, 2, 2.25) CURVE(3.31, 2.50, 2.25, 2.5) break; } case BatteryCellType::HE4: { const float expected_ah = 2.3; if(cellVoltage > 4.15){ return 100; } CURVE(4.15, 4.02, 0, 0.25) CURVE(4.02, 3.91, 0.25, 0.5) CURVE(3.91, 3.81, 0.5, 0.75) CURVE(3.81, 3.72, 0.75, 1) CURVE(3.72, 3.61, 1, 1.25) CURVE(3.61, 3.62, 1.25, 1.5) CURVE(3.62, 3.53, 1.5, 1.75) CURVE(3.53, 3.45, 1.75, 2) CURVE(3.45, 3.21, 2, 2.25) CURVE(3.21, 2.80, 2.25, 2.3) break; } } return 0.f; } float getRemainingWattHours() { float target_mah = getTarget_mAh(); if (const auto avgVoltage = controllers.getAvgVoltage(); avgVoltage) { return (target_mah / 1000.f) * 3.7 * configs.battery.cellsParallel.value * configs.battery.cellsSeries.value * (getBatteryPercentage(*avgVoltage, BatteryCellType(configs.battery.cellType.value)) / 100); } else return 0.; } float getPercentageByWh(float wh) { const float maxWh = (getTarget_mAh() / 1000.f) * 3.7 * configs.battery.cellsParallel.value * configs.battery.cellsSeries.value; return maxWh / wh; } float getTarget_mAh() { float target_mah = 2000; //default if(BatteryCellType(configs.battery.cellType.value) == BatteryCellType::_22P) target_mah = 2200; if(BatteryCellType(configs.battery.cellType.value) == BatteryCellType::HG2) target_mah = 3000; if(BatteryCellType(configs.battery.cellType.value) == BatteryCellType::MH1) target_mah = 3200; if(BatteryCellType(configs.battery.cellType.value) == BatteryCellType::VTC5) target_mah = 2600; if(BatteryCellType(configs.battery.cellType.value) == BatteryCellType::BAK_25R) target_mah = 2500; if(BatteryCellType(configs.battery.cellType.value) == BatteryCellType::HE4) target_mah = 2300; return target_mah; } std::string getBatteryPercentageString() { if (const auto avgVoltage = controllers.getAvgVoltage(); avgVoltage) { std::string output = fmt::format("Battery: {:.1f}%", getBatteryPercentage(*avgVoltage, BatteryCellType(configs.battery.cellType.value))); return output; } else return "No Battery."; } std::string getBatteryAdvancedPercentageString() { std::string output = fmt::format("Battery: {:.1f}%", getPercentageByWh(drivingStatistics.batteryWhEstimate)); return output; } std::string getBatteryRemainingWattHoursString() { return fmt::format("{:.1f}Wh", getRemainingWattHours()); } std::string getBatteryCellTypeString() { return fmt::format("Cells: {}", toString(BatteryCellType(configs.battery.cellType.value))); } std::string getRemainingRangeString() { return fmt::format("{:.1f} km", getRemainingWattHours() / configs.battery.watthoursPerKilometer.value); } std::string getBatteryDebugString() { if (const auto avgVoltage = controllers.getAvgVoltage(); avgVoltage) { return fmt::format("{:.1f}V {}A", *avgVoltage, sumCurrent); } return "No Battery"; } namespace battery { std::optional bootBatPercentage; std::optional bootBatWh; }