Last active
December 10, 2015 01:58
-
-
Save tvwerkhoven/4363703 to your computer and use it in GitHub Desktop.
Plot coconutBattery.app output
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
@file coconut_plot.py -- plot Macbook battery data | |
@author Tim van Werkhoven | |
@date 20121223 | |
@copyright Copyright (c) 2012 Tim van Werkhoven <[email protected]> | |
This scripts plots data exported from [CoconutBattery.app][1] as CSV. This | |
can be useful to graphically investigate your battery performance. | |
Besides plotting the data, the script also fits a straight line to the | |
number of cycles and capacity as function of time, and calculates the | |
correlation between capacicty and cycles. | |
Because you might have started logging data later, adding the age in months | |
with --age will add a zero-point to the data. This adds a tuple (age=0, | |
capacity=max, cycles=0) to the dataset. | |
[1]: http://www.coconut-flavour.com/coconutbattery/ | |
This file is licensed under the Creative Commons Attribution-Share Alike | |
license versions 3.0 or higher, see | |
http://creativecommons.org/licenses/by-sa/3.0/ | |
""" | |
############################################################################# | |
### PREAMBLE | |
############################################################################# | |
# Libs | |
import numpy as np | |
import scipy as sp | |
import scipy.stats | |
import pylab as plt | |
from matplotlib.dates import datestr2num, epoch2num | |
import time | |
import os, sys | |
import argparse | |
AUTHOR = "Tim van Werkhoven ([email protected])" | |
DATE = "20121223" | |
def main(): | |
# Parse & check options | |
(parser, args) = parsopts() | |
# Load data: skip one row (header), use comma as delimiter, convert | |
# first column to data, convert '-' to 0 in last column | |
cocodat = np.loadtxt(args.datafile, skiprows=1, delimiter=',', dtype=int, converters={0: datestr2num, 2: lambda c: c.strip('-') or -1}) | |
# If the age of the battery is given, add zero-point data to dataset | |
if (args.age): | |
# Date in months, average month is 365.25/12 days | |
secpmon = (24*3600*365.25/12) | |
zerotime = int(0.5+epoch2num(time.time()-args.age*secpmon)) | |
cocodat = np.vstack([np.r_[zerotime, args.capacity, 0], cocodat]) | |
# Plot capacity as function of time | |
# ======================================================================= | |
# Calc regression between time and capacity | |
slope, icept, rval, pval, stderr = scipy.stats.linregress(cocodat[:,0], cocodat[:,1]) | |
print "Plotting Battery capacity vs time (%.2g%%/day)" % (slope*100./args.capacity) | |
fig = plt.figure(10); fig.clf(); | |
ax1 = fig.add_subplot(111) | |
ax1.set_title("Battery capacity vs time (%.2g%%/day)" % (slope*100./args.capacity)) | |
# Plot data | |
ax1.plot_date(cocodat[:,0], cocodat[:,1], 'o') | |
ax1.set_ylabel("Capacity [mAh]") | |
# Plot fit | |
ax1.plot_date(cocodat[:,0], cocodat[:,0]*slope + icept, '--') | |
# Add second y-axis for percentage data | |
ax2 = ax1.twinx() | |
ax2.set_yticks(np.linspace(0,1, len(ax1.get_yticks()))) | |
ax2.set_yticklabels(["%.0f" % x for x in ax1.get_yticks()*100./args.capacity]) | |
ax2.set_ylabel("Capacity [%]") | |
fig.autofmt_xdate() | |
fig.savefig("%s_capacity_vs_time.pdf" % (args.plotname)) | |
# Plot cycles as function of time | |
# ======================================================================= | |
# Select data | |
cocodat2 = cocodat[cocodat[:,2] >= 0] | |
# Calc regression between time and cycles | |
slope, icept, rval, pval, stderr = scipy.stats.linregress(cocodat2[:,0], cocodat2[:,2]) | |
print "Plotting Charging cycles vs time (%.2g/day)" % (slope) | |
fig = plt.figure(20); fig.clf(); | |
ax1 = fig.add_subplot(111) | |
ax1.set_title("Charging cycles vs time (%.2g/day)" % (slope)) | |
# Plot data | |
ax1.plot_date(cocodat2[:,0], cocodat2[:,2], 'o') | |
# Plot fit | |
ax1.plot_date(cocodat2[:,0], cocodat2[:,0]*slope + icept, '--') | |
# Format axes | |
ax1.set_ylabel("Cycles [N]") | |
fig.autofmt_xdate() | |
fig.savefig("%s_cycles_vs_time.pdf" % (args.plotname)) | |
# Plot capacity vs cycles | |
# ======================================================================= | |
# Calc regression between cycles and capacity | |
slope, icept, rval, pval, stderr = scipy.stats.linregress(cocodat2[:,2], cocodat2[:,1]) | |
print "Charging cycles vs capacity (R=%.2g)" % (rval) | |
fig = plt.figure(30); fig.clf(); | |
ax1 = fig.add_subplot(111) | |
ax1.set_title("Charging cycles vs capacity (R^2=%.2f)" % (rval**2.0)) | |
# Plot data | |
ax1.plot(cocodat2[:,2], cocodat2[:,1], 'o') | |
# Plot fit | |
fitx = np.r_[cocodat2[:,2].min(), cocodat2[:,2].max()] | |
ax1.plot(fitx, fitx*slope + icept, '--') | |
# Format axes | |
ax1.set_ylabel("Capacity [mAh]") | |
ax1.set_xlabel("Cycles [N]") | |
# Add second y-axis for percentage data | |
ax2 = ax1.twinx() | |
ax2.set_yticks(np.linspace(0,1, len(ax1.get_yticks()))) | |
ax2.set_yticklabels(["%.0f" % x for x in ax1.get_yticks()*100./args.capacity]) | |
ax2.set_ylabel("Capacity [%]") | |
fig.savefig("%s_capacity_vs_cycles.pdf" % (args.plotname)) | |
def parsopts(): | |
### Parse program options and return results | |
parser = argparse.ArgumentParser(description='Plot Coconut battery data.', epilog='Comments & bugreports to %s' % (AUTHOR), prog='coconut_plot') | |
parser.add_argument('datafile', | |
help='Coconut datafile to process') | |
parser.add_argument('--capacity', metavar='C', default=5770, type=int, | |
help='Design capacity of battery (5770 mAh)') | |
parser.add_argument('--plotname', metavar='name', default='coconut', | |
help='Names for plot (None)') | |
parser.add_argument('--age', type=float, help='Battery age in months') | |
g4 = parser.add_argument_group("Miscellaneous options") | |
g4.add_argument('-v', dest='debug', action='append_const', const=1, | |
help='increase verbosity') | |
g4.add_argument('-q', dest='debug', action='append_const', const=-1, | |
help='decrease verbosity') | |
args = parser.parse_args() | |
# Check & fix some options | |
checkopts(parser, args) | |
# Return results | |
return (parser, args) | |
# Check command-line options | |
def checkopts(parser, args): | |
args.verb = 0 | |
if (args.debug): | |
args.verb = sum(args.debug) | |
if (args.verb > 1): | |
# Print some debug output | |
print "Running program %s for tgt %s" % (sys.argv[0], args.tgtname) | |
print args | |
# In case of errors | |
if (False): | |
parser.print_usage() | |
exit(-1) | |
# Run main program, must be at end | |
if __name__ == "__main__": | |
sys.exit(main()) | |
# EOF |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
date | capacity | loadcycles | |
---|---|---|---|
2012-02-01 | 5342 | - | |
2012-02-27 | 5452 | - | |
2012-04-11 | 5293 | - | |
2012-05-16 | 5271 | - | |
2012-09-11 | 5161 | - | |
2012-09-25 | 5272 | - | |
2012-12-04 | 5083 | - | |
2012-12-04 | 5083 | 139 | |
2012-12-05 | 5154 | 141 | |
2012-12-06 | 4836 | 141 | |
2012-12-07 | 5079 | 141 | |
2012-12-10 | 4981 | 142 | |
2012-12-12 | 5087 | 143 | |
2012-12-13 | 5243 | 143 | |
2012-12-16 | 5055 | 146 | |
2012-12-17 | 4976 | 146 | |
2012-12-19 | 5119 | 147 | |
2012-12-20 | 5281 | 148 | |
2012-12-21 | 5208 | 149 | |
2012-12-22 | 5224 | 149 | |
2012-12-23 | 5060 | 149 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment