#!/bin/sh
# Copyright One Laptop Per Child
# Power loging script 
# Released under GPLv3

# On an XO 1.5 we can use the EC SCI wakup to force readings to happen.
# This is usefull for taking measuements using a solar panel.
# to use this you either have to sudo chmod o+w /sys/power/ec or you 
# have to run as root.

VERSION="0.9.4"
FDATE=`date "+%y%m%d-%H%M%S"`
HOST=`hostname -s`
B_INFO=/sys/class/power_supply/olpc-battery

# This is the delay in the wallclock_delay.  Its the minimum sample time for the
# system date.
WALL_PERIOD=2.5 

# This is the (approx) delay inbetween readings.
# This delay should be some where in the middle of integer mutiples of WALL_PERIOD
# delays that bound the desired delay.
# That way if our timer and system date are out of phase you don't end up having to wait a whole
# new WALL_PERIOD for the RTC check to expire.
DELAY=60

if [ -e /sys/class/dmi/id/product_version ]
then
        XO_VERSION=$(< /sys/class/dmi/id/product_version )
else
        XO_VERSION="1"
fi

if [[ $(< $B_INFO/present ) = 0 ]]
then
	echo "Waiting on a battery"
	until [ $(< $B_INFO/present ) = 1 ] 
	do 
		sleep 1
	done
	echo "Found one."
fi

# Now that we wait for a battery we can use the battery serial number in the filename
DS_SERNUM=$(< $B_INFO/serial_number )
LOGFILE="sol-$FDATE-$DS_SERNUM.csv"

ACR_PROP="charge_counter"

if [ ! -e $B_INFO/$ACR_PROP ]
then
	ACR_PROP="accum_current"
fi

if [ -e /bootpart/boot/olpc_build ]
then
	BUILD=$(< /bootpart/boot/olpc_build )
fi

if [ -e /boot/olpc_build ]
then
	BUILD=$(< /boot/olpc_build )
fi

if [ -e $B_INFO/eeprom ]
then
# Skip 64 bytes and read 5 bytes; display without an address and in single byte mode
# I don't use od directly since the -j skip does not do a real fseek.
	echo "Reading eeprom data."
	MFG_SER=`dd if=$B_INFO/eeprom bs=1 skip=64 count=5 2> /dev/null | od -A n -t x1` 
	CHGCNT=`dd if=$B_INFO/eeprom bs=1 skip=74 count=2 2> /dev/null| od -A n -t x1 ` 
	CHGSOC=`dd if=$B_INFO/eeprom bs=1 skip=76 count=1 2> /dev/null| od -A n -t x1 ` 
	DISCNT=`dd if=$B_INFO/eeprom bs=1 skip=77 count=2 2> /dev/null| od -A n -t x1 ` 
	DISSOC=`dd if=$B_INFO/eeprom bs=1 skip=79 count=1 2> /dev/null| od -A n -t x1 ` 
else
	echo "Can't read the eeprom data because your kernel dosen't support eeprom dump"
	MFG_SER="NA"
	CHGCNT="NA"
	CHGSOC="NA"
	DISCNT="NA"
	DISSOC="NA"
fi

ec_wakeup() {
	test $XO_VERSION = "1" && return
	byte3=$(( ($1 >> 24) & 0xff )) 
	byte2=$(( ($1 >> 16) & 0xff )) 
	byte1=$(( ($1 >> 8 ) & 0xff )) 
	byte0=$(( ($1 & 0xff) )) 
	printf "36:0 %x %x %x %x \n" $byte3 $byte2 $byte1 $byte0  > /sys/power/ec
}

echo "Starting log $LOGFILE"
echo

echo "solar_log Ver: $VERSION" > $LOGFILE
echo -n "HOST: " >> $LOGFILE 
echo $HOST  >> $LOGFILE 
echo -n "DATE: " >> $LOGFILE 
echo `date` >> $LOGFILE
echo -n "DATESEC: " >> $LOGFILE
echo `date +%s` >> $LOGFILE
echo -n "ECVER: " >> $LOGFILE 
echo `cat /ofw/ec-name` >> $LOGFILE
echo -n "OFWVER: " >> $LOGFILE 
echo `cat /ofw/openprom/model` >> $LOGFILE
echo -n "MODEL: " >> $LOGFILE 
echo `cat /ofw/model` >> $LOGFILE
echo -n "SERNUM: " >> $LOGFILE
echo `cat /ofw/serial-number` >> $LOGFILE
echo -n "BATTECH: " >> $LOGFILE 
echo `cat $B_INFO/technology` >> $LOGFILE
echo -n "BATMFG: " >> $LOGFILE 
echo `cat $B_INFO/manufacturer` >> $LOGFILE
echo -n "BATSER: " >> $LOGFILE 
echo $DS_SERNUM >> $LOGFILE
echo -n "BUILD: " >> $LOGFILE 
echo $BUILD >> $LOGFILE
echo -n "MFGSER: " >> $LOGFILE
echo $MFG_SER >> $LOGFILE
echo -n "CHGCNT: " >> $LOGFILE
echo $CHGCNT >> $LOGFILE
echo -n "CHGSOC: " >> $LOGFILE
echo $CHGSOC >> $LOGFILE
echo -n "DISCNT: " >> $LOGFILE
echo $DISCNT >> $LOGFILE
echo -n "DISSOC: " >> $LOGFILE
echo $DISSOC >> $LOGFILE
echo -n "WALL_PERIOD: " >> $LOGFILE
echo $WALL_PERIOD: >> $LOGFILE
echo -n "DELAY: " >> $LOGFILE
echo $DELAY: >> $LOGFILE
echo -n "XOVER: " >> $LOGFILE
echo $XO_VERSION >> $LOGFILE
# Allow the addition of some descriptive text from the cmd line
echo -n "COMMENT: " >> $LOGFILE
echo $1 >> $LOGFILE
echo "<StartData>" >> $LOGFILE

# feed this the wall clock time in seconds you wish to delay
# It will spin until that time has passed.  If the system is suspeneded
# it may sleep more.
function wallclock_delay {
	DATE1=`date +%s`
	EXPIRE=$((DATE1+$1))
        while [ `date +%s` -lt $EXPIRE ]; do
        	sleep $WALL_PERIOD	 
	done
}

# convert a number into 2's complement
function conv_2s_comp {

# this has since been changed in the kernel so that it returns
# a signed value rather than unsigned. which fixes the math
# So if its already negative then bail

	if [ $1 -lt 0 ]
	then
		echo $1
		return
	fi

	if [ $1 -gt 32767 ]
	then
		echo $(( 0 - (65535-$1+1) )) 
		return
	fi

	echo $1
}

CAPACITY=capacity_level
if [ -n $B_INFO/$CAPACITY ]
then
	CAPACITY=capacity
fi

function get_acr {

    local acr_temp

    acr_temp=$(< $B_INFO/$ACR_PROP )
    test $XO_VERSION = "1" && acr_temp=$(conv_2s_comp ${acr_temp:-0})
    echo ${acr_temp:-0}
}

function get_seconds {
	echo `date +%s`
}

function take_reading {
	CAPLEVEL=$(< $B_INFO/$CAPACITY )
	VOLT=$(< $B_INFO/voltage_avg)
	CURR=$(< $B_INFO/current_avg)
	TEMP=$(< $B_INFO/temp)
	STAT=$(< $B_INFO/status)

	ACR=$(get_acr)
	ACR_DIFF=$(( ${ACR-0}-${START_ACR-0} ))
	if [ XO_VERSION == "1" ]
	then
		MAh=$(( $ACR_DIFF * 625 / 1500 ))
	else
		MAh=$ACR_DIFF
	fi
	NET_MINUTES=$(( ( $(get_seconds) - ${START_TIME} ) / 60 )) 
	echo `date +%s`",$CAPLEVEL,$VOLT,$CURR,$TEMP,$ACR,$STAT,$MAh,$NET_MINUTES" | tee -a $LOGFILE
}
# Make sure we can write the ec wakup command 
# even as non-root
test $XO_VERSION != "1" && sudo chmod o+w /sys/power/ec

START_ACR=$(get_acr)
START_TIME=$(get_seconds)

# When charging the batt V changes rapidly.  Take some startup samples so that first long delay sample has a more accurate Vb.

take_reading
ec_wakeup 10000
sleep 10

take_reading
ec_wakeup 10000
sleep 10

FULL_EDGE=0

while true
do
 	if [[ $STAT != "Full" ]] 
	then
		FULL_EDGE=0
	fi

	if [[ $FULL_EDGE = 0 ]]
	then
		take_reading
	else 
		STAT=$(< $B_INFO/status)
	fi

	if [[ $FULL_EDGE = 0 && $STAT = "Full" ]]
	then
		FULL_EDGE=1
	fi
#	sync
	# EC wakup is in mili-seconds from now.
	ec_wakeup $(( $DELAY * 1000 ))
#	sleep $DELAY
	wallclock_delay $DELAY
done
