-
Notifications
You must be signed in to change notification settings - Fork 2
/
batteryburner.py
138 lines (102 loc) · 3.38 KB
/
batteryburner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import argparse
import os
import time
import numpy as np
import subprocess
import ctypes
import sys
from threading import Thread
from batteryusageparser import parse_battery_report
def powerparser():
parser = argparse.ArgumentParser(
description='Continuously poll power usage and battery usage'
'measurements and save them for post-processing.'
)
parser.add_argument('--test-dir', type=str, required=True,
help='Where the temp battery reports will reside for the burn test.')
parser.add_argument('--single-drop', action='store_true', default=False,
help='Wait for a drop in percentage before starting the test.')
return parser
class Burner(Thread):
def __init__(self, stopper):
Thread.__init__(self)
self.stopper = stopper
def run(self):
while not self.stopper.is_stop_set():
a = np.random.rand(1000000)
f = np.fft.fft(a)
np.fft.ifft(f, n=len(a))
class DaemonStopper(object):
def __init__(self):
self.stopping = False
def is_stop_set(self):
return self.stopping
def stop(self):
self.stopping = True
class disable_file_system_redirection:
_disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
_revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection
def __enter__(self):
self.old_value = ctypes.c_long()
self.success = self._disable(ctypes.byref(self.old_value))
def __exit__(self, type, value, traceback):
if self.success:
self._revert(self.old_value)
def get_battery_level(test_dir):
# Call powercfg.exe /BATTERYREPORT
battery_loc = os.path.join(test_dir, 'batteryreport{}.html'.format(str(int(time.time()))))
command = ['powercfg.exe', '/BATTERYREPORT', '/OUTPUT']
command.append(battery_loc)
with disable_file_system_redirection():
subprocess.check_call(command)
return parse_battery_report(battery_loc)
def main(single_drop=False):
parser = powerparser()
args = parser.parse_args()
args = dict(vars(args))
starttime = int(time.time())
outputdir = args['test_dir']
tmpdir = os.path.join(outputdir, 'batteryburntmp')
if not os.path.exists(tmpdir):
os.mkdir(tmpdir)
currlevel = get_battery_level(tmpdir)
if args['single_drop']:
# Drops that happen too early can skew mWh measurements
print("Pausing until battery drop detected...")
detected = False
while not detected:
time.sleep(60)
level = get_battery_level(tmpdir)
if level['battery'] != currlevel['battery']:
print("Detected drop, starting recording.")
detected = True
sys.exit(0)
drops_todo = 2
if currlevel['battery'] <= 98:
print("Battery burn not required.")
sys.exit(0)
else:
drops_todo = currlevel['battery'] - 99
print("Current battery level: %s" % str(currlevel['battery']))
print("Waiting for %s drops in percentage..." % str(drops_todo))
# Start up burners
stopper = DaemonStopper()
burners = [Burner(stopper) for _ in range(20)]
for batburn in burners:
batburn.start()
prevlevel = currlevel
while drops_todo > 0:
time.sleep(60)
currlevel = get_battery_level(tmpdir)
if currlevel['battery'] != prevlevel['battery']:
drops_todo -= 1
print("Waiting for %s drops in percentage..." % str(drops_todo))
prevlevel = currlevel
print("Battery burn done. Stopping the burners...")
stopper.stop()
for batburn in burners:
batburn.join()
endtime = int(time.time())
print("Battery burn complete in %s seconds." % str(endtime - starttime))
if __name__=="__main__":
main()