This archive contains answers to questions sent to Unidata support through mid-2025. Note that the archive is no longer being updated. We provide the archive for reference; many of the answers presented here remain technically correct, even if somewhat outdated. For the most up-to-date information on the use of NSF Unidata software and data services, please consult the Software Documentation first.
Ryan, the problem was the bulletin came in on same day so it was filed into the 0th hour for that day. i added some code to file the bulletin into the next day 0th hour. the ua2nc code is attached, could you test it out for any problems. robb... On Thu, 7 Sep 2006, Ryan Torn wrote: > New Ticket: ua2nc bug > > I think I have found a fairly major bug in the decoder ua2nc. The > report time on Chinese rawinsonde observations at 00UTC are typically > marked as 23 UTC on the previous day. For example, the 00 UTC > rawinsonde observation for the 7th has a report time of 23 UTC on the > 6th. When this report is fed through the ua2nc decoder, the program > thinks the time is 00 UTC on the 6th because the program rounds up the > hour, but cannot round up the day. This error caused me to assimilate > observations from the wrong time over China. Could you please come up > with a solution to this problem? I have enclosed a Chinese rawinsonde > observation as an example > > Ryan > > TTAA 03231 52203 99928 16662 00000 00103 ///// ///// 92768 20071 000^M^M > 00 85501 20880 11505 70140 07663 25007 50580 12390 29512 40746 25128 ^M^M > 27015 30949 41559 28022 25069 52358 28025 20213 53159 28025 15398 ^M^M > 54963 29035 10653 599// 31024 88228 54558 28527 88109 601// 30026 ^M^M > 77156 28535=^M^M > > -- > Ryan Torn > Graduate Research Assistant > Dept. of Atmospheric Sciences > University of Washington, Box 351640 > Seattle, WA 98195 > Phone 206-685-1851 > > > > Ticket Details > =================== > Ticket ID: YUU-262958 > Department: Support netCDF Decoders > Priority: Normal > Status: Open > Link: > http://www.unidata.ucar.edu/esupport/staff/index.php?_m=tickets&_a=viewticket&ticketid=1855 > =============================================================================== Robb Kambic Unidata Program Center Software Engineer III Univ. Corp for Atmospheric Research address@hidden WWW: http://www.unidata.ucar.edu/ ===============================================================================
#!/usr/bin/perl # # Decodes Upperair (WMO TEMP) reports according to: # WMO Manual on codes volume 1 No. 306 # FM 35-IX Ext. TEMP - Upper-level pressure, temperture, humidity, and # wind reports from a fixed land station. # # usage with input from STDIN: ua2nc [-v] [-h] [-n (old|new)] cdlfile [datadir] [yyyymm] # # -v flag prints out the reports in the uaLog.log file, very verbose # use NetCDF ; use Time::Local ; #no encoding; # process command line switches $upperair = "new"; while( $_ = $ARGV[0], /^-/ ) { shift ; last if /^--$/ ; /^(-v)/ && $verbose++ ; /^(-h)/ && $hourly++; #create hourly files instead of daily /^(-n)/ && ( $upperair = shift ); } # new netCDF file naming covention is default if( $upperair eq "new" ) { if( $hourly ) { $upperair = "00"; } else { $upperair = "_0000"; } $upperairPrefix = "Upperair_"; } else { $upperair = "_upperair"; $upperairPrefix = ""; } # 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: ua2nc [-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/uaLog.log.2" ) { `rm -f $datadir/uaLog.log.3`; `mv $datadir/uaLog.log.2 $datadir/uaLog.log.3`; } if( -e "$datadir/uaLog.log.1" ) { `mv $datadir/uaLog.log.1 $datadir/uaLog.log.2`; } if( -e "$datadir/uaLog.log" ) { `mv $datadir/uaLog.log $datadir/uaLog.log.1`; } # redirect STDOUT and STDERR open( STDOUT, ">$datadir/uaLog.log" ) || die "could not open $datadir/uaLog.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 ) ; } %bin = ( "23", "00", "00", "00", "01", "00", "02", "03", "03", "03", "04", "03", "05", "06", "06", "06", "07", "06", "08", "09", "09", "09", "10", "09", "11", "12", "12", "12", "13", "12", "14", "15", "15", "15", "16", "15", "17", "18", "18", "18", "19", "18", "20", "21", "21", "21", "22", "21" ) ; # 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' ; # set defaults %mand_ttaa = ("99", 99999, "00", 1000, "92", 925, "85", 850, "70", 700, "50", 500, "40", 400, "30", 300, "25", 250, "20", 200, "15", 150, "10", 100 ) ; %mand_ttcc = ("70", 1, "50", 1, "30", 1, "20", 1, "10", 1, "07", 1, "05", 1, "03", 1, "02", 1, "01", 1 ) ; %mand_pres = ( "1000", 1, "925", 2, "850", 3, "700", 4, "500", 5, "400", 6, "300", 7, "250", 8, "200", 9, "150", 10, "100", 11 , "70", 12, "50", 13, "30", 14, "20", 15, "10", 16, "07", 17, "05", 18, "03", 19, "02", 20, "01", 21 ) ; %TTAA_top_wind_level = ( "/", 99999, "0", 1000, "9", 925, "8", 850, "7", 700, "5", 500, "4", 400, "3", 300, "2", 200, "1", 100 ) ; %TTCC_top_wind_level = ( "/", 100, "0", 0, "7", 70, "5", 50, "3", 30, "2", 20, "1", 0) ; # defaults $manLevel = 22 ; $sigTLevel = 65 ; $sigWLevel = 50 ; $mWndNum = 4 ; $mTropNum = 4 ; $staNameLen = 6 ; # open cdl and get dimensions for variables open( CDL, "$cdlfile" ) || die "could not open $cdlfile: $!\n" ; $i = 0 ; while( <CDL> ) { if( s#^\s*manLevel\s*=\s*(\d{1,5})## ) { $manLevel = $1 ; } elsif( s#^\s*sigTLevel\s*=\s*(\d{1,5})## ) { $sigTLevel = $1 ; } elsif( s#^\s*sigWLevel\s*=\s*(\d{1,5})## ) { $sigWLevel = $1 ; } elsif( s#^\s*mWndNum\s*=\s*(\d{1,5})## ) { $mWndNum = $1 ; } elsif( s#^\s*mTropNum\s*=\s*(\d{1,5})## ) { $mTropNum = $1 ; } elsif( s#^\s*staNameLen\s*=\s*(\d{1,5})## ) { $staNameLen = $1 ; } elsif( s#^\s*variables## ) { last ; } } close CDL ; # initialize record buffer and variables makedataref(); # read in station data if( -e "etc/snstns.tbl" ) { $sfile = "etc/snstns.tbl" ; } elsif( -e "./snstns.tbl" ) { $sfile = "./snstns.tbl" ; } else { die "Can't find snstns.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 ; $wmo_id = $2 ; $wmo_id = "0" . $wmo_id if( length( $wmo_id ) == 4 ) ; ( $lat, $lon, $elev ) = split ; $lat = sprintf( "%7.2f", $lat / 100 ) ; $lon = sprintf( "%7.2f", $lon / 100) ; # set these vars ( $lat, $lon, $elev, $id ) $STATIONS{ "$wmo_id" } = "$lat $lon $elev $id" ; } close STATION ; # read in list of already processed reports if it exists # open ua.lst, list of reports processed in the last 24 hours. if( -e "$datadir/ua.lst" ) { open( LST, "$datadir/ua.lst" ) || die "could not open $datadir/ua.lst: $!\n" ; while( <LST> ) { ( $stn, $yyyymmddhh, $record, @R ) = split ; $rpt_hash{ "$stn $yyyymmddhh" } = "$record @R" ; } close LST ; } # Now begin parsing file and decoding observations breaking on cntrl C $/ = "\cC" ; # 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 $timeout seconds on select\n" ; atexit() ; } atexit( "eof" ) if( eof( STDIN )) ; # Process each line of ua bulletins, header first $_ = <STDIN> ; next unless( /TT|PP/ ) ; s#\cC## ; s#\cM##g ; s#\cA\n## ; s#\c^##g ; s#^\n##g ; 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 transmission times against current time $cday = (gmtime())[ 3 ] ; $chour = (gmtime())[ 2 ] ; # 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" ; # Separate 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 undef( $stn_name ) ; next if( /NIL/ ) ; s#\n# #g ; s#^\d{5}.*((TT)(AA|BB|CC|DD)|(PP)(BB|DD))#$1# ; next unless ( s# ?((TT)(AA|BB|CC|DD)|(PP)(BB|DD)) {1,2}(\d\d)(\d\d)([\d\/]) (\d{5}) ## ) ; #print "$&\n" ; $type = $1 ; $rday = $6 ; $rhour = $7 ; $indicator = $8 ; $stn = $9 ; if( $rday =~ /^[5678]/ ) { $rday -= 50 ; $knots = 1 ; } else { $knots = 0 ; } # 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 ) ; # increment rday & check if last day of month if( $rhour == 23 ) { incrementRDAY() ; $tmpyyyymm = $theyear . $themonth ; } } 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 ) . $bin{ "$rhour" } ; if( defined( $STATIONS{ "$stn" } )) { # station's lat, lon, elev, id. ( $lat, $lon, $elev, $id ) = split( ' ', $STATIONS{ "$stn" } ) ; $id = $AS6 unless( defined( $id )) ; } else { print "WMO Id $stn not in station table\n" ; next ; } if( defined( $rpt_hash{ "$stn $yyyymmddhh" } )) { ( $record, @R ) = split( ' ', $rpt_hash{ "$stn $yyyymmddhh" } ) ; $rpt_hash{ "$stn $yyyymmddhh" } .= " $type" if( index( "@R", $type ) == -1 ) ; } else { $record = -1 ; } # clean data s#\s+# #g ; $_ .= ' ' ; $j = 0 ; # process reports SWITCH: { ttaa( $_ ), last SWITCH if( $type eq "TTAA" ) ; ttbb( $_ ), last SWITCH if( $type eq "TTBB" ) ; ttcc( $_ ), last SWITCH if( $type eq "TTCC" ) ; ttdd( $_ ), last SWITCH if( $type eq "TTDD" ) ; ppbbdd( $_ ), last SWITCH if( $type eq "PPBB" || $type eq "PPDD" ) ; } # no data extracted next unless $j ; # covert knots to meters/second if necessary if( $knots && $#wspd != -1 ) { for( $i = 0; $i < $j; $i++ ) { $wspd[ $i ] *= 0.5144 if( $wspd[ $i ] != $F ) ; } } # we have a legal report, open a Netcdf file $status = doNet() ; # print output printvars() if( $verbose ) ; # output the NetCDF data here makedataref(); # record already exists for $stn if( $record >= 0 ) { $status = NetCDF::recget( $ncid, $record, \@dataref ) ; print "NetCDF::recget status = $status\n" if( $status ) ; } else { # initialize new record $record = $recnum ; $rpt_hash{ "$stn $yyyymmddhh" } = "$record $type" ; $recnum++ ; $dataref[ 0 ] = \$stn ; if( defined( $id )) { $id = padstr( $id, $staNameLen ) ; $dataref[ 1 ] = \$id ; $dataref[ 2 ] = \$lat ; $dataref[ 3 ] = \$lon ; $dataref[ 4 ] = \$elev ; } $dataref[ 5 ] = \theTime( "synoptic" ) ; $dataref[ 11 ] = \theTime( "release" ) ; # release time } setvars() ; $status = NetCDF::recput( $ncid, $record, \@dataref ) ; if( $status ) { # failure print "Wmo =$stn, Type =$type, Rpt: $rday$rhour, ", "Trans: $bday$bhour, recput =$status\n" ; } else { $status = NetCDF::sync( $ncid ) ; #print "Syncing $ncfile with ncid $ncid\n" ; } atexit( "eof" ) if( eof( STDIN )) ; if( $type eq "TTAA" || $type eq "TTCC" ) { undef( $numMand ) ; undef( @prMan ) ; undef( @htMan ) ; undef( @tpMan ) ; undef( @tdMan ) ; undef( @wdMan ) ; undef( @wsMan ) ; undef( $numTrop ) ; undef( @prTrop ) ; undef( @tpTrop ) ; undef( @tdTrop ) ; undef( @wdTrop ) ; undef( @wsTrop ) ; undef( $numMwnd ) ; undef( @prMaxW ) ; undef( @wdMaxW ) ; undef( @wsMaxW ) ; } elsif( $type eq "TTBB" || $type eq "TTDD" ) { undef( $numSigT ) ; undef( @prSigT ) ; undef( @tpSigT ) ; undef( @tdSigT ) ; undef( %prSigT ) ; } elsif( $type eq "PPBB" || $type eq "PPDD" ) { undef( $numSigW ) ; undef( @htSigW ) ; undef( @wsSigW ) ; undef( @wdSigW ) ; undef( %htSigW ) ; } undef( $i ) ; undef( $j ) ; undef( $k ) ; undef( @pres ) ; undef( @ht ) ; undef( @t ) ; undef( @td ) ; undef( @wdir ) ; undef( @wspd ) ; undef( $trop_pres ) ; undef( $trop_t ) ; undef( $trop_td_dep ) ; undef( $trop_wdir ) ; undef( $trop_wspd ) ; undef( $max_pres ) ; undef( $max_wdir ) ; undef( $max_wspd ) ; } # end foreach report } # end while( 1 ) atexit( "eof" ) ; exit( 0 ) ; #should never get here # setvars into record sub setvars { # enter report data into proper record location SWITCH1: { ( $type eq "TTAA" || $type eq "TTCC" ) && do { $numMand = ${$dataref[ 6 ]} ; # number of mandatory # data already inserted into record, append/overwrite if( $numMand != $F ) { @prMan = @{ $dataref[ 13 ] } ; @htMan = @{ $dataref[ 14 ] } ; @tpMan = @{ $dataref[ 15 ] } ; @tdMan = @{ $dataref[ 16 ] } ; @wdMan = @{ $dataref[ 17 ] } ; @wsMan = @{ $dataref[ 18 ] } ; } else { # first data write @prMan = @ML ; @htMan = @ML ; @tpMan = @ML ; @tdMan = @ML ; @wdMan = @ML ; @wsMan = @ML ; } if( $pres[ 1 ] == 1000 ) { $prMan[ 0 ] = $pres[ 0 ] ; $htMan[ 0 ] = $ht[ 0 ] ; $tpMan[ 0 ] = $t[ 0 ] ; $tdMan[ 0 ] = $td[ 0 ] ; $wdMan[ 0 ] = $wdir[ 0 ] ; $wsMan[ 0 ] = $wspd[ 0 ] ; $j = 1 ; } else { $j = 0 ; } for( $i = $j; $i < $manLevel; $i++ ) { $k = $mand_pres{ "$pres[ $i ]" } ; next unless( defined( $k )) ; $prMan[ $k ] = $pres[ $i ] ; $htMan[ $k ] = $ht[ $i ] ; $tpMan[ $k ] = $t[ $i ] ; $tdMan[ $k ] = $td[ $i ] ; $wdMan[ $k ] = $wdir[ $i ] ; $wsMan[ $k ] = $wspd[ $i ] ; } $numMand = 0 ; for( $i = 0 ; $i < $manLevel; $i++ ) { $numMand++ if( $prMan[ $i ] != $F ) ; } $dataref[ 6 ] = \$numMand ; $dataref[ 13 ] = \@prMan ; $dataref[ 14 ] = \@htMan ; $dataref[ 15 ] = \@tpMan ; $dataref[ 16 ] = \@tdMan ; $dataref[ 17 ] = \@wdMan ; $dataref[ 18 ] = \@wsMan ; # enter topopause data $numTrop = ${$dataref[ 10 ]} ; # number of tropopause # data already inserted into record, append/overwrite if( $numTrop != $F ) { @prTrop = @{ $dataref[ 25 ] } ; @tpTrop = @{ $dataref[ 26 ] } ; @tdTrop = @{ $dataref[ 27 ] } ; @wdTrop = @{ $dataref[ 28 ] } ; @wsTrop = @{ $dataref[ 29 ] } ; } else { # first data write @prTrop = @TL ; @tpTrop = @TL ; @tdTrop = @TL ; @wdTrop = @TL ; @wsTrop = @TL ; } # enter new tropopause data if( defined( $trop_pres ) && $numTrop == $F ) { $prTrop[ 0 ] = $trop_pres ; $tpTrop[ 0 ] = $trop_t ; $tdTrop[ 0 ] = $trop_td_dep ; $wdTrop[ 0 ] = $trop_wdir ; $wsTrop[ 0 ] = $trop_wspd ; $numTrop = 1; } elsif( defined( $trop_pres ) ) { for( $i = 0; $i < $mTropNum; $i++ ) { if( $prTrop[ $i ] == $trop_pres ) { $index = $i ; last ; } elsif( $prTrop[ $i ] == $F ) { $numTrop++ ; $index = $i ; last ; } } $prTrop[ $index ] = $trop_pres ; $tpTrop[ $index ] = $trop_t ; $tdTrop[ $index ] = $trop_td_dep ; $wdTrop[ $index ] = $trop_wdir ; $wsTrop[ $index ] = $trop_wspd ; } $dataref[ 25 ] = \@prTrop ; $dataref[ 26 ] = \@tpTrop ; $dataref[ 27 ] = \@tdTrop ; $dataref[ 28 ] = \@wdTrop ; $dataref[ 29 ] = \@wsTrop ; $dataref[ 10 ] = \$numTrop ; # enter maximum wind data $numMwnd = ${$dataref[ 9 ]} ; # number of max winds # data already inserted into record, append/overwrite if( $numMwnd != $F ) { @prMaxW = @{ $dataref[ 30 ] } ; @wdMaxW = @{ $dataref[ 31 ] } ; @wsMaxW = @{ $dataref[ 32 ] } ; } else { # first data write @prMaxW = @MWL ; @wdMaxW = @MWL ; @wsMaxW = @MWL ; } # enter new max wind data if( defined( $max_pres ) && $numMwnd == $F ) { $prMaxW[ 0 ] = $max_pres ; $wdMaxW[ 0 ] = $max_wdir ; $wsMaxW[ 0 ] = $max_wspd ; $numMwnd = 1; } elsif( defined( $max_pres ) ) { for( $i = 0; $i < $mWndNum; $i++ ) { if( $prMaxW[ $i ] == $max_pres ) { $index = $i ; last ; } elsif( $prMaxW[ $i ] == $F ) { $numMwnd++ ; $index = $i ; last ; } } $prMaxW[ $index ] = $max_pres ; $wdMaxW[ $index ] = $max_wdir ; $wsMaxW[ $index ] = $max_wspd ; } $dataref[ 30 ] = \@prMaxW ; $dataref[ 31 ] = \@wdMaxW ; $dataref[ 32 ] = \@wsMaxW ; $dataref[ 9 ] = \$numMwnd ; last SWITCH1 ; } ; ( $type eq "TTBB" || $type eq "TTDD" ) && do { $k = ${$dataref[ 7 ]} ; # number of Sig Temps # data already inserted into record, overwrite or # append if( $k != $F ) { @prSigT = @{ $dataref[ 19 ] } ; @tpSigT = @{ $dataref[ 20 ] } ; @tdSigT = @{ $dataref[ 21 ] } ; for( $i = 0; $i < $k; $i++ ) { $prSigT[ $i ] = sprintf( "%3.1f", $prSigT[ $i ] ) ; $prSigT{ "$prSigT[ $i ]" } = $i ; } for( $i = 0; $i <= $#pres; $i++ ) { if( defined( $prSigT{ "$pres[ $i ]"} )) { $j = $prSigT{ "$pres[ $i ]" } ; $tpSigT[ $j ] = $t[ $i ] ; $tdSigT[ $j ] = $td[ $i ] ; } else { # append $prSigT[ $k ] = $pres[ $i ] ; $tpSigT[ $k ] = $t[ $i ] ; $tdSigT[ $k ] = $td[ $i ] ; $k++ ; } } while( $k > $sigTLevel ) { $k-- ; pop( @prSigT ) ; pop( @tpSigT ) ; pop( @tdSigT ) ; print "High sigTLevel $stn $type $k\n" ; } $dataref[ 7 ] = \$k ; $dataref[ 19 ] = \@prSigT ; $dataref[ 20 ] = \@tpSigT ; $dataref[ 21 ] = \@tdSigT ; } else { # first data entry $numSigT = $#pres +1 ; $dataref[ 7 ] = \$numSigT ; for( $i = $numSigT ; $i < $sigTLevel; $i++ ) { $pres[ $i ] = $F ; $t[ $i ] = $F ; $td[ $i ] = $F ; } $dataref[ 19 ] = \@pres ; $dataref[ 20 ] = \@t ; $dataref[ 21 ] = \@td ; } $dataref[ 12 ] = \$indicator if( $type eq "TTBB" ) ; last SWITCH1 ; } ; ( $type eq "PPBB" || $type eq "PPDD" ) && do { $k = ${$dataref[ 8 ]} ; # number of Sig Winds # data already inserted into record, overwrite or # append if( $k != $F ) { @htSigW = @{ $dataref[ 22 ] } ; @wdSigW = @{ $dataref[ 23 ] } ; @wsSigW = @{ $dataref[ 24 ] } ; for( $i = 0; $i < $k; $i++ ) { $htSigW[ $i ] = sprintf( "%3.1f", $htSigW[ $i ] ) ; $htSigW{ "$htSigW[ $i ]" } = $i ; } for( $i = 0; $i <= $#ht; $i++ ) { if( defined( $htSigW{ "$ht[ $i ]" } )) { $j = $htSigW{ "$ht[ $i ]" } ; $wsSigW[ $j ] = $wspd[ $i ] ; $wdSigW[ $j ] = $wdir[ $i ] ; } else { # append $htSigW[ $k ] = $ht[ $i ] ; $wsSigW[ $k ] = $wspd[ $i ] ; $wdSigW[ $k ] = $wdir[ $i ] ; $k++ ; } } while( $k > $sigWLevel ) { $k-- ; pop( @htSigW ) ; pop( @wsSigW ) ; pop( @wdSigW ) ; print "High sigWLevel $stn $type $k\n" ; } $dataref[ 8 ] = \$k ; $dataref[ 22 ] = \@htSigW ; $dataref[ 23 ] = \@wdSigW ; $dataref[ 24 ] = \@wsSigW ; } else { # first data entry $numSigW = $#wdir +1 ; $dataref[ 8 ] = \$numSigW ; for( $i = $numSigW; $i < $sigWLevel; $i++ ) { $ht[ $i ] = $F ; $wdir[ $i ] = $F ; $wspd[ $i ] = $F ; } $dataref[ 22 ] = \@ht ; $dataref[ 23 ] = \@wdir ; $dataref[ 24 ] = \@wspd ; } last SWITCH1 ; } ; } } # 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 . "_perl_ua.nc" ; $ncfile = $datadir . "/" . $upperairPrefix . substr( $yyyymmddhh, 0, 8 ) . $upperair . ".nc" ; if( $hourly ) { $ncfile = $datadir . "/" . $upperairPrefix . substr( $yyyymmddhh, 0, 8 ) . "_" . substr( $yyyymmddhh, 8 ) . $upperair . ".nc" ; } # writing to same file return 1 if( $ncfile eq $lastNc ) ; # current time $thetime = time() ; # save current file 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 files 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( $ncid ) ; } } # 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 { my( @rec ) ; for( $i=0; $i<$j; $i++ ) { $rec[$i] = sprintf "%6s%8s%8s%10s%10s%13s", $pres[$i], $ht[$i], $t[$i], $td[$i], $wdir[$i], $wspd[$i] ; } # Output decoded sounding print "Station: $stn, report type: $type, j = $j\n" ; print "Time: $yyyymmddhh\n" ; print "---------------------------------------------------------\n" ; print "Pres Ht Temp DewPt Dir Spd\n" ; print " mb m C C deg m/s \n" ; print "---------------------------------------------------------\n" ; for( $i = 0; $i < $j; $i++ ) { print "$rec[$i]\n" ; } print "\n" ; if( $trop_t ) { print "Tropopause values\n" ; print "---------------------------------------------------------\n" ; # trop_t, trop_td_dep trop_wdir, trop_wspd $trop = sprintf "%6s%15s%10s%10s%13s", $trop_pres, $trop_t, $trop_td_dep, $trop_wdir, $trop_wspd ; print "$trop\n\n" ; } if( $max_wdir ) { # max_pres, max_wdir, max_wspd print "Maximum values\n" ; print "---------------------------------------------------------\n" ; $max = sprintf "%6s%35s%13s", $max_pres, $max_wdir, $max_wspd ; print "$max\n\n" ; } } # end printvars # execute at exit sub atexit { my( $sig ) = @_ ; if( $sig eq "eof" ) { print "eof on STDIN --shutting down\n" ; } elsif( defined( $sig )) { print "Caught SIG$sig --shutting down\n" ; } # open ua.lst, list of reports processed in the last 8 hours. open( LST, ">$datadir/ua.lst" ) || die "could not open $datadir/ua.lst: $!\n" ; select( LST ) ; # remove rpt 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 $Ncfile ( keys %Nets ) { ( $ncid, $recnum, $nctime ) = split( " ", $Nets{ $Ncfile } ) ; print STDOUT "Closing $Ncfile with ncid $ncid\n" ; $status = NetCDF::close( $ncid ) ; print "STDOUT NetCDF::close status = $status\n" if( $status ) ; } close( STDOUT ) ; close( STDERR ) ; exit( 0 ) ; } # end atexit # pad str to correct length sub padstr { ( $str, $len ) = @_ ; my( $size, $i ) ; $size = length( $str ) ; for( $i = $size; $i < $len; $i++ ) { $str .= "\0" ; #print "$str,\n" ; } 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 makedataref { undef( @dataref ) ; $F = 99999 ; $F0 = 99999 ; $F1 = 99999 ; $F2 = 99999 ; $F3 = 99999 ; $F4 = 99999 ; $F5 = 99999 ; $F6 = 99999 ; $F7 = 99999 ; $F8 = 99999 ; $F85 = 99999 ; $F9 = 99999 ; $F10 = 99999 ; $A = \$F ; $S6 = "\0" x $staNameLen ; $AS6 = \$S6 ; @ML = ( 99999 ) x ( $manLevel ) ; @ML0 = ( 99999 ) x ( $manLevel ) ; @ML1 = ( 99999 ) x ( $manLevel ) ; @ML2 = ( 99999 ) x ( $manLevel ) ; @ML3 = ( 99999 ) x ( $manLevel ) ; @ML4 = ( 99999 ) x ( $manLevel ) ; @ML5 = ( 99999 ) x ( $manLevel ) ; @STL = ( 99999 ) x ( $sigTLevel ) ; @STL0 = ( 99999 ) x ( $sigTLevel ) ; @STL1 = ( 99999 ) x ( $sigTLevel ) ; @STL2 = ( 99999 ) x ( $sigTLevel ) ; @SWL = ( 99999 ) x ( $sigWLevel ) ; @SWL0 = ( 99999 ) x ( $sigWLevel ) ; @SWL1 = ( 99999 ) x ( $sigWLevel ) ; @SWL2 = ( 99999 ) x ( $sigWLevel ) ; @TL = ( 99999 ) x ( $mTropNum ) ; @TL0 = ( 99999 ) x ( $mTropNum ) ; @TL1 = ( 99999 ) x ( $mTropNum ) ; @TL2 = ( 99999 ) x ( $mTropNum ) ; @TL3 = ( 99999 ) x ( $mTropNum ) ; @TL4 = ( 99999 ) x ( $mTropNum ) ; @MWL = ( 99999 ) x ( $mWndNum ) ; @MWL0 = ( 99999 ) x ( $mWndNum ) ; @MWL1 = ( 99999 ) x ( $mWndNum ) ; @MWL2 = ( 99999 ) x ( $mWndNum ) ; # default netCDF record structure, contains all vars for the UA reports @dataref = ( \$F0, \$S6, \$F1, \$F2, \$F3, \$F4, \$F5, \$F6, \$F7, \$F8, \$F85, \$F9, \$F10, \@ML0, \@ML1, \@ML2, \@ML3, \@ML4, \@ML5, \@STL0, \@STL1, \@STL2, \@SWL0, \@SWL1, \@SWL2, \@TL0, \@TL1, \@TL2, \@TL3, \@TL4, \@MWL0, \@MWL1, \@MWL2 ) ; } # TTAA type of reports, mandatory levels: surface to 100 mb sub ttaa { my( $TTAA, $stride, $toplevel, $offset, $thermo, $wind ) ; ( $TTAA ) = @_ ; $stride = 18 ; # when winds present in report $toplevel = $TTAA_top_wind_level{ "$indicator" } ; # mandatory levels, sfc thru 100 mb for( $offset = 0 ; $offset < length( $TTAA ) ; $offset += $stride, $j++ ) { $pres[ $j ] = $ht[ $j ] = $t[ $j ] = $td[ $j ] = $wdir[ $j ] = $wspd[ $j ] = $F ; $pres[ $j ] = substr( $TTAA, $offset, 2 ) ; last unless( defined( $mand_ttaa{ "$pres[ $j ]" } ) ) ; $pres[ $j ] = $mand_ttaa{ "$pres[ $j ]" } ; # $stride is set to 12 when no winds are reported $stride = 12 if( $pres[ $j ] < $toplevel ) ; if( substr( $TTAA, $offset+2, 10 ) =~ m#([\d/]{3}) ([\d/]{5}) # ) { $ht[ $j ] = $1 ; dec_thermo( $2 ) ; } else { $ht[ $j ] = "///" ; } if(( $stride == 18 || $pres[ $j ] == 99999 ) && substr( $TTAA, $offset + 12, 6 ) =~ m#([\d/]{5}) # ) { dec_wind( $1 ) ; } if( $pres[ $j ] == 99999 ) { # surface $sfc_pres = $pres[ $j ] = ( $ht[ $j ] < 100 ) ? $ht[ $j ] + 1000 : $ht[ $j ] ; $ht[ $j ] = $elev ; } $ht[ $j ] .= "0" if( $pres[ $j ] <= 500 ) ; # 500 mb and above if( $ht[ $j ] =~ m#/# ) { $ht[ $j ] = $F ; # 1000 mb special case for below sea level } elsif( $pres[ $j ] == 1000 && $pres[ $j ] > $sfc_pres ) { if( $ht[ $j ] > 500 ) { $ht[ $j ] -= 500 ; $ht[ $j ] *= -1 ; } # 925 mb special case for below sea level } elsif( $pres[ $j ] == 925 && $pres[ $j ] > $sfc_pres ) { if( $ht[ $j ] > 500 ) { $ht[ $j ] -= 500 ; $ht[ $j ] *= -1 ; } } elsif( $pres[ $j ] == 925 && $ht[ $j ] < 250 ) { # 925 mb $ht[ $j ] = "1" . $ht[ $j ] ; } elsif( $pres[ $j ] == 850 ) { # 850 mb $ht[ $j ] = "1" . $ht[ $j ] ; } elsif( $pres[ $j ] == 700 ) { # 700 mb if( $ht[ $j ] < 350 ) { $ht[ $j ] = "3" . $ht[ $j ] ; } else { $ht[ $j ] = "2" . $ht[ $j ] ; } } elsif( $pres[ $j ] == 250 && $ht[ $j ] < 5000 ) { # 250 mb $ht[ $j ] = "1" . $ht[ $j ] ; } elsif( $pres[ $j ] =~ /200|150|100$/ ) { # 200, 150, &100 mb $ht[ $j ] = "1" . $ht[ $j ] ; } } # End of loop thru TTAA mandatory levels pop( @pres ) unless( $mand_pres{ "$pres[ $j ]" } ) ; # Tropopause near end of TTAA if( $TTAA !~ / 88999/ && $TTAA =~ m# 88(\d\d\d) ([\d/]{5}) ([\d/]{5}) # ) { $trop_pres = $1 ; $thermo = $2 ; $wind = $3 ; # sets trop_t, trop_td_dep trop_dec_thermo( $thermo ) ; # sets trop_wdir, trop_wspd trop_dec_wind( $wind ) ; } # End of tropopause level in TTAA group # Maximum Wind if( $TTAA !~ / 77999/ && $TTAA =~ m# (66|77)(\d\d\d) ([\d/]{5}) # ) { $max_pres = $2 ; # sets max_wdir, max_wspd max_dec_wind( $3 ) ; } # End of maximum wind level in TTAA group } #end ttaa # TTBB reports, significant levels with respect to temp: surface to 100 mb sub ttbb { my( $TTBB, $offset ) ; ( $TTBB ) = @_ ; $indicator = $F if( $indicator eq '/' ) ; $offset = 0 ; while( $offset < length( $TTBB )) { last unless substr( $TTBB, $offset, 2 ) =~ /00|11|22|33|44|55|66|77|88/; $pres[ $j ] = $ht[ $j ] = $t[ $j ] = $td[ $j ] = $wdir[ $j ] = $wspd[ $j ] = $F ; if( substr( $TTBB, $offset+2, 10 ) =~ /(\d{3}) (\d{5}) / ) { $pres[ $j ] = $1 ; dec_thermo( $2 ) ; $pres[ $j ] = "1" . $pres[ $j ] if( $pres[ $j ] < 100 ) ; if( defined( $mand_pres{ "$pres[ $j ]" } )) { $offset += 12 ; next ; } $pres[ $j ] = sprintf( "%3.1f", $pres[ $j ] ) ; $j++ ; } $offset += 12 ; } # End of TTBB } #end ttbb # TTCC type of reports, mandatory levels: under 100 mb sub ttcc { my( $TTCC, $stride, $toplevel, $offset, $thermo, $wind ) ; ( $TTCC ) = @_ ; $stride = 18 ; # when winds present in report $toplevel = $TTCC_top_wind_level{ "$indicator" } ; if( ! defined( $toplevel )) { print "Undefined Wind termination level: $level, $stn $type ", "Rpt: $rday$rhour Trans: $bday$bhour\n" ; return ; } # mandatory levels, 70 mb thru 1 mb for( $offset = 0 ; $offset < length( $TTCC ) ; $offset += $stride, $j++ ) { $pres[ $j ] = $ht[ $j ] = $t[ $j ] = $td[ $j ] = $wdir[ $j ] = $wspd[ $j ] = $F ; $pres[ $j ] = substr( $TTCC, $offset, 2 ) ; last unless( defined( $mand_ttcc{ "$pres[ $j ]" } ) ) ; # $stride is set to 12 when no winds are reported $stride = 12 if( $pres[ $j ] < $toplevel ) ; if( substr( $TTCC, $offset+2, 10 ) =~ m#([\d/]{3}) ([\d/]{5}) # ) { $ht[ $j ] = $1 ; dec_thermo( $2 ) ; } else { $ht[ $j ] = "///" ; } if( $stride == 18 && substr( $TTCC, $offset +12, 6 ) =~ m#([0-3/][\d/]{4}) # ) { dec_wind( $1 ) ; } if( $ht[ $j ] =~ m#/# ) { $ht[ $j ] = $F ; } else { $ht[ $j ] .= "0" ; } if( $pres[ $j ] == 70 ) { # 70 mb $ht[ $j ] = "1" . $ht[ $j ] ; } elsif( $pres[ $j ] =~ /50|30|20/ ) { # 50,30,20 mb $ht[ $j ] = "2" . $ht[ $j ] ; } else { # 10, 7, 5, 3, 2, 1 mb $ht[ $j ] = "3" . $ht[ $j ] ; } } # end of loop thru TTCC mandatory levels pop( @pres ) unless( $mand_pres{ "$pres[ $j ]" } ) ; # Tropopause near end of TTCC if not found in TTAA group if( $TTCC !~ / 88999/ && $TTCC =~ m# 88(\d\d)(\d) ([\d/]{5}) ([\d/]{5}) # ) { $trop_pres = $1 . "." . $2 ; $thermo = $3 ; $wind = $4 ; $trop_pres += 0.5 ; $trop_pres =~ s#\.\d*## ; # sets trop_t, trop_td_dep trop_dec_thermo( $thermo ) ; # sets trop_wdir, trop_wspd trop_dec_wind( $wind ) ; } # End of tropopause level in TTCC group # Maximum Wind if( $TTCC !~ / 77999/ && $TTCC =~ m# (66|77)(\d\d)(\d) ([\d/]{5}) # ) { $max_pres = $2 . "." . $3 ; max_dec_wind( $4 ) ; $max_pres += 0.5 ; $max_pres =~ s#\.\d*## ; }# End of maximum wind level in TTCC group } #end ttcc # TTDD reports, significant levels with respect to temperature: under 100 mb sub ttdd { my( $TTDD, $offset ) ; ( $TTDD ) = @_ ; $offset = 0 ; while( $offset < length( $TTDD )) { last unless substr( $TTDD, $offset, 2 ) =~ /00|11|22|33|44|55|66|77|88/; $pres[ $j ] = $ht[ $j ] = $t[ $j ] = $td[ $j ] = $wdir[ $j ] = $wspd[ $j ] = $F ; if( substr( $TTDD, $offset +2, 10 ) =~ /(\d{3}) (\d{5}) / ) { $pres[ $j ] = sprintf( "%3.1f", $1 / 10 ) ; dec_thermo( $2 ) ; $j++ ; } $offset += 12 ; } } #end ttdd # PP(BB|DD) reports, significant levels with respect to winds: all levels sub ppbbdd { my( $PP, $offset, $i, $ten_thousand, $thousand ) ; ( $PP ) = @_ ; $offset = 0 ; while( $offset < length( $PP )) { last unless substr( $PP, $offset, 1 ) eq "9" ; $ten_thousand = substr( $PP, $offset +1, 1 ) ; for( $i = 0; $i < 3; $i++ ) { last if(( $thousand = substr( $PP, $offset +2 + $i, 1 )) eq "/" ) ; $pres[ $j ] = $ht[ $j ] = $t[ $j ] = $td[ $j ] = $wdir[ $j ] = $wspd[ $j ] = $F ; $ht[ $j ] = ( $ten_thousand . $thousand . "000" ) * 0.3048 ; next unless $ht[ $j ] ; if( substr( $PP, $offset +6 + $i * 6, 6 ) =~ /(\d{5}) / ) { dec_wind( $1 ) ; } next if( $wdir[ $j ] eq $F || $wspd[ $j ] eq $F ) ; $ht[ $j ] = sprintf( "%3.1f", $ht[ $j ] ) ; $j++ ; } $offset += 6 + $i * 6 ; } } # end ppbbdd sub dec_thermo { my( $thermo ) = @_ ; my( $td_dep ) ; $t[ $j ] = substr( $thermo, 0, 2 ) . "." . substr( $thermo, 2, 1 ) ; $td_dep = substr( $thermo, 3, 2 ) ; if( $t[ $j ] =~ m#/# ) { $t[ $j ] = $F ; } else { $t[ $j ] =~ s/^0// ; if( $t[ $j ] =~ m#[13579]$# ) { $t[ $j ] = "-" . $t[ $j ] ; } } if( $td_dep =~ m#/# ) { $td[ $j ] = $F ; } else { # change to report Dew-point depression instead of Dew-point if( $td_dep <= 50 ) { #$td[ $j ] = $t[ $j ] - $td_dep / 10.0 ; $td[ $j ] = $td_dep / 10.0 ; } else { #$td[ $j ] = $t[ $j ] - ( $td_dep -50.0 ) ; $td[ $j ] = $td_dep -50.0 ; } $td[ $j ] *= 1.0 ; } # Convert to kelvin $t[ $j ] = celsius2kelvin( $t[ $j ] ) ; return ; } # end dec_thermo sub trop_dec_thermo { my( $thermo ) = @_ ; my( $td_dep ) ; $trop_t = substr( $thermo, 0, 2 ) . "." . substr( $thermo, 2, 1 ) ; $td_dep = substr( $thermo, 3, 2 ) ; if( $trop_t =~ m#/# ) { $trop_t = $F ; } else { $trop_t =~ s/^0// ; $trop_t = "-" . $trop_t if( $trop_t =~ m#[13579]$# ) ; } if( $td_dep =~ m#/# ) { $trop_td_dep = $F ; } else { # change to report Dew-point depression instead of Dew-point if( $td_dep <= 50 ) { #$trop_td_dep = $trop_t - $td_dep/10.0 ; $trop_td_dep = $td_dep/10.0 ; } else { #$trop_td_dep = $trop_t - ( $td_dep -50.0 ) ; $trop_td_dep = $td_dep -50.0 ; } $trop_td_dep *= 1.0 ; } # Convert to kelvin $trop_t = celsius2kelvin( $trop_t ) ; } # end trop_dec_thermo # sub dec_wind { my( $wind ) = @_ ; $wdir[ $j ] = substr( $wind, 0, 2 ) ; $wspd[ $j ] = substr( $wind, 2, 3 ) ; if( $wdir[ $j ] =~ m#/# ) { $wdir[ $j ] = $wspd[ $j ] = $F ; } else { $wdir[ $j ] =~ s/^0// ; $wdir[ $j ] .= "0" ; if( $wspd[ $j ] > 500 ) { $wdir[ $j ] += 5 ; $wspd[ $j ] -= 500 ; } } } # end dec_wind # sub trop_dec_wind { my( $wind ) = @_ ; $trop_wdir = substr( $wind, 0, 2 ) ; $trop_wspd = substr( $wind, 2, 3 ) ; if( $trop_wdir =~ m#/# ) { $trop_wdir = $trop_wspd = $F ; } else { $trop_wdir =~ s/^0// ; $trop_wdir .= "0" ; if( $trop_wspd > 500 ) { $trop_wdir += 5 ; $trop_wspd -= 500 ; } $trop_wspd *= 0.5144 if( $knots ) ; } } # end trop_dec_wind # calculate max winds sub max_dec_wind { my( $wind ) = @_ ; $max_wdir = substr( $wind, 0, 2 ) ; $max_wspd = substr( $wind, 2, 3 ) ; if( $max_wdir =~ m#/# ) { $max_wdir = $max_wspd = $F ; } else { $max_wdir =~ s/^0// ; $max_wdir .= "0" ; if( $max_wspd > 500 ) { $max_wdir += 5 ; $max_wspd -= 500 ; } $max_wspd *= 0.5144 if( $knots ) ; } } # end max_dec_wind # returns kelvin given celsius temperature sub celsius2kelvin { my( $celsius ) = @_ ; if( $celsius == $F ) { return $F ; } else { return $celsius + 273.15 ; } } # end celsius2kelvin sub incrementRDAY { if( $themonth =~ /02/ ) { if( $rday < 28 ) { $rday++; return; } } elsif( $themonth =~ /04|06|09|11/ ) { if( $rday < 30 ) { $rday++; return; } } elsif( $themonth =~ /01|03|05|07|08|10|12/ ) { if( $rday < 31 ) { $rday++; return; } } # have to calculate first day of month $thetime = timegm(0, 0, $rhour, $rday, $themonth -1, $thedecade, 0,0,0); $thetime += 3600; ( $ss, $mm, $rhour, $rday, $themonth, $theyear, $wday, $yday, $isdst ) = gmtime( $thetime ) ; $theyear = ( $theyear < 100 ? $theyear : $theyear - 100 ) ; $theyear = "20" . sprintf( "%02d", $theyear ) ; $themonth++ ; $themonth = sprintf( "%02d", $themonth ) ; $rday = sprintf( "%02d", $rday ) ; $rhour = sprintf( "%02d", $rhour ) ; } # end incrementRDAY sub theTime { my( $ss, $mm, $hh, $mday, $mon, $fmon, $year, $fyear, $wday, $yday, $isdst ) ; my( $when ) = @_ ; $mm = 0 ; if( $when eq "synoptic" ) { $mday = substr( $yyyymmddhh, 6, 2 ) ; $hh = substr( $yyyymmddhh, 8, 2 ) ; $fmon = substr( $yyyymmddhh, 4, 2 ) ; $fyear = substr( $yyyymmddhh, 2, 2 ) ; } elsif( $when eq "release" ) { $mday = $rday ; $hh = $rhour ; $fmon = $themonth ; $fyear = $thedecade ; } $time = timegm( 0, $mm, $hh, $mday, $fmon -1, $fyear, 0, 0, 0 ) ; return $time ; } # end theTime