#!/usr/local/bin/perl # PERL program to scan the tropical storm mailng list files and print # relevant information about the current hurricanes. # Copyright (C) 1996 Thomas R. Metcalf # # This software is provided "as is" and is subject to change without # notice. No warranty of any kind is made with regard to this software, # including, but not limited to, the implied warranties of # merchantability and fitness for a particular purpose. The author shall # not be liable for any errors or for direct, indirect, special, # incidental or consequential damages in connection with the furnishing, # performance, or use of this software: use it at your own risk. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this library; if not, write to the Free # Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # #setpriority(0,0,getpriority(0,0)+4); # nice the process &RESET(); %nummonth = ('JAN',1,'FEB',2,'MAR',3,'APR',4,'MAY',5,'JUN',6,'JUL',7,'AUG',8, 'SEP',9,'OCT',10,'NOV',11,'DEC',12); @daymonth = (0,31,28,31,30,31,30,31,31,30,31,30,31); @daymonthleap = (0,31,29,31,30,31,30,31,31,30,31,30,31); @dayyear = (0,31,59,90,120,151,181,212,243,273,304,334); $debug = 1; # set to 1 to get debug print statements $lastline = ""; ($nowsec,$nowmin,$nowhour,$nowmday,$nowmon,$nowyear,$nowwday,$nowyday,$nowisdst) = gmtime; if ($nowyear>=94) {$nowyear+=1900;} else {$nowyear+=2000;} $now = $nowyear + ($nowyday+1 + ($nowhour/24.0) + ($nowmin/1440.))/366.0; @badnames = ("RELOCATED","TEST","TD","TS","NONAME"); while (<>) { chop; s/\cM//g; # Get rid of ctrl-m's s/\cC/NNNN/g; s/\cA/NNNN/g; $doubleline = $lastline." ".$_; switch: { if (/NEXT\s+(ADVISORY|WARNINGS?)\s+AT/i) { # End of message &DUMP(); &RESET(); } if (/NNNN/i) { # End of message &DUMP(); &RESET(); } if (/^\.\.\.NOTICE\.\.\./i) { # End of message &DUMP(); &RESET(); } if (/^\s*REMARKS\:\s*$/i) { # End of message &DUMP(); &RESET(); } if (/^G\.\s+/i && $abcmessage) { # End of message for abc type messages &DUMP(); &RESET(); } if (/^\cC/ || /^\cA/) { # End of message &DUMP(); &RESET(); } if (/unsubscribe/i) { # End of message &DUMP(); &RESET(); } if (eof) { # End of file &DUMP(); &RESET(); } if (/(exercise\s+exercise|exercise\s+simulation|exercise\s+response)|THIS\s+IS\s+A\s+DRILL/i) {$exercise=1;} # WMO Header if (/^((WT|TP|WH|AB)[A-Z][A-Z]\d{1,2})\s+(PGTW|KNHC|PHNL|KGWC|RODN|PHNC|ABRF|KMIA|PHFO)\s+(\d+|DDHHMM)(\s+COR\s*|\s+REL\s*|\s*)$/i) { # Start of Message &DUMP(); &RESET(); if (length($1) == 5) { # Something like WTPN1 --> WTPN01 $wmoheader = substr($1,0,4)."0".substr($1,4,1); } else { $wmoheader = $1; } if ($debug) {print "WMO: $wmoheader\n";} last switch; } if (/(TROPICAL\s+DEPRESSION|TROPICAL\s+STORM|TROPICAL\s+CYCLONE|HURRICANE|TYPHOON)\s+(.*)(\s+|\s*\.\.\.\s*)(SPECIAL\s+|)(FORECAST\/ADVISORY|ADVISORY|DISCUSSION|WARNING|POSITION\s+ESTIMATE)(\s+NUMBER|\s+NR|)/i && $name eq '???') { # start of message $nlen = @namarr = split(/\s+/,$1); if ($nlen==1) { $type = substr($namarr[0],0,3); } elsif ($nlen==2) { $type = substr($namarr[1],0,3); } $nlen = @namarr = split(/\s+/,$2); $name = $namarr[0]; if ($nlen==2) { # Check for a secondary name in parentheses (PGTW) if ($namarr[1]=~/\((.*)\)/) { $secondname = $1; if (&ISDEPRESSION($name) && !&ISDEPRESSION($secondname)) { $temp = $name; # Use the seconday name if its better $name = $secondname; $secondname = $temp; } if ($debug) {print "$name $secondname\n"} } } $name =~ s/\(//; $name =~ s/\)//; if ($debug) {print "$type $name\n";} last switch; } if (/(TROPICAL\s+DEPRESSION|TROPICAL\s+STORM|TROPICAL\s+CYCLONE|HURRICANE|TYPHOON)\s+(.*)\s+WITH(\s+|\s+A\s+)CENTRAL\s+PRESSURE(\s+|\s+OF\s+)\d+\s+HPA/i) { # start of message $nlen = @namarr = split(/\s+/,$1); if ($nlen==1) { $type = substr($namarr[0],0,3); } elsif ($nlen==2) { $type = substr($namarr[1],0,3); } $nlen = @namarr = split(/\s+/,$2); $name = $namarr[0]; if ($nlen==2) { # Check for a secondary name in parentheses (PGTW) if ($namarr[1]=~/\((.*)\)/) { $secondname = $1; if (&ISDEPRESSION($name) && !&ISDEPRESSION($secondname)) { $temp = $name; # Use the seconday name if its better $name = $secondname; $secondname = $temp; } if ($debug) {print "$name $secondname\n"} } } $name =~ s/\(//; $name =~ s/\)//; if ($debug) {print "$type $name\n";} } # forecasts are considered active when the word FORECAST is found. # This is somewhat restrictive, but is necessary as garbled # observations would otherwise look into the forecast data as if # is was an observation. This is avoided since no forecasts are # output in DUMP if there was no valid observation. if (/(FORECAST|OUTLOOK)/i) { $forecast_active=1; } # Get name of type "A. TROPICAL DEPRESSION 01W" #if (/^A\.\s+(TROPICAL\s+DEPRESSION|TROPICAL\s+STORM|TROPICAL\s+CYCLONE|HURRICANE|TYPHOON)\s+(.*)/ && $name eq '???' && !$forecast_active) { # $abcmessage = 1; # flag to check type of message # $nlen = @namarr = split(/\s+/,$1); # if ($nlen==1) { # $type = substr($namarr[0],0,3); # } elsif ($nlen==2) { # $type = substr($namarr[1],0,3); # } # $nlen = @namarr = split(/\s+/,$2); # if ($nlen <= 2) { # To many words means a bad name # $name = $namarr[0]; # if ($nlen==2) { # Check for a secondary name in parentheses (PGTW) # if ($namarr[1]=~/\((.*)\)/) { # $secondname = $1; # if (&ISDEPRESSION($name) && !&ISDEPRESSION($secondname)) { # $temp = $name; # Use the seconday name if its better # $name = $secondname; # $secondname = $temp; # } # if ($debug) {print "A. $name $secondname\n"} # } else {$name='???';} # Two words, but wrong format # } # } # $name =~ s/\(//; # $name =~ s/\)//; # if ($debug) {print "A. $type $name\n";} # last switch; #} if (/AT\s+(\d{6,6})\s+UTC/i && $day eq "???" && $time eq "???" && !$forecast_active) { # time $day = substr($1,0,2); $time = substr($1,2,2); $minute = substr($1,4,2); ($nowsec,$nowmin,$nowhour,$nowmday,$nowmon,$nowyear,$nowwday,$nowyday,$nowisdst) = gmtime(time); $nmonth = $nowmon+1; if ($nowyear > 93) {$year = $nowyear+1900;} else {$year = $nowyear+2000;} ($year,$nmonth) = &GETNMONTH($year,$nmonth,$nowmday,$day); $year2 = substr($year,2,2); } if (/AT\s+(\d{8,8})Z/i && $day eq "???" && $time eq "???" && !$forecast_active) { # time $nmonth = substr($1,0,2); $day = substr($1,2,2); $time = substr($1,4,2); $minute = substr($1,6,2); ($nowsec,$nowmin,$nowhour,$nowmday,$nowmon,$nowyear,$nowwday,$nowyday,$nowisdst) = gmtime(time); if ($nowyear > 93) {$year = $nowyear+1900;} else {$year = $nowyear+2000;} ($year,$junkmonth) = &GETNMONTH($year,$nmonth,$nowmday,$day); $year2 = substr($year,2,2); } # Get time of format "B. 082030Z" or "B. 14/2226Z" #if (/^B\.\s+(\d{2,2})(\/|)(\d{4,4})Z/i && $day eq "???" && $time eq "???" && !$forecast_active && $abcmessage) { # $day = $1; # $time = substr($3,0,2); # $minute = substr($3,2,2); # ($nowsec,$nowmin,$nowhour,$nowmday,$nowmon,$nowyear,$nowwday,$nowyday,$nowisdst) = gmtime(time); # $nmonth = $nowmon+1; # if ($nowyear > 93) {$year = $nowyear+1900;} # else {$year = $nowyear+2000;} # ($year,$nmonth) = &GETNMONTH($year,$nmonth,$nowmday,$day); # $year2 = substr($year,2,2); # if ($debug) {print "B. $year $nmonth $day $time $minute\n"; } # last switch; #} if (/(\d{1,4}|MIDNIGHT|NOON)\s*(Z| AM| PM|)\s*(HST |PST |PDT |MST |MDT |CST |CDT |EST |EDT |AST |ADT |)(SUN|MON|TUE|WED|THU|FRI|SAT)\s+(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)\s+([^ ]*)\s+(\d\d\d\d)/i) { # Time if ("\U$1" eq "MIDNIGHT") { $time = 0; } elsif ("\U$1" eq "NOON") { $time = 12; } else { $timelen = length($1); if ($timelen < 3) {$time = $1; $minute="00";} elsif ($timelen == 3) { $time=substr($1,0,1); $minute=substr($1,1,2); } else {$time=substr($1,0,2); $minute=substr($1,2,2);} } $dow = $4; $month = "\U$5"; $nmonth = $nummonth{$month}; $day = $6; $year = $7; $year2 = substr($year,2,2); if ("\U$2" eq " PM" && $1 < 12) {$time += 12;} if ("\U$2" eq " AM" && $1 == 12) {$time = "00";} if ("\U$3" eq "HST ") {$time += 10;} # convert to UT if ("\U$3" eq "PST ") {$time += 8;} if ("\U$3" eq "MST ") {$time += 7;} if ("\U$3" eq "CST ") {$time += 6;} if ("\U$3" eq "EST ") {$time += 5;} if ("\U$3" eq "AST ") {$time += 4;} if ("\U$3" eq "PDT ") {$time += 7;} if ("\U$3" eq "MDT ") {$time += 6;} if ("\U$3" eq "CDT ") {$time += 5;} if ("\U$3" eq "EDT ") {$time += 4;} if ("\U$3" eq "ADT ") {$time += 3;} while ($time >= 24) {$time -= 24; $day += 1;} if ($year2 % 4 == 0) { if ($day > $daymonthleap[$nmonth]) { $day = $day % $daymonthleap[$nmonth]; $nmonth++; } } else { if ($day > $daymonth[$nmonth]) { $day = $day % $daymonth[$nmonth]; $nmonth++; } } if ($nmonth > 12) {$nmonth=1; ++$year; ++$year2;} if ($debug) {print "$time $dow $month $day $year\n"; } last switch; } # match string of form " 251800Z6 --- 34.7N4 131.5E0" if (/(\D*)(\d*)Z(\d*)(\D*)---(\D*)(\d*\.\d*)( +N| +S|N|S)(\d\D*)(\d*\.\d*)( E| W|E|W)(\d\D*)/i) { # The NW Pacific storm reports have no month in them! So, to # compute the month, I use the the date when this program is run. # This assumes that it is run within 20 days of the date of the # report. ($nowsec,$nowmin,$nowhour,$nowmday,$nowmon,$nowyear,$nowwday,$nowyday,$nowisdst) = gmtime(time); $latstr = $6.$7; $lonstr = $9.$10; $latstr =~ s/ORTH//gi; # NORTH --> N $latstr =~ s/OUTH//gi; # SOUTH --> S $lonstr =~ s/AST//gi; # EAST --> E $lonstr =~ s/EST//gi; # WEST --> W if ($forecast_active) { $nforecast = $#for_day+1; $for_name[$nforecast] = "\U$name"; $for_day[$nforecast] = substr($2,0,2); $for_time[$nforecast] = substr($2,2,2); $for_minute[$nforecast] = substr($2,4,2); ($for_year[$nforecast],$for_nmonth[$nforecast]) = &GETNMONTH($year,$nmonth,$day,$for_day[$nforecast]); $for_year2[$nforecast] = substr($for_year[$nforecast],2,2); $for_lat[$nforecast] = substr($latstr,0,length($latstr)-1); $for_lon[$nforecast] = substr($lonstr,0,length($lonstr)-1); $for_latns[$nforecast] = substr($latstr,length($latstr)-1,1); $for_lonew[$nforecast] = substr($lonstr,length($lonstr)-1,1); if ($debug) {print "Typhoon ".$for_latitude[$nforecast]." ".$for_longitude[$nforecast]." ".$nforecast."\n";} } else { $day = substr($2,0,2); $time = substr($2,2,2); $minute = substr($2,4,2); $nmonth = $nowmon+1; if ($nowyear > 93) {$year = $nowyear+1900;} else {$year = $nowyear+2000;} ($year,$nmonth) = &GETNMONTH($year,$nmonth,$nowmday,$day); $year2 = substr($year,2,2); $latitude = substr($latstr,0,length($latstr)-1); $longitude = substr($lonstr,0,length($lonstr)-1); $latns = substr($latstr,length($latstr)-1,1); $lonew = substr($lonstr,length($lonstr)-1,1); if ($debug) {print "$time $day $nmonth $year $latitude $latns $longitude $lonew\n";} } last switch; } # Get observation of type "INITIAL 25/0300Z 21.6N 166.6W 25 KTS" if (m#INITIAL +(\D*)(\d*)/(\d*)Z(\D*)(\d*\.\d*)( +N| +S|N|S)(\D*)(\d*\.\d*)( E| W|E|W)(\D*)(\d*)(\D*)(KTS|KT|MPH)#i) { $latstr = $5.$6; $lonstr = $8.$9; $latstr =~ s/ORTH//gi; # NORTH --> N $latstr =~ s/OUTH//gi; # SOUTH --> S $lonstr =~ s/AST//gi; # EAST --> E $lonstr =~ s/EST//gi; # WEST --> W $latitude = substr($latstr,0,length($latstr)-1); $longitude = substr($lonstr,0,length($lonstr)-1); $latns = substr($latstr,length($latstr)-1,1); $lonew = substr($lonstr,length($lonstr)-1,1); $winds = $11; if ("\U$13" eq "MPH") {$winds = int($winds*0.869+0.5);} if ($debug) {print "$latitude $latns $longitude $lonew $winds\n";} last switch; } # Get forcasts of type "12HR VT 25/1200Z 22.1N 167.6W 25 KTS" if (m#(\d*)HR VT(\D*)(\d*)/(\d*)Z(\D*)(\d*\.\d*)( N| S|N|S)(\D*)(\d*\.\d*)( E| W|E|W)(\D*)(\d*)(\D*)(KTS|KT|MPH)#i) { $forecast_active = 1; # this is a forecast $latstr = $6.$7; $lonstr = $9.$10; $latstr =~ s/ORTH//gi; # NORTH --> N $latstr =~ s/OUTH//gi; # SOUTH --> S $lonstr =~ s/AST//gi; # EAST --> E $lonstr =~ s/EST//gi; # WEST --> W $nforecast = $#for_day+1; $for_name[$nforecast] = "\U$name"; $for_day[$nforecast] = $3; $timelen = length($4); if ($timelen == 4) { $for_time[$nforecast] = substr($4,0,2); $for_minute[$nforecast] = substr($4,2,2) } elsif ($timelen == 3) { $for_time[$nforecast] = substr($4,0,1); $for_minute[$nforecast] = substr($4,1,2) } elsif ($timelen == 2) { $for_time[$nforecast] = substr($4,0,2); $for_minute[$nforecast] = "00"; } elsif ($timelen == 1) { $for_time[$nforecast] = $4; $for_minute[$nforecast] = "00"; } ($for_year[$nforecast],$for_nmonth[$nforecast]) = &GETNMONTH($year,$nmonth,$day,$for_day[$nforecast]); $for_year2[$nforecast] = substr($for_year[$nforecast],2,2); $for_lat[$nforecast] = substr($latstr,0,length($latstr)-1); $for_lon[$nforecast] = substr($lonstr,0,length($lonstr)-1); $for_latns[$nforecast] = substr($latstr,length($latstr)-1,1); $for_lonew[$nforecast] = substr($lonstr,length($lonstr)-1,1); $for_winds[$nforecast] = $12; if ("\U$14" eq "MPH") {$for_winds[$nforecast] = int($for_winds[$nforecast]*0.869+0.5);} $for_gusts[$nforecast] = "???"; if ($debug) {print "$latstr $lonstr\n";} last switch; } # Get forcasts of type "FORECAST VALID 25/1200Z 22.1N 167.6W " if (m#[FORECAST|OUTLOOK] +VALID(\D*)(\d+)/(\d+)Z(\D*)(\d+\.\d*)( N| S|N|S)(\D*)(\d+\.\d*)( E| W|E|W)#i) { $forecast_active = 1; # this is a forecast $latstr = $5.$6; $lonstr = $8.$9; $latstr =~ s/ORTH//gi; # NORTH --> N $latstr =~ s/OUTH//gi; # SOUTH --> S $lonstr =~ s/AST//gi; # EAST --> E $lonstr =~ s/EST//gi; # WEST --> W $nforecast = $#for_day+1; $for_name[$nforecast] = "\U$name"; $for_day[$nforecast] = $2; $timelen = length($3); if ($timelen == 4) { $for_time[$nforecast] = substr($3,0,2); $for_minute[$nforecast] = substr($2,2,2) } elsif ($timelen == 3) { $for_time[$nforecast] = substr($3,0,1); $for_minute[$nforecast] = substr($3,1,2) } elsif ($timelen == 2) { $for_time[$nforecast] = substr($3,0,2); $for_minute[$nforecast] = "00"; } elsif ($timelen == 1) { $for_time[$nforecast] = $3; $for_minute[$nforecast] = "00"; } ($for_year[$nforecast],$for_nmonth[$nforecast]) = &GETNMONTH($year,$nmonth,$day,$for_day[$nforecast]); $for_year2[$nforecast] = substr($for_year[$nforecast],2,2); $for_lat[$nforecast] = substr($latstr,0,length($latstr)-1); $for_lon[$nforecast] = substr($lonstr,0,length($lonstr)-1); $for_latns[$nforecast] = substr($latstr,length($latstr)-1,1); $for_lonew[$nforecast] = substr($lonstr,length($lonstr)-1,1); if ($debug) {print "$latstr $lonstr\n";} last switch; } if (/CENTER +(|RE)LOCATED +NEAR +(\D*)(\d*\.\d*)( +N| +S|N|S)(\D*)(\d*\.\d*)( +E| +W|E|W) +AT/i && !$forecast_active) { # Position $latstr = $3.$4; $lonstr = $6.$7; $latstr =~ s/ORTH//gi; # NORTH --> N $latstr =~ s/OUTH//gi; # SOUTH --> S $lonstr =~ s/AST//gi; # EAST --> E $lonstr =~ s/EST//gi; # WEST --> W $latitude = substr($latstr,0,length($latstr)-1); $longitude = substr($lonstr,0,length($lonstr)-1); $latns = substr($latstr,length($latstr)-1,1); $lonew = substr($lonstr,length($lonstr)-1,1); if ($debug) {print "$latitude $latns $longitude $lonew\n";} last switch; } if (/\sPOSITION(\D*)(\d*\.\d*)( +N| +S|N|S)(\D*)(\d*\.\d*)( +E| +W|E|W)/i && !$forecast_active) { # Position $latstr = $2.$3; $lonstr = $5.$6; $latstr =~ s/ORTH//gi; # NORTH --> N $latstr =~ s/OUTH//gi; # SOUTH --> S $lonstr =~ s/AST//gi; # EAST --> E $lonstr =~ s/EST//gi; # WEST --> W $latitude = substr($latstr,0,length($latstr)-1); $longitude = substr($lonstr,0,length($lonstr)-1); $latns = substr($latstr,length($latstr)-1,1); $lonew = substr($lonstr,length($lonstr)-1,1); if ($debug) {print "$latitude $latns $longitude $lonew\n";} last switch; } if ($doubleline =~ /NEAR\s+(\d*\.\d*)\s+(NORTH|SOUTH)\s+(\d*\.\d*)\s+(EAST|WEST)/i && !$forecast_active) { $latitude = $1; $latns = substr($2,0,1); if ($debug) {print "*** $latitude $latns\n";} $longitude = $3; $lonew = substr($4,0,1); if ($debug) {print "*** $longitude $lonew\n";} } if ($doubleline =~ /NEAR\s+(\d*\.\d*)\s+(NORTH|SOUTH)\s+(\d*\.\d*)\s+(EAST|WEST)\s+AT\s+(\d{4,6})\s+UTC/i && !$forecast_active) { $latitude = $1; $latns = substr($2,0,1); if ($debug) {print "*** $latitude $latns\n";} $longitude = $3; $lonew = substr($4,0,1); if ($debug) {print "*** $longitude $lonew\n";} if ($nmonth ne "???" && $day ne "???" && $year ne "???") { if (length($5) == 4) { ($year,$nmonth) = &GETNMONTH($year,$nmonth,$day,$day); $year2[$nforecast] = substr($year,2,2); $time = substr($5,0,2); $minute = substr($5,2,2); } elsif (length($5) == 6) { ($year,$nmonth) = &GETNMONTH($year,$nmonth,$day,substr($5,0,2)); $year2[$nforecast] = substr($year,2,2); $day = substr($5,0,2); $time = substr($5,2,2); $minute = substr($5,4,2); } } } if ($doubleline =~ /AT\s+(\d{4,6})\s+UTC\D*NEAR\s+(\d*\.\d*)\s+(NORTH|SOUTH)\s+(\d*\.\d*)\s+(EAST|WEST)/i && !$forecast_active) { $latitude = $2; $latns = substr($3,0,1); if ($debug) {print "*** $latitude $latns\n";} $longitude = $4; $lonew = substr($5,0,1); if ($debug) {print "*** $longitude $lonew\n";} if ($nmonth ne "???" && $day ne "???" && $year ne "???") { if (length($1) == 4) { ($year,$nmonth) = &GETNMONTH($year,$nmonth,$day,$day); $year2[$nforecast] = substr($year,2,2); $time = substr($1,0,2); $minute = substr($1,2,2); } elsif (length($1) == 6) { ($year,$nmonth) = &GETNMONTH($year,$nmonth,$day,substr($1,0,2)); $year2[$nforecast] = substr($year,2,2); $day = substr($1,0,2); $time = substr($1,2,2); $minute = substr($1,4,2); } } } if (/FORECAST\s+POSITION\s+AT\s+(\d{6,6})\s+UTC\s+(|NEAR\s+)(\d*\.\d*)\s+(NORTH|SOUTH)\s+(\d*\.\d*)\s+(EAST|WEST)/i) { $forecast_active=1; # this is a forecast $nforecast = $#for_day+1; $for_name[$nforecast] = "\U$name"; $for_day[$nforecast] = substr($1,0,2); $for_time[$nforecast] = substr($1,2,2); $for_minute[$nforecast] = substr($1,4,2); ($for_year[$nforecast],$for_nmonth[$nforecast]) = &GETNMONTH($year,$nmonth,$day,$for_day[$nforecast]); $for_year2[$nforecast] = substr($for_year[$nforecast],2,2); $for_lat[$nforecast] = $3; $for_lon[$nforecast] = $5; $for_latns[$nforecast] = substr($4,0,1); $for_lonew[$nforecast] = substr($6,0,1); } if (/LATITUDE +(\D*)(\d*\.\d*) +(NORTH|SOUTH)/i && !$forecast_active) { # Position $latitude = $2; $latns = substr($3,0,1); if ($debug) {print "*** $latitude $latns\n";} #last switch; } if (/LONGITUDE +(\D*)(\d*\.\d*) +(WEST|EAST)/i && !$forecast_active) { # Position $longitude = $2; $lonew = substr($3,0,1); if ($debug) {print "*** $longitude $lonew\n";} #last switch; } # Latitude like "C. 10.1N/2" #if (/^C\.\s+(\d*\.\d*)(N|S)/i && !$forecast_active && $abcmessage) { # $latitude = $1; # $latns = substr($2,0,1); # if ($debug) {print "C. $latitude $latns\n";} # last switch; #} # Longitude like "D. 173.5E/6" #if (/^D\.\s+(\d*\.\d*)(E|W)/i && !$forecast_active && $abcmessage) { # $longitude = $1; # $lonew = substr($2,0,1); # if ($debug) {print "D. $longitude $lonew\n";} # last switch; #} if (/(PRESENT +MOVEMENT +.* +OR +)(\D*)(\d*) +DEGREES +AT +(\D*)(\d*) +(KT|KTS)/i && !$forecast_active) { # Motion $course = $3; $speed = $5; if ($debug) {print "$course $speed\n";} last switch; } if (/(MOVEMENT +PAST +.* +HOURS)(\D*)(\d*) +DEGREES +AT +(\D*)(\d*) +(KT|KTS)/i && !$forecast_active) { # Motion $course = $3; $speed = $5; if ($debug) {print "$course $speed\n";} #last switch; } if ($doubleline=~/TOWARD( +THE +|\.\.\.)(|NORTH[ -]+|SOUTH[ -]+|EAST[ -]+|WEST[ -]+) *(|NORTHEAST +|SOUTHEAST +|NORTHWEST +|SOUTHWEST +)(NEAR|AT) +(\d*) +(MPH|KT|KNOT)/i && !$forecast_active) { # Motion $testspeed = $5; $speedunit = $6; $scourse = $2." ".$3; $scourse =~ s/ / /g; $ncourse = @crsarr = split(/ /,$scourse); if ($ncourse > 0) { $cv = 0; $ncourse = 0; foreach $c (@crsarr) { if ($c eq "NORTH") {$cv += 0; ++$ncourse;} elsif ($c eq "SOUTH") {$cv += 180; ++$ncourse;} elsif ($c eq "EAST") {$cv += 90; ++$ncourse;} elsif ($c eq "WEST") {$cv += 270; ++$ncourse;} elsif ($c eq "NORTHEAST") {$cv += 45; ++$ncourse;} elsif ($c eq "SOUTHEAST") {$cv += 135; ++$ncourse;} elsif ($c eq "NORTHWEST") {$cv += 315; ++$ncourse;} elsif ($c eq "SOUTHWEST") {$cv += 225; ++$ncourse;} } if ($ncourse > 0) {$course = int($cv/$ncourse + 0.5);} } if (&ISNUMBER($testspeed)) {$speed = $testspeed;} else {$speed = 0;} if ("\U$speedunit" eq "MPH") {$speed = int($speed*0.869+0.5);} if ($debug) {print "($scourse $course $speed\n";} #last switch; } if ($doubleline =~ /MOVING\s+(|SLOWLY\s+)(|NORTH +|SOUTH +|EAST +|WEST +) *(|NORTHEAST +|SOUTHEAST +|NORTHWEST +|SOUTHWEST +) *(|N +|S +|E +|W +) *(|NE +|SE +|NW +|SW +)(NEAR|AT|AT ABOUT) +(\d*) +(MPH|KT|KNOT)/i && !$forecast_active) { # Motion $scourse = $2." ".$3." ".$4." ".$5; $scourse =~ s/ / /g; $ncourse = @crsarr = split(/ /,$scourse); if ($ncourse > 0) { $cv = 0; $ncourse = 0; foreach $c (@crsarr) { if ($c eq "NORTH") {$cv += 0; ++$ncourse;} elsif ($c eq "SOUTH") {$cv += 180; ++$ncourse;} elsif ($c eq "EAST") {$cv += 90; ++$ncourse;} elsif ($c eq "WEST") {$cv += 270; ++$ncourse;} elsif ($c eq "NORTHEAST") {$cv += 45; ++$ncourse;} elsif ($c eq "SOUTHEAST") {$cv += 135; ++$ncourse;} elsif ($c eq "NORTHWEST") {$cv += 315; ++$ncourse;} elsif ($c eq "SOUTHWEST") {$cv += 225; ++$ncourse;} elsif ($c eq "N") {$cv += 0; ++$ncourse;} elsif ($c eq "S") {$cv += 180; ++$ncourse;} elsif ($c eq "E") {$cv += 90; ++$ncourse;} elsif ($c eq "W") {$cv += 270; ++$ncourse;} elsif ($c eq "NE") {$cv += 45; ++$ncourse;} elsif ($c eq "SE") {$cv += 135; ++$ncourse;} elsif ($c eq "NW") {$cv += 315; ++$ncourse;} elsif ($c eq "SW") {$cv += 225; ++$ncourse;} } if ($ncourse > 0) {$course = int($cv/$ncourse + 0.5);} } if (&ISNUMBER($7)) {$speed = $7;} else {$speed = 0;} if ("\U$8" eq "MPH") {$speed = int($speed*0.869+0.5);} if ($debug) {print "$scourse $course $speed\n";} #last switch; } if (/CENTRAL +PRESSURE(\D*)(\d*) +(MB|HPA)/i && !$forecast_active) { # Pressure $pressure = $2; if ($debug) {print "$pressure\n";} #last switch; } if ($doubleline =~ /WIND(\D*)(\d*)(\D*)(KT|KTS|MPH|KNOT)/i && !($doubleline =~ /REPORT/i) && ($winds eq "???" || $forecast_active)) { if ($forecast_active) { # Assumes that forecast position came first $for_winds[$nforecast] = $2; if (!&ISNUMBER($for_gusts[$nforecast])) { $for_gusts[$nforecast]="???"; } if ("\U$4" eq "MPH") {$for_winds[$nforecast] = int($for_winds[$nforecast]*0.869+0.5);} if ($debug) {print "Forecast Wind: ".$for_winds[$nforecast]."\n";} } else { $winds = $2; if ("\U$4" eq "MPH") {$winds = int($winds*0.869+0.5);} if ($debug) {print "$winds $gusts\n";} } } if (/(MAX\.|MAX|MAXIMUM)\s+(SUSTAINED\s+|)(WIND|WINDS)\s*\.\.\.\s*(\d+)\s*(KT|KTS|MPH|KNOT|KM\/HR)/i && (!$forecast_active)) { $winds = $4; if ("\U$5" eq "MPH") {$winds = int($winds*0.869+0.5);} if ("\U$5" eq "KM/HR") {$winds = int($winds*0.55+0.5);} if ($debug) {print "$winds $gusts\n";} } # Wind like F. T5.0/5.0/D1.5/24HRS (212330) #if (/^F\.\s+T(\d+\.\d+)\/(\d+\.\d+)/ && !$forecast_active && $abcmessage) { # if ($debug) {print "F. $1\n";} # if (&ISNUMBER($1)) { # Dvorak scale # if ($1 < 1.0 && $1 > 0.0) {$winds = $1*25.0;} # elsif ($1 >= 1.0 && $1 <= 1.5) {$winds = 25.0;} # elsif ($1 > 1.5 && $1 <= 2.5) {$winds = $1*10.0+10.0;} # elsif ($1 > 2.5 && $1 <= 4.0) {$winds = $1*20.0-15.0;} # elsif ($1 > 4.0 && $1 <= 7.0) {$winds = $1*25.0-35.0;} # elsif ($1 > 7.0) {$winds = $1*30.0-70.0;} # } # last switch; #} if ($doubleline =~ /GUST(\D*)(\d*)(\D*)(KT|KTS|MPH|KNOT)/i && !($doubleline =~ /REPORT/i) && ($gusts eq "???" || $forecast_active)) { if ($forecast_active) { # Assumes that forecast position came first $for_gusts[$nforecast] = $2; if (!&ISNUMBER($for_winds[$nforecast])) { $for_winds[$nforecast]="???"; } if ("\U$4" eq "MPH") { $for_gusts[$nforecast] = int($for_gusts[$nforecast]*0.869+0.5); } if ($debug) {print "Forecast Gusts: ".$for_gusts[$nforecast]."\n";} } else { $gusts = $2; if ("\U$4" eq "MPH") {$gusts = int($gusts*0.869+0.5);} if ($debug) {print "$winds $gusts\n";} } } if (m#[FORECAST|OUTLOOK] +VALID +(\D*)(\d*)/(\d*)Z +(\D*)(\d*\.\d*)( +N| +S|N|S)(\D*)(\d*\.\d*)( +E| +W|E|W)#i) { # Forecast $forecast_active=1; # this is a forecast $nforecast = $#for_day+1; $for_name[$nforecast] = "\U$name"; $for_day[$nforecast] = $2; $timelen = length($3); if ($timelen == 4) { $for_time[$nforecast] = substr($3,0,2); $for_minute[$nforecast] = substr($3,2,2) } elsif ($timelen == 3) { $for_time[$nforecast] = substr($3,0,1); $for_minute[$nforecast] = substr($3,1,2) } elsif ($timelen == 2) { $for_time[$nforecast] = substr($3,0,2); $for_minute[$nforecast] = "00"; } elsif ($timelen == 1) { $for_time[$nforecast] = $3; $for_minute[$nforecast] = "00"; } ($for_year[$nforecast],$for_nmonth[$nforecast]) = &GETNMONTH($year,$nmonth,$day,$for_day[$nforecast]); $for_year2[$nforecast] = substr($for_year[$nforecast],2,2); $for_lat[$nforecast] = $5; $for_lon[$nforecast] = $8; $for_latns[$nforecast] = $6; $for_lonew[$nforecast] = $9; if ($debug) {print "$5 $6 $8 $9\n";} last switch; } $nothing = 1; } $lastline = $_; } # END OF MAIN PROGRAM ... SUBROUTINES FOLLOW sub ISNUMBER { # Is the argument a number, e.g. 1.0, .1, 1, 1e4, 1.0e4, etc.? # Returns true or false. $_[0] =~ /^\s*(\+|-|)(\d+|\d+\.\d*|\d*\.\d+)(e\d+|)\s*$/i; } sub RESET { # Reset global variables if ($debug) {print "RESET\n";} # The following hold data for the actual observation $time="???"; $minute="00"; $dow="???"; $month="???"; $nmonth="???"; $day="???"; $year="???"; $latitude="???"; $longitude="???"; $latns="???"; $lonew="???"; $course="???"; $speed="???"; $pressure="???"; $winds="???"; $gusts="???"; $name="???"; $type="???"; $wmoheader="???"; @for_name="???"; # The for_ arrays hold the data for the forecasts @for_time="???"; @for_minutes="00"; @for_nmonth="???"; @for_day="???"; @for_year="???"; @for_year2="???"; @for_lat="???"; @for_lon="???"; @for_latns="???"; @for_lonew="???"; @for_winds="???"; @for_gusts="???"; $nforecast=0; $forecast_active = 0; # Flag to check whether data is a FORECAST $abcmessage = 0; # Flag to check if message is of the A., B. etc. type $exercise=0; # Flag to check if message is an exercise } sub DUMP { # Check that name is no in the bad list $name =~ s/\(//; $name =~ s/\)//; $name = "\U$name"; $metaname = $name; $metaname =~ s/(\W)/\\\1/g; # make any metacharacters literal $nbadname = grep(/^$metaname$/i,@badnames); # Dump the actual data to STDOUT if (&ISNUMBER($year) && &ISNUMBER($nmonth) && &ISNUMBER($day) && &ISNUMBER($time) && &ISNUMBER($latitude) && &ISNUMBER($longitude) && $latns ne "???" && $latns=~/\w+/ && $lonew ne "???" && $lonew=~/\w+/ && $name ne "???" && $name=~/\w+/ && $nbadname<=0 && $name ne "EXERCISE" && $wmoheader ne "???" && $type ne "???" && $type=~/\w+/ && !$exercise) { $doy = $dayyear[$nmonth-1]+$day; if ($nmonth > 2 && ($year % 4) == 0) {$doy++;} # leap year if (!&ISNUMBER($minute)) {$minute="00";} if ($minute < 0) {$minute = -$minute;} if ($minute > 60) {$minute = "00";} $ddate = $year + ($doy + $time/24.0 + $minute/1440.0)/366.0; if (&ISDEPRESSION($name)) {$name = &CHECKDEPNAME($name);} if (!&ISNUMBER($winds)) {$winds = "???";} if (!&ISNUMBER($gusts)) {$gusts = "???";} if (&ISNUMBER($winds) && &ISNUMBER($gusts) && $gusts < $winds) {$gusts = "???";} if (!&ISNUMBER($speed)) {$speed = "???";} if (!&ISNUMBER($course)) {$course = "???";} $outstr = "$year $nmonth $day $time.$minute UT $latitude $latns $longitude $lonew ${name}-$year2 $course T $speed kt $pressure mb $winds $gusts kt $type ACT $ddate $wmoheader $now"; $outstr =~ s/ / /g; # print only if the data is older than now plus twleve hours if ($now+12.1*0.00011385 > $ddate) { print "$outstr\n"; } # Now dump the forecast data to STDOUT. Note: No forecasts are ouput if # there was no actual observation. This is a safety feature to ensure # that garbled messages are not output. if ($#for_year < $#for_winds) { # How many forecasts? $nforecast=$#for_year } else { $nforecast=$#for_winds } for ($i=1; $i<=$nforecast; $i++) { if (&ISNUMBER($for_year[$i]) && &ISNUMBER($for_nmonth[$i]) && &ISNUMBER($for_day[$i]) && &ISNUMBER($for_time[$i]) && &ISNUMBER($for_lat[$i]) && &ISNUMBER($for_lon[$i]) && $for_latns[$i] ne "???" && $for_latns[$i]=~/\w+/ && $for_lonew[$i] ne "???" && $for_lonew[$i]=~/\w+/ && &ISNUMBER($for_winds[$i]) && $for_name[$i] ne "???" && $for_name[$i]=~/\w+/ && !$exercise) { $for_name[$i] =~ s/\(//; $for_name[$i] =~ s/\)//; if (&ISDEPRESSION($for_name[$i])) {$for_name[$i] = &CHECKDEPNAME($for_name[$i]);} $foryear = $for_year[$i]; $foryear2 = $for_year2[$i]; $fornmonth = $for_nmonth[$i]; $forday = $for_day[$i]; $fortime = $for_time[$i]; $forminute = $for_minute[$i]; if (&ISNUMBER($forminute)) { $forminute = $for_minute[$i];} else { $forminute = "00";} if ($forminute < 0) {$forminute = -$forminute;} if ($forminute > 60) {$forminute = "00";} $forlat = $for_lat[$i]; $forlon = $for_lon[$i]; $forlatns = $for_latns[$i]; $forlonew = $for_lonew[$i]; $forwinds = $for_winds[$i]; $forgusts = $for_gusts[$i]; $forname = $for_name[$i]; $fordoy = $dayyear[$fornmonth-1]+$forday; if ($fornmonth > 2 && ($foryear % 4) == 0) {$fordoy++;} # leap year $forddate = $foryear + ($fordoy + $fortime/24.0 + $forminute/1440.0)/366.0; if (!&ISNUMBER($forwinds)) {$forwinds = "???";} if (!&ISNUMBER($forgusts)) {$forgusts = "???";} if (&ISNUMBER($forwinds) && &ISNUMBER($forgusts) && $forgusts < $forwinds) {$forgusts = "???";} $outstr = "$foryear $fornmonth $forday $fortime.$forminute UT $forlat $forlatns $forlon $forlonew ${forname}-$foryear2 ??? T ??? kt ??? mb $forwinds $forgusts kt ??? FOR $forddate $wmoheader $ddate"; $outstr =~ s/ / /g; # Print if the forecast is no more than 84 hours (72+12) after now if ($now+84.1*0.00011385 > $forddate) { print "$outstr\n"; } } } } } sub GETNMONTH { local($nowyear,$nowmonth,$nowday,$day) = @_; # Set the month number given the day of month and the current year, # current month, and current day. Returns adjusted year and month. # $nowyear is like 1994 # $nowmonth is like 8 (for August) # $nowday is like 31 # $day is like 31 # Use same month as now if within +7 or -20 days from now. Not symmetric # because there is not likely to be a time far in the future (the # longest forecast should be 3 days), but there is likely to be a time # in the past (old observation). local($year,$month) = ($nowyear,$nowmonth); if (($nowday-$day) > 20) { # Is a month border crossed forwards? $month++; } if ($month > 12) { $year++; $month=1; } if (($day-$nowday) > 7) { # Is a month border crossed backwards? $month--; } if ($month < 1) { $year--; $month=12; } if ($debug) {print "GETNMONTH: ",$nowyear," ",$nowmonth," ",$nowday," ",$year," ",$month," ",$day,"\n";} ($year,$month); # return values } sub ISDEPRESSION { # Check if a name looks like a tropical depression name local($newname) = @_; local($junk) = 0; local($HyphenNumNew) = 0; $HyphenNumNew = split(/\-/,$newname); # How many hyphens in the new name? if ($HyphenNumNew > 2 || $newname =~ /\d+/ || $newname =~ /[^a-z0-9 -]/i || $newname =~ /^\d+[A-Z|]/i || $newname =~ /^one$/i || $newname =~ /^one\-/i || $newname =~ /^two$/i || $newname =~ /^two\-/i || $newname =~ /^three$/i || $newname =~ /^three\-/i || $newname =~ /^four$/i || $newname =~ /^four\-/i || $newname =~ /^five$/i || $newname =~ /^five\-/i || $newname =~ /^six$/i || $newname =~ /^six\-/i || $newname =~ /^seven$/i || $newname =~ /^seven\-/i || $newname =~ /^eight$/i || $newname =~ /^eight\-/i || $newname =~ /^nine$/i || $newname =~ /^nine\-/i || $newname =~ /^ten$/i || $newname =~ /^ten\-/i || $newname =~ /^eleven$/i || $newname =~ /^eleven\-/i || $newname =~ /^twelve$/i || $newname =~ /^twelve\-/i || $newname =~ /^thirteen$/i || $newname =~ /^thirteen\-/i || $newname =~ /^fourteen$/i || $newname =~ /^fourteen\-/i || $newname =~ /^fifteen$/i || $newname =~ /^fifteen\-/i || $newname =~ /^sixteen$/i || $newname =~ /^sixteen\-/i || $newname =~ /^seventeen$/i || $newname =~ /^seventeen\-/i || $newname =~ /^eighteen$/i || $newname =~ /^eighteen\-/i || $newname =~ /^nineteen$/i || $newname =~ /^nineteen\-/i || $newname =~ /^twenty\-/i || # is there a dash after twenty etc.? $newname =~ /^twenty$/i || $newname =~ /^thirty\-/i || $newname =~ /^thirty$/i || $newname =~ /^fourty\-/i || $newname =~ /^fourty$/i || $newname =~ /^forty\-/i || $newname =~ /^forty$/i || $newname =~ /^fifty\-/i || $newname =~ /^fifty$/i || $newname =~ /^sixty\-/i || $newname =~ /^sixty$/i || $newname =~ /^seventy\-/i || $newname =~ /^seventy$/i || $newname =~ /^eighty\-/i || $newname =~ /^eighty$/i || $newname =~ /^ninety\-/i || $newname =~ /^ninety$/i) {$junk=1;} else {$junk=0;} $junk; } sub CHECKDEPNAME { local($name) = @_[0]; $name =~ s/^ONE\-/01/i; $name =~ s/^ONE/01/i; $name =~ s/^TWO\-/02/i; $name =~ s/^TWO/02/i; $name =~ s/^THREE\-/03/i; $name =~ s/^THREE/03/i; $name =~ s/^FIVE\-/05/i; $name =~ s/^FIVE/05/i; $name =~ s/^TEN\-/10/i; $name =~ s/^TEN/10/i; $name =~ s/^ELEVEN\-/11/i; $name =~ s/^ELEVEN/11/i; $name =~ s/^TWELVE\-/12/i; $name =~ s/^TWELVE/12/i; $name =~ s/^THIRTEEN\-/13/i; $name =~ s/^THIRTEEN/13/i; $name =~ s/^FOURTEEN\-/14/i; $name =~ s/^FOURTEEN/14/i; $name =~ s/^FOUR\-/04/i; $name =~ s/^FOUR/04/i; $name =~ s/^FIFTEEN\-/15/i; $name =~ s/^FIFTEEN/15/i; $name =~ s/^SIXTEEN\-/16/i; $name =~ s/^SIXTEEN/16/i; $name =~ s/^SIX\-/06/i; $name =~ s/^SIX/06/i; $name =~ s/^SEVENTEEN\-/17/i; $name =~ s/^SEVENTEEN/17/i; $name =~ s/^SEVEN\-/07/i; $name =~ s/^SEVEN/07/i; $name =~ s/^EIGHTEEN\-/18/i; $name =~ s/^EIGHTEEN/18/i; $name =~ s/^EIGHT\-/08/i; $name =~ s/^EIGHT/08/i; $name =~ s/^NINETEEN\-/19/i; $name =~ s/^NINETEEN/19/i; $name =~ s/^NINE\-/09/i; $name =~ s/^NINE/09/i; $name =~ s/^TWENTY\-ONE\-/21/i; $name =~ s/^TWENTY\-ONE/21/i; $name =~ s/^TWENTY\-TWO\-/22/i; $name =~ s/^TWENTY\-TWO/22/i; $name =~ s/^TWENTY\-THREE\-/23/i; $name =~ s/^TWENTY\-THREE/23/i; $name =~ s/^TWENTY\-FOUR\-/24/i; $name =~ s/^TWENTY\-FOUR/24/i; $name =~ s/^TWENTY\-FIVE\-/25/i; $name =~ s/^TWENTY\-FIVE/25/i; $name =~ s/^TWENTY\-SIX\-/26/i; $name =~ s/^TWENTY\-SIX/26/i; $name =~ s/^TWENTY\-SEVEN\-/27/i; $name =~ s/^TWENTY\-SEVEN/27/i; $name =~ s/^TWENTY\-EIGHT\-/28/i; $name =~ s/^TWENTY\-EIGHT/28/i; $name =~ s/^TWENTY\-NINE\-/29/i; $name =~ s/^TWENTY\-NINE/29/i; $name =~ s/^TWENTY\-/20/i; $name =~ s/^TWENTY/20/i; $name =~ s/^THIRTY\-ONE\-/31/i; $name =~ s/^THIRTY\-ONE/31/i; $name =~ s/^THIRTY\-TWO\-/32/i; $name =~ s/^THIRTY\-TWO/32/i; $name =~ s/^THIRTY\-THREE\-/33/i; $name =~ s/^THIRTY\-THREE/33/i; $name =~ s/^THIRTY\-FOUR\-/34/i; $name =~ s/^THIRTY\-FOUR/34/i; $name =~ s/^THIRTY\-FIVE\-/35/i; $name =~ s/^THIRTY\-FIVE/35/i; $name =~ s/^THIRTY\-SIX\-/36/i; $name =~ s/^THIRTY\-SIX/36/i; $name =~ s/^THIRTY\-SEVEN\-/37/i; $name =~ s/^THIRTY\-SEVEN/37/i; $name =~ s/^THIRTY\-EIGHT\-/38/i; $name =~ s/^THIRTY\-EIGHT/38/i; $name =~ s/^THIRTY\-NINE\-/39/i; $name =~ s/^THIRTY\-NINE/39/i; $name =~ s/^THIRTY\-/30/i; $name =~ s/^THIRTY/30/i; $name; }