Implemented reasonably good gnuplot output.
authorLadislav Laska <laska@kam.mff.cuni.cz>
Thu, 13 Nov 2014 21:35:57 +0000 (22:35 +0100)
committerLadislav Laska <laska@kam.mff.cuni.cz>
Thu, 13 Nov 2014 21:35:57 +0000 (22:35 +0100)
accutool

index e236d4e03a2de68fe5384908c8e7007e72694e23..e6305e5497a529253f938f210f646a3a4ddcc02a 100755 (executable)
--- a/accutool
+++ b/accutool
@@ -8,31 +8,43 @@ use Text::CSV::Slurp;
 use List::Util qw(reduce sum);
 
 
+sub dprintf {
+       printf STDERR "(Debug) ";
+       printf STDERR @_;
+}
+
+sub iprintf {
+       printf STDERR @_;
+}
+
 my $opt_source = "/dev/ttyUSB0";
 my $opt_baud = 9600;
 my $opt_log = "accucell.log";
 my $opt_output = undef;
 my $opt_gnuplot = "VT,AT";
 my $opt_help = 0;
+my $opt_debug = 0;
 
-GetOptions( 
+GetOptions(
        'help|h' => \$opt_help,
-       'source|s=s' => \$opt_source, 
+       'source|s=s' => \$opt_source,
        'baud|b=i' => \$opt_baud,
        'log|l=s' => \$opt_log,
        'output|o=s' => \$opt_output,
-       'gnutplot|g=s' => \$opt_gnuplot
+       'debug|d' => \$opt_debug,
+       'gnuplot|g=s' => \$opt_gnuplot
        ) and @ARGV == 0 or $opt_help = 1;
 
 if ($opt_help) {
-       print <<END ;
+       iprintf <<END ;
 Valid options are (position does not matter):
     -h   --help             Print this help and don't do anything else.
     -s   --source=file      Use given file as data input. Usually a serial port or CSV logfile.
-    -b   --baud=9600        When source is serial port, this is speed
+    -b   --baud=9600        When source is serial port, this is speed.
     -l   --log=log-file     Save sourced data to this file as CSV.
-    -o   --output=file      Save processed output to this file (gnuplot source, graph, ...) [currently not implemented]
-    -g   --gnuplot=format   Use gnuplot to plot data. [currently not implemented]
+    -o   --output=file      Save processed output to this file (gnuplot source, graph, ...)
+    -g   --gnuplot=format   Use gnuplot to plot data.
+       -d   --debug                    Show some stuff for ease of debugging.
 END
        exit 1;
 }
@@ -46,15 +58,15 @@ if (-c $opt_source) {
        my $acu = Accucell->new($opt_source, $opt_baud) or die("Could not connect to charger: $!");
        my $msg;
 
-       print STDERR "Waiting for process start.";
+       iprintf "Waiting for process start.";
        while (1) {
                $msg = $acu->read_message();
                last if ($msg->{"active"} != 0);
                print ".";
        }
-       print " got it\n";
+       iprintf " got it\n";
        open( FH, ">$opt_log" );
-       print FH "time,".join(",", @Accucell::fields)."\n";
+       iprintf FH "time,".join(",", @Accucell::fields)."\n";
 
        my $basetime = time;
        while (1) {
@@ -63,14 +75,14 @@ if (-c $opt_source) {
                $line .= $msg->{$_}."," for @Accucell::fields;
                $line =~ s/,$/\n/g;
                print FH $line;
-               print "> ".$line;
+               iprintf "> ".$line;
                $raw_data .= "$line";
                flush FH;
                last if ($msg->{"active"} == 0);
                $msg = $acu->read_message();
        }
 
-       print "Charger gone inactive, finishing...\n" if ($msg->{"active"} == 0);
+       iprintf "Charger gone inactive, finishing...\n" if ($msg->{"active"} == 0);
 
        close FH;
 } elsif (-f $opt_source) {
@@ -79,7 +91,7 @@ if (-c $opt_source) {
        $raw_data = <FH>;
        close FH;
 } else {
-       print "Invalid input file.\n";
+       iprintf "Invalid input file.\n";
 }
 
 if ($raw_data ne "") {
@@ -94,48 +106,95 @@ if ($raw_data ne "") {
                $last = $item->{time};
        }
        $wh /= 60*60.0;
-       print $wh." Wh\n";
+       iprintf $wh." Wh\n";
 
        # Compute average current & voltage
-       print sum(map { $_->{current} } @$data)/@$data." A\n";
-       print sum(map { $_->{voltage} } @$data)/@$data." V\n";
+       iprintf sum(map { $_->{current} } @$data)/@$data." A\n";
+       iprintf sum(map { $_->{voltage} } @$data)/@$data." V\n";
 } else {
-       print "No data to process.";
+       iprintf "No data to process.";
 }
 
 if ($opt_gnuplot ne "") {
+       my $terminals = {
+               "png" => "set terminal pngcairo size 1024,768 font 'OpenSans, Verdana, Helvetica, Arial, sans-serif' rounded dashed",
+               "svg" => "set terminal svg size 1024,768 fname 'OpenSans, Verdana, Helvetica, Arial, sans-serif' fsize '9' rounded dashed",
+               "qt"  => "set terminal qt"
+       };
        my @csv_fields = split /,/, ','.(split /\n/, $raw_data)[0];
        my %csv_index;
        @csv_index{@csv_fields} = (0..$#csv_fields);
 
-#set y2range [0:5]
-#set yrange [0:0.5]
-       my $gp_script = <<END ;
+       # Select output terminal
+       my $out_terminal = "qt"; # Qt is our default terminal.
+       my $out_file     = "";
+       my $out_string   = "";
+       if (defined $opt_output) {
+               # If the output is a known image format, guess around it.
+               if ($opt_output =~ m/\.(png|svg)$/) {
+                       $out_terminal = $1;
+                       $out_file     = $opt_output;
+                       $opt_output  .= ".gp";
+               } else {
+               # .. otherwise just guess it's the gnuplot source and choose png.
+                       $out_terminal = "png";
+                       $out_file     = "accutool-gnuplot-default.png";
+                       $out_file     = "$opt_output.png" unless $opt_output eq "-";
+               }
+               $out_string = "set output '$out_file'";
+       }
+
+       my $gp_script = <<END;
+
+$out_string
+$terminals->{$out_terminal}
+
+set key center top
+
+set style line 1 lc rgb '#8b1a0e' pt 1 ps 1 lt 1 lw 2 # --- red
+set style line 2 lc rgb '#5e9c36' pt 6 ps 1 lt 1 lw 2 # --- green
+set style line 3 lc rgb '#ffe22c' pt 6 ps 1 lt 1 lw 2 # --- green
+
+# Setup border
+set style line 11 lc rgb '#808080' lt 1
+set border 3 back ls 11
+
+# define grid
+set style line 12 lc rgb '#808080' lt 0 lw 1
+set grid back ls 12
+
+# Setup the data
 set datafile separator ","
 set y2tics border
 set ytics nomirror
-set xlabel "Time in seconds"
+set tics nomirror
+set y2range [2.5:5]
+#set yrange [0:2]
+set xlabel "Time in minutes"
 set ylabel "current (A), charge (Ah)"
 set y2label "voltage (V)"
 END
        my $sep = "plot ";
        my @graphs = split /,/, $opt_gnuplot;
        my %gp_choices = (
-               "vt" => [$csv_index{voltage}, "Battery voltage"],
-               "it" => [$csv_index{current}, "Battery current"],
-               "v1t" => [$csv_index{cell_voltage_1}, "Cell 1 voltage"],
-               "v2t" => [$csv_index{cell_voltage_2}, "Cell 2 voltage"],
-               "v3t" => [$csv_index{cell_voltage_3}, "Cell 3 voltage"],
-               "v4t" => [$csv_index{cell_voltage_4}, "Cell 4 voltage"],
-               "v5t" => [$csv_index{cell_voltage_5}, "Cell 5 voltage"],
-               "v6t" => [$csv_index{cell_voltage_6}, "Cell 6 voltage"],
-               "v7t" => [$csv_index{cell_voltage_7}, "Cell 7 voltage"],
-               "v8t" => [$csv_index{cell_voltage_8}, "Cell 8 voltage"],
+               "vt" => [$csv_index{voltage},         "Battery voltage",  1,    "axes x1y2"],
+               "it" => [$csv_index{current},         "Battery current",  1,    ""],
+               "cap" => [$csv_index{capacity},       "Battery capacity", 1000, ""],
+               "v1t" => [$csv_index{cell_voltage_1}, "Cell 1 voltage",   1,    "axes x1y2"],
+               "v2t" => [$csv_index{cell_voltage_2}, "Cell 2 voltage",   1,    "axes x1y2"],
+               "v3t" => [$csv_index{cell_voltage_3}, "Cell 3 voltage",   1,    "axes x1y2"],
+               "v4t" => [$csv_index{cell_voltage_4}, "Cell 4 voltage",   1,    "axes x1y2"],
+               "v5t" => [$csv_index{cell_voltage_5}, "Cell 5 voltage",   1,    "axes x1y2"],
+               "v6t" => [$csv_index{cell_voltage_6}, "Cell 6 voltage",   1,    "axes x1y2"],
+               "v7t" => [$csv_index{cell_voltage_7}, "Cell 7 voltage",   1,    "axes x1y2"],
+               "v8t" => [$csv_index{cell_voltage_8}, "Cell 8 voltage",   1,    "axes x1y2"],
                );
+       my $l = 0;
        foreach my $graph (map { lc } @graphs) {
+               $l++;
                if (defined $gp_choices{$graph}) {
-                       my ($field, $label) = @{$gp_choices{$graph}};
-                       $gp_script .= "$sep \"$opt_source\" using 1:$field with lines title \"$label\"";
+                       my ($field, $label, $scale, $more) = @{$gp_choices{$graph}};
+                       $gp_script .= "$sep \"$opt_source\" using (\$1/60):(\$$field/$scale) with lines ls $l title \"$label\" $more";
                        $sep = ",";
                } else {
                        warn "Ignoring unknown gnuplot argument $graph.";
@@ -144,12 +203,23 @@ END
 
        $gp_script .= "\n";
 
+       if ($opt_debug) {
+               my $d = $gp_script;
+               $d =~ s/\n/\n(Debug) /g;
+               $d =~ s/^/(Debug) /;
+               dprintf "Here is the finished GNUPlot file:\n$d\n";
+               dprintf "="x80 ."\n"
+       }
+
        if (not defined $opt_output) {
-               system("echo 'set terminal qt\n' '$gp_script' | gnuplot -persist");
+               open( FH, "|gnuplot -persist") or die "Could not execute gnuplot";
+               print FH $gp_script;
+               close FH;
+       } elsif ($opt_output eq "-") {
+               print $gp_script;
        } else {
-               open( FH, ">$opt_output" ) or die "Could not open output file '$opt_output'.";
+               open( FH, ">$opt_output" ) or die "Could not open output file '$opt_output' ";
                print FH $gp_script;
                close FH;
        }
-
 }