#!/usr/bin/perl
#
#  usage: syn2nc [-h] [-n (old|new)] cdlfile [datadir] [yyyymm] < ncfile
#
#
use NetCDF ;
use Time::Local ;
#no encoding;

# process command line switches
$syn = "new";
while ($_ = $ARGV[0], /^-/) {
	 shift;
       last if /^--$/;
		/^(-v)/ && $verbose++;
		/^(-h)/ && $hourly++; #create hourly files instead of daily
		/^(-n)/ && ( $syn = shift );
		/^(-i)/ && $ignoreDate++;
}
# new netCDF file naming covention is default
if( $syn eq "new" ) {
	if( $hourly ) {
		$syn = "00";
	} else {
		$syn = "_0000";
	}
	$synPrefix = "Surface_Synoptic_";
} else {
	$syn = "_syn";
	$synPrefix = "";
}
# process input parameters
if( $#ARGV == 0 ) {
	$cdlfile = $ARGV[ 0 ] ;
} elsif( $#ARGV == 1 ) {
	$cdlfile = $ARGV[ 0 ] ;
	if( $ARGV[ 1 ] =~ /^\d/ ) {
		$yyyymm = $ARGV[ 1 ] ;
	} else {
		$datadir = $ARGV[ 1 ] ;
	}
} elsif( $#ARGV == 2 ) {
	$cdlfile = $ARGV[ 0 ] ;
	$datadir = $ARGV[ 1 ] ;
	$yyyymm = $ARGV[ 2 ] ;
} else {
	die "Usage with input on STDIN: syn2nc [-v] [-h] [-n (old|new)] cdlfile [datadir] [yyyymm]\n";
}

# check for cdl and netCDF ncgen
die "Missing cdlfile parameter: $!\n" unless  -e $cdlfile ;
if( -e "util/ncgen" ) {
	$ncgen = "util/ncgen" ;
} elsif( -e "/usr/local/ldm/util/ncgen" ) {
	$ncgen = "/usr/local/ldm/util/ncgen" ;
} elsif( -e "/upc/netcdf/bin/ncgen" ) {
	$ncgen = "/upc/netcdf/bin/ncgen" ;
} elsif( -e "./ncgen" ) {
	$ncgen = "./ncgen" ;
} else {
	open( NCGEN, "which ncgen |" ) ;
	$ncgen = <NCGEN> ;
	close( NCGEN ) ;

	if( $ncgen =~ /no ncgen/ ) {
		die "Can't find NetCDF utility 'ncgen' in PATH, util/ncgen
/usr/local/ldm/util/ncgen, /upc/netcdf/bin/ncgen, or ./ncgen : $!\n" ;
	} else {
		$ncgen = "ncgen" ;
	}
}
# the data directory 
$datadir = "." if( ! $datadir ) ;
system( "mkdir -p $datadir" ) if( ! -e $datadir ) ;
if( -e "$datadir/synLog.log.2" ) {
	`rm -f $datadir/synLog.log.3`;
	`mv $datadir/synLog.log.2 $datadir/synLog.log.3`;
}
if( -e "$datadir/synLog.log.1" ) {
	`mv $datadir/synLog.log.1 $datadir/synLog.log.2`;
}
if( -e "$datadir/synLog.log" ) {
	`mv $datadir/synLog.log $datadir/synLog.log.1`;
}
# redirect STDOUT and STDERR
open( STDOUT, ">$datadir/synLog.log" ) ||
		die "could not open $datadir/synLog.log: $!\n" ;
open( STDERR, ">&STDOUT" ) ||
		die "could not dup stdout: $!\n" ;
select( STDERR ) ; $| = 1 ;
select( STDOUT ) ; $| = 1 ;

# year and month
if( ! $yyyymm ) {
	$theyear = (gmtime())[ 5 ] ;
	$theyear = ( $theyear < 100 ? $theyear : $theyear - 100 ) ;
	$thedecade = sprintf( "%02d", $theyear ) ;
	$theyear = "20" . sprintf( "%02d", $theyear ) ;
	$themonth = (gmtime())[ 4 ] ;
	$themonth++ ;
	$themonth = sprintf( "%02d", $themonth ) ;
	$yyyymm = "$theyear$themonth" ;
} else {
	die "yyyymm must be 6 in length: $!\n" if( length( $yyyymm ) != 6 ) ;
	$theyear = substr( $yyyymm, 0, 4 ) ;
	$themonth = substr( $yyyymm, 4 ) ;
	$thedecade = substr( $yyyymm, 2, 2 ) ;
}
# set error handling to verbose only
$status = NetCDF::opts( VERBOSE ) ;

# set interrupt handler
$SIG{ 'INT' }  = 'atexit' ;
$SIG{ 'KILL' }  = 'atexit' ;
$SIG{ 'TERM' }  = 'atexit' ;
$SIG{ 'QUIT' }  = 'atexit' ;

%CDL = (
"rep_type", 0, "wmoId", 1, "stnName", 2, "time_obs", 3, "time_nominal", 4, 
"Lat", 5, "Lon", 6, "elev", 7, "stnType", 8, "meanWind", 9, "VIS", 10, 
"DIR", 11, "SPD", 12, "T", 13, "humidity", 14, "TD", 15, "PRES", 16, 
"SLP", 17, "char_Ptend", 18, "Ptend", 19, "PRECIP_amt", 20, "PRECIP_period", 21, "WXpresent", 22, "WXpast", 23, "cloudCover", 24, "cloudLow", 25, 
"cloudMiddle", 26, "cloudHigh", 27, "shipTrueDIR", 28, "shipAvgSPD", 29, 
"Tw", 30, "Pwa", 31, "Hwa", 32, "Hwa_tenths", 33, "swellDIR1", 34, "Ps1", 35, 
"Hs1", 36, "swellDIR2", 37, "Ps2", 38, "Hs2", 39, "iceType", 40, "iceThick", 41, "iceRate", 42, "Tb", 43, "ICE", 44, "iceConcen", 45, "iceStage", 46, 
"iceLand", 47, "iceEdge", 48, "iceSituation", 49, "Tmax", 50, "Tmin", 51, 
"groundState", 52, "snowDepth", 53, "PRECIP_amt24", 54, "cloudObsured", 55, 
"cloudGenus", 56, "cloudHeight", 57, "report", 58 ) ; 

# set defaults
$rep_type_len = 4 ;
$stnName_len = 6 ;
$cloud_levels = 3 ;
$r_len = 256 ;

# open cdl and get dimensions for variables
open( CDL, "$cdlfile" ) || die "could not open $cdlfile: $!\n" ;
while( <CDL> ) {
	if( s#^\s*rep_type_len\s*=\s*(\d{1,5})## ) {
		$rep_type_len = $1 ;
	} elsif( s#^\s*stnName_len\s*=\s*(\d{1,5})## ) {
		$stnName_len = $1 ;
	} elsif( s#^\s*cloud_levels\s*=\s*(\d{1,5})## ) {
		$cloud_levels = $1 ;
	} elsif( s#^\s*r_len\s*=\s*(\d{1,5})## ) {
		$r_len = $1 ;
	} elsif( s#^\s*variables## ) {
		last ;
	}
}
$F = -99999 ;
$A = \$F ;
$SR = "\0" x $rep_type_len ;
$ASR = \$SR ;
$SN = "\0" x $stnName_len ;
$ASN = \$SN ;
@CL = ( $F, $F, $F ) ;
@CL = ( -99999 ) x ( $cloud_levels ) ;
$ACL = \@CL ;
$SRpt = "\0" x $r_len ;
$ASRpt = \$SRpt ;

# default netCDF record structure, contains all vars for the BUOY reports
@defaultrec = ( $ASR, $A, $ASN, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, 
 $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, 
 $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A, $A,
 $A, $A, $A, $A, $A, $ACL, $ACL, $ACL, $ASRpt ) ;

# two fold purpose array, if entry > 0, then var is requested and it's value
# is the position in the record, except first entry
@W = ( 0 ) x ( $#defaultrec +1 ) ;
$W[ 0 ] = -1 ;

# create record structure according to variables
$i = 0 ;
while( <CDL> ) {
	if( s#^\s*(char|int|long|double|float) (\w{1,25})## ) {
		( $number ) = $CDL{ $2 } ;
		push( @rec, $defaultrec[ $number ] ) ;
		$W[ $number ] = $i++ ;
	}
}
close CDL ;
undef( @defaultrec ) ;
undef( %CDL ) ;

# cloud base height
@HGHT = ( 25, 75, 150, 250, 450, 800, 1250, 1750, 2250, 2500 ) ;
# PRECIP period
@tR = ( 0, 6, 12 ,18, 24, 1, 2, 3, 9, 15 ) ;
# wave height
@WHGHT = ( 0, 0.05, 0.3, 0.875, 1.875, 3.25, 5, 7.5, 11.5, 14 ) ;

# read in station data
if( -e "etc/lsfstns.tbl" ) {
	$sfile = "etc/lsfstns.tbl" ;
} elsif( -e "./lsfstns.tbl" ) {
	$sfile = "./lsfstns.tbl" ;
} else {
	die "Can't find lsfstns.tbl station file.: $!\n" ;
}
open( STATION, "$sfile" ) || die "could not open $sfile: $!\n" ;

while( <STATION> ) {
	s#^(\w{3,6})?\s+(\d{4,5}).{40}## ;
	$id = $1 ;
	$wmoId = $2 ;
	$wmoId = "0" . $wmoId if( length( $wmoId ) == 4 ) ;
	( $Lat, $Lon, $elev ) = split ;
	$Lat = sprintf( "%7.2f", $Lat / 100 ) ;
	$Lon = sprintf( "%7.2f", $Lon / 100) ;

	# set these vars ( $wmoId, $Lat, $Lon, $elev ) 
	$STATIONS{ "$wmoId" } = "$Lat $Lon $elev $id" ;
}
close STATION ;

# read in list of already processed reports if it exists
# open syn.lst, list of reports processed in the last ? hours.
if( -e "$datadir/syn.lst" ) {
	open( LST, "$datadir/syn.lst" ) || 
		die "could not open $datadir/syn.lst: $!\n" ;
	while( <LST> ) {
		( $wmoId, $yyyymmddhh, $record ) = split ;
		$rpt_hash{ "$wmoId $yyyymmddhh" }  = $record ;
	}
	close LST ;
}
# Now begin parsing file and decoding observations breaking on cntrl C
$/ = "\cC" ;

# main loop   set select processing here from STDIN
START:
while( 1 ) {
	open( STDIN, '-' ) ;
	vec($rin,fileno(STDIN),1) = 1;
	$timeout = 1200 ; # 20 minutes
	$nfound = select( $rout = $rin, undef, undef, $timeout );
	# timed out
	if( ! $nfound ) {
		print "Shut down, time out 20 minutes\n" ;
		atexit() ;
	}
	atexit( "eof" ) if( eof( STDIN ) ) ;

	# Process each line of syn bulletins, header first
	$_ = <STDIN> ;
	aaxx( $_ ) if( /AAXX/ ) ;
	atexit( "eof" ) if( eof( STDIN ) ) ;
} # end while( 1 )
atexit( "eof" );
exit( 0 ) ; #should never get here

# AAXX reports
sub aaxx {

( $_ ) = @_ ;

s#\cC## ;
s#\cM##g ;
s#\cA\n## ;
s#\c^##g ;
# Eat header and get bulletin time
s#\d\d\d \n## ;
s#\w{4}\d{1,2} \w{4} (\d{2})(\d{2})(\d{2}).*\n## ;
$bday = $1 ;
$bhour = $2 ;
$bhour = "23" if( $bhour eq "24" ) ;
$bmin = $3 ;
next unless ( $bday && defined( $bhour ) && defined( $bmin ) ) ;
next if( $bmin > 59 || $bhour > 23 || $bday > 31 ) ;
# check for valid bulletin times against current time
$cday = (gmtime())[ 3 ] ;
$chour = (gmtime())[ 2 ] ;
if( $ignoreDate ) {
	$cday = $bday;
	$chour = $bhour;
}
# skip bulletins over 24 hours old or in the future
if( $bday == $cday ) {
	next if( $bhour > $chour ) ;
} else { #  $bday != $cday, skip over day old reports
	next if( $bday < $cday -1 ) ;
 	if( $bday > $cday ) {
		next if( $cday != 1 || $bday < 28) ;
	}
 	next if( $bhour < $chour ) ;
}
# reset year and month
$theyear = (gmtime())[ 5 ] ;
$theyear = ( $theyear < 100 ? $theyear : $theyear - 100 ) ;
$thedecade = sprintf( "%02d", $theyear ) ;
$theyear = "20" . sprintf( "%02d", $theyear ) ;
$themonth = (gmtime())[ 4 ] ;
$themonth++ ;
$themonth = sprintf( "%02d", $themonth ) ;
$yyyymm = "$theyear$themonth" ;
if( s#(AAXX)\s+(\d\d)(\d\d)(\d)?\s*\n## ) {
	$rep_type = $1 ;
	$rday = $2 ;
	$rhour = $3 ;
	if( $4 ) {
		$Windicator = $4 ;
	} else {
		$Windicator = 4 ;
	}
} elsif( s#(AAXX)\s*\n## ) {
	$rep_type = $1 ;
	$rday = $bday ;
	$rhour = $bhour ;
	$Windicator = 4 ; # default is knots
} else {
	next ;
}
# check for valid times
next unless ($rday && defined( $rhour )) ;
next if( $rhour > 23 || $rday > 31 ) ;
# skip reports over 24 hours old
$tmpyyyymm = $yyyymm ;
if( $rday == $cday ) {
	next if( $rhour > $chour ) ;
} else { #  $rday != $cday, skip over day old reports
	next if( $rday < ( $cday -1 ) ) ;
 	if( $rday > $cday ) {
		next if( $cday != 1 || $rday < 28) ;
		# cday = 1, reset month and year
		$tmpmonth = sprintf( "%02d", $themonth -1 ) ;
		if( $tmpmonth == 0 ) {
			$tmpmonth = "12" ;
			$tmpyear = sprintf( "%04d", $theyear -1 );
		} else {
			$tmpyear = $theyear ;
		}
		$tmpyyyymm = $tmpyear .  $tmpmonth ;
	}
	next if( $rhour < $chour ) ;
}
$yyyymmddhh = $tmpyyyymm . sprintf( "%02d", $rday ) . sprintf( "%02d", $rhour );
# Seperate bulletins into reports 
if( /=\n/ ) {
	s#=\s+\n#=\n#g ;
} else {
	s#\n# #g ;
}
@reports = split( /=\n/ ) ;

for ( @reports ) { # Process each report in the bulletin
	next if( /^\n|NIL|MIS/ ) ;
	s#\n# #g ;
	s#\s+# #g ;
	s#-\s*##g ;
	# save original report 
	$report = "AAXX " . $_ ;
	$_ .=  " " ;

	# decode Section 0 
	s#^(\d{5}) ## ;
	$wmoId = $1 ;
	next unless( defined( $wmoId ) ) ;
	# extract Lat, Lon, elev, stnName
	if( defined( $STATIONS{ "$wmoId" } ) ) {
		( $Lat, $Lon, $elev, $stnName ) =
			split( " ", $STATIONS{ "$wmoId" } ) ;
		if( defined( $stnName ) ) {
			$stnName = padstr( $stnName, $stnName_len ) ; 
		} else {
			undef( $stnName ) ;
		}
	}
	# decode Section 1 
	next unless( s#^(\d)(\d)(\d|/)(\d\d|..) ## ) ;
	$PRECIP_amt = 0 if( $1 == 3 ) ; # precipitation indicator
	$stnType = $2 ;
	$cloudHeight = $HGHT[ $3 ] if( $3 ne "/" ) ;
	$tmp = $4 ;
	if( $tmp =~ m#\d\d# ) {
		if( $tmp < 51 ) {
			$VIS = $tmp * 0.1 ;
		} elsif( $tmp < 81 ) {
			$VIS = $tmp -50 ;
		} elsif( $tmp < 90 ) {
			$VIS = (( $tmp -80 ) * 5 ) + 30 ;
		} elsif( $tmp == 90 ) {
			$VIS = 0.025 ;
		} elsif( $tmp == 91 ) {
			$VIS = 0.05 ;
		} elsif( $tmp == 92 ) {
			$VIS = 0.3 ;
		} elsif( $tmp == 93 ) {
			$VIS = 0.5 ;
		} elsif( $tmp == 94 ) {
			$VIS = 1 ;
		} elsif( $tmp == 95 ) {
			$VIS = 2 ;
		} elsif( $tmp == 96 ) {
			$VIS = 4 ;
		} elsif( $tmp == 97 ) {
			$VIS = 10 ;
		} elsif( $tmp == 98 ) {
			$VIS = 20 ;
		} elsif( $tmp == 99 ) {
			$VIS = 50 ;
		}
	}
	next unless( s#^(\d|/)(\d\d|//)(\d\d|//) ## ) ;
	$cloudCover = $1 if( $1  ne "/" ) ;
	if( $2 ne "//" && $2 <= 36 ) {
		$DIR = $2 * 10 ;
		$SPD = $3 ;
		if( $SPD == 99 ) {
			s#00(\d{3}) ## ;
			$SPD = $1 ;
		}
		$SPD *= 0.5144 if( $Windicator > 2 ) ;
	} 
	s#00/// ## ; # eat SPD place holder if necessary
	while( 1 ) {
		if( s#^1((\d)(\d{3})|////) ## ) {
			if( $1 ne "////" ) {
				$T = $3 * 0.10 ;
				$T *= -1.0 if( $2 == 1 ) ;
			}
		} 
		if( s#^29(\d{3}|///) ## ) {
			$humidity = $1 if( $1 ne "///" ) ;
		} 
		if( s#^2((0|1|9)(\d{2,3})(/)?|////) ## ) {
			if( $1 ne "////" ) {
				if( $4 ne "/" ) {
					$TD = $3 * 0.10 ;
				} else {
					$TD = $3 ;
				}
				$TD *= -1.0 if( $2 == 1 ) ;
			}
		}
		last if( m#^333|^444|^555# ) ; 
		if( s#^3(\d{4}|////) ## ) {
			if( $1 ne "////" ) {
				$PRES = $1 * 0.10 ;
				$PRES += 1000 if( $PRES < 100 ) ;
			}
		}
		last if( m#^444|^555# ) ; 
		if( s#^4(\d{4}|////) ## ) {
			if( $1 ne "////" ) {
				$SLP = $1 * 0.10 ;
				$SLP += 1000 if( $SLP < 100 ) ;
			}
		}
		last if( m#^555# ) ; 
		if( s#^5(\d|/)(\d{3}|///) ## ) {
			$char_Ptend = $1 if( $1 ne "/" ) ;
			$Ptend = $2 * 0.10 if( $2 ne "///" ) ;
		}
		if( s#^6((\d{3})(\d)|////) ## ) {
			if( $1 ne "////" ) {
				if( $2 < 990 ) {
					$PRECIP_amt = $2 ;
				} else {
					$PRECIP_amt = ( $2 -990 ) * 0.1 ;
				}
				$PRECIP_period = $tR[ $3 ] ;
			}
		}
		if( s#^7(\d\d|//)(\d\d|//) ## ) {
			$WXpresent = $1 if( $1 ne "//" ) ;
			$WXpast = $2 if( $2 ne "//" ) ;
		}
		if( s#^8(\d|/)(\d|/)(\d|/)(\d|/) ## ) {
			$cloudCover = $1 if( $1 ne "/" ) ;
			$cloudLow = $2 if( $2 ne "/" ) ;
			$cloudMiddle = $3 if( $3 ne "/" ) ;
			$cloudHigh = $4 if( $4 ne "/" ) ;
		}
		if( s#^9((\d\d)(\d\d)|////) ## ) {
			if( $1 ne "////" ) {
				$rhour = $2 ;
				$rmin = $3 ;
				$rhour = $bhour if( $rhour > 23 );
				$rmin = $bmin if( $rmin > 59 );
				$time_obs = theTime( "obs" ) ;
				$time_nominal = theTime( "nominal" ) ;
			}
		}
		last ;
	} # end Section 1

	# decode Section 2 
	while( ! m#^333|^444|^555# ) {
		if( s#^222(.)(.) ## ) {
			if( $1 ne "/" && $1 != 9 ) {
				$shipTrueDIR = $1 * 45 ;
				if( $2 != 0 ) {
					$shipAvgSPD = (( $2 -1 ) * 5 ) +3 ;
				} else {
					$shipAvgSPD = 0 ;
				}
			}
		} else { # section 2 header missing
			last ;
		}
		if( s#^0(\d|/)(\d{3}|///) ## ) {
			$Tw = $2 * 0.10 if( $2 ne "///" ) ; # Water temp
			$Tw *= -1.0 if( $1 ne "/" && ( $1 % 2 ) == 1 ) ;
		}
		if( s#^1(\d\d|//)(\d\d|//) ## ) {
			# Period of waves in secs by instrument
			$Pwa = $1 if( $1 ne "//" ) ; 
			# Height of waves in .5 meters units by instrument
			$Hwa = $2 / 2.0 if( $2 ne "//" ) ; 
		}
		if( s#^2(\d\d|//)(\d\d|//) ## ) {
			# Period of waves in secs
			$Pwa = $1 if( $2 ne "//" ) ; 
			# Height of waves in .5 meters units
			$Hwa = $2 / 2.0 if( $2 ne "//" ) ; 
		}
		last if( m#^333|^444|^555# ) ;
		if( s#^3(\d\d)(\d\d|//) ## ) {
			$swellDIR1 = $1 * 10 if( $1 != 99 ) ;
			$swellDIR2 = $2 * 10 if( $2 ne "//" && $2 != 99 ) ;
			if( s#^4(\d\d|//)(\d\d|//) ## ) {
				# Period of swell in secs
				$Ps1 = $1 if( $1  ne "//" ) ; 
				# Height of swell in .5 meters units
				$Hs1 = $2 / 2.0 if( $2  ne "//" ) ; 
			}
			if( defined( $swellDIR2 ) && s#^5(\d\d)(\d\d) ## ) {
				# Period of swell in secs
				$Ps2 = $1 ; 
				# Height of swell in .5 meters units
				$Hs2 = $2 / 2.0 ; 
			}
		}
		s#^5//// ## ;
		if( s#^6(\d|/)(\d\d|//)(\d|/) ## ) {
			# Type, Thickness, and Rate of ICE
			$iceType = $1 if( $1 ne "/" ) ; 
			$iceThick = $2 if( $2 ne "//" ) ; 
			$iceRate = $3 if( $3 ne "/" ) ; 
		}
		if( s#^70(\d{3}|///) ## ) {
			$Hwa_tenths = $1 * 0.10 if( $1 ne "///" ) ;
		}
		if( s#^8(\d|/)(\d{3}|///) ## ) {
			$Tb = $2 * 0.10 if( $2 ne "///" ) ;
			$Tb *= -1 if( $1 ne "/" && $1 != 0 && $1 != 5 ) ;
		}
		$ICE = 1 if( s#ICE ## ) ;
		last if( m#^333|^444|^555# ) ;
		if( s#^(\d|/)(\d|/)(\d|/)(\d|/)(\d|/) ## ) {
			$iceConcen = $1 if( $1 ne "/" ) ;
			$iceStage = $2 if( $2 ne "/" ) ;
			$iceLand = $3 if( $3 ne "/" ) ;
			$iceEdge = $4 if( $4 ne "/" ) ;
			$iceSituation = $5 if( $5 ne "/" ) ;
		}
		last;
	} # end Section 2

	# decode Section 3 
	while( ! m#^444|^555# ) {
		last unless( s#^333 ## ) ;
		s#^0.... ## ;
		if( s#^1((0|1|9)(\d{3})|////) ## ) {
			if( $1 ne "////" ) {
				$Tmax = $3 * 0.10 ;
				$Tmax *= -1.0 if( $2 == 1 ) ;
			}
		}
		if( s#^2((0|1|9)(\d{3})|////) ## ) {
			if( $1 ne "////" ) {
				$Tmin = $3 * 0.10 ;
				$Tmin *= -1.0 if( $2 == 1 ) ;
			}
		}
		s#^3(\d)(\d{3}|///) ## ;
		last if( m#^444|^555# ) ;
		if( s#^4(\d)(\d{3}|///) ## ) {
			$groundState = $1 ;
			$snowDepth = $2 ;
		}
		last if( m#^555# ) ;
		$count = 0 ;
		while( ! m#^6|^7|^8|^9# ) {
			s#^.{5} ## ;
			last if( $count++ > 10 || ! $_ ) ;
		}
		if( s#^6((\d{3})(\d)|////) ## ) {
			if( $1 ne "////" ) {
				if( $2 < 990 ) {
					$PRECIP_amt = $2 ;
				} else {
					$PRECIP_amt = ( $2 -990 ) * 0.1 ;
				}
				$PRECIP_period = $tR[ $3 ] ;
			}
		}
		if( s#^7(\d{4}|////) ## ) {
			$PRECIP_amt24 = $1 * 0.1  if( $1 ne "////" &&
				$1 != 9999 ) ;
		}
		$count = 0 ;
		while( ! m#80000# && s#^8(\d)(\d)(\d\d) ## ) {
			if( $count == 0 ) {
				@cloudObsured = ( $F ) x ( $cloud_levels ) ;
				@cloudGenus = ( $F ) x ( $cloud_levels ) ;
				@cloudHeight = ( $F ) x ( $cloud_levels ) ;
			}
			$cloudObsured[ $count ] = $1 ; 
			$cloudGenus[ $count ] = $2 ; 
			$cloudHeight[ $count ] = getHeight( $3 ) ;
			last if( $count++ == 2 || ! $_ ) ;
		}
		$meanWind = $1  if( s#^912(\d\d) ## ) ;
		$Hwa_tenths = $WHGHT[ $1 ]  if( s#^924(\d)(\d) ## ) ;
		last ;
	} # end Section 3

	# Section 4 not decoded
	#while( ! m#^555# ) {
	#	s#^444 ## ;
	#	s#(.....) ## ;
	#	print "Group 444 $1 not decoded\n" ;
	#	last ;
	#} # end Section 4

	# Section 5 not decoded

	# output record
	doOutput() ;
	undef( $rmin ) ;
} # end foreach report
} # end aaxx report

# output a report
sub doOutput {

printvars() if( $verbose ) ;

# we have a legal report, open or switch to a Netcdf file
$status = doNet( ) ;

if( $status ) { # OK to write data
	# set variables into dataref
	setvars() ;
	# report already entered, rewrite the record with new data
	if( defined( $rpt_hash{ "$wmoId $yyyymmddhh" } ) ) {
		( $record, $ntime ) =
			split( " ", $rpt_hash{ "$wmoId $yyyymmddhh" } ) ;
	} else { # new report, next record number
		$record = $recnum ;
		$rpt_hash{ "$wmoId $yyyymmddhh" } = $record ;
		$recnum++ ;
	}
	# enter report data into NetCDF file
	$status = NetCDF::recput( $ncid, $record, [ @dataref ] );
	if( $status ) { # failure
		print "NetCDF::recput status = $status\n" ;
		chop $report while( index( $report, "\0" ) != -1 ) ;
		print "$ncfile\n$report\n" ;
	} else {
		$status = NetCDF::sync( $ncid ) ;
		#print "Syncing $ncfile with ncid $ncid\n" ;
	}
}
# undefine vars
undefinevars() ;
}  # end doOutput

# undefine all report vars
sub undefinevars
{
# report vars
undef( $report ) ;
undef( $wmoId ) ;
undef( $stnName ) ;
undef( $time_obs ) ;
undef( $Lat ) ;
undef( $Lon ) ;
undef( $elev ) ;
undef( $stnType ) ;
undef( $meanWind ) ;
undef( $VIS ) ;
undef( $DIR ) ;
undef( $SPD ) ;
undef( $T ) ;
undef( $humidity ) ;
undef( $TD ) ;
undef( $PRES ) ;
undef( $SLP ) ;
undef( $char_Ptend ) ;
undef( $Ptend ) ;
undef( $PRECIP_amt ) ;
undef( $PRECIP_period ) ;
undef( $WXpresent ) ;
undef( $WXpast ) ;
undef( $cloudCover ) ;
undef( $cloudLow ) ;
undef( $cloudMiddle ) ;
undef( $cloudHigh ) ;
undef( $shipTrueDIR ) ;
undef( $shipAvgSPD ) ;
undef( $Tw ) ;
undef( $Pwa ) ;
undef( $Hwa ) ;
undef( $Hwa_tenths ) ;
undef( $swellDIR1 ) ;
undef( $Ps1 ) ;
undef( $Hs1 ) ;
undef( $swellDIR2 ) ;
undef( $Ps2 ) ;
undef( $Hs2 ) ;
undef( $iceType ) ;
undef( $iceThick ) ;
undef( $iceRate ) ;
undef( $Tb ) ;
undef( $ICE ) ;
undef( $iceConcen ) ;
undef( $iceStage ) ;
undef( $iceLand ) ;
undef( $iceEdge ) ;
undef( $iceSituation ) ;
undef( $Tmax ) ;
undef( $Tmin ) ;
undef( $groundState ) ;
undef( $snowDepth ) ;
undef( $PRECIP_amt24 ) ;
undef( @cloudObsured ) ;
undef( @cloudGenus ) ;
undef( @cloudHeight ) ;
} # end of undefinevARS

# setvars into record
sub setvars {

$report = padstr( $report, $r_len ) ;

undef( @dataref ) ;

@dataref = @rec ;

# report vars
$dataref[ $W[ 0 ] ] = \$rep_type if( $W[ 0 ] != -1 ) ;
$dataref[ $W[ 1 ] ] = \$wmoId if( $W[ 1 ] && defined( $wmoId ) ) ;
$dataref[ $W[ 2 ] ] = \$stnName if( $W[ 2 ] && defined( $stnName ) ) ;
$dataref[ $W[ 3 ] ] = \$time_obs if( $W[ 3 ] && defined( $time_obs ) ) ;
$dataref[ $W[ 4 ] ] = \$time_nominal if( $W[ 4 ] && defined( $time_nominal ) ) ;
$dataref[ $W[ 5 ] ] = \$Lat if( $W[ 5 ] && defined( $Lat ) ) ;
$dataref[ $W[ 6 ] ] = \$Lon if( $W[ 6 ] && defined( $Lon ) ) ;
$dataref[ $W[ 7 ] ] = \$elev if( $W[ 7 ] && defined( $elev ) ) ;
$dataref[ $W[ 8 ] ] = \$stnType if( $W[ 8 ] && defined( $stnType ) ) ;
$dataref[ $W[ 9 ] ] = \$meanWind if( $W[ 9 ] && defined( $meanWind ) ) ;
$dataref[ $W[ 10 ] ] = \$VIS if( $W[ 10 ] && defined( $VIS ));
$dataref[ $W[ 11 ] ] = \$DIR if( $W[ 11 ] && defined( $DIR ) ) ;
$dataref[ $W[ 12 ] ] = \$SPD if( $W[ 12 ] && defined( $SPD ) ) ;
$dataref[ $W[ 13 ] ] = \$T if( $W[ 13 ] && defined( $T ) ) ;
$dataref[ $W[ 14 ] ] = \$humidity if( $W[ 14 ] && defined( $humidity ) ) ;
$dataref[ $W[ 15 ] ] = \$TD if( $W[ 15 ] && defined( $TD ) ) ;
$dataref[ $W[ 16 ] ] = \$PRES if( $W[ 16 ] && defined( $PRES ) ) ;
$dataref[ $W[ 17 ] ] = \$SLP if( $W[ 17 ] && defined( $SLP ) ) ;
$dataref[ $W[ 18 ] ] = \$char_Ptend if( $W[ 18 ] && defined( $char_Ptend ) ) ;
$dataref[ $W[ 19 ] ] = \$Ptend if( $W[ 19 ] && defined( $Ptend ) ) ;
$dataref[ $W[ 20 ] ] = \$PRECIP_amt if( $W[ 20 ] && defined( $PRECIP_amt ) ) ;
$dataref[ $W[ 21 ] ] = \$PRECIP_period if( $W[ 21 ] && 
	defined( $PRECIP_period ) ) ;
$dataref[ $W[ 22 ] ] = \$WXpresent if( $W[ 22 ] && defined( $WXpresent ) ) ;
$dataref[ $W[ 23 ] ] = \$WXpast if( $W[ 23 ] && defined( $WXpast ));
$dataref[ $W[ 24 ] ] = \$cloudCover if( $W[ 24 ] && defined( $cloudCover ) ) ;
$dataref[ $W[ 25 ] ] = \$cloudLow if( $W[ 25 ] && defined( $cloudLow ) ) ;
$dataref[ $W[ 26 ] ] = \$cloudMiddle if( $W[ 26 ] && defined( $cloudMiddle ) ) ;
$dataref[ $W[ 27 ] ] = \$cloudHigh if( $W[ 27 ] && defined( $cloudHigh ) ) ;
$dataref[ $W[ 28 ] ] = \$shipTrueDIR if( $W[ 28 ] && defined( $shipTrueDIR ) ) ;
$dataref[ $W[ 29 ] ] = \$shipAvgSPD if( $W[ 29 ] && defined( $shipAvgSPD ) ) ;
$dataref[ $W[ 30 ] ] = \$Tw if( $W[ 30 ] && defined( $Tw ) ) ;
$dataref[ $W[ 31 ] ] = \$Pwa if( $W[ 31 ] && defined( $Pwa ) ) ;
$dataref[ $W[ 32 ] ] = \$Hwa if( $W[ 32 ] && defined( $Hwa ) ) ;
$dataref[ $W[ 33 ] ] = \$Hwa_tenths if( $W[ 33 ] && defined( $Hwa_tenths ) ) ;
$dataref[ $W[ 34 ] ] = \$swellDIR1 if( $W[ 34 ] && defined( $swellDIR1 ) ) ;
$dataref[ $W[ 35 ] ] = \$Ps1 if( $W[ 35 ] && defined( $Ps1 ) ) ;
$dataref[ $W[ 36 ] ] = \$Hs1 if( $W[ 36 ] && defined( $Hs1 ) ) ;
$dataref[ $W[ 37 ] ] = \$swellDIR2 if( $W[ 37 ] && defined( $swellDIR2 ) ) ;
$dataref[ $W[ 38 ] ] = \$Ps2 if( $W[ 38 ] && defined( $Ps2 ));
$dataref[ $W[ 39 ] ] = \$Hs2 if( $W[ 39 ] && defined( $Hs2 ) ) ;
$dataref[ $W[ 40 ] ] = \$iceType if( $W[ 40 ] && defined( $iceType ) ) ;
$dataref[ $W[ 41 ] ] = \$iceThick if( $W[ 41 ] && defined( $iceThick ) ) ;
$dataref[ $W[ 42 ] ] = \$iceRate if( $W[ 42 ] && defined( $iceRate ) ) ;
$dataref[ $W[ 43 ] ] = \$Tb if( $W[ 43 ] && defined( $Tb ) ) ;
$dataref[ $W[ 44 ] ] = \$ICE if( $W[ 44 ] && defined( $ICE ) ) ;
$dataref[ $W[ 45 ] ] = \$iceConcen if( $W[ 45 ] && defined( $iceConcen ) ) ;
$dataref[ $W[ 46 ] ] = \$iceStage if( $W[ 46 ] && defined( $iceStage ) ) ;
$dataref[ $W[ 47 ] ] = \$iceLand if( $W[ 47 ] && defined( $iceLand ) ) ;
$dataref[ $W[ 48 ] ] = \$iceEdge if( $W[ 48 ] && defined( $iceEdge ));
$dataref[ $W[ 49 ] ] = \$iceSituation if( $W[ 49 ] && defined( $iceSituation ) );
$dataref[ $W[ 50 ] ] = \$Tmax if( $W[ 50 ] && defined( $Tmax ) ) ;
$dataref[ $W[ 51 ] ] = \$Tmin if( $W[ 51 ] && defined( $Tmin ) ) ;
$dataref[ $W[ 52 ] ] = \$groundState if( $W[ 52 ] && defined( $groundState ) ) ;
$dataref[ $W[ 53 ] ] = \$snowDepth if( $W[ 53 ] && defined( $snowDepth ) ) ;
$dataref[ $W[ 54 ] ] = \$PRECIP_amt24 if( $W[ 54 ] && defined( $PRECIP_amt24 ) );
$dataref[ $W[ 55 ] ] = \@cloudObsured if( $W[ 55 ] && defined( @cloudObsured ) );
$dataref[ $W[ 56 ] ] = \@cloudGenus if( $W[ 56 ] && defined( @cloudGenus ) ) ;
$dataref[ $W[ 57 ] ] = \@cloudHeight if( $W[ 57 ] && defined( @cloudHeight ) ) ;
$dataref[ $W[ 58 ] ] = \$report if( $W[ 58 ] && defined( $report ) ) ;

} # end setvars

# create a netcdf file or reopen a existing one
sub doNet {

my( $Ncfile, $Id, $Num, $Time, $baseTime, $offset, $rpt, $stn ) ;

#$ncfile = $datadir . "/" . $yyyymmddhh . "_syn.nc" ;
$ncfile = $datadir . "/" . $synPrefix . substr( $yyyymmddhh, 0, 8 ) . 
	$syn . ".nc" ;
if( $hourly ) {
	$ncfile = $datadir . "/" . $synPrefix . substr( $yyyymmddhh, 0, 8 ) . 
		"_" . substr( $yyyymmddhh, 8 ) . $syn . ".nc" ;
}
# writing to same Ncfile
return 1 if( $ncfile eq $lastNc ) ;

# current time
$thetime = time() ;
# save current Ncfile info
$Nets{ $lastNc } = "$ncid $recnum $thetime" if( $lastNc ) ;

# File is open, get ncfile id and recnum and reset the time 
if( defined( $Nets{ $ncfile } ) ) { # already open for writes
	( $ncid, $recnum, $ncTime ) = split( " ", $Nets{ $ncfile } ) ;
	$ncTime = $thetime ;
	$lastNc =  $ncfile ;
	return 1 ;
}
# close Ncfiles with no activity for 20 minutes
foreach $Ncfile ( keys %Nets ) {
	( $Id, $Num, $Time ) = split( " ", $Nets{ $Ncfile } ) ;
	if( $thetime - $Time > 1200 ) {
		print "Closing $Ncfile with ncid $Id, No write for > 20 Minutes\n" ;
		$status = NetCDF::close( $Id ) ;
		delete( $Nets{ $Ncfile } ) ;
#	} elsif( $Ncfile eq $lastNc ) {
#		print "Syncing $lastNc with ncid $ncid\n" ;
#		$status = NetCDF::sync( $Id ) ;
	}
}
# remove rpt entries older than 24 hours
$baseTime = $yyyymm . $cday . $chour ;
foreach $rpt ( keys %rpt_hash ) {
	( $stn, $Time ) = split( " ", $rpt ) ;
	$offset = $baseTime - $Time ;
	next if( $offset < 100 ) ;  # same day ok & previous day ok > $chour
	delete( $rpt_hash{ $rpt } ) ;
}
# open or create ncfiles
if( -e $ncfile ) {
	$ncid = NetCDF::open( "$ncfile", WRITE ) ;
	return 0 if( $ncid == -1 ) ;
	$recNum_id = NetCDF::dimid( $ncid, "recNum" ) ;
	$name_id =  "xxxxxxxx"  ;
	$recnum =  -1  ;
	# get current value of recnum
	NetCDF::diminq( $ncid, $recNum_id, $name_id, $recnum ) ;
} else {
	system( "$ncgen -o $ncfile $cdlfile" ) ;
	$ncid = NetCDF::open( "$ncfile", WRITE ) ;
	return 0 if( $ncid == -1 ) ;
	# NetCDF record counter
	$recnum = 0 ;
}
$Nets{ $ncfile } = "$ncid $recnum $thetime" ;
$lastNc = $ncfile ;
print "Opening $ncfile with ncid $ncid\n" ;

return 1 ;
} # end doNet


# print vars
sub printvars
{

# original report 
print "report = $report \n" if( defined( $report ) ) ;
print "\n" ;

# report vars
print "rep_type = $rep_type \n" if( defined( $rep_type ) ) ;
print "wmoId = $wmoId \n" if( defined( $wmoId ) ) ;
print "stnName = $stnName \n" if( defined( $stnName ) ) ;
print "dd = $dd \n" if( defined( $dd ) ) ;
print "themonth = $themonth \n" if( defined( $themonth ) ) ;
print "theyear = $theyear \n" if( defined( $theyear ) ) ;
print "rhour = $rhour \n" if( defined( $rhour ) ) ;
print "rmin = $rmin \n" if( defined( $rmin ) ) ;
print "time_obs = $time_obs \n" if( defined( $time_obs ) ) ;
print "time_nominal = $time_nominal \n" if( defined( $time_nominal ) ) ;
print "yyyymmddhh = $yyyymmddhh \n" if( defined( $yyyymmddhh ) ) ;
print "Windicator = $Windicator \n" if( defined( $Windicator ) ) ;
print "Lat = $Lat \n" if( defined( $Lat ) ) ;
print "Lon = $Lon \n" if( defined( $Lon ) ) ;
print "elev = $elev \n" if( defined( $elev ) ) ;
print "stnType = $stnType \n" if( defined( $stnType ) ) ;
print "meanWind = $meanWind \n" if( defined( $meanWind ) ) ;
print "VIS = $VIS \n" if( defined( $VIS ) ) ;
print "DIR = $DIR \n" if( defined( $DIR ) ) ;
print "SPD = $SPD \n" if( defined( $SPD ) ) ;
print "T = $T \n" if( defined( $T ) ) ;
print "humidity = $humidity \n" if( defined( $humidity ) ) ;
print "TD = $TD \n" if( defined( $TD ) ) ;
print "PRES = $PRES \n" if( defined( $PRES ) ) ;
print "SLP = $SLP \n" if( defined( $SLP ) ) ;
print "char_Ptend = $char_Ptend \n" if( defined( $char_Ptend ) ) ;
print "Ptend = $Ptend \n" if( defined( $Ptend ) ) ;
print "PRECIP_amt = $PRECIP_amt \n" if( defined( $PRECIP_amt ) ) ;
print "PRECIP_period = $PRECIP_period \n" if( defined( $PRECIP_period ) ) ;
print "WXpresent = $WXpresent \n" if( defined( $WXpresent ) ) ;
print "WXpast = $WXpast \n" if( defined( $WXpast ) ) ;
print "cloudCover = $cloudCover \n" if( defined( $cloudCover ) ) ;
print "cloudLow = $cloudLow \n" if( defined( $cloudLow ) ) ;
print "cloudMiddle = $cloudMiddle \n" if( defined( $cloudMiddle ) ) ;
print "cloudHigh = $cloudHigh \n" if( defined( $cloudHigh ) ) ;
print "shipTrueDir = $shipTrueDir \n" if( defined( $shipTrueDir ) ) ;
print "shipAvgSPD = $shipAvgSPD \n" if( defined( $shipAvgSPD ) ) ;
print "Tw = $Tw \n" if( defined( $Tw ) ) ;
print "Pwa = $Pwa \n" if( defined( $Pwa ) ) ;
print "Hwa = $Hwa \n" if( defined( $Hwa ) ) ;
print "Hwa_tenths = $Hwa_tenths \n" if( defined( $Hwa_tenths ) ) ;
print "swellDir1 = $swellDir1 \n" if( defined( $swellDir1 ) ) ;
print "Ps1 = $Ps1 \n" if( defined( $Ps1 ) ) ;
print "Hs1 = $Hs1 \n" if( defined( $Hs1 ) ) ;
print "swellDir2 = $swellDir2 \n" if( defined( $swellDir2 ) ) ;
print "Ps2 = $Ps2 \n" if( defined( $Ps2 ) ) ;
print "Hs2 = $Hs2 \n" if( defined( $Hs2 ) ) ;
print "iceType = $iceType \n" if( defined( $iceType ) ) ;
print "iceThick = $iceThick \n" if( defined( $iceThick ) ) ;
print "iceRate = $iceRate \n" if( defined( $iceRate ) ) ;
print "Tb = $Tb \n" if( defined( $Tb ) ) ;
print "ICE = $ICE \n" if( defined( $ICE ) ) ;
print "iceConcen = $iceConcen \n" if( defined( $iceConcen ) ) ;
print "iceStage = $iceStage \n" if( defined( $iceStage ) ) ;
print "iceLand = $iceLand \n" if( defined( $iceLand ) ) ;
print "iceEdge = $iceEdge \n" if( defined( $iceEdge ) ) ;
print "iceSituation = $iceSituation \n" if( defined( $iceSituation ) ) ;
print "Tmax = $Tmax \n" if( defined( $Tmax ) ) ;
print "Tmin = $Tmin \n" if( defined( $Tmin ) ) ;
print "groundState = $groundState \n" if( defined( $groundState ) ) ;
print "snowDepth = $snowDepth \n" if( defined( $snowDepth ) ) ;
print "PRECIP_amt24 = $PRECIP_amt24 \n" if( defined( $PRECIP_amt24 ) ) ;
print "cloudObsured = @cloudObsured \n" if( defined( @cloudObsured ) ) ;
print "cloudGenus = @cloudGenus \n" if( defined( @cloudGenus ) ) ;
print "cloudHeight = @cloudHeight \n" if( defined( @cloudHeight ) ) ;
print "report = $report \n" if( defined( $report ) ) ;
print "\n" ;
} # end printvars

# execute at exit
sub atexit
{
local( $sig ) = @_ ;

if( $sig eq "eof" ) {
	print "eof on STDIN --shutting down\n" ;
} elsif( defined( $sig )) {
	print "Caught SIG$sig --shutting down\n" ;
}
# open syn.lst, list of reports processed in the last ? hours.
open( LST, ">$datadir/syn.lst" ) || 
	die "could not open $datadir/syn.lst: $!\n" ;
select( LST ) ;
# remove stn entries older than 24 hours
$yyyymmddhh = $yyyymm . $cday . $chour ;
foreach $rpt ( keys %rpt_hash ) {
	( $stn, $Time ) = split( " ", $rpt ) ;
	$offset = $yyyymmddhh - $Time ;
	next unless( $offset < 100 ) ;  # same day ok & previous day ok > $chour
	print "$rpt $rpt_hash{ $rpt }\n" ;
}
close LST ;
foreach $file ( keys %Nets ) {
	( $ncid, $recnum, $nctime ) = split( " ", $Nets{ $file } ) ;
	print STDOUT "Closing $file with ncid $ncid\n" ;
	$status = NetCDF::close( $ncid ) ;
}
close( STDOUT ) ;
close( STDERR ) ;
exit( 0 ) ;

}

# pad str to correct length
sub padstr
{
( $str, $len ) = @_ ;

local( $size, $i ) ;

$size = length( $str ) ;

for( $i = $size; $i < $len; $i++ ) {
        $str .= "\0" ;
}
if( $size > $len ) {
	print STDOUT "String length is over $len chars long:\n $str\n" 
		if( $verbose ) ;
	$str = substr( $str, 0, $len ) ;
}
return $str ;
} # end padstr

sub theTime
{

my( $when, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $t_adj ) ;

( $when ) = @_ ;

$t_adj = 0;  # use to make nomial time
$hour = $rhour ;
$mday = substr( $yyyymmddhh, 6, 2 ) ;
$mon = substr( $yyyymmddhh, 4, 2 ) ;
$year = substr( $yyyymmddhh, 2, 2 ) ;

if( $when eq "obs" ) {
	$min = $rmin ;
} elsif( $when eq "nominal" ) {
	if( $rmin > 14 && $rmin < 45 ) {
		$min = $rmin ;
	} elsif( $rmin > 44 ) {
		$min = 0 ;
		$t_adj = 3600; #add 1 hour
	} else {
		$min = 0 ;
	} 
}
$time = timegm(0, $min, $hour, $mday, $mon -1, $year, 0, 0, 0) + $t_adj;

return $time ; 

} # theTime

sub getHeight {

my( $Height, $cloudHeight ) ;

( $Height ) = @_ ;

if( $Height < 51 ) {
	$cloudHeight = $Height * 30 ;
} elsif( $Height < 81 ) {
	$cloudHeight = ( $Height -50 ) * 300 ;
} elsif( $Height < 90 ) {
	$cloudHeight = (( $Height -80 ) * 1500 ) + 9000 ;
} elsif( $Height == 90 ) {
	$cloudHeight = 25 ;
} elsif( $Height == 91 ) {
	$cloudHeight = 75 ;
} elsif( $Height == 92 ) {
	$cloudHeight = 150 ;
} elsif( $Height == 93 ) {
	$cloudHeight = 250 ;
} elsif( $Height == 94 ) {
	$cloudHeight = 450 ;
} elsif( $Height == 95 ) {
	$cloudHeight = 800 ;
} elsif( $Height == 96 ) {
	$cloudHeight = 1250 ;
} elsif( $Height == 97 ) {
	$cloudHeight = 1750 ;
} elsif( $Height == 98 ) {
	$cloudHeight = 2250 ;
} elsif( $Height == 99 ) {
	$cloudHeight = 2500 ;
}

return $cloudHeight ;

} # end getHeight
