add Geo::TAF changes from Robin Johnson + CTY1902
authorDirk Koopman <djk@tobit.co.uk>
Thu, 26 Feb 2009 11:11:53 +0000 (11:11 +0000)
committerDirk Koopman <djk@tobit.co.uk>
Thu, 26 Feb 2009 11:11:53 +0000 (11:11 +0000)
Geo/TAF/Makefile.PL
Geo/TAF/README
Geo/TAF/TAF.pm
data/cty.dat
data/prefix_data.pl
perl/Version.pm

index 48e01cbd0927d9a411b1d9870738f6c44147d6e4..8658fed3275b7f5add4cf66e62f747e24ecfb333 100644 (file)
@@ -7,6 +7,12 @@ WriteMakefile(
     'VERSION_FROM'     => 'TAF.pm', # finds $VERSION
     'PREREQ_PM'                => {}, # e.g., Module::Name => 1.1
     ($] >= 5.005 ?    ## Add these new keywords supported since 5.005
+# Stuff for >= 5.005
       (ABSTRACT_FROM => 'TAF.pm', # retrieve abstract from module
-       AUTHOR     => 'Dirk Koopman <djk@localdomain>') : ()),
+       AUTHOR     => 'Dirk Koopman <djk@tobit.co.uk>, Robin H. Johnson, L<mailto:robbat2@gentoo.org>')
+# Stuff for < 5.005
+         :
+         ()
+# FIN
+         ),
 );
index a12b490d19799b46a3c37cbc9fc8af9f80fe1896..c4a1ffe972fa60d1c845554c68138a6e040c87e0 100644 (file)
@@ -28,6 +28,7 @@ DEPENDENCIES
 COPYRIGHT AND LICENCE
 
 Copyright (C) 2003 Dirk Koopman G1TLH
+Portions Copyright (C) 2009 Robin H. Johnson
 
 This library is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself. 
index 155f29443fbb8124feee9fc35298ac369040b0c4..aa10a78d1f239058ef47625ca0ddfde0cea4cc1a 100644 (file)
@@ -13,36 +13,41 @@ use 5.005;
 use strict;
 use vars qw($VERSION);
 
-$VERSION = '1.04-1';
+$VERSION = '1.05';
 
 
 my %err = (
-                  '1' => "No valid ICAO designator",
-                  '2' => "Length is less than 10 characters",
-                  '3' => "No valid issue time",
-                  '4' => "Expecting METAR or TAF at the beginning", 
-                 );
+               '1' => "No valid ICAO designator",
+               '2' => "Length is less than 10 characters",
+               '3' => "No valid issue time",
+               '4' => "Expecting METAR or TAF at the beginning",
+               );
 
 my %clt = (
-                  SKC => 1,
-                  CLR => 1,
-                  NSC => 1,
-                  BLU => 1,
-                  WHT => 1,
-                  GRN => 1,
-                  YLO => 1,
-                  AMB => 1,
-                  RED => 1,
-                  BKN => 1,
-                  NIL => 1,
-                 );
+               SKC             => 1,
+               CLR     => 1,
+               NSC     => 1,
+               NSD     => 1,
+               'BLU+'  => 1,
+               BLU             => 1,
+               WHT     => 1,
+               GRN     => 1,
+               YLO     => 1,
+               YLO1    => 1,
+               YLO2    => 1,
+               AMB     => 1,
+               RED     => 1,
+               BKN     => 1,
+               NIL     => 1,
+               '///'   => 1,
+               );
 
 my %ignore = (
-                         AUTO => 1,
-                         COR => 1,
-                        );
+               'AUTO' => 1, # Automatic weather system in usage
+               'COR'  => 1, # Correction issued (US)
+               'CCA'  => 1, # Correction issued (EU)
+               );
 
-                  
 # Preloaded methods go here.
 
 sub new
@@ -58,7 +63,7 @@ sub metar
        my $self = shift;
        my $l = shift;
        return 2 unless length $l > 10;
-       $l = 'METAR ' . $l unless $l =~ /^\s*(?:METAR|TAF)\s/i;
+       $l = 'METAR ' . $l unless $l =~ /^\s*(?:METAR|TAF|SPECI)\s/i;
        return $self->decode($l);
 }
 
@@ -67,7 +72,16 @@ sub taf
        my $self = shift;
        my $l = shift;
        return 2 unless length $l > 10;
-       $l = 'TAF ' . $l unless $l =~ /^\s*(?:METAR|TAF)\s/i;
+       $l = 'TAF ' . $l unless $l =~ /^\s*(?:METAR|TAF|SPECI)\s/i;
+       return $self->decode($l);
+}
+
+sub speci
+{
+       my $self = shift;
+       my $l = shift;
+       return 2 unless length $l > 10;
+       $l = 'SPECI ' . $l unless $l =~ /^\s*(?:METAR|TAF|SPECI)\s/i;
        return $self->decode($l);
 }
 
@@ -82,6 +96,7 @@ sub as_strings
        my $self = shift;
        my @out;
        for (@{$self->{chunks}}) {
+               next if $_->type =~ m/^Geo::TAF::[A-Z]+::IGNORE$/;
                push @out, $_->as_string;
        }
        return @out;
@@ -97,7 +112,7 @@ sub as_chunk_strings
 {
        my $self = shift;
        my @out;
-       
+
        for (@{$self->{chunks}}) {
                push @out, $_->as_chunk;
        }
@@ -117,7 +132,7 @@ sub raw
 
 sub is_weather
 {
-       return $_[0] =~ /^\s*(?:(?:METAR|TAF)\s+)?[A-Z]{4}\s+\d{6}Z?\s+/;
+       return $_[0] =~ /^\s*(?:(?:METAR|TAF|SPECI)\s+)?[A-Z]{4}\s+\d{6}Z?\s+/;
 }
 
 sub errorp
@@ -135,33 +150,25 @@ sub decode
        my $l = uc shift;
 
        $l =~ s/=$//;
-       
-    # Fix dodgy TAFs.
-    # TAFs like this are non-standard, but I have seen these examples in
-    # real life, and that is, after all, what this code needs to cope with. [DW]
-    $l =~ s/\b(BECMG)(\d{4})\b/$1 $2/g;        # Some people can't use a space bar
-    $l =~ s/\bTEMP0\b/TEMPO/g;         # Some people use zero instead of a letter O
-    $l =~ s/\bBEC\b/BECMG/g;           # And some people can't spell BECMG
-    
+
        my @tok = split /\s+/, $l;
 
        $self->{line} = join ' ', @tok;
-       
-       # do we explicitly have a METAR or a TAF
+
+       # Count how many problems we have
+       $self->{decode_failures} = 0;
+
+       # do we explicitly have a METAR, SPECI or TAF
        my $t = shift @tok;
-       if ($t eq 'TAF') {
-               $self->{taf} = 1;
-       } elsif ($t eq 'METAR') {
-               $self->{taf} = 0;
+       if ($t =~ /^(TAF|METAR|SPECI)$/) {
+               $self->{report_type} = $t;
+               $self->{taf} = $t eq 'TAF';
        } else {
            return 4;
        }
 
        # next token is the ICAO dseignator
        $t = shift @tok;
-    # ignore AMD (amendment) token if present.
-    $t = shift @tok if $t eq 'AMD';
-
        if ($t =~ /^[A-Z]{4}$/) {
                $self->{icao} = $t;
        } else {
@@ -170,9 +177,6 @@ sub decode
 
        # next token is an issue time
        $t = shift @tok;
-    # ignore AMD (amendment) token if present.
-    $t = shift @tok if $t eq 'AMD';
-
        if (my ($day, $time) = $t =~ /^(\d\d)(\d{4})Z?$/) {
                $self->{day} = $day;
                $self->{time} = _time($time);
@@ -193,107 +197,175 @@ sub decode
        # we are now into the 'list' of things that can repeat over and over
 
        my @chunk = (
-                                $self->_chunk('HEAD', $self->{taf} ? 'TAF' : 'METAR', 
-                                                          $self->{icao}, $self->{day}, $self->{time})
+                                $self->_chunk('HEAD', $self->{report_type},
+                                                          $self->{icao}, $self->{day}, $self->{time}),
+                                $self->_chunk('BLOCK'), # new block always now
                                );
-       
-       push @chunk, $self->_chunk('VALID', $self->{valid_day}, $self->{valid_from}, 
-                                                          $self->{valid_to}) if $self->{valid_day};
+
+       if($self->{valid_day}) {
+               push @chunk, $self->_chunk('VALID');
+               push @chunk, $self->_chunk('PERIOD', $self->{valid_from}, $self->{valid_to}, $self->{valid_day}, );
+               push @chunk, $self->_chunk('BLOCK'); # new block always now
+       }
+
+       my ($c0, $c1, $expect, @remark_buffer, $ignore_no_length_change);
+       my ($day, $time, $percent, $sort, $dir);
+       my ($wdir, $spd, $gust, $unit);
+       my ($viz, $vunit);
+       my ($m, $p);
 
        while (@tok) {
                $t = shift @tok;
-               
+               # Count number of items in chunk, and use to determine if we could not
+               # decode.
+               $c0 = $#chunk;
+               # If this is NOT set, and the count doesn't change, we failed a decode
+               $ignore_no_length_change = 0;
+
+               # This is just so the rest patches easier
+               if(!defined($t)) {
+
                # temporary 
-               if ($t eq 'TEMPO' || $t eq 'BECMG') {
-                       
-                       # next token may be a time if it is a taf
-                       my ($from, $to);
-                       if (@tok && (($from, $to) = $tok[0] =~ /^(\d\d)(\d\d)$/)) {
-                               if ($self->{taf} && $from >= 0 && $from <= 24 && $to >= 0 && $to <= 24) {
-                                       shift @tok;
-                                       $from = _time($from * 100);
-                                       $to = _time($to * 100);
-                               } else {
-                                       undef $from;
-                                       undef $to;
-                               }
+               } elsif ($t eq 'TEMPO' || $t eq 'TEMP0' || $t eq 'BECMG') {
+                       # TEMPO occurs with both a oh and a zero, in some bad automated hardware
+                       $t = 'TEMPO' if $t eq 'TEMP0';
+                       push @chunk, $self->_chunk('BLOCK'); # new block always now
+                       push @chunk, $self->_chunk($t);
+                       $expect = 'PERIOD';
+
+               # time range
+               } elsif ($expect eq 'PERIOD' || $t =~ /^(\d\d)(\d\d)\/(\d\d)(\d\d)$/) {
+                       undef $expect;
+                       # next token may be a period if it is a taf
+                       # Two possible formats:
+                       # XXYY = hour XX to hour YY (but only valid after TEMPO/BECMG)
+                       # AABB/CCDD = day aa hour bb TO day cc hour dd (after TEMPO/BECMG, but ALSO valid after HEAD)
+                       my ($from_time, $to_time, $from_day, $to_day);
+                       my ($got_time, $got_day);
+                       if (($from_time, $to_time) = $t =~ /^(\d\d)(\d\d)$/) {
+                               $got_time = 1;
+                       } elsif (($from_day, $from_time, $to_day, $to_time) = $t =~ /^(\d\d)(\d\d)\/(\d\d)(\d\d)$/) {
+                               $got_time = $got_day = 1;
+                       }
+                       if ($got_time && $self->{taf} && $from_time >= 0 && $from_time <= 24 && $to_time >= 0 && $to_time <= 24) {
+                               $from_time = _time($from_time * 100);
+                               $to_time = _time($to_time * 100);
+                       } else {
+                               undef $from_time;
+                               undef $to_time;
+                               undef $got_time;
                        }
-                       push @chunk, $self->_chunk($t, $from, $to);                     
+                       if($got_time && $got_day && $from_day >= 1 && $from_day <= 31 && $to_day >= 1 && $to_day <= 31) {
+                               # do not shift tok, we did it already
+                       } else {
+                               undef $from_day;
+                               undef $to_day;
+                               undef $got_day;
+                       }
+                       push @chunk, $self->_chunk('PERIOD', $from_time, $to_time, $from_day, $to_day) if $got_time;
 
                # ignore
                } elsif ($ignore{$t}) {
-                       ;
-                       
-        # no sig weather
+                       push @chunk, $self->_chunk('IGNORE', $t);
+
+               # no sig weather
                } elsif ($t eq 'NOSIG' || $t eq 'NSW') {
                        push @chunk, $self->_chunk('WEATHER', 'NOSIG');
 
+               # // means the automated system cannot determine the precipiation at all
+               } elsif ($t eq '//') {
+                       push @chunk, $self->_chunk('WEATHER', $t);
+
                # specific broken on its own
                } elsif ($t eq 'BKN') {
                        push @chunk, $self->_chunk('WEATHER', $t);
-                       
-        # other 3 letter codes
+
+               # wind shear (is followed by a runway designation)
+               } elsif ($t eq 'WS') {
+                       push @chunk, $self->_chunk('WEATHER', $t);
+
+               # other 3 letter codes
                } elsif ($clt{$t}) {
                        push @chunk, $self->_chunk('CLOUD', $t);
-                       
+
                # EU CAVOK viz > 10000m, no cloud, no significant weather
                } elsif ($t eq 'CAVOK') {
                        $self->{viz_dist} ||= ">10000";
                        $self->{viz_units} ||= 'm';
                        push @chunk, $self->_chunk('CLOUD', 'CAVOK');
 
-        # AMD group (end for now)
-        } elsif ($t eq 'AMD') {
-            last;
-
-        # RMK group (end for now)
-               } elsif ($t eq 'RMK') {
-                       last;
-
-        # from
-        } elsif (my ($time) = $t =~ /^FM(\d\d\d?\d?)Z?$/ ) {
-           $time .= '0' while length($time) < 4;
-                       push @chunk, $self->_chunk('FROM', _time($time));
-
-        # Until
-        } elsif (($time) = $t =~ /^TI?LL?(\d\d\d?\d?)Z?$/ ) {
-           $time .= '0' while length($time) < 4;
-                       push @chunk, $self->_chunk('TIL', _time($time));
-
-        # probability
-        } elsif (my ($percent) = $t =~ /^PROB(\d\d)$/ ) {
-
-                       # next token may be a time if it is a taf
-                       my ($from, $to);
-                       if (@tok && (($from, $to) = $tok[0] =~ /^(\d\d)(\d\d)$/)) {
-                               if ($self->{taf} && $from >= 0 && $from <= 24 && $to >= 0 && $to <= 24) {
-                                       shift @tok;
-                                       $from = _time($from * 100);
-                                       $to = _time($to * 100);
-                               } else {
-                                       undef $from;
-                                       undef $to;
-                               }
+               # RMK group (end for now)
+               } elsif ($t eq 'RMK' or $t eq 'RKM') {
+                       #push @chunk, $self->_chunk('RMK', join(' ',@tok));
+                       $self->{in_remark} = $c0;
+                       push @chunk, $self->_chunk('BLOCK'); # new block always now
+                       #last;
+
+               # from
+               } elsif (($day,$time) = $t =~ /^FM(\d\d)?(\d\d\d\d)Z?$/ ) {
+                       push @chunk, $self->_chunk('BLOCK'); # new block always now
+                       push @chunk, $self->_chunk('FROM', _time($time), $day);
+
+               # Until
+               } elsif (($day,$time) = $t =~ /^TL(\d\d)?(\d\d\d\d)Z?$/ ) {
+                       push @chunk, $self->_chunk('BLOCK'); # new block always now
+                       push @chunk, $self->_chunk('TIL', _time($time), $day);
+
+               # At
+               # Seen at http://stoivane.iki.fi/metar/
+               } elsif (($day,$time) = $t =~ /^AT(\d\d)?(\d\d\d\d)Z?$/ ) {
+                       push @chunk, $self->_chunk('BLOCK'); # new block always now
+                       push @chunk, $self->_chunk('AT', _time($time), $day);
+
+               # probability
+               } elsif (($percent) = $t =~ /^PROB(\d\d)$/ ) {
+                       push @chunk, $self->_chunk('BLOCK'); # new block always now
+                       $expect = 'PERIOD';
+                       push @chunk, $self->_chunk('PROB', $percent);
+
+               # runway
+               } elsif (($sort, $dir) = $t =~ /^(RWY?|LDG|TKOF|R)(\d\d\d?[RLC]?)$/ ) {
+                       # Special case,
+                       # there is a some broken METAR hardware out there that codes:
+                       # 'RWY01 /0100VP2000N'
+                       # TODO: include the full regex here
+                       if($tok[0] =~ /^\/[MP]?\d{4}/) {
+                               $t .= shift @tok;
+                               unshift @tok, $t
                        }
-                       push @chunk, $self->_chunk('PROB', $percent, $from, $to);
+                       push @chunk, $self->_chunk('RWY', $sort, $dir);
 
-        # runway
-        } elsif (my ($sort, $dir) = $t =~ /^(RWY?|LDG)(\d\d[RLC]?)$/ ) {
+               # runway, but as seen in wind shear
+               # eg: LDG RWY25L
+               } elsif (($sort) = $t =~ /^(LDG|TKOF)$/ ) {
+                       my $t2;
+                       $t2 = shift @tok;
+                       ($dir) = $t2 =~ /^RWY(\d\d[RLC]?)$/;
                        push @chunk, $self->_chunk('RWY', $sort, $dir);
 
                # a wind group
-               } elsif (my ($wdir, $spd, $gust, $unit) = $t =~ /^(\d\d\d|VRB)(\d\d)(?:G(\d\d))?(KT|MPH|MPS|KMH)$/) {
-                       
+               } elsif (($wdir, $spd, $gust, $unit) = $t =~ /^([\dO]{3}|VRB|\/{3})([\dO]{2}|\/{2})(?:G([\dO]{2,3}))?(KTS?|MPH|MPS|KMH)$/) {
                        my ($fromdir, $todir);
-                       
-                       if      (@tok && (($fromdir, $todir) = $tok[0] =~ /^(\d\d\d)V(\d\d\d)$/)) {
+
+                       # More hardware suck, oh vs. zero
+                       $wdir =~ s/O/0/g if $wdir;
+                       $spd  =~ s/O/0/g if $spd;
+                       $gust =~ s/O/0/g if $gust;
+
+                       # it could be variable so look at the next token
+                       if      (@tok && (($fromdir, $todir) = $tok[0] =~ /^([\dO]{3})V([\dO]{3})$/)) {
                                shift @tok;
+                               $fromdir =~ s/O/0/g;
+                               $todir =~ s/O/0/g;
                        }
-                       
-                       # it could be variable so look at the next token
 
-                       $spd = 0 + $spd;
+                       # Part of the hardware is bad
+                       $wdir = 'NA' if $wdir eq '///';
+                       $spd = 'NA' if $spd eq '//';
+
+                       $spd = 0 + $spd unless $spd eq 'NA';
                        $gust = 0 + $gust if defined $gust;
+                       $unit = 'kt' if $unit eq 'KTS';
                        $unit = ucfirst lc $unit;
                        $unit = 'm/sec' if $unit eq 'Mps';
                        $self->{wind_dir} ||= $wdir;
@@ -301,13 +373,25 @@ sub decode
                        $self->{wind_gusting} ||= $gust;
                        $self->{wind_units} ||= $unit;
                        push @chunk, $self->_chunk('WIND', $wdir, $spd, $gust, $unit, $fromdir, $todir);
-                       
+
+               # wind not reported
+               # MHRO does not seem to follow this rule.
+               } elsif ($t =~ /^\/{5}$/) {
+                       if($self->{icao} eq 'MHRO') {
+                               ; # TODO: We will do something here once we figure what MHRO uses this field for
+                               push @chunk, $self->_chunk('IGNORE', $t);
+                       } else {
+                               push @chunk, $self->_chunk('WIND', 'NR', undef, undef, undef, undef, undef);
+                       }
+
                # pressure 
-               } elsif (my ($u, $p, $punit) = $t =~ /^([QA])(?:NH)?(\d\d\d\d)(INS?)?$/) {
+               } elsif (my ($u, $p, $punit) = $t =~ /^([QA])(?:NH)?(\d{4}|\/{4}|)(INS?)?$/) {
 
-                       $p = 0 + $p;
+                       $p = 'NA' if $p eq '////';
+                       $p = 'NA' if $p eq '' or !defined($p);
+                       $p = 0.0 + $p unless $p eq 'NA';
                        if ($u eq 'A' || $punit && $punit =~ /^I/) {
-                               $p = sprintf "%.2f", $p / 100;
+                               $p = sprintf("%.2f", $p / 100.0) unless $p eq 'NA';
                                $u = 'in';
                        } else {
                                $u = 'hPa';
@@ -317,82 +401,224 @@ sub decode
                        push @chunk, $self->_chunk('PRESS', $p, $u);
 
                # viz group in metres
-               } elsif (my ($viz, $mist) = $t =~ m!^(\d\d\d\d[NSEW]{0,2})([A-Z][A-Z])?$!) {
-                       $viz = $viz eq '9999' ? ">10000" : 0 + $viz;
+               # May be \d{4}NDV per http://www.caa.co.uk/docs/33/CAP746.PDF
+               # //// = unknown
+               # strictly before the remark section. After RMK plain numbers mean other things.
+               } elsif (!defined $self->{in_remark} and ($viz, $dir) = $t =~ m/^(\d\d\d\d|\/{4})([NSEW]{1,2}|NDV)?$/) {
+                       if($viz eq '////') {
+                               $viz = 'NA';
+                       } else {
+                               $viz = $viz eq '9999' ? ">10000" : 0 + $viz;
+                       }
                        $self->{viz_dist} ||= $viz;
                        $self->{viz_units} ||= 'm';
-                       push @chunk, $self->_chunk('VIZ', $viz, 'm');
-                       push @chunk, $self->_chunk('WEATHER', $mist) if $mist;
-
-               # viz group in KM
-               } elsif (($viz) = $t =~ m!^(\d+)KM$!) {
-                       $viz = $viz eq '9999' ? ">10000" : 0 + $viz;
+                       $dir = undef if $dir && $dir eq 'NDV';
+                       push @chunk, $self->_chunk('VIZ', $viz, 'm', $dir);
+                       #push @chunk, $self->_chunk('WEATHER', $mist) if $mist;
+
+               # viz group in integral KM, feet, M
+               } elsif (($viz, $vunit) = $t =~ m/^(\d+|\/{1,3})(KM|FT|M)$/) {
+                       if($viz =~ /^\/+$/) {
+                               $viz = 'NA';
+                       } else {
+                               $viz = $viz eq '9999' ? ">10000" : 0 + $viz;
+                       }
+                       $vunit = lc $vunit;
                        $self->{viz_dist} ||= $viz;
-                       $self->{viz_units} ||= 'Km';
-                       push @chunk, $self->_chunk('VIZ', $viz, 'Km');
+                       $self->{viz_units} ||= $vunit;
+                       push @chunk, $self->_chunk('VIZ', $viz, $vunit);
 
-        # viz group in miles and fraction of a mile with space between
-               } elsif (my ($m) = $t =~ m!^(\d)$!) {
-            my ($viz, $denom);
-            if (@tok && (($viz, $denom) = $tok[0] =~ m!^(\d)/(\d)SM$!)) {
+               # viz group in miles and faction of a mile with space between
+               } elsif (my ($m) = $t =~ m/^(\d)$/) {
+                       if (@tok && (($viz) = $tok[0] =~ m/^(\d\/\d)SM$/)) {
                                shift @tok;
-               $denom ||= 1;
-               $viz = $m + $viz / $denom;
+                               $viz = "$m $viz";
                                $self->{viz_dist} ||= $viz;
-                $self->{viz_units} ||= 'Miles';
-                push @chunk, $self->_chunk('VIZ', $viz, 'Miles');
+                               $self->{viz_units} ||= 'miles';
+                               push @chunk, $self->_chunk('VIZ', $viz, 'miles');
                        }
-                       
+
                # viz group in miles (either in miles or under a mile)
-        } elsif (my ($lt, $mviz, $denom) = $t =~ m!^([MP])?(\d+)(?:/(\d))?SM$!) {
-           $denom ||= 1;
-           $mviz /= $denom;
-            $mviz = '<' . $mviz if $lt and $lt eq 'M';
-            $mviz = '>' . $mviz if $lt and $lt eq 'P';
-                       $self->{viz_dist} ||= $mviz;
-            $self->{viz_units} ||= 'Miles';
-                       push @chunk, $self->_chunk('VIZ', $mviz, 'Miles');
-                       
+               } elsif (my ($lt, $viz) = $t =~ m/^(M|P)?(\d+(:?\/\d)?|\/{1,3})SM$/) {
+                       if($viz =~ /^\/+$/) {
+                               $viz = 'NA';
+                       }
+                       $viz = '<' . $viz if $lt eq 'M';
+                       $viz = '>' . $viz if $lt eq 'P';
+                       $self->{viz_dist} ||= $viz;
+                       $self->{viz_units} ||= 'Stat. Miles';
+                       push @chunk, $self->_chunk('VIZ', $viz, 'miles');
+
+               # Runway deposits state per ICAO
+               # 8 digits
+               # (DR,DR),ER,CR,(eR,eR),(BR,BR)
+               # "ER,CR,eR,eR" == CLRD when previous deposits are removed
+               # Also an alternate form, xxyzCLRD.
+               } elsif (my ($rwy, $type, $extent, $depth, $braking) = $t =~ m/^(\d\d)(\d|\/|C)(\d|\/|L)(\d\d|\/\/|RD|CL)(\d\d|\/\/|RD)$/) {
+                       # Runway desginator
+                       if($rwy == 99) {
+                               $rwy = 'LAST';
+                       } elsif($rwy == 88) {
+                               $rwy = 'ALL';
+                       } elsif($rwy >= 50) {
+                               $rwy = ($rwy-50).'R';
+                       } else {
+                               $rwy = $rwy.'L';
+                       }
+
+                       # Type
+                       # Not processed here
+
+                       # Extent
+                       # Not processed here
+
+                       # Depth
+                       if($depth eq 'RD' or $depth eq 'CL') {
+                               # Previous contaminination cleared
+                               $type = 'CLRD';
+                               $extent = undef;
+                               $depth = undef;
+                               $braking = undef if $braking eq 'RD';
+                       } elsif($depth eq '//') {
+                               ; # pass-thru
+                       } elsif($depth == 0) {
+                               $depth = '<1mm';
+                       } elsif($depth <= 90) {
+                               $depth .= 'mm';
+                       } elsif($depth == 91) {
+                               # BAD!
+                       } elsif($depth >= 92 && $depth <= 97) {
+                               # 92 = 10cm ... 97 = 35cm
+                               $depth = sprintf('%dcm', (($depth - 90) * 5));
+                       } elsif($depth == 99) {
+                               $depth = '>40cm';
+                       } elsif($depth == 99) {
+                               $extent = 'CVRD';
+                               $depth = 'NR';
+                       }
+
+                       # Friction / Breaking action
+                       if(defined($braking) && $braking < 91) {
+                               $braking = sprintf('%.2f', $braking/100.0);
+                       } # Other codes are handling in the print
+
+                       push @chunk, $self->_chunk('DEP', $rwy, $type, $extent, $depth, $braking);
+
                # runway visual range
-               } elsif (my ($rw, $rlt, $range, $vlt, $var, $runit, $tend) = $t =~ m!^R(\d\d[LRC]?)/([MP])?(\d\d\d\d)(?:V([MP])(\d\d\d\d))?(?:(FT)/?)?([UND])?$!) {
-                       $runit = 'm' unless $runit;
-                       $runit = lc $unit;
+               } elsif (my ($rw, $rlt, $range, $vlt, $var, $runit, $tend) = $t =~ m/^R(\d\d\d?[LRC]?)\/([MP])?(\d\d\d\d?)(?:V([MP])?(\d\d\d\d?))?((?:FT)\/?)?([UND])?$/) {
+                       $runit = 'm' unless defined($runit) && length($runit) > 0;
+                       $runit = lc $runit;
                        $range = "<$range" if $rlt && $rlt eq 'M';
                        $range = ">$range" if $rlt && $rlt eq 'P';
                        $var = "<$var" if $vlt && $vlt eq 'M';
                        $var = ">$var" if $vlt && $vlt eq 'P';
                        push @chunk, $self->_chunk('RVR', $rw, $range, $var, $runit, $tend);
-               
+
                # weather
-               } elsif (my ($deg, $w) = $t =~ /^(\+|\-|VC)?([A-Z][A-Z]{1,4})$/) {
+               } elsif (not defined $self->{in_remark} && my ($deg, $w) = $t =~ /^(\+|\-)?([A-Z][A-Z]{1,6})$/) {
                        push @chunk, $self->_chunk('WEATHER', $deg, $w =~ /([A-Z][A-Z])/g);
-                        
-        # cloud and stuff 
-               } elsif (my ($amt, $height, $cb) = $t =~ m!^(FEW|SCT|BKN|OVC|SKC|CLR|VV|///)(\d\d\d|///)(CB|TCU)?$!) {
-                       push @chunk, $self->_chunk('CLOUD', $amt, $height eq '///' ? 0 : $height * 100, $cb) unless $amt eq '///' && $height eq '///';
+               # cloud and stuff
+               # /// is the TCU column means that the automated system is unable to detect it
+               } elsif (my ($amt, $height, $cb) = $t =~ m/^(FEW|SCT|BKN|OVC|SKC|CLR|VV|\/{3})(\d\d\d|\/{3})(CB|TCU|CBMAM|ACC|CLD|\/\/\/)?$/) {
+                       push @chunk, $self->_chunk('CLOUD', $amt, $height eq '///' ? 0 : $height * 100, $cb);
 
                # temp / dew point
-        } elsif (my ($ms, $t, $n, $d) = $t =~ m!^T?(M)?(\d\d)/(M)?(\d\dZ?)?$!) {
-                       $t = 0 + $t;
+               } elsif (my ($ms, $temp, $n, $d) = $t =~ m/^(M)?(\d\d)\/(M)?(\d\d)?$/) {
+                       $temp = 0 + $temp;
                        $d = 0 + $d;
-                       $t = -$t if defined $ms;
+                       $temp = -$temp if defined $ms;
                        $d = -$d if defined $d && defined $n;
-                       $self->{temp} ||= $t;
+                       $self->{temp} ||= $temp;
                        $self->{dewpoint} ||= $d;
-                       push @chunk, $self->_chunk('TEMP', $t, $d);
-               } 
+                       push @chunk, $self->_chunk('TEMP', $temp, $d);
                
-       }                       
+               # Remark section containing exact cloud type + okta number
+               # cloud type codes in Geo::TAF::EN::CLOUD
+               # example: CI1AC1TCU4 = Cirrus 1/8, Altocumulus 1/8, Towering Cumulus 4/8
+               # example: SN2SC1SC3SC2
+               } elsif (my $ct = $t =~ m/^((?:CI|CS|CC|AS|AC|ACC|ST|NS|SC|SF|SN|CF|CU|TCU|CB)\d)+$/) {
+                       foreach my $ct (split m/((?:CI|CS|CC|AS|AC|ACC|ST|NS|SC|SF|SN|CF|CU|TCU|CB)\d)/, $t) {
+                               chomp $ct;
+                               next if(length($ct) == 0);
+                               $t = $ct;
+                               $ct =~ s/\d+$//;
+                               $t =~ s/^$ct//;
+                               push @chunk, $self->_chunk('CLOUD', $t, $ct)
+                       }
+
+               # pressure equivilent @ sea level
+               } elsif (($p) = $t =~ /^SLP(\d\d\d)$/) {
+                       $p = 0+$p;
+                       $p = sprintf '%.1f', 1000+$p/10.0;
+                       push @chunk, $self->_chunk('SLP', $p, 'hPa');
+
+               # station type
+               } elsif (defined $self->{in_remark} && ($type) = $t =~ /^AO(1|2)$/) {
+                       $type = ($type == '1' ? '-' : '+').'PRECIP';
+                       push @chunk, $self->_chunk('STATION_TYPE', $type);
+
+               # US NWS:
+               # Hourly Precipitation Amount (P)
+               # 3- and 6-Hour Precipitation Amount (3, 6)
+               # 24-Hour Precipitation Amount (7)
+               #
+               # The specification says 4 digits after the type code, but some stations only have 3:
+               # CXKA 011100Z AUTO 35002KT M28/M31 RMK AO1 3010 SLP219 T12761306 50023
+               # ^^^ 0.1 inches in the 3 hour period
+               #
+               # KW22 011135Z AUTO 23016G23KT 10SM BKN029 OVC036 02/M02 A2988 RMK A02 P000
+               # ^^^ 0.0 inches in the last hour
+               } elsif (defined $self->{in_remark} && my ($precip_period, $precip) = $t =~ /^(3|6|7|P)(\d{3,4})$/) {
+                       $precip_period = 24 if $precip_period eq '7';
+                       $precip_period = 1 if $precip_period eq 'P';
+                       push @chunk, $self->_chunk('PRECIP', $precip, $precip_period);
+
+               # other remarks go to a text buffer for now
+               #} elsif (defined $self->{in_remark} && length($t) > 0) {
+               } elsif (defined $self->{in_remark}) {
+                       print "Adding to remark buffer: $t\n";
+                       push @remark_buffer, $t;
+                       $ignore_no_length_change = 1;
+
+               #X#} elsif (1) {
+               #X#     print "Debug marker: $t\n";
+               #X#     $ignore_no_length_change = 1;
+
+               } elsif(0) {
+
+
+               # End of processing
+               }
+
+               $c1 = $#chunk;
+               if($c0 == $c1 && $ignore_no_length_change == 0) {
+                       push @chunk, $self->_chunk('RMK','Failed to decode: '.$t);
+                       $self->{decode_failures}++;
+               }
+       }
+
+       if (@remark_buffer) {
+               push @chunk, $self->_chunk('BLOCK') unless ($c0 == $c1);
+               push @chunk, $self->_chunk('RMK', join(' ', @remark_buffer));
+       }
        $self->{chunks} = \@chunk;
        return undef;   
 }
 
-sub _chunk
+sub _pkg
 {
        my $self = shift;
        my $pkg = shift;
        no strict 'refs';
        $pkg = $self->{chunk_package} . '::' . $pkg;
+       return $pkg;
+}
+sub _chunk
+{
+       my $self = shift;
+       my $pkg = shift;
+       no strict 'refs';
+       $pkg = $self->_pkg($pkg);
        return $pkg->new(@_);
 }
 
@@ -409,7 +635,7 @@ sub AUTOLOAD
        return if $name eq 'DESTROY';
 
        *$AUTOLOAD = sub {return $_[0]->{$name}};
-    goto &$AUTOLOAD;
+       goto &$AUTOLOAD;
 }
 
 #
@@ -419,6 +645,7 @@ sub AUTOLOAD
 #
 
 package Geo::TAF::EN;
+sub type { return __PACKAGE__; }
 
 sub new
 {
@@ -454,15 +681,15 @@ sub day
        return "${d}th";
 }
 
-
 package Geo::TAF::EN::HEAD;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 sub as_string
 {
        my $self = shift;
-       return "$self->[0] for $self->[1] issued at $self->[3] on " . $self->day($self->[2]);
+       return sprintf "%s for %s issued at %s on %s", $self->[0], $self->[1], $self->[3], $self->day($self->[2]);
 }
 
 package Geo::TAF::EN::VALID;
@@ -472,48 +699,73 @@ use vars qw(@ISA);
 sub as_string
 {
        my $self = shift;
-       return "valid from $self->[1] to $self->[2] on " . $self->day($self->[0]);
+       return "valid";
+       # will be followed by a PERIOD block
 }
 
 
 package Geo::TAF::EN::WIND;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+my %wst = (
+       NA      => 'unknown',
+       NR      => 'not reported',
+       VRB     => 'variable',
+);
 
-# direction, $speed, $gusts, $unit, $fromdir, $todir
+# $direction, $speed, $gusts, $unit, $fromdir, $todir
 sub as_string
 {
        my $self = shift;
-       my $out = "wind";
-       $out .= $self->[0] eq 'VRB' ? " variable" : " $self->[0]";
-    $out .= " varying between $self->[4] and $self->[5]" if defined $self->[4];
-       $out .= ($self->[0] eq 'VRB' ? '' : " degrees") . " at $self->[1]";
-       $out .= " gusting $self->[2]" if defined $self->[2];
-       $out .= $self->[3];
+       my $out;
+       $out  = sprintf("wind %s", ($wst{$self->[0]} ? $wst{$self->[0]}: $self->[0]));
+       $out .= sprintf(" varying between %s && %s", $self->[4], $self->[5]) if defined $self->[4];
+       $out .= sprintf("%s at %s", ($self->[0] eq 'VRB' ? '' : " degrees"), $wst{$self->[1]} ? $wst{$self->[1]} : $self->[1]) if defined $self->[1];
+       $out .= sprintf(" gusting %s", $self->[2]) if defined $self->[2] && $self->[1] ne 'NA';
+       $out .= $self->[1] eq 'NA' ? ' speed' : $self->[3] if defined $self->[3];
        return $out;
 }
 
 package Geo::TAF::EN::PRESS;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+# $pressure, $unit
+sub as_string
+{
+       my $self = shift;
+       return sprintf "QNH pressure not available" if $self->[0] eq 'NA';
+       return sprintf "QNH pressure %s%s", $self->[0], $self->[1];
+}
+
+package Geo::TAF::EN::SLP;
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 # $pressure, $unit
 sub as_string
 {
        my $self = shift;
-       return "QNH $self->[0]$self->[1]";
+       return sprintf "SLP pressure not available" if $self->[0] eq 'NA';
+       return sprintf "SLP pressure %s%s", $self->[0], $self->[1];
 }
 
 # temperature, dewpoint
 package Geo::TAF::EN::TEMP;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 sub as_string
 {
        my $self = shift;
-       my $out = "temperature $self->[0]C";
-       $out .= " dewpoint $self->[1]C" if defined $self->[1];
+       my $out;
+       $out  = sprintf("temperature %sC", $self->[0]);
+       $out .= sprintf(" dewpoint %sC", $self->[1]) if defined $self->[1];
 
        return $out;
 }
@@ -521,87 +773,135 @@ sub as_string
 package Geo::TAF::EN::CLOUD;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 my %st = (
-                 VV => 'vertical visibility',
-                 SKC => "no cloud",
-                 CLR => "no cloud no significant weather",
-                 SCT => "3-4 oktas",
-                 BKN => "5-7 oktas",
-                 FEW => "0-2 oktas",
-                 OVC => "8 oktas overcast",
-                 CAVOK => "no cloud below 5000ft >10Km visibility no significant weather (CAVOK)",
-                 CB => 'thunder clouds',
-          TCU => 'towering cumulus',
-                 NSC => 'no significant cloud',
-                 BLU => '3 oktas at 2500ft 8Km visibility',
-                 WHT => '3 oktas at 1500ft 5Km visibility',
-                 GRN => '3 oktas at 700ft 3700m visibility',
-                 YLO => '3 oktas at 300ft 1600m visibility',
-                 AMB => '3 oktas at 200ft 800m visibility',
-                 RED => '3 oktas at <200ft <800m visibility',
-                 NIL => 'no weather',
-                 '///' => 'some',
-                );
-
+               VV    => 'vertical visibility',
+               SKC   => "no cloud",
+               CLR   => "no cloud no significant weather",
+               SCT   => "3-4 oktas/scattered",
+               BKN   => "5-7 oktas/broken",
+               FEW   => "0-2 oktas/few",
+               OVC   => "8 oktas/overcast",
+               '///' => 'some',
+);
+
+my %cloud_code = (
+               # Cloud codes found in remarks, followed by an okta
+               # same order as the SCT/BWN/FEW/OVC codes.
+               CI   => 'Cirrus',
+               CS   => 'Cirrostratus',
+               CC   => 'Cirrocumulus',
+               AS   => 'Altostratus',
+               AC   => 'Altocumulus',
+               ACC  => 'Altocumulus Castellanus',
+               ST   => 'Stratus',
+               NS   => 'Nimbostratus',
+               SC   => 'Stratoculumus',
+               SF   => 'Stratus Fractus',
+               CF   => 'Cumulus Fractus',
+               CU   => 'Cumulus',
+               TCU  => 'Towering Cumulus',
+               CB   => 'Cumulonimbus', # aka thunder clouds
+
+               # not official, but seen often in Canada: METAR CYVR 262319Z 09011KT 1 1/2SM -SN FEW003 BKN006 OVC010 00/ RMK SN2SC1SC3SC2
+               SN   => 'Snow clouds',
+);
+
+my %col = (
+               'CAVOK' => "no cloud below 5000ft >10km visibility no significant weather (CAVOK)",
+               'NSC'   => 'no significant cloud',
+               'NCD'   => "no cloud detected",
+               'BLU+'  => '3 oktas at >2500ft >8km visibility',
+               'BLU'   => '3 oktas at 2500ft 8km visibility',
+               'WHT'   => '3 oktas at 1500ft 5km visibility',
+               'GRN'   => '3 oktas at 700ft 3700m visibility',
+               'YLO1'  => '3 oktas at 500ft 2500m visibility',
+               'YLO2'  => '3 oktas at 300ft 1600m visibility',
+               'YLO'   => '3 oktas at 300ft 1600m visibility', # YLO2 and YLO are meant to be identical
+               'AMB'   => '3 oktas at 200ft 800m visibility',
+               'RED'   => '3 oktas at <200ft <800m visibility',
+               'NIL'   => 'no weather',
+);
+
+my %st_storm = (
+               CB    => 'cumulonimbus',
+               TCU   => 'towering cumulus',
+               CBMAM => 'cumulonimbus mammatus',
+               ACC   => 'altocumulus castellatus',
+               CLD   => 'standing lenticular',
+               # if you get this, the automated sensors are unable to decide
+               '///' => 'unknown cumulus',
+);
+
+# $amt, $height, $cb
 sub as_string
 {
        my $self = shift;
-       return $st{$self->[0]} if @$self == 1;
-       return $st{$self->[0]} . " $self->[1]ft" if $self->[0] eq 'VV';
-       return $st{$self->[0]} . " cloud at $self->[1]ft" . ((defined $self->[2]) ? " with $st{$self->[2]}" : "");
+       return $col{$self->[0]} if @$self == 1 && $col{$self->[0]};
+       if(@$self == 2 && (int($self->[0]) eq "$self->[0]") and defined $cloud_code{$self->[1]}) {
+               return sprintf "%s %d/8 cover", $cloud_code{$self->[1]}, $self->[0];
+       }
+       return sprintf("%s %sft", $st{$self->[0]}, $self->[1]) if $self->[0] eq 'VV';
+       my $out = sprintf("%s cloud", $st{$self->[0]});
+       $out .= sprintf(' at %sft', $self->[1]) if $self->[1];
+       $out = 'unknown cloud cover' if $self->[1] == 0 && $self->[0] eq '///';
+       $out .= sprintf(" with %s", $st_storm{$self->[2]}) if $self->[2];
+       return $out;
 }
 
 package Geo::TAF::EN::WEATHER;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 my %wt = (
-                 '+' => 'heavy',
-          '-' => 'light',
-          'VC' => 'in the vicinity',
-
-                 MI => 'shallow',
-                 PI => 'partial',
-                 BC => 'patches of',
-                 DR => 'low drifting',
-                 BL => 'blowing',
-                 SH => 'showers',
-                 TS => 'thunderstorms containing',
-                 FZ => 'freezing',
-                 RE => 'recent',
-                 
-                 DZ => 'drizzle',
-                 RA => 'rain',
-                 SN => 'snow',
-                 SG => 'snow grains',
-                 IC => 'ice crystals',
-                 PE => 'ice pellets',
-                 GR => 'hail',
-                 GS => 'small hail/snow pellets',
-                 UP => 'unknown precip',
-                 
-                 BR => 'mist',
-                 FG => 'fog',
-                 FU => 'smoke',
-                 VA => 'volcanic ash',
-                 DU => 'dust',
-                 SA => 'sand',
-                 HZ => 'haze',
-                 PY => 'spray',
-                 
-                 PO => 'dust/sand whirls',
-                 SQ => 'squalls',
-                 FC => 'tornado',
-                 SS => 'sand storm',
-                 DS => 'dust storm',
-                 '+FC' => 'water spouts',
-                 WS => 'wind shear',
-                 'BKN' => 'broken',
-
-                 'NOSIG' => 'no significant weather',
-                 
-                );
+               '+'   => 'heavy',
+               '-'   => 'light',
+               'VC'  => 'in the vicinity',
+
+               'MI'  => 'shallow',
+               'PI'  => 'partial',
+               'BC'  => 'patches of',
+               'DR'  => 'low drifting',
+               'BL'  => 'blowing',
+               'SH'  => 'showers',
+               'TS'  => 'thunderstorms containing',
+               'FZ'  => 'freezing',
+               'RE'  => 'recent',
+
+               'DZ'  => 'drizzle',
+               'RA'  => 'rain',
+               'SN'  => 'snow',
+               'SG'  => 'snow grains',
+               'IC'  => 'ice crystals',
+               'PE'  => 'ice pellets',
+               'GR'  => 'hail',
+               'GS'  => 'small hail/snow pellets',
+               'UP'  => 'unknown precip',
+               '//'  => 'unknown weather',
+
+               'BR'  => 'mist',
+               'FG'  => 'fog',
+               'FU'  => 'smoke',
+               'VA'  => 'volcanic ash',
+               'DU'  => 'dust',
+               'SA'  => 'sand',
+               'HZ'  => 'haze',
+               'PY'  => 'spray',
+
+               'PO'  => 'dust/sand whirls',
+               'SQ'  => 'squalls',
+               'FC'  => 'tornado',
+               'SS'  => 'sand storm',
+               'DS'  => 'dust storm',
+               '+FC' => 'water spouts',
+               'WS'  => 'wind shear',
+               'BKN' => 'broken',
+
+               'NOSIG' => 'no significant weather',
+               'PRFG'  => 'fog banks', # officially PR is a modifier of FG
+               );
 
 sub as_string
 {
@@ -611,7 +911,7 @@ sub as_string
        my ($vic, $shower);
        my @in;
        push @in, @$self;
-       
+
        while (@in) {
                my $t = shift @in;
 
@@ -628,9 +928,9 @@ sub as_string
                        shift;
                        next;
                }
-               
+
                push @out, $wt{$t};
-               
+
                if (@out && $shower) {
                        $shower = 0;
                        push @out, $wt{'SH'};
@@ -641,18 +941,60 @@ sub as_string
        return join ' ', @out;
 }
 
+package Geo::TAF::EN::STATION_TYPE;
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+# $code
+sub as_string
+{
+       my $self = shift;
+       my $code = shift;
+       my $out = 'Automated station';
+       if($code eq '+PRECIP') {
+               $out .= ' cannot detect precipitation';
+       } elsif($code eq '-PRECIP') {
+               $out .= ' has precipitation discriminator';
+       }
+}
+
+package Geo::TAF::EN::PRECIP;
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+# $precip, $period
+sub as_string
+{
+       my $self = shift;
+       my $precip = $self->[0];
+       my $period = $self->[1];
+       if($period == 1) {
+               return sprintf 'precipitation %.2f inches in last hour', $precip;
+       } elsif($period == 24) {
+               return sprintf '24 hour total precipitation %.2f inches', $precip;
+       } else {
+               return sprintf '%d-hour precipitation %.2f', $period, $precip;
+       }
+}
+
 package Geo::TAF::EN::RVR;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
+# $rw, $range, $var, $runit, $tend;
 sub as_string
 {
        my $self = shift;
-       my $out = "visual range on runway $self->[0] is $self->[1]$self->[3]";
-       $out .= " varying to $self->[2]$self->[3]" if defined $self->[2];
+       my $out;
+       $out  = sprintf("visual range on runway %s is %s%s", $self->[0], $self->[1], $self->[3]);
+       $out .= sprintf(" varying to %s%s", $self->[2], $self->[3]) if defined $self->[2];
        if (defined $self->[4]) {
                $out .= " decreasing" if $self->[4] eq 'D';
                $out .= " increasing" if $self->[4] eq 'U';
+               $out .= " unchanged"  if $self->[4] eq 'N';
        }
        return $out;
 }
@@ -660,50 +1002,89 @@ sub as_string
 package Geo::TAF::EN::RWY;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
+my %rwy = (
+                 LDG => 'landing',
+                 SKC => 'take-off',
+               );
 sub as_string
 {
        my $self = shift;
-       my $out = $self->[0] eq 'LDG' ? "landing " : '';  
-       $out .= "runway $self->[1]";
+       my $out;
+       if($rwy{$self->[0]}) {
+               $out .= $rwy{$self->[0]} . ' ';
+       }
+       $out .= sprintf("runway %s", $self->[1]);
        return $out;
 }
 
 package Geo::TAF::EN::PROB;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
+# $percent, $from, $to;
 sub as_string
 {
        my $self = shift;
-    
-       my $out = "probability $self->[0]%";
-       $out .= " $self->[1] to $self->[2]" if defined $self->[1];
-       return $out;
+
+       return sprintf("probability %s%%", $self->[0]);
+       # will be followed by a PERIOD block
 }
 
 package Geo::TAF::EN::TEMPO;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 sub as_string
 {
        my $self = shift;
-       my $out = "temporarily";
-       $out .= " $self->[0] to $self->[1]" if defined $self->[0];
-
-       return $out;
+       return "temporarily";
+       # will be followed by a PERIOD block
 }
 
 package Geo::TAF::EN::BECMG;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 sub as_string
 {
        my $self = shift;
-       my $out = "becoming";
-       $out .= " $self->[0] to $self->[1]" if defined $self->[0];
+       return "becoming";
+       # will be followed by a PERIOD block
+}
+
+package Geo::TAF::EN::PERIOD;
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+sub as_string
+{
+       my $self = shift;
+       # obj, from_time, to_time, from_day, to_day
+       my ($out, $format);
+       $out = 'period from ';
+       # format 1 = time only, no date
+       # format 2 = time, one day (or two days that are the same value)
+       # format 3 = time and two different day
+       $format = 1 if defined $self->[0] && defined $self->[1];
+       if(defined $self->[2]) {
+               $format = 3;
+               $format-- if not defined $self->[3] or $self->[2] == $self->[3];
+       }
+       if($format == 2) {
+               $out .= sprintf("%s to %s on %s", $self->[0], $self->[1], $self->day($self->[2]));
+       } elsif($format == 3) {
+               $out .= sprintf("%s %s to %s %s", $self->day($self->[2]), $self->[0], $self->day($self->[3]), $self->[1]);
+       } elsif($format == 1) {
+               $out .= sprintf("%s to %s", $self->[0], $self->[1]);
+       } else {
+               $out .= 'BAD PERIOD';
+       }
 
        return $out;
 }
@@ -711,34 +1092,176 @@ sub as_string
 package Geo::TAF::EN::VIZ;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 sub as_string
 {
-    my $self = shift;
+       my $self = shift;
+
+       my $out = 'visibility ';
+       return $out.'not available' if $self->[0] eq 'NA';
+       return $out.sprintf("%s%s%s", ($self->[2] ? $self->[2].' ' : ''), $self->[0], $self->[1]);
+}
 
-    return "visibility $self->[0]$self->[1]";
+package Geo::TAF::EN::DEP;
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+my %cover_type = (
+               0               => 'clear & dry',
+               1               => 'damp',
+               2               => 'wet/water patches',
+               3               => 'frost-covered',
+               4               => 'dry snow',
+               5               => 'wet snow',
+               6               => 'slush',
+               7               => 'ice',
+               8               => 'compacted snow',
+               9               => 'frozen ruts',
+               '/'             => 'unknown',
+               'CLRD'  => 'cleared',
+               );
+
+my %extent = (
+               1               => '<10%',
+               2               => '11-25%',
+               5               => '26-50%',
+               9               => '51-100%',
+               '/'             => 'not reported',
+               'CVRD'  => 'non-operational',
+               );
+
+my %depth = (
+               'NR' => 'not reported',
+               '//' => 'not significent',
+               );
+
+my %breaking = (
+               95              => 'good',
+               94              => 'medium/good',
+               93              => 'medium',
+               92              => 'medium/poor',
+               91              => 'poor',
+               99              => 'unreliable',
+               '//'    => 'not reported',
+               );
+
+# $rwy, $cover_type, $extent, $depth, $braking
+sub as_string
+{
+       my $self = shift;
+
+       my $out;
+       $out  = sprintf 'Runway %s conditions: %s', $self->[0], $cover_type{$self->[1]};
+       if(defined($self->[2])) {
+               $out .= sprintf(', extent %s',$extent{$self->[2]});
+       }
+       if(defined($self->[3])) {
+               $_ = $depth{$self->[3]};
+               $_ = $self->[3] unless $_;
+               $out .= sprintf(', depth %s', $_);
+       }
+       if(defined($self->[4])) {
+               $_ = $depth{$self->[4]};
+               $out .= sprintf(', braking action %s', $_) if $_;
+               $out .= sprintf(', friction coefficient %s', $self->[4]) unless $_;
+       }
+       $out .= ';';
+
+       return $out;
 }
 
 package Geo::TAF::EN::FROM;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 sub as_string
 {
-    my $self = shift;
+       my $self = shift;
 
-    return "from $self->[0]";
+       if($self->[1]) {
+               return sprintf("from %s on the %s", $self->[0],$self->day($self->[1]));
+       } else {
+               return sprintf("from %s", $self->[0]);
+       }
 }
 
 package Geo::TAF::EN::TIL;
 use vars qw(@ISA);
 @ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+sub as_string
+{
+       my $self = shift;
+
+       if($self->[1]) {
+               return sprintf("until %s on the %s", $self->[0],$self->day($self->[1]));
+       } else {
+               return sprintf("until %s", $self->[0]);
+       }
+}
+
+package Geo::TAF::EN::AT;
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
 
 sub as_string
 {
-    my $self = shift;
+       my $self = shift;
 
-    return "until $self->[0]";
+       if($self->[1]) {
+               return sprintf("at %s on the %s", $self->[0],$self->day($self->[1]));
+       } else {
+               return sprintf("at %s", $self->[0]);
+       }
+}
+
+package Geo::TAF::EN::RMK;
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+sub as_string
+{
+       my $self = shift;
+
+       return sprintf("remark %s", $self->[0]);
+}
+
+package Geo::TAF::EN::IGNORE;
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+sub as_string
+{
+       my $self = shift;
+       return '';
+}
+
+package Geo::TAF::EN::BLOCK;
+=pod
+=begin classdoc
+
+The 'BLOCK' marker is used to explicitly indicate a new block. If producing
+human-readable output, this signifies that new line should be started.
+
+@return nothing
+
+=end classdoc
+=cut
+use vars qw(@ISA);
+@ISA = qw(Geo::TAF::EN);
+sub type { return __PACKAGE__; }
+
+sub as_string
+{
+       my $self = shift;
+       return '';
 }
 
 # Autoload methods go after =cut, and are processed by the autosplit program.
@@ -818,7 +1341,7 @@ a new constructor.
 
 If you sub-class the built-in English translation routines then 
 you can pick this up by called the constructor thus:-
+
   my $t = Geo::TAF->new(chunk_package => 'Geo::TAF::ES');
 
 or whatever takes your fancy.
@@ -913,7 +1436,7 @@ Returns a stringified version of any error returned by L<decode()>
 
 =item taf()
 
-Returns whether this object is a taf or not.
+Returns whether this object is a TAF or not.
 
 =item icao()
 
@@ -1012,10 +1535,12 @@ L<http://www.ar-group.com/icaoiata.htm>
 =head1 AUTHOR
 
 Dirk Koopman, L<mailto:djk@tobit.co.uk>
+With additions/corrections by Robin H. Johnson, L<mailto:robbat2@gentoo.org>
 
 =head1 COPYRIGHT AND LICENSE
 
 Copyright (c) 2003 by Dirk Koopman, G1TLH
+Portions Copyright (C) 2009 Robin H. Johnson
 
 This library is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself. 
index 8fbc1c44959b7fd1391c74f1def24d750a95d0eb..1dad21c1630f43e3f845e35bfc2e70bc4eeae0ad 100644 (file)
@@ -106,7 +106,7 @@ Sierra Leone:             35:  46:  AF:    8.50:    13.20:     0.0:  9L:
 West Malaysia:            28:  54:  AS:    3.20:  -101.60:    -8.0:  9M2:
     9M1,9M2,9M4,9W2,9W4;
 East Malaysia:            28:  54:  OC:    5.80:  -118.10:    -8.0:  9M6:
-    9M6,9M8,9W6,9W8,=9M1CSQ,=9M1CSS,=9M4SEA,=9M4SMO;
+    9M6,9M8,9W6,9W8,=9M4SEA,=9M4SMO;
 Nepal:                    22:  42:  AS:   27.70:   -85.30:   -5.75:  9N:
     9N;
 Rep. of Congo:            36:  52:  AF:   -4.30:   -15.30:    -1.0:  9Q:
@@ -138,7 +138,7 @@ Pakistan:                 21:  41:  AS:   30.00:   -70.00:    -5.0:  AP:
 Scarborough Reef:         27:  50:  AS:   15.10:  -117.50:    -8.0:  BS7:
     BS7;
 Taiwan:                   24:  44:  AS:   23.80:  -121.00:    -8.0:  BV:
-    BM,BN,BO,BP,BQ,BU,BV,BW,BX;
+    BM,BN,BO,BP,BQ,BU,BV,BW,BX,=VERSION;
 Pratas Island:            24:  44:  AS:   20.40:  -116.40:    -8.0:  BV9P:
     BM9P,BN9P,BO9P,BP9P,BQ9P,BU9P,BV9P,BW9P,BX9P;
 China:                    24:  44:  AS:   40.00:  -116.40:    -8.0:  BY:
@@ -187,11 +187,12 @@ Antarctica:               13:  74:  SA:  -65.00:    64.00:    -4.0:  CE9:
     ANT,AX0,FT0Y(30)[70],FT2Y(30)[70],FT4Y(30)[70],FT5Y(30)[70],FT8Y(30)[70],
     LU1Z[73],R1AN,VH0(39)[69],VI0(39)[69],VJ0(39)[69],VK0(39)[69],VL0(39)[69],
     VM0(39)[69],VN0(39)[69],VZ0(39)[69],ZL5(30)[71],ZM5(30)[71],ZS7(38)[67],
-    =8J1RF(39)[67],=8J1RL(39)[67],=CE9/K2ARB(30)[71],=DP0GVN(38)[67],
-    =DP1POL(38)[67],=KC4/K2ARB(30)[71],=KC4AAA(39),=KC4AAC[73],
-    =KC4USB(12)[72],=KC4USV(30)[71],=LU4ZS[73],=OP0LE(38)[67],=OP0OL(38)[67],
-    =R1ANR(38)[67],=VP8DJB[73],=VP8DKF(30)[71],=VP8DLJ[73],=VP8PJ[73],
-    =VP8ROT[73];
+    =8J1RF(39)[67],=8J1RL(39)[67],=CE9/K2ARB(30)[71],=CE9XX[73],
+    =DP0GVN(38)[67],=DP1POL(38)[67],=KC4/K2ARB(30)[71],=KC4AAA(39),
+    =KC4AAC[73],=KC4USB(12)[72],=KC4USV(30)[71],=LU/FT5YJ[73],=LU4ZS[73],
+    =OP0LE(38)[67],=OP0OL(38)[67],=OR3AX(30)[70],=OR4AX(30)[70],
+    =R1ANR(38)[67],=VP8DJB[73],=VP8DKF(30)[71],=VP8DLJ[73],=VP8DLM[73],
+    =VP8PJ[73],=VP8ROT[73];
 Cuba:                     08:  11:  NA:   21.50:    80.00:     5.0:  CM:
     CL,CM,CO,T4;
 Morocco:                  33:  37:  AF:   32.00:     5.00:     0.0:  CN:
@@ -325,20 +326,20 @@ Scotland:                 14:  27:  EU:   55.80:     4.30:     0.0:  GM:
     =GB0GDS,=GB0GEI,=GB0GHD,=GB0GKR,=GB0GNE,=GB0HHW,=GB0KGS,=GB0KTC,=GB0LCS,
     =GB0LTM,=GB0MFG,=GB0MLM,=GB0MOL,=GB0NHL,=GB0OS,=GB0OYT,=GB0PPE,=GB0QWM,
     =GB0RBS,=GB0SHP,=GB0SI,=GB0SK,=GB0SKY,=GB0SS,=GB0SSF,=GB0TI,=GB100MAS,
-    =GB125BRC,=GB150NRL,=GB1EPC,=GB1FS,=GB1FVT,=GB1OL,=GB2AGG,=GB2AST,=GB2AYR,
-    =GB2CHG,=GB2DHS,=GB2DTM,=GB2FBM,=GB2FIO,=GB2FSM,=GB2GEO,=GB2GNL,=GB2GTM,
-    =GB2HI,=GB2HRH,=GB2HST,=GB2HSW,=GB2IAS,=GB2IGB,=GB2IGS,=GB2IMM,=GB2IOC,
-    =GB2IOG,=GB2IOT,=GB2JUNO,=GB2KDS,=GB2KHL,=GB2LAY,=GB2LBN,=GB2LCL,=GB2LCP,
-    =GB2LGB,=GB2LHI,=GB2LMG,=GB2LNM,=GB2LO,=GB2LP,=GB2LS,=GB2LSS,=GB2LT,
-    =GB2LTH,=GB2LTN,=GB2MAS,=GB2MDG,=GB2MOD,=GB2MOF,=GB2MSL,=GB2MUL,=GB2NAG,
-    =GB2NBC,=GB2NCL,=GB2NEF,=GB2NL,=GB2NTS,=GB2OWM,=GB2OYC,=GB2PBF,=GB2PS,
-    =GB2RB,=GB2RRL,=GB2SKG,=GB2SLH,=GB2SPD,=GB2SSF,=GB2STB,=GB2TDS,=GB2TI,
-    =GB2WBB,=GB3GM,=GB400CA,=GB4AAS,=GB4CGW,=GB4DAS,=GB4GM,=GB4LNM,=GB4NFE,
-    =GB4PMS,=GB4RAF,=GB4SLH,=GB4TSR,=GB4ZBS,=GB50ATC,=GB50JS,=GB50SWL,=GB5AST,
-    =GB5BBS,=GB5CO,=GB5FHC,=GB5JS,=GB5OL,=GB5RO,=GB5SI,=GB5TI,=GB60BBC,
-    =GB60CRB,=GB60NTS,=GB6MI,=GB6SA,=GB6SM,=GB6TAA,=GB6WW,=GB700BSB,=GB75GD,
-    =GB75SCP,=GB75STT,=GB8AYR,=GB8CA,=GB8CF,=GB8CI,=GB8CM,=GB8CN,=GB8CO,
-    =GB8CSL,=GB8CY,=GB8FF,=GB8OO,=GB8RU,=GB93AM;
+    =GB125BRC,=GB150NRL,=GB1EPC,=GB1FS,=GB1FVT,=GB1OL,=GB250RB,=GB2AGG,
+    =GB2AST,=GB2AYR,=GB2CHG,=GB2DHS,=GB2DTM,=GB2FBM,=GB2FIO,=GB2FSM,=GB2GEO,
+    =GB2GNL,=GB2GTM,=GB2HI,=GB2HLB,=GB2HRH,=GB2HST,=GB2HSW,=GB2IAS,=GB2IGB,
+    =GB2IGS,=GB2IMM,=GB2IOC,=GB2IOG,=GB2IOT,=GB2JUNO,=GB2KDS,=GB2KHL,=GB2LAY,
+    =GB2LBN,=GB2LCL,=GB2LCP,=GB2LGB,=GB2LHI,=GB2LMG,=GB2LNM,=GB2LO,=GB2LP,
+    =GB2LS,=GB2LSS,=GB2LT,=GB2LTH,=GB2LTN,=GB2MAS,=GB2MDG,=GB2MOD,=GB2MOF,
+    =GB2MSL,=GB2MUL,=GB2NAG,=GB2NBC,=GB2NCL,=GB2NEF,=GB2NL,=GB2NTS,=GB2OWM,
+    =GB2OYC,=GB2PBF,=GB2PS,=GB2RB,=GB2RRL,=GB2SKG,=GB2SLH,=GB2SPD,=GB2SSF,
+    =GB2STB,=GB2TDS,=GB2TI,=GB2WBB,=GB3GM,=GB400CA,=GB4AAS,=GB4CGW,=GB4DAS,
+    =GB4GM,=GB4LNM,=GB4NFE,=GB4PMS,=GB4RAF,=GB4SLH,=GB4TSR,=GB4ZBS,=GB50ATC,
+    =GB50JS,=GB50SWL,=GB5AST,=GB5BBS,=GB5CO,=GB5FHC,=GB5JS,=GB5OL,=GB5RO,
+    =GB5SI,=GB5TI,=GB60BBC,=GB60CRB,=GB60NTS,=GB6MI,=GB6SA,=GB6SM,=GB6TAA,
+    =GB6WW,=GB700BSB,=GB75GD,=GB75SCP,=GB75STT,=GB8AYR,=GB8CA,=GB8CF,=GB8CI,
+    =GB8CM,=GB8CN,=GB8CO,=GB8CSL,=GB8CY,=GB8FF,=GB8OO,=GB8RU,=GB93AM;
 Shetland:                 14:  27:  EU:   60.40:     1.50:     0.0:  *GM/s:
     GZ,MZ,=2M0BDR,=2M0BDT,=2M0ZET,=GB2ELH,=GM0AVR,=GM0CXQ,=GM0CYJ,=GM0DJI,
     =GM0EKM,=GM0ILB,=GM0ULK,=GM1KKI,=GM1ZNR,=GM3KLA,=GM3WHT,=GM3ZET,=GM3ZNM,
@@ -359,8 +360,8 @@ Wales:                    14:  27:  EU:   51.50:     3.20:     0.0:  GW:
     =GB2MLM,=GB2MOP,=GB2RFS,=GB2RSC,=GB2RTB,=GB2SAC,=GB2SDD,=GB2SIP,=GB2TD,
     =GB2TTA,=GB2VK,=GB2WDS,=GB2WFF,=GB2WHO,=GB2WSF,=GB4BPL,=GB4CI,=GB4DPS,
     =GB4HMD,=GB4HMM,=GB4LRG,=GB4LSG,=GB4MD,=GB4MDI,=GB4MUU,=GB4NDG,=GB4SA,
-    =GB4SDD,=GB4SMM,=GB4SNF,=GB4TMS,=GB4XXX,=GB5BS/J,=GB5FI,=GB5SIP,=GB60VLY,
-    =GB6AR,=GB6GW,=GB6OQA,=GB750CC,=GB8OQE;
+    =GB4SDD,=GB4SMM,=GB4SNF,=GB4TMS,=GB4XXX,=GB5BS/J,=GB5FI,=GB5ONG,=GB5SIP,
+    =GB60VLY,=GB6AR,=GB6GW,=GB6OQA,=GB750CC,=GB8OQE;
 Solomon Islands:          28:  51:  OC:   -9.40:  -160.00:   -11.0:  H4:
     H4;
 Temotu:                   32:  51:  OC:  -10.70:  -165.80:   -11.0:  H40:
@@ -382,7 +383,7 @@ Dominican Republic:       08:  11:  NA:   18.50:    70.00:     4.0:  HI:
 Colombia:                 09:  12:  SA:    4.60:    74.10:     5.0:  HK:
     5J,5K,HJ,HK;
 San Andres/Providencia:   07:  11:  NA:   12.50:    81.70:     5.0:  HK0/a:
-    5J0,5K0,HJ0,HK0,=5K0T(8);
+    5J0,5K0,HJ0,HK0;
 Malpelo I.:               09:  12:  SA:    4.00:    81.10:     5.0:  HK0/m:
     =HK0TU;
 South Korea:              25:  44:  AS:   37.50:  -127.00:    -9.0:  HL:
@@ -404,8 +405,8 @@ Italy:                    15:  28:  EU:   41.90:   -12.50:    -1.0:  I:
 Italy (Africa):           33:  37:  AF:   35.40:   -12.50:    -1.0:  *IG9:
     IG9,IH9;
 Sardinia:                 15:  28:  EU:   39.20:    -9.10:    -1.0:  IS:
-    IM0,IS,IW0U,IW0V,IW0W,IW0X,IW0Y,IW0Z,=IQ0AG,=IQ0AH,=IQ0AI,=IQ0AK,=IQ0AL,
-    =IQ0AM,=IQ0EH,=IQ0HO,=IQ0QP,=IQ0SS;
+    IM0,IS,IW0U,IW0V,IW0W,IW0X,IW0Y,IW0Z,=II0SB,=IQ0AG,=IQ0AH,=IQ0AI,=IQ0AK,
+    =IQ0AL,=IQ0AM,=IQ0EH,=IQ0HO,=IQ0QP,=IQ0SS;
 Sicily:                   15:  28:  EU:   37.50:   -14.00:    -1.0:  *IT9:
     IB9,ID9,IE9,IF9,II9,IJ9,IO9,IQ9,IR9,IT,IU9,IW9,IZ9;
 Djibouti:                 37:  48:  AF:   11.60:   -43.20:    -3.0:  J2:
@@ -424,7 +425,7 @@ Japan:                    25:  45:  AS:   35.70:  -139.80:    -9.0:  JA:
     7J,7K,7L,7M,7N,8J,8K,8L,8M,8N,JA,JB,JC,JE,JF,JG,JH,JI,JJ,JK,JL,JM,JN,JO,
     JP,JQ,JR,JS;
 Minami Torishima:         27:  90:  OC:   24.30:  -154.00:   -10.0:  JD/m:
-    =JD1BME,=JD1BMM,=JD1YAA,=JD1YBJ;
+    =JD1BME,=JD1BMM,=JD1BND,=JD1YAA,=JD1YBJ;
 Ogasawara:                27:  45:  AS:   27.50:  -141.00:    -9.0:  JD/o:
     JD1;
 Mongolia:                 23:  32:  AS:   47.90:  -106.90:    -8.0:  JT:
@@ -461,35 +462,35 @@ United States:            05:  08:  NA:   43.00:    87.90:     5.0:  K:
     =K9VV(5),=K9WZB(3)[6],=K9YC(3)[6],=KA2EYH(4),=KA4OTB(4),=KA8Q(5),
     =KB4AMA(4),=KB7Q(4)[6],=KC4HW(4),=KC4SAW(4),=KC6R(4)[7],=KC7UP(4)[6],
     =KD4HXT(3)[6],=KD4SN(4),=KD5M(5)[8],=KE3D(4)[7],=KE4KWE(4),=KE4KY(4),
-    =KE4MBP(4),=KE7NO(4)[6],=KF7NN(5)[8],=KG4CUY(4),=KG4NOZ(4),=KG7HF(5)[8],
-    =KH6DX(3)[6],=KH7WW(3)[6],=KL1SE(4)[8],=KL7OO(3)[6],=KL7WP(3)[6],=KN4Q(4),
-    =KN5H(3)[6],=KN6RO(5)[8],=KO7X(4)[7],=KP2F(4)[8],=KP3M(5)[8],=KR4F(4),
-    =KR4TI(4),=KS4X(4),=KS5A(3)[6],=KS7T(4)[6],=KT2Z(4)[7],=KU1CW(4)[7],
-    =KU8E(5),=KV6O(4)[7],=KV9R(5),=KY4F(4),=KZ4V(4),=KZ5OH(5)[8],=N1CC(4)[7],
-    =N1QXV(4)[7],=N1WQ(4)[7],=N2BJ(4),=N2BZP(3)[6],=N2IC(4)[7],=N2NS(4),
-    =N2WN(4),=N3BB(4)[7],=N3HE(4),=N3KCJ(3)[6],=N3PV(3)[6],=N3ZI(3)[6],
-    =N3ZZ(3)[6],=N4ARO(4),=N4AU(4),=N4BCB(4),=N4CB(3)[6],=N4CBK(4),
-    =N4CYV(4)[7],=N4DW(4),=N4ECJ(4),=N4GK(4),=N4GN(4),=N4HID(4),=N4IJ(4)[7],
-    =N4IR(4),=N4JF(4),=N4KC(4),=N4KG(4),=N4KZ(4),=N4LS(3)[6],=N4LW(4),
-    =N4NM(4),=N4NO(4),=N4OGW(4)[7],=N4PF(4),=N4PT(3)[6],=N4QS(4),=N4RR(4),
-    =N4SL(3)[6],=N4TN(4),=N4TZ(4),=N4UC(4),=N4VN(4),=N4VV(4),=N4XM(4),
-    =N4ZI(4),=N4ZZ(4),=N5RA(3)[6],=N5VI(5)[8],=N6AR(5)[8],=N6CY(5)[8],
-    =N6DT(4)[7],=N6RFM(5)[8],=N6ZO(5)[8],=N7DF(4)[7],=N7IV(4)[7],=N7KA(4)[7],
-    =N7NG(4)[6],=N7VR(4)[6],=N8GZ(3)[6],=N8II(5),=N8NA(5),=N8PR(5),=N8RA(5),
-    =N8RR(5),=N8WXQ(5),=N9ADG(3)[6],=N9JRZ(5),=NA4C(4),=NA4K(4),=NA4M(4)[7],
-    =NB7V(4)[6],=ND2T(3)[6],=NE4M(4),=NE8J(5),=NH0Y(3)[6],=NH6CN(4)[8],
-    =NI9K(5),=NJ2P(3)[6],=NJ4I(4),=NL7FK(4)[8],=NN7A(4)[7],=NO9E(5),
-    =NP2CB(5)[8],=NP3D(5)[8],=NQ4U(4),=NS0I(5)[8],=NS2X(4),=NT4TT(3)[6],
-    =NU4B(4),=NU4N(4),=NV4B(4),=NW7O(4)[6],=NW8U(5),=NX9T(5),=NY4N(4),
-    =W0AH(5)[8],=W0BR(5)[8],=W0ID(5)[8],=W0JLC(5)[8],=W0QQG(5)[8],
-    =W0UCE(5)[8],=W0YK(3)[6],=W0YR(5)[8],=W1ESE(3)[6],=W1NN(4),=W1RET(4),
-    =W1RH(3)[6],=W1SKU(4),=W1YY(3)[6],=W2OO(4),=W2PK(3)[6],=W2VJN(3)[6],
-    =W2WB(3)[6],=W3CP(3)[6],=W3FAF(4)[7],=W3HDH(4),=W3HKK(4),=W3IQ(4),
-    =W4BCG(4),=W4CID(4),=W4CKD(4),=W4DAN(4),=W4DEC(4),=W4DIM(4),=W4DVG(4),
-    =W4EEH(4),=W4FIN(4),=W4GHD(4),=W4GKM(4),=W4HRC(4),=W4JSI(4),=W4KW(4),
-    =W4LC(4),=W4LSC(3)[6],=W4NBS(4),=W4NI(4),=W4NJK(3)[6],=W4NL(4),=W4NTI(4),
-    =W4NZ(4),=W4PA(4),=W4PV(4),=W4RJ(4),=W4RK(4)[7],=W4RYW(4),=W4SK(4),
-    =W4UAT(3)[6],=W4UDX(4),=W4UHF(4),=W4UR(4),=W4WL(4),=W4YOK(4)[7],
+    =KE4MBP(4),=KE7NO(4)[6],=KF7NN(5)[8],=KG4CUY(4),=KG4NOZ(4),=KG4W,
+    =KG7HF(5)[8],=KH6DX(3)[6],=KH7WW(3)[6],=KL1SE(4)[8],=KL7OO(3)[6],
+    =KL7WP(3)[6],=KN4Q(4),=KN5H(3)[6],=KN6RO(5)[8],=KO7X(4)[7],=KP2F(4)[8],
+    =KP3M(5)[8],=KR4F(4),=KR4TI(4),=KS4X(4),=KS5A(3)[6],=KS7T(4)[6],
+    =KT2Z(4)[7],=KU1CW(4)[7],=KU8E(5),=KV6O(4)[7],=KV9R(5),=KY4F(4),=KZ4V(4),
+    =KZ5OH(5)[8],=N1CC(4)[7],=N1QXV(4)[7],=N1WQ(4)[7],=N2BJ(4),=N2BZP(3)[6],
+    =N2IC(4)[7],=N2NS(4),=N2WN(4),=N3BB(4)[7],=N3HE(4),=N3KCJ(3)[6],
+    =N3PV(3)[6],=N3ZI(3)[6],=N3ZZ(3)[6],=N4ARO(4),=N4AU(4),=N4BCB(4),
+    =N4CB(3)[6],=N4CBK(4),=N4CYV(4)[7],=N4DW(4),=N4ECJ(4),=N4GK(4),=N4GN(4),
+    =N4HID(4),=N4IJ(4)[7],=N4IR(4),=N4JF(4),=N4KC(4),=N4KG(4),=N4KZ(4),
+    =N4LS(3)[6],=N4LW(4),=N4NM(4),=N4NO(4),=N4OGW(4)[7],=N4PF(4),=N4PT(3)[6],
+    =N4QS(4),=N4RR(4),=N4SL(3)[6],=N4TN(4),=N4TZ(4),=N4UC(4),=N4VN(4),
+    =N4VV(4),=N4XM(4),=N4ZI(4),=N4ZZ(4),=N5RA(3)[6],=N5VI(5)[8],=N6AR(5)[8],
+    =N6CY(5)[8],=N6DT(4)[7],=N6RFM(5)[8],=N6ZO(5)[8],=N7DF(4)[7],=N7IV(4)[7],
+    =N7KA(4)[7],=N7NG(4)[6],=N7VR(4)[6],=N8GZ(3)[6],=N8II(5),=N8NA(5),
+    =N8PR(5),=N8RA(5),=N8RR(5),=N8WXQ(5),=N9ADG(3)[6],=N9JRZ(5),=NA4C(4),
+    =NA4K(4),=NA4M(4)[7],=NB7V(4)[6],=ND2T(3)[6],=NE4M(4),=NE8J(5),
+    =NH0Y(3)[6],=NH6CN(4)[8],=NI9K(5),=NJ2P(3)[6],=NJ4I(4),=NL7FK(4)[8],
+    =NN7A(4)[7],=NO9E(5),=NP2CB(5)[8],=NP3D(5)[8],=NQ4U(4),=NS0I(5)[8],
+    =NS2X(4),=NT4TT(3)[6],=NU4B(4),=NU4N(4),=NV4B(4),=NW7O(4)[6],=NW8U(5),
+    =NX9T(5),=NY4N(4),=W0AH(5)[8],=W0BR(5)[8],=W0ID(5)[8],=W0JLC(5)[8],
+    =W0QQG(5)[8],=W0UCE(5)[8],=W0YK(3)[6],=W0YR(5)[8],=W1ESE(3)[6],=W1NN(4),
+    =W1RET(4),=W1RH(3)[6],=W1SKU(4),=W1YY(3)[6],=W2OO(4),=W2PK(3)[6],
+    =W2VJN(3)[6],=W2WB(3)[6],=W3CP(3)[6],=W3FAF(4)[7],=W3HDH(4),=W3HKK(4),
+    =W3IQ(4),=W4BCG(4),=W4CID(4),=W4CKD(4),=W4DAN(4),=W4DEC(4),=W4DIM(4),
+    =W4DVG(4),=W4EEH(4),=W4FIN(4),=W4GHD(4),=W4GKM(4),=W4HRC(4),=W4JSI(4),
+    =W4KW(4),=W4LC(4),=W4LSC(3)[6],=W4NBS(4),=W4NI(4),=W4NJK(3)[6],=W4NL(4),
+    =W4NTI(4),=W4NZ(4),=W4PA(4),=W4PV(4),=W4RJ(4),=W4RK(4)[7],=W4RYW(4),
+    =W4SK(4),=W4UAT(3)[6],=W4UDX(4),=W4UHF(4),=W4UR(4),=W4WL(4),=W4YOK(4)[7],
     =W5JBV(5)[8],=W5JR(3)[6],=W5REA(5)[8],=W6AAN(5)[8],=W6IHG(5)[8],
     =W6IZT(5)[8],=W6LFB(4)[7],=W6NRJ(5)[8],=W6NWS(5)[8],=W6PU(4)[7],
     =W6TER(4)[7],=W6UB(4)[8],=W6XR(5)[8],=W7DO(5)[8],=W7ED(4)[6],=W7FG(4)[7],
@@ -509,7 +510,7 @@ United States:            05:  08:  NA:   43.00:    87.90:     5.0:  K:
 Guantanamo Bay:           08:  11:  NA:   19.90:    75.20:     5.0:  KG4:
     KG4;
 Mariana Is.:              27:  64:  OC:   15.20:  -145.80:   -10.0:  KH0:
-    AH0,KH0,NH0,WH0,=KG6SL,=VERSION;
+    AH0,KH0,NH0,WH0,=KG6SL;
 Baker & Howland Is.:      31:  61:  OC:    0.50:   176.00:    12.0:  KH1:
     AH1,KH1,NH1,WH1;
 Guam:                     27:  64:  OC:   13.50:  -144.80:   -10.0:  KH2:
@@ -541,7 +542,7 @@ Virgin Is.:               08:  11:  NA:   18.30:    64.90:     4.0:  KP2:
 Puerto Rico:              08:  11:  NA:   18.50:    66.20:     4.0:  KP4:
     KP3,KP4,NP3,NP4,WP3,WP4;
 Desecheo I.:              08:  11:  NA:   18.30:    67.50:     4.0:  KP5:
-    KP5,NP5,WP5;
+    KP5,NP5,WP5,=K5D;
 Norway:                   14:  18:  EU:   60.00:   -10.70:    -1.0:  LA:
     LA,LB,LC,LD,LE,LF,LG,LH,LI,LJ,LK,LL,LM,LN;
 Argentina:                13:  14:  SA:  -34.60:    58.40:     3.0:  LU:
@@ -550,38 +551,40 @@ Argentina:                13:  14:  SA:  -34.60:    58.40:     3.0:  LU:
     =L30EY/D,=L30EY/V,=L40E/D,=L44D/D,=L80AA/D,=L84VI/D,=L8D/X,=LO0D/D,
     =LO7E/D,=LU/DH4PB/R,=LU/DH4PB/S,=LU1AEE/D,=LU1AF/D,=LU1CDP/D,=LU1DHO/D,
     =LU1DK/D,=LU1DMA/E,=LU1DZ/E,=LU1DZ/P,=LU1DZ/Q,=LU1DZ/R,=LU1DZ/S,=LU1DZ/X,
-    =LU1EJ/W,=LU1EQ/D,=LU1EUU/W,=LU1EYW/D,=LU1OFN/I,=LU1VOF/D,=LU1VZ/V,
-    =LU1XAW/X,=LU1XWC/E,=LU1XY/X,=LU1YU/D,=LU1YY/Y,=LU2CRM/XA,=LU2DT/D,
-    =LU2DT/LH,=LU2DVI/H,=LU2EE/D,=LU2EE/E,=LU2EJB/X,=LU2VC/D,=LU2VDV/D,
-    =LU2WV/O,=LU2XX/X,=LU3CQ/D,=LU3DC/D,=LU3DJI/D,=LU3DJI/W,=LU3DOC/D,
-    =LU3DR/D,=LU3DR/V,=LU3DXG/D,=LU3DZO/D,=LU3EOU/D,=LU3ES/D,=LU3ES/V,
-    =LU3ES/W,=LU3HKA/D,=LU3HKA/H,=LU4AAO/D,=LU4DA/D,=LU4DBP/D,=LU4DBT/D,
-    =LU4DQ/D,=LU4DRC/Y,=LU4DRH/D,=LU4DRH/E,=LU4EHP/V,=LU4EJ/D,=LU4ELE/D,
-    =LU4ESP/D,=LU4ETN/D,=LU4ETN/W,=LU4EV/Q,=LU4UZW/D,=LU4WG/W,=LU5BE/D,
-    =LU5BOJ/O,=LU5DEM/D,=LU5DEM/V,=LU5DEM/W,=LU5DIT/D,=LU5DIT/V,=LU5DIT/W,
-    =LU5DRV/D,=LU5DRV/V,=LU5DT/D,=LU5DV/D,=LU5DWS/D,=LU5EAO/D,=LU5EFX/Y,
-    =LU5EJL/D,=LU5EWO/D,=LU5FZ/D,=LU5VAT/D,=LU5XC/X,=LU6DBL/D,=LU6DBL/W,
-    =LU6DKT/D,=LU6DRD/D,=LU6DRD/E,=LU6DRN/D,=LU6DRR/D,=LU6EC/W,=LU6EJJ/D,
-    =LU6EPE/D,=LU6EPR/D,=LU6EPR/E,=LU6EU/D,=LU6EYK/X,=LU6JJ/D,=LU6UAL/D,
-    =LU6UO/D,=LU6UO/P,=LU6UO/Q,=LU6UO/R,=LU6UO/S,=LU6UO/X,=LU6XAH/X,=LU7AC/D,
-    =LU7BTO/D,=LU7DBL/D,=LU7DID/V,=LU7DID/Y,=LU7DIR/D,=LU7DJJ/W,=LU7DP/D,
-    =LU7DR/D,=LU7DSY/D,=LU7DSY/V,=LU7DSY/W,=LU7DW/D,=LU7DZL/D,=LU7DZL/E,
-    =LU7EGH/V,=LU7EGY/D,=LU7EHL/D,=LU7EO/D,=LU7EPC/D,=LU7EPC/W,=LU7HW/D,
-    =LU7VCH/D,=LU7WFM/W,=LU7WW/W,=LU8ADX/D,=LU8DCH/D,=LU8DCH/Q,=LU8DIP/D,
-    =LU8DR/D,=LU8DRA/W,=LU8DRH/D,=LU8DSJ/D,=LU8DWR/D,=LU8DWR/V,=LU8EBJ/D,
-    =LU8EBJ/E,=LU8EBK/D,=LU8EBK/E,=LU8ECF/D,=LU8ECF/E,=LU8EEM/D,=LU8EFF/D,
-    =LU8EGS/D,=LU8EHQ/D,=LU8EHQ/E,=LU8EHQ/W,=LU8EKB/W,=LU8EKC/D,=LU8EOT/X,
+    =LU1EEZ/D,=LU1EJ/W,=LU1EQ/D,=LU1EUU/W,=LU1EYW/D,=LU1OFN/I,=LU1VOF/D,
+    =LU1VZ/V,=LU1WCR/W,=LU1WF/W,=LU1WP/W,=LU1XAW/X,=LU1XWC/E,=LU1XY/X,
+    =LU1YU/D,=LU1YY/Y,=LU2AGQ/D,=LU2CRM/XA,=LU2DT/D,=LU2DT/LH,=LU2DVI/H,
+    =LU2EE/D,=LU2EE/E,=LU2EJB/X,=LU2VC/D,=LU2VCD/V,=LU2VDV/D,=LU2WV/O,
+    =LU2XBI/XA,=LU2XX/X,=LU3CQ/D,=LU3DC/D,=LU3DJI/D,=LU3DJI/W,=LU3DOC/D,
+    =LU3DR/D,=LU3DR/V,=LU3DXG/D,=LU3DXG/W,=LU3DZO/D,=LU3EOU/D,=LU3ES/D,
+    =LU3ES/V,=LU3ES/W,=LU3HKA/D,=LU3HKA/H,=LU4AAO/D,=LU4DA/D,=LU4DBP/D,
+    =LU4DBT/D,=LU4DQ/D,=LU4DRC/Y,=LU4DRH/D,=LU4DRH/E,=LU4EHP/V,=LU4EJ/D,
+    =LU4ELE/D,=LU4ESP/D,=LU4ETN/D,=LU4ETN/W,=LU4EV/Q,=LU4UZW/D,=LU4WG/W,
+    =LU5BE/D,=LU5BE/XA,=LU5BOJ/O,=LU5DEM/D,=LU5DEM/V,=LU5DEM/W,=LU5DIT/D,
+    =LU5DIT/V,=LU5DIT/W,=LU5DRV/D,=LU5DRV/V,=LU5DT/D,=LU5DV/D,=LU5DWS/D,
+    =LU5EAO/D,=LU5EFX/Y,=LU5EJL/D,=LU5EWO/D,=LU5FZ/D,=LU5VAT/D,=LU5XC/X,
+    =LU6DBL/D,=LU6DBL/W,=LU6DDC/D,=LU6DG/D,=LU6DKT/D,=LU6DRD/D,=LU6DRD/E,
+    =LU6DRN/D,=LU6DRR/D,=LU6DTB/D,=LU6EC/W,=LU6EJJ/D,=LU6EPE/D,=LU6EPR/D,
+    =LU6EPR/E,=LU6EU/D,=LU6EYK/X,=LU6JJ/D,=LU6UAL/D,=LU6UO/D,=LU6UO/P,
+    =LU6UO/Q,=LU6UO/R,=LU6UO/S,=LU6UO/X,=LU6WG/W,=LU6XAH/X,=LU7AC/D,=LU7BTO/D,
+    =LU7DBL/D,=LU7DID/V,=LU7DID/Y,=LU7DIR/D,=LU7DJJ/W,=LU7DP/D,=LU7DR/D,
+    =LU7DSY/D,=LU7DSY/V,=LU7DSY/W,=LU7DW/D,=LU7DZL/D,=LU7DZL/E,=LU7EGH/V,
+    =LU7EGY/D,=LU7EHL/D,=LU7EO/D,=LU7EPC/D,=LU7EPC/W,=LU7HW/D,=LU7VCH/D,
+    =LU7WFM/W,=LU7WW/W,=LU8ADX/D,=LU8DCH/D,=LU8DCH/Q,=LU8DIP/D,=LU8DR/D,
+    =LU8DRA/W,=LU8DRH/D,=LU8DSJ/D,=LU8DWR/D,=LU8DWR/V,=LU8EBJ/D,=LU8EBJ/E,
+    =LU8EBK/D,=LU8EBK/E,=LU8ECF/D,=LU8ECF/E,=LU8EEM/D,=LU8EFF/D,=LU8EGS/D,
+    =LU8EHQ/D,=LU8EHQ/E,=LU8EHQ/W,=LU8EHV/D,=LU8EKB/W,=LU8EKC/D,=LU8EOT/X,
     =LU8EOT/Y,=LU8ERH/D,=LU8EXJ/D,=LU8EXN/D,=LU8FOZ/V,=LU8VCC/D,=LU8WFT/Q,
-    =LU8XC/X,=LU8XW/X,=LU8XW/XD,=LU9ARB/D,=LU9AUC/D,=LU9DBK/X,=LU9DKX/X,
-    =LU9DPD/XA,=LU9EI/F,=LU9EJS/E,=LU9ESD/D,=LU9ESD/F,=LU9ESD/V,=LU9ESD/W,
-    =LU9ESD/Y,=LU9EV/LH,=LU9JMG/J,=LW1DAL/D,=LW1EXU/D,=LW1EXU/Y,=LW2DX/E,
-    =LW2DX/P,=LW2DX/Q,=LW2DX/R,=LW2DX/S,=LW2DX/Y,=LW2EFS/D,=LW2ENB/D,
-    =LW3DKC/D,=LW3DKC/E,=LW3DKO/D,=LW3DKO/E,=LW3HAQ/D,=LW4DRH/D,=LW4DRH/E,
-    =LW4DRV/D,=LW4ECV/D,=LW4EM/E,=LW4EM/LH,=LW5DR/LH,=LW5DWX/D,=LW5EE/D,
-    =LW5EE/V,=LW5EOL/D,=LW6DTM/D,=LW7DAF/D,=LW7DAF/W,=LW7DLY/D,=LW7DNS/E,
-    =LW7EJV/D,=LW7WFM/W,=LW8DMK/D,=LW8DMK/W,=LW8EAG/D,=LW8ECQ/D,=LW8EU/D,
-    =LW8EXF/D,=LW9DCF/Y,=LW9DX/D,=LW9EAG/D,=LW9EAG/V,=LW9EAG/W,=LW9EVA/D,
-    =LW9EVA/E;
+    =LU8XC/X,=LU8XW/X,=LU8XW/XD,=LU9ARB/D,=LU9AUC/D,=LU9DBK/X,=LU9DF/D,
+    =LU9DKX/X,=LU9DO/D,=LU9DPD/XA,=LU9EI/F,=LU9EJS/E,=LU9ESD/D,=LU9ESD/F,
+    =LU9ESD/V,=LU9ESD/W,=LU9ESD/Y,=LU9EV/D,=LU9EV/LH,=LU9JMG/J,=LW1DAL/D,
+    =LW1DE/D,=LW1EXU/D,=LW1EXU/Y,=LW2DX/E,=LW2DX/P,=LW2DX/Q,=LW2DX/R,=LW2DX/S,
+    =LW2DX/Y,=LW2EFS/D,=LW2ENB/D,=LW3DKC/D,=LW3DKC/E,=LW3DKO/D,=LW3DKO/E,
+    =LW3HAQ/D,=LW4DRH/D,=LW4DRH/E,=LW4DRV/D,=LW4ECV/D,=LW4EM/E,=LW4EM/LH,
+    =LW5DR/LH,=LW5DWX/D,=LW5EE/D,=LW5EE/V,=LW5EOL/D,=LW6DTM/D,=LW7DAF/D,
+    =LW7DAF/W,=LW7DLY/D,=LW7DNS/E,=LW7EDH/D,=LW7EJV/D,=LW7WFM/W,=LW8DMK/D,
+    =LW8DMK/W,=LW8EAG/D,=LW8ECQ/D,=LW8EU/D,=LW8EXF/D,=LW9DCF/Y,=LW9DX/D,
+    =LW9EAG/D,=LW9EAG/V,=LW9EAG/W,=LW9EVA/D,=LW9EVA/E;
 Luxembourg:               14:  27:  EU:   49.60:    -6.20:    -1.0:  LX:
     LX;
 Lithuania:                15:  29:  EU:   54.50:   -25.50:    -2.0:  LY:
@@ -593,7 +596,8 @@ Peru:                     10:  12:  SA:  -10.00:    76.00:     5.0:  OA:
 Lebanon:                  20:  39:  AS:   33.80:   -35.80:    -2.0:  OD:
     OD;
 Austria:                  15:  28:  EU:   47.30:   -13.30:    -1.0:  OE:
-    OE,=4U1VIC,=4U1WED;
+    OE,=4U1VIC,=4U1WED,=OE3AGA/AAW,=OE3AIS/AAW,=OE3HM/AAW,=OE3KKA/AAW,
+    =OE3RPB/AAW,=OE3SGA/AAW,=OE3WWB/AAW;
 Finland:                  15:  18:  EU:   60.20:   -25.00:    -2.0:  OH:
     OF,OG,OH,OI,OJ;
 Aland Is.:                15:  18:  EU:   60.20:   -20.00:    -2.0:  OH0:
@@ -782,7 +786,8 @@ Uzbekistan:               17:  30:  AS:   41.20:   -69.30:    -5.0:  UK:
 Kazakhstan:               17:  30:  AS:   43.30:   -76.90:    -5.0:  UN:
     UN,UO,UP,UQ;
 Ukraine:                  16:  29:  EU:   50.40:   -30.50:    -2.0:  UR:
-    EM,EN,EO,U5,UR,US,UT,UU,UV,UW,UX,UY,UZ;
+    EM,EN,EO,U5,UR,US,UT,UU,UV,UW,UX,UY,UZ,=UR2XO/WAP,=UR5KCC/WAP,=UR5KGG/WAP,
+    =UR8LV/WAP,=UT1KY/WAP,=UT7UA/WAP;
 Antigua & Barbuda:        08:  11:  NA:   17.10:    61.80:     4.0:  V2:
     V2;
 Belize:                   07:  11:  NA:   17.30:    88.80:     6.0:  V3:
@@ -805,8 +810,8 @@ Canada:                   05:  09:  NA:   45.00:    80.00:     4.0:  VE:
     XM,XN1(5)[9],XN2(2)[9],XO0(2)[4],XO1(1)[2],XO2(5)[9],=CY2ZT/2(5),
     =K3FMQ/VE2(2),=KD3RF/VE2(2),=KD3TB/VE2(2),=VA2BY(2),=VA2CT(2),=VA2DO(2),
     =VA2DXE(2),=VA2KCE(2),=VA2RHJ(2),=VA2UA(2),=VA2VFT(2),=VA2ZM(2),
-    =VA3NA/2(2),=VB2C(2),=VB2R(2),=VB2V(2),=VC2C(2),=VE2/K3FMQ(2),=VE2ACP(2),
-    =VE2AE(2),=VE2AG(2),=VE2AOF(2),=VE2AQS(2),=VE2AS(2),=VE2BQB(2),=VE2CSI(2),
+    =VA3NA/2(2),=VB2C(2),=VB2R(2),=VB2V(2),=VC2C(2),=VE2/K3FMQ(2),=VE2AE(2),
+    =VE2AG(2),=VE2AOF(2),=VE2AQS(2),=VE2AS(2),=VE2BQB(2),=VE2CSI(2),
     =VE2CVI(2),=VE2DMG(2),=VE2DS(2),=VE2DWU(2),=VE2DXY(2),=VE2DYW(2),
     =VE2DYX(2),=VE2EAK(2),=VE2EDL(2),=VE2EDX(2),=VE2ELL(2),=VE2ENB(2),
     =VE2END(2),=VE2ENR(2),=VE2ERU(2),=VE2FCV(2),=VE2GSA(2),=VE2GSO(2),
@@ -814,7 +819,7 @@ Canada:                   05:  09:  NA:   45.00:    80.00:     4.0:  VE:
     =VE2PR(2),=VE2QRZ(2),=VE2RB(2),=VE2TVU(2),=VE2UA(2),=VE2VH(2),=VE2WDX(2),
     =VE2WT(2),=VE2XAA/2(2),=VE2XY(2),=VE2YM(2),=VE2Z(2),=VE2ZC(5),=VE2ZM(5),
     =VE2ZV(5),=VE3EY/2(2),=VE3NE/2(2),=VE3RHJ/2(2),=VE8AJ(2),=VE8PW(2),
-    =VE8RCS(2),=VER20081209,=VY0AA(4)[3],=VY0PW(4)[3],=VY2MGY/3(4)[4];
+    =VE8RCS(2),=VER20090219,=VY0AA(4)[3],=VY0PW(4)[3],=VY2MGY/3(4)[4];
 Australia:                30:  59:  OC:  -22.00:  -135.00:   -10.0:  VK:
     AX,VH,VI,VJ,VK,VL,VM,VN,VZ;
 Heard I.:                 39:  68:  AF:  -53.00:   -73.40:    -5.0:  VK0H:
@@ -828,13 +833,13 @@ Lord Howe I.:             30:  60:  OC:  -31.60:  -159.10:   -10.5:  VK9L:
     AX9L,VH9L,VI9L,VJ9L,VK9AL,VK9CL,VK9FL,VK9GL,VK9KL,VK9L,VL9L,VM9L,VN9L,
     VZ9L;
 Mellish Reef:             30:  56:  OC:  -17.60:  -155.80:   -10.0:  VK9M:
-    AX9M,VH9M,VI9M,VJ9M,VK9FM,VK9KM,VK9M,VL9M,VM9M,VN9M,VZ9M;
+    AX9M,VH9M,VI9M,VJ9M,VK9FM,VK9KM,VK9M,VL9M,VM9M,VN9M,VZ9M,=VK9GMW;
 Norfolk I.:               32:  60:  OC:  -29.00:  -168.00:   -11.5:  VK9N:
     AX9,VH9,VI9,VJ9,VK9,VK9CN,VL9,VM9,VN9,VZ9;
 Willis I.:                30:  55:  OC:  -16.20:  -150.00:   -10.0:  VK9W:
     AX9W,VH9W,VI9W,VJ9W,VK9FW,VK9KW,VK9W,VL9W,VM9W,VN9W,VZ9W,=VK9DWX;
 Christmas I.:             29:  54:  OC:  -10.50:  -105.70:    -7.0:  VK9X:
-    AX9X,VH9X,VI9X,VJ9X,VK9FX,VK9KX,VK9X,VL9X,VM9X,VN9X,VZ9X;
+    AX9X,VH9X,VI9X,VJ9X,VK9FX,VK9KX,VK9X,VL9X,VM9X,VN9X,VZ9X,=JA1XGI/VK9;
 Anguilla:                 08:  11:  NA:   18.30:    63.00:     4.0:  VP2E:
     VP2E;
 Montserrat:               08:  11:  NA:   16.80:    62.20:     4.0:  VP2M:
@@ -852,8 +857,8 @@ Falkland Is.:             13:  16:  SA:  -51.70:    57.90:     4.0:  VP8:
 South Georgia:            13:  73:  SA:  -54.30:    36.80:     2.0:  VP8/g:
     =VP8DIF,=VP8SGK;
 South Shetland:           13:  73:  SA:  -62.00:    58.30:     4.0:  VP8/h:
-    CE9,=DT8A,=ED3RKL,=HF0APAS,=HF0POL,=HL8KSJ,=LU1ZC,=LZ0A,=R1ANF,=VP8/LZ1UQ,
-    =VP8DJK;
+    CE9,=DT8A,=ED3RKL,=HC0/FT5YJ,=HF0APAS,=HF0POL,=HL8KSJ,=LU1ZC,=LZ0A,
+    =OA0/FT5YJ,=R1ANF,=VP8/LZ1UQ,=VP8DJK;
 South Orkney:             13:  73:  SA:  -60.00:    45.50:     3.0:  VP8/o:
     =AY1ZA,=LU1ZA;
 South Sandwich:           13:  73:  SA:  -57.00:    26.70:     2.0:  VP8/s:
index 03399006677678ac38d331df9ed544bacdba6eaa..e10c1d7069c6fd15dd485bb4e74837105e6b0cf0 100644 (file)
   '=4U7ITU' => '274',
   '=4U8ITU' => '274',
   '=4U9ITU' => '274',
-  '=5K0T' => '77',
   '=8J1RF' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=8J1RL' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
-  '=9M1CSQ' => '303,529',
-  '=9M1CSS' => '303,529',
   '=9M4SEA' => '303,529',
   '=9M4SMO' => '303,529',
   '=AA6DY' => '220',
   '=AY5E/D' => '119,398,399',
   '=AY7DSY/D' => '119,398,399',
   '=CE9/K2ARB' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
+  '=CE9XX' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=CY2ZT/2' => '191',
   '=DJ4SN/LU/X' => '119,398,399',
   '=DP0GVN' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=GB2000SET' => '66',
   '=GB200A' => '66',
   '=GB200HNT' => '66',
+  '=GB250RB' => '64,353',
   '=GB2AGG' => '64,353',
   '=GB2ANG' => '66',
   '=GB2AST' => '64,353',
   '=GB2GU' => '65',
   '=GB2HDG' => '66',
   '=GB2HI' => '64,353',
+  '=GB2HLB' => '64,353',
   '=GB2HRH' => '64,353',
   '=GB2HST' => '64,353',
   '=GB2HSW' => '64,353',
   '=GB5JS' => '64,353',
   '=GB5MOB' => '61',
   '=GB5OL' => '64,353',
+  '=GB5ONG' => '66',
   '=GB5RO' => '64,353',
   '=GB5SI' => '64,353',
   '=GB5SIP' => '66',
   '=GM8LNH' => '353',
   '=GM8MMA' => '353',
   '=GM8YEC' => '353',
+  '=HC0/FT5YJ' => '208',
   '=HF0APAS' => '208',
   '=HF0POL' => '208',
   '=HK0TU' => '76',
   '=HL8KSJ' => '208',
+  '=II0SB' => '85',
   '=IQ0AG' => '85',
   '=IQ0AH' => '85',
   '=IQ0AI' => '85',
   '=IQ0HO' => '85',
   '=IQ0QP' => '85',
   '=IQ0SS' => '85',
+  '=JA1XGI/VK9' => '194',
   '=JD1BME' => '93',
   '=JD1BMM' => '93',
+  '=JD1BND' => '93',
   '=JD1YAA' => '93',
   '=JD1YBJ' => '93',
   '=JW2FL' => '366',
   '=K4XG' => '220',
   '=K4XU' => '220',
   '=K4ZGB' => '220',
+  '=K5D' => '116',
   '=K5EK' => '220',
   '=K5KG' => '220',
   '=K5MA' => '220',
   '=KF7NN' => '220',
   '=KG4CUY' => '220',
   '=KG4NOZ' => '220',
+  '=KG4W' => '220',
   '=KG6ASO' => '102',
   '=KG6DX' => '102',
   '=KG6SL' => '111',
   '=LO7E/D' => '119,398,399',
   '=LU/DH4PB/R' => '119,398,399',
   '=LU/DH4PB/S' => '119,398,399',
+  '=LU/FT5YJ' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=LU1AEE/D' => '119,398,399',
   '=LU1AF/D' => '119,398,399',
   '=LU1CDP/D' => '119,398,399',
   '=LU1DZ/R' => '119,398,399',
   '=LU1DZ/S' => '119,398,399',
   '=LU1DZ/X' => '119,398,399',
+  '=LU1EEZ/D' => '119,398,399',
   '=LU1EJ/W' => '119,398,399',
   '=LU1EQ/D' => '119,398,399',
   '=LU1EUU/W' => '119,398,399',
   '=LU1OFN/I' => '119,398,399',
   '=LU1VOF/D' => '119,398,399',
   '=LU1VZ/V' => '119,398,399',
+  '=LU1WCR/W' => '119,398,399',
+  '=LU1WF/W' => '119,398,399',
+  '=LU1WP/W' => '119,398,399',
   '=LU1XAW/X' => '119,398,399',
   '=LU1XWC/E' => '119,398,399',
   '=LU1XY/X' => '119,398,399',
   '=LU1YY/Y' => '119,398,399',
   '=LU1ZA' => '206',
   '=LU1ZC' => '208',
+  '=LU2AGQ/D' => '119,398,399',
   '=LU2CRM/XA' => '119,398,399',
   '=LU2DT/D' => '119,398,399',
   '=LU2DT/LH' => '119,398,399',
   '=LU2EE/E' => '119,398,399',
   '=LU2EJB/X' => '119,398,399',
   '=LU2VC/D' => '119,398,399',
+  '=LU2VCD/V' => '119,398,399',
   '=LU2VDV/D' => '119,398,399',
   '=LU2WV/O' => '119,398,399',
+  '=LU2XBI/XA' => '119,398,399',
   '=LU2XX/X' => '119,398,399',
   '=LU3CQ/D' => '119,398,399',
   '=LU3DC/D' => '119,398,399',
   '=LU3DR/D' => '119,398,399',
   '=LU3DR/V' => '119,398,399',
   '=LU3DXG/D' => '119,398,399',
+  '=LU3DXG/W' => '119,398,399',
   '=LU3DZO/D' => '119,398,399',
   '=LU3EOU/D' => '119,398,399',
   '=LU3ES/D' => '119,398,399',
   '=LU4WG/W' => '119,398,399',
   '=LU4ZS' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=LU5BE/D' => '119,398,399',
+  '=LU5BE/XA' => '119,398,399',
   '=LU5BOJ/O' => '119,398,399',
   '=LU5DEM/D' => '119,398,399',
   '=LU5DEM/V' => '119,398,399',
   '=LU5XC/X' => '119,398,399',
   '=LU6DBL/D' => '119,398,399',
   '=LU6DBL/W' => '119,398,399',
+  '=LU6DDC/D' => '119,398,399',
+  '=LU6DG/D' => '119,398,399',
   '=LU6DKT/D' => '119,398,399',
   '=LU6DRD/D' => '119,398,399',
   '=LU6DRD/E' => '119,398,399',
   '=LU6DRN/D' => '119,398,399',
   '=LU6DRR/D' => '119,398,399',
+  '=LU6DTB/D' => '119,398,399',
   '=LU6EC/W' => '119,398,399',
   '=LU6EJJ/D' => '119,398,399',
   '=LU6EPE/D' => '119,398,399',
   '=LU6UO/R' => '119,398,399',
   '=LU6UO/S' => '119,398,399',
   '=LU6UO/X' => '119,398,399',
+  '=LU6WG/W' => '119,398,399',
   '=LU6XAH/X' => '119,398,399',
   '=LU7AC/D' => '119,398,399',
   '=LU7BTO/D' => '119,398,399',
   '=LU8EHQ/D' => '119,398,399',
   '=LU8EHQ/E' => '119,398,399',
   '=LU8EHQ/W' => '119,398,399',
+  '=LU8EHV/D' => '119,398,399',
   '=LU8EKB/W' => '119,398,399',
   '=LU8EKC/D' => '119,398,399',
   '=LU8EOT/X' => '119,398,399',
   '=LU9ARB/D' => '119,398,399',
   '=LU9AUC/D' => '119,398,399',
   '=LU9DBK/X' => '119,398,399',
+  '=LU9DF/D' => '119,398,399',
   '=LU9DKX/X' => '119,398,399',
+  '=LU9DO/D' => '119,398,399',
   '=LU9DPD/XA' => '119,398,399',
   '=LU9EI/F' => '119,398,399',
   '=LU9EJS/E' => '119,398,399',
   '=LU9ESD/V' => '119,398,399',
   '=LU9ESD/W' => '119,398,399',
   '=LU9ESD/Y' => '119,398,399',
+  '=LU9EV/D' => '119,398,399',
   '=LU9EV/LH' => '119,398,399',
   '=LU9JMG/J' => '119,398,399',
   '=LW1DAL/D' => '119,398,399',
+  '=LW1DE/D' => '119,398,399',
   '=LW1EXU/D' => '119,398,399',
   '=LW1EXU/Y' => '119,398,399',
   '=LW2DX/E' => '119,398,399',
   '=LW7DAF/W' => '119,398,399',
   '=LW7DLY/D' => '119,398,399',
   '=LW7DNS/E' => '119,398,399',
+  '=LW7EDH/D' => '119,398,399',
   '=LW7EJV/D' => '119,398,399',
   '=LW7WFM/W' => '119,398,399',
   '=LW8DMK/D' => '119,398,399',
   '=NW8U' => '220',
   '=NX9T' => '220',
   '=NY4N' => '220',
+  '=OA0/FT5YJ' => '208',
+  '=OE3AGA/AAW' => '124',
+  '=OE3AIS/AAW' => '124',
+  '=OE3HM/AAW' => '124',
+  '=OE3KKA/AAW' => '124',
+  '=OE3RPB/AAW' => '124',
+  '=OE3SGA/AAW' => '124',
+  '=OE3WWB/AAW' => '124',
   '=OP0LE' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=OP0OL' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
+  '=OR3AX' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
+  '=OR4AX' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=R1ANF' => '208',
   '=R1ANR' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=R35NP' => '176,427,432',
   '=TX5CW' => '45,330',
   '=TX7LX' => '44',
   '=TX9' => '330',
+  '=UR2XO/WAP' => '177',
+  '=UR5KCC/WAP' => '177',
+  '=UR5KGG/WAP' => '177',
+  '=UR8LV/WAP' => '177',
+  '=UT1KY/WAP' => '177',
+  '=UT7UA/WAP' => '177',
   '=VA2BY' => '191',
   '=VA2CT' => '191',
   '=VA2DO' => '191',
   '=VB2V' => '191',
   '=VC2C' => '191',
   '=VE2/K3FMQ' => '191',
-  '=VE2ACP' => '191',
   '=VE2AE' => '191',
   '=VE2AG' => '191',
   '=VE2AOF' => '191',
   '=VE8AJ' => '191',
   '=VE8PW' => '191',
   '=VE8RCS' => '191',
-  '=VER20081209' => '191',
-  '=VERSION' => '111',
+  '=VER20090219' => '191',
+  '=VERSION' => '9',
   '=VK0HI' => '199',
   '=VK0IR' => '199',
   '=VK9AA' => '195',
   '=VK9DWX' => '198',
+  '=VK9GMW' => '196',
   '=VP6DX' => '331',
   '=VP8/LZ1UQ' => '208',
   '=VP8DIF' => '205',
   '=VP8DJK' => '208',
   '=VP8DKF' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=VP8DLJ' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
+  '=VP8DLM' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=VP8PJ' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=VP8ROT' => '99,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386',
   '=VP8SGK' => '205',
index 7dd45ac2acd0e6f479f705f6c419bec011ad3e7a..944658fa42bc8aaab2644531a9fc3fe1ccfce565 100644 (file)
@@ -11,6 +11,6 @@ use vars qw($version $subversion $build);
 
 $version = '1.55';
 $subversion = '0';
-$build = '36';
+$build = '37';
 
 1;