lincomatic / open_evse

Firmware for Open EVSE
GNU General Public License v3.0
115 stars 165 forks source link

Recode energy meter usage calculation for better accuracy #130

Closed toofishes closed 3 years ago

toofishes commented 3 years ago

For amps and volt measurements with large decimal portions (e.g. 11.9 amps, or 119.9 volts), we were introducing a pretty serious truncation error in our existing calcUsage function. This is due to using integer math and the default behavior of truncation, and dividing earlier than necessary, causing a loss of precision.

The new code is extensively commented to describe what it is doing and how it works, so hopefully no one is confused in the future with the rather odd coefficients involved. The important part to note is never getting too close to the upper limits of what a uint32_t variable can handle.

The easy way to look at this and believe that it is the equivalent operation, but not as quick to truncate, is to multiply the divisors involved: 1000 * 1000 == 1,000,000 == 16 * 4 * 15625.

To calculate the error the existing code was potentially introducing, and to test a fix, I put the following test harness together: https://gist.github.com/toofishes/f50fce0e051f1e0c685544f3cf5a21a2 It is basically the existing calcUsage function along with other bits of the code needed to run and execute it. There are three function versions present in the file, and it can be compiled on your PC (g++ -Wall -Os test.cpp) to try it out: a) calcUsage, the existing function b) calcUsageNew, the proposed replacement c) calcUsageDouble, which uses floating point math so we get the "true" result

Here's the result of this testing, which shows that in a contrived worse case scenario, the existing code can under-record delivered power by 17% in relative terms, or 100-300 Ws in absolute terms. The new function is generally equal to or within 1 Ws of the floating point calculated value.

Slowest standard charge rate possible, realistic worst case scenario for old code
114.999 Volts, 5.999 Amps, 999 ms elapsed
Using FP: 689 Ws
Original: 569 Ws    Error: 17.417%  120 Ws
New:      688 Ws    Error: 0.145%   1 Ws

A fairly typical US Level 1 setup on a 15 amp circuit
119.900 Volts, 11.900 Amps, 999 ms elapsed
Using FP: 1425 Ws
Original: 1307 Ws   Error: 8.281%   118 Ws
New:      1424 Ws   Error: 0.070%   1 Ws

A typical EU setup on a 32 amp circuit
229.900 Volts, 31.900 Amps, 999 ms elapsed
Using FP: 7326 Ws
Original: 7091 Ws   Error: 3.208%   235 Ws
New:      7325 Ws   Error: 0.014%   1 Ws

Higher powered US Level 2 dedicated 40 amp circuit
239.900 Volts, 39.900 Amps, 999 ms elapsed
Using FP: 9562 Ws
Original: 9311 Ws   Error: 2.625%   251 Ws
New:      9561 Ws   Error: 0.010%   1 Ws

The highest values this code should ever see
249.900 Volts, 79.900 Amps, 999 ms elapsed
Using FP: 19947 Ws
Original: 19651 Ws  Error: 1.484%   296 Ws
New:      19946 Ws  Error: 0.005%   1 Ws
lincomatic commented 3 years ago

Wow, great work, thanks for the PR!