#! /usr/bin/perl
#
# stochastic power consumption comparison of two different configurations

use warnings;
use diagnostics;
use strict;

# commands to switch between two different states
my @cmd = ('echo performance > /sys/module/pcie_aspm/parameters/policy',
           'echo powersave > /sys/module/pcie_aspm/parameters/policy');
#my @cmd = ('echo suspend > /sys/bus/usb/devices/5-6/power/level',
#           'echo auto    > /sys/bus/usb/devices/5-6/power/level');

# maximal number of power readings between changes of states
my $interval = 5;


BEGIN {

    my $oldC = 0;
    my $oldV = 0;
    
    sub getpower() {
        
        my $C=0;
        my $V=0;
        my $p;
        while (1) {
            chomp($C = `cat /sys/class/power_supply/BAT0/current_now`);
            chomp($V = `cat /sys/class/power_supply/BAT0/voltage_now`);
            
            # avoid duplicate readings
            if ($C == $oldC && $V == $oldV) {
                sleep(1);
                next;
            }
            
            $p = 1e-12 * $C * $V;

            # avoid bad readings
            if ($p < 3 || 20 < $p) {
                print "Ignoring unreasonable reading: C=${C}uA, V=${V}uV\n"; 
                sleep(1);
                next;
            }
            
            last;
        }
        
        $oldC = $C;
        $oldV = $V;
        
        return $p;
    }
}


my @list = (
    [ (0., 0., 0., 0., 0., 0., 0., 0., 0., 0.) ],
    [ (0., 0., 0., 0., 0., 0., 0., 0., 0., 0.) ]
    );

my @sum   = (0., 0.);
my @sum2  = (0., 0.);
my @n     = (0, 0);

while (1) {

    for (my $state = 0; $state < 2; $state++) {
        print "State change: ", $cmd[$state], ": ", `$cmd[$state]`, "\n";

        # randomize interval length to avoid unlucky synchronisations with
        # periodic tasks being run on the machine
        my $max = int(rand($interval))+1;
        for (my $i = 0; $i <= $max; $i++) {
            my $p = getpower();
            next if $i==0;
            push @{$list[$state]}, $p;
            shift @{$list[$state]};

            my $avg10 = 0.;
            foreach my $v (@{$list[$state]}) { $avg10 += $v; }
            $avg10 /= scalar @{$list[$state]};

            $sum[$state]  += $p;
            $sum2[$state] += $p*$p;
            $n[$state]++;
            
            my $avg    = $sum[$state] / $n[$state];
            my $stddev = sqrt($sum2[$state] / $n[$state] - $avg*$avg);
#            printf "Sum[%i]=%.2f\n", $state, $sum[$state];
            printf "Status %i: cur: %.2fW, avg 10: %.2fW, ".
                "tot. avg: %.2fW, stddev: %.2fW (n=%i)\n",
                $state, $p, $avg10, $avg, $stddev, $n[$state];
        }
    }

}
