#!/usr/bin/python

# Copyright One Laptop Per Child 
# Released under GPLv2
# Version 1.3

# Quick and dirty script to calculate the things I was doing with a spreadsheet in OpenOffice

import sys
import csv
import os
import getopt

# Conversion defs
SEC 	= 0
SOC 	= 1
Vb 	= 2
Ib 	= 3
Tb 	= 4
ACR 	= 5

# Results defs
Th 	= 0
Iavg 	= 1
NetACR	= 2
Deltat	= 3
Vavg	= 4
Watts	= 5
Wh	= 6

# 6.5uV / .015 mOhm sense resistor / 1000 = raw ACR -> ACR in mAh 
ACR2mAh = 6.25 / .015 / 1000

def convert_data(filename):
	try:
		# Seconds 
		converted[SEC] = float(row[SEC])
		# State of Charge (Convert to float just for consistency)
		converted[SOC] = float(row[SOC])
		# Volts
		converted[Vb] = float(row[Vb])/1000000
		# Current mA
		converted[Ib] = float(row[Ib])/1000
		# Batt Temp in C
		converted[Tb] = float(row[Tb])/100
		# ACR mAh
		# Old versions of the logging script have this number as an unsinged 16-bit
		# But its really a 2's complement so you have to fixup to make the math work across 
		# a rollover.
		if int(row[ACR]) < 0:
			# Allready converted. So good go 
			converted[ACR] = float(row[ACR])*ACR2mAh
		else:
			intval = int(row[ACR])
			if (intval & 0x8000):
				intval = -((~intval & 0xffff) +1)
			converted[ACR] = float(intval)*ACR2mAh
	except:
		print "Convert Error: %s" % filename
		print row
			
def process_data():
	result[Th] 	= (converted[SEC] - Tz) / 3600	
	result[Deltat] 	= converted[SEC] - converted_prev[SEC]
	result[Iavg] 	= (converted[ACR] - converted_prev[ACR]) / (result[Deltat] / 3600)
	result[NetACR]	= converted[ACR] - ACRz
	result[Vavg]	= (converted[Vb] + converted_prev[Vb]) / 2
	result[Watts]	= result[Vavg] * result[Iavg] / 1000
	result[Wh]	= result[Wh] + (result[Watts] * result[Deltat] / 3600)

def pretty_print(data):
	for each in data:
		print '%9.3f' % each ,
	print

def pretty_out(data):
	retval = []
	for each in data:
		retval.append('%10.3f\t' % each)
	return retval

def usage():
	print 'process-pwr_log <options> <files>'
	print "-b, --batsort :  output bat sernum rather than filename for the summary info"

# Eeek. Globals.  Bad Richard.
converted 	= [0.0,0.0,0.0,0.0,0.0,0.0,0.0]
converted_prev 	= [0.,0.,0.,0.,0.,0.]
result 		= [0.,0.,0.,0.,0.,0.,0.] 
Tz = 0.0
ACRz = 0.0
batsort = 0
sersort = 0

try:
	opts, args = getopt.getopt(sys.argv[1:], "hbs", ["batsort", "help", "sersort"])
except getopt.GetoptError, err:
	# print help information and exit:
	print str(err) # will print something like "option -a not recognized"
	usage()
	sys.exit(2)

filenames = args

for o, a in opts:
        if o in ("-b", "--batsort"):
		batsort = 1
        elif o in ("-h", "--help"):
		usage()
		sys.exit(1)
	elif o in ("-s", "--sersort"):
		sersort = 1

print "Summary key: Filename, Build, Total Time, NetACR, WattHours, Min W, Max W, Avg W"
for filename in filenames:

	converted 	= [0.0,0.0,0.0,0.0,0.0,0.0,0.0]
	converted_prev 	= [0.,0.,0.,0.,0.,0.]
	result 		= [0.,0.,0.,0.,0.,0.,0.] 
	build_no	= 'Unknown'
	bat_ser		= 'None'
	lap_ser		= 'None'

	output_filename = "processed-"+ os.path.splitext(filename)[0] + ".csv"
	writer = csv.writer(open(output_filename, "wb"))
	reader = csv.reader(open(filename,"rb"))
	for row in reader:
		writer.writerow(row)
		if not row:
			continue
		try:
			if row[0].startswith('BUILD:'):
				build_no = row[0].split(':')[1]
		except:
			build_no = 'Err'
		try:
			if row[0].startswith('BATSER:'):
				bat_ser = row[0].split(':')[1]
		except:
			bat_ser = 'Err'

		try:
			if row[0].startswith('SERNUM:'):
				lap_ser = row[0].split(':')[1]
		except:
			lap_ser = 'Err'

		if row[0] == '<StartData>':	
			break
	# Header 
	writer.writerow(['Net T(hours)','I Avg(mA)','Net ACR(mA)','Delta T(sec)','V Avg','Watts','Net Wh'])
	try:
		row = reader.next()
	except:
		print "Err: %s line %d " % (filename,reader.line_num)
		continue
	convert_data(filename)
	# Starting point for relative measuements
	Tz = converted[SEC]
	ACRz = converted[ACR]
	# Setup the first calculation
	converted_prev = converted[:]
	# Keep the div by zero from occuring
	converted_prev[SEC] = Tz-1
	process_data()
	# Fixup the errors from the starting entry
	result[Deltat] = 0
	result[Wh] = 0
	# Init min & Max.  This really should be initialized to the 
	# first real value but that does not happen until the 2nd interation
	# and I feel lazy.  If we ever hit 20W some thing else is wrong anyway
	minW 		= 20.0
	maxW 		= -20.0
	writer.writerow(pretty_out(result))
	converted_prev = converted[:]
	# Run the rest of the data
	for row in reader:
		if not row:
			continue
		convert_data(filename)
		process_data()
		if result[Watts] > maxW and (round(result[Watts],3) != 0.0) :
			maxW = result[Watts]
		if (result[Watts] < minW) and (round(result[Watts],3) != 0.0):
			minW = result[Watts]
		converted_prev = converted[:]
		writer.writerow(pretty_out(result))
	# Summary of the run
	summary = []
	summary.append(result[Th])
	summary.append(result[NetACR])
	summary.append(result[Wh])
	summary.append(minW)
	summary.append(maxW)
	if result[Th] != 0.0:
		summary.append(result[Wh]/result[Th])
	else:
		summary.append(0.0)

	if batsort and bat_ser != 'None':
		print bat_ser, 
	elif sersort and lap_ser != 'None':
		print lap_ser,
	else:
		print filename,
	
	print '\t',
	print build_no + ': \t', 
	pretty_print(summary)

