#!/usr/bin/perl

$version = '0.15' ; #  Apr 11 2015
# decode R3 historical data from Chaney Instruments acurite weather station 01036
# read hex strings output from rd-acurite.R3.pl, or as printed by Wireshark or tshark -x (with some manipulation)
#
# Andrew Daviel March 2015
# some code from acurite.py Matthew Wall
#
# data goes something like this (32 bytes per record, 33 per USB read operation)):
# 17 records of header data
# 1 record which includes the data record count
# data line N (the most recent data)
# data line N-1 (next most recent)
# ..
# data line 1 (the oldest data)
# 1 record which includes the timestamp for the most recent data line
#
# data records do not always align in the same place on the line.
# mostly they do, though the rainfall record occurs on the next line
# the magic string "ff ff aa 55" appears to mark the end of variable-length
# header data, and hence the start of fixed-length records.

$debug = shift ;

# mapping table for wind direction index to degrees
our %IDX_TO_DEG = (6,360.0, 14,22.5, 12,45.0, 8,67.5, 10,90.0, 11,112.5,
  9,135.0, 13,157.5, 15,180.0, 7,202.5, 5,225.0, 1,247.5,
  3,270.0, 2,292.5, 0,315.0, 4,337.5) ;

use Time::Local ;


$i = $j = 0 ;
while (<STDIN>) {
  chomp ;
  if (/#/) { next ; } # ignore comments
  tr/\t/ / ;
  if (/failed/) { last ; } # 
  ($time,@F) = split(/ /) ; # $time might be capture time, or a frame number - just ignore it
  $n = scalar(@F) ;
  if ($n != 33 or $F[0] != 3) {  
    die "Does not look like R3 data: $_\n" ; # first byte of historical data is 03
  }
  $id = shift(@F) ; # drop ID
  $n = scalar(@F) ;
  if ($debug > 1) { print "$i $c $_\n" ; }
  if (/ff ff aa 55/ and $i < 25) {  # look for magic string in header data
    $F = join(' ',@F) ;
    $c = index($F,'ff aa 55') ; 
    if ($debug) { print "$i found $c $F\n" ; }
    $szpos = $i * $n + $c/3 + 23 ; # compute location of data size record
    unless ($k) { $k = $i ; }
  }
  unless ($id == 3) { print STDERR "$i bad ID $id\n" ; }
  foreach $v (@F) {
    push (@data,hex($v)) ; # push all data bytes from all input records onto one big array
  }
  if ($k and $i > $k) {
    unless ($szpos) { die "Did not find /ff aa 55/ pattern\n" ; }
    $size = $data[$szpos+1] * 0x100 + $data[$szpos] + $data[$szpos-1] * 0x10000 ;
    $lines = ($size+3601)/257 ;
    $dlines = ($size-768)/257 ; # compute number of actual data lines
    if ($debug) { print "szpos $szpos Size $size lines: $lines data lines: $dlines\n" ; }
    $k = 0 ;
  }
  if ($i == $lines-1) {
    # last ; # if back-ported to live USB read routine, stop here instead of waiting to timeout/fail
  }
  $i++ ;
}
$ndata = @data ;
if ($debug) { print "nlines $i size $ndata\n" ; }
$k = $szpos+2+$dlines*32 ; # compute offset to timestamp record
if ($debug>1) { print "Last line: " ; }
for ($j=0;$j<32;$j++) {
  $D[$j] = $data[$k] ; $k++ ;
  if ($debug>1) { print "$D[$j] " ; }
}
if ($debug>1) { print "\n" ; }
$yr = 2000 + $D[10] ; $mn = $D[11] ; $dy = $D[12] ;  # timestamp bytes are year/mon/day/hour/min
$hr = $D[13] ; $mi = $D[14] ;
if ($debug) { print "$yr-$mn-$dy $hr:$mi\n" ; }
$utime = timelocal(0,$mi,$hr,$dy,$mn-1,$yr-1900) ; # convert to Unix time
$time2 = localtime($utime) ;                       # and back to local to prove it worked
if ($debug) { print "Time $time2\n" ; }
$utime -= (12 * 60 * ($dlines - 1)) ;              # compute timestamp of earliest record

# comments ignored by gnuplot
print "#1DateTime,2Indoor Temperature C, 3Indoor Humidity %, 4Outdoor Temperature C ,5Outdoor Humidity % ,6Wind Chill F, 7Heat Index C ,8Dew Point C,9Pressure kPa ,10Wind direction,11Wind Current KPH, 12Wind Peak KPH, 13Wind Average KPH, 14Rainfall  mmm\n" ;
print "#1\t\t\t2  3  4 5  6 7 8  9  10 11 12 13 14\n" ;


for ($i=1;$i<=$dlines;$i++) {
  $k = $szpos+2+$dlines*32 - $i*32 ;
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($utime);
  $dtime = sprintf("%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d",$year+1900,$mon+1,$mday,$hour,$min,$sec) ;
  $utime += 12 * 60 ; # compute timestamp for next record, assuming 12 minute steps
  if ($debug>1) { print "Line $i\t" ; }
  for ($j=0;$j<32;$j++) {
    $D[$j] = $data[$k] ; $k++ ;  # copy 32 bytes of next data record from all-data array
    if ($debug>1) { print "$D[$j] " ; }
  }
  if ($debug>1) { print "\n" ; }
  print "$dtime\t" ;  # print time for this record
  # decode various 8 and 16-bit values from data record
  $indoortemp = ($D[1]+$D[0]*256)/18-100 ; # this gives Celsius; the raw data is 1/10 degree Fahrenheit
  $heatindex = ($D[3]+$D[2]*256)/18-100 ;
  $indoorhumidity = $D[5] ; # percent humidity
  $outdoorhumidity = $D[7] ;
  $windchill = ($D[9]+$D[8]*256)/18-100 ;
  $outdoortemp = ($D[11]+$D[10]*256)/18-100 ;
  $dewpoint = ($D[13]+$D[12]*256)/18-100 ;
  $pressure = ($D[15]+$D[14]*256)/10 ;  # this gives kPa
  $winddir = $IDX_TO_DEG{$D[17]} ; # use mapping table
  $wind = ($D[19]+$D[18]*256)/16 ; # current wind speed kph
  $windpeak = ($D[21]+$D[20]*256)/16 ;
  $windavg  = ($D[23]+$D[22]*256)/16 ;
  $rain = $D[25] * 0.254 ; # this gives mm; raw data is 1/100 inch
  #print "$indoortemp $indoorhumidity $outdoortemp $outdoorhumidity $windchill $heatindex $dewpoint $pressure $winddir $wind $windpeak $windavg $rain\n" ;
  printf ("%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f \n",
   $indoortemp,$indoorhumidity,$outdoortemp,$outdoorhumidity,$windchill,$heatindex,$dewpoint,$pressure,$winddir,$wind,$windpeak,$windavg,$rain) ;
}
