bufr.pm:bufrdump.pl_source

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

bufr.pm:bufrdump.pl_source [2018-04-25 08:41:39]
pals
bufr.pm:bufrdump.pl_source [2022-05-31 09:29:31]
Line 1: Line 1:
-<code perl bufrdump.pl> 
-#!/usr/bin/perl -w 
  
-# (C) Copyright 2010-2018, met.no 
-# 
-# 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 program; if not, write to the Free Software 
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
-# 02110-1301, USA. 
- 
-# pod included at end of file 
- 
-use strict; 
-use Getopt::Long; 
-use Pod::Usage qw(pod2usage); 
- 
-use constant DEFAULT_TABLE_PATH => '/usr/local/lib/bufrtables'; 
-my $BUFRDUMP = 'bufrdump'; # You should add path if bufrdump is 
-                           # installed in a non-standard place 
- 
-# Parse command line options 
-my %option = (); 
-GetOptions( 
-           \%option, 
-           'help', 
-           'tablepath=s', # Set BUFR table path 
-           'filter=s',    # Decode observations meeting the filter criteria specified only 
-           'param=s',     # Decode/print specified parameters only 
-           'csv',         # Use CSV format for printing 
-           'delimiter=s', # Choose the delimiter for the CSV format 
-           'sort',        # Sort on stationid (wmonr/nationalnr/call_sign/buoy_id/aircraft/wigosid) 
-           'sort_on=s',   # Sort on specified parameter 
-           'station=s',   # Filter on list of stations 
-           'transform=s', # Do the transformations in <transformation file> 
-           'lon1=i', 
-           'lat1=i', 
-           'lon2=i', 
-           'lat2=i', 
-           'obstype=s',   # Force observation type 
-       ) or pod2usage(-verbose => 0); 
- 
-# User asked for help 
-pod2usage(-verbose => 1) if $option{help}; 
- 
-# Make sure there is at least one input file 
-pod2usage(-verbose => 0) unless @ARGV; 
- 
-# --csv can only be used together with --param 
-pod2usage(-verbose => 0) if $option{csv} && !$option{param}; 
- 
-# Prevent ECMWF software from printing table info 
-$ENV{PRINT_TABLE_NAMES} = 'false'; 
- 
-# Set BUFR table path environment variable used by bufrdump 
-if ($option{tablepath}) { 
-    # Command line option --tablepath overrides all 
-    $ENV{BUFR_TABLES} = $option{tablepath}; 
-} elsif (!$ENV{BUFR_TABLES}) { 
-    $ENV{BUFR_TABLES} = DEFAULT_TABLE_PATH; 
-} 
-# ECMWF software requires trailing '/' in bufrpath 
-$ENV{BUFR_TABLES} .= '/' if substr($ENV{BUFR_TABLES},-1) ne '/'; 
- 
-die "Directory for BUFR tables: $ENV{BUFR_TABLES} does not exist" 
-    if ! -d $ENV{BUFR_TABLES}; 
- 
-my $obstype = $option{obstype} ? "--obstype $option{obstype}" : ''; 
-my $filt = $option{filter} ? "$option{filter}" : ''; 
-my $lon1 = $option{lon1} ? "--lon1 $option{lon1}" : ''; 
-my $lat1 = $option{lat1} ? "--lat1 $option{lat1}" : ''; 
-my $lon2 = $option{lon2} ? "--lon2 $option{lon2}" : ''; 
-my $lat2 = $option{lat2} ? "--lat2 $option{lat2}" : ''; 
-my $del = $option{delimiter} ? "$option{delimiter}" : ';'; 
-my $delimiter = $option{delimiter} ? "--delimiter $del" : ''; 
- 
-# Any filter criteria provided? 
-my ($filter, $criteria_ref, $num_alt_ref) = get_filter_conditions($filt); 
- 
-# Any specific stations requested? 
-my ($req_id, $req_stn_ref) = get_requested_stations($option{station}); 
- 
-# Any specific parameters specified? 
-my ($params_ref, $forced_params_ref) = get_params($option{param}); 
- 
-my $csv = $option{csv} ? 1 : 0; 
-# First line in CSV should be the parameters 
-print join($del, @$params_ref) . "\n" if $csv; 
- 
-# Any transformations of units specified? 
-my $transform_file = $option{transform} ? $option{transform} : 0; 
-my $transform_ref = read_transformation_file($transform_file); 
- 
-# Any sorting requested? 
-my $sort = $option{sort} ? 1 : 0; 
-my $sort_on = $option{sort_on} ? $option{sort_on} : ''; 
- 
-# What kind of sorting is required (if any)? 
-($sort_on, my $by) = get_sort_method($sort_on, $sort); 
- 
-# Loop for processing of BUFR input files 
-my %data_of = (); 
-foreach my $inputfname (@ARGV) { 
- 
-    # Dump the content of the BUFR file using the Fortran program $BUFRDUMP 
-    my $fortran_options = "$obstype $filter $lon1 $lat1 $lon2 $lat2"; 
-    my $dump = `$BUFRDUMP $fortran_options $inputfname`; 
-    die if $?; # Reason for bufrdump failing should have been printed to STDERR 
- 
-    # Then process the output from the dump 
-    my @lines = split /\n/, $dump; 
-    # Add an empty line to simplify processing 
-    push @lines, ''; 
-    my $stnid = ''; 
- 
-    # Skip first (blank) line 
-    shift @lines; 
- 
-    my @lines_to_print; 
-    my %msg; # Hash with parameter name as key, parameter value as value 
- 
-  LINE: 
-    while (defined(my $line = shift @lines)) { 
-        # Each new message starts with a blank line 
-        if ($line !~ /^\s*$/) { 
-            # Skip error messages from libbufr, which should start with space(s) 
-            next LINE if $line =~ /^\s+/; 
-            # Build up the message to be (possibly) printed 
-            my ($param, $value) = ($line =~ /^(.+)=\s*(.*?)\s*$/); 
-            # Know only of one case where next check is necessary: if 
-            # a CCITT IA5 value contains new line (\n) 
-            next LINE if !defined $value; 
-            if ($transform_file && $transform_ref->{$param}) { 
-                # Replace value with transformed value 
-                my $transform = $transform_ref->{$param}; 
-                $transform =~ s/\$x/$value/g; 
-                $value = eval $transform; 
-                die "Couldn't parse following transform:\n" 
-                    . $transform . "\nReason:\n$@" if $@; 
-                $line =~ s/=.*/=$value/; 
-            } 
-            $msg{$param} = $value; 
-            push @lines_to_print, $line; 
-        } 
- 
-        if ($line =~ /^\s*$/ or @lines == 0) { 
-            # A full message has been completed. Should it be printed? 
-            if ($filt && filter_obs(\%msg, $criteria_ref, $num_alt_ref)) { 
-                # Skip this message 
-            } elsif ($req_id && filter_station(\%msg, $req_id, $req_stn_ref)) { 
-                # Skip this station 
-            } else { 
-                # Print the message (or if --sort or --sort_on: save the message) 
-                my $txt = ''; 
-                if ($params_ref) { 
-                    # Print the params in @$params_ref if exists in 
-                    # message, in same order as in @$params_ref 
-                    foreach my $name (@$params_ref) { 
-                        if (exists $msg{$name}) { 
-                            $txt .= $csv ? $msg{$name} . $del : "$name=$msg{$name}\n"; 
-                        } elsif ($forced_params_ref->{$name}) { 
-                            $txt .= $csv ? '-32767' . $del : "$name=-32767\n"; 
-                        } elsif ($csv) { 
-                            $txt .= $del; 
-                        } 
-                    } 
-                } else { 
-                    foreach my $line2 (@lines_to_print) { 
-                        $line2 =~ s/=\s+/=/; 
-                        $txt .= $line2 . "\n"; 
-                    } 
-                } 
-                if ($csv) { 
-                    # Remove last $del 
-                    for (1 .. length($del)) { 
-                        chop $txt; 
-                    } 
-                } 
-                if ($txt) { 
-                    if ($sort) { 
-                        # Sort wmonr before nationalnr before call 
-                        # sign before buoy_id before aircraft before wigosid 
-                        if ($msg{wmonr}) { 
-                            $stnid = '00_' . $msg{wmonr}; 
-                        } elsif ($msg{nationalnr}) { 
-                            $stnid = '10_' . $msg{nationalnr}; 
-                        } elsif ($msg{call_sign}) { 
-                            $stnid = '20_' . $msg{call_sign}; 
-                        } elsif ($msg{buoy_id}) { 
-                            $stnid = '30_' . $msg{buoy_id}; 
-                        } elsif ($msg{aircraft}) { 
-                            $stnid = '40_' . $msg{aircraft}; 
-                        } elsif ($msg{wigosid}) { 
-                            $stnid = '50_' . $msg{wigosid}; 
-                        } else { 
-                            # Skip observation if no station identification found 
-                            next LINE; 
-                        } 
-                        if ($sort_on) { 
-                            my $val = exists $msg{$sort_on} ? $msg{$sort_on} : ''; 
-                            my $key = $stnid . '|' . $val; 
-                            $data_of{$key} = exists $data_of{$key} 
-                            ? $data_of{$key} . "$txt\n" : "$txt\n"; 
-                        } else { 
-                            $data_of{$stnid} = exists $data_of{$stnid} 
-                            ? $data_of{$stnid} . "$txt\n" : "$txt\n"; 
-                        } 
-                    } elsif ($sort_on) { 
-                        my $val = exists $msg{$sort_on} ? $msg{$sort_on} : ''; 
-                        $data_of{$val} = exists $data_of{$val} 
-                        ? $data_of{$val} . "$txt\n" : "$txt\n"; 
-                    } else { 
-                        # No sorting. We can print the line immediately 
-                        print $txt .= "\n" if $txt; 
-                    } 
-                } 
-                $txt = ''; 
-            }               # Finished message 
-            @lines_to_print = (); 
-            %msg = (); 
-        } 
-    } 
-} 
- 
-# If sorting requested, we cannot print before now 
-if ($sort && $sort_on) { 
-    for (sort $by keys %data_of) { 
-        print $data_of{$_}; 
-    } 
-} elsif ($sort) { 
-    for (sort keys %data_of) { 
-        print $data_of{$_}; 
-    } 
-} elsif ($sort_on) { 
-    # Print observations with missing value for the sort parameter lastly 
-    my $data_of_missing_value = $data_of{''} || ''; 
-    delete $data_of{''}; 
-    for (sort $by keys %data_of) { 
-        print $data_of{$_}; 
-    } 
-    print $data_of_missing_value if $data_of_missing_value; 
-} 
- 
- 
-# Read the filter conditions (if any). Return the filter option to be 
-# used by bufrdump, the found criteria (if any) as well as the number 
-# of succeeding alternatives for each criterium 
-sub get_filter_conditions { 
-    my $filt = shift; 
-    return ('') if ! $filt; 
- 
-    my $fortran_filter = ''; 
-    my @f; 
-    if ($filt =~ /,/) { 
-        # Argument to --filter is a comma separated list 
-        @f = split /,/, $filt; 
-    } else { 
-        # Argument to --filter is a file 
-        $fortran_filter = "--filter $filt"; 
-        open my $FILTER, '<', $filt 
-            or die "Cannot open $filt: $!"; 
-        # Skip the criteria meant for Fortran parsing, i.e. proceed to 
-        # first line following a blank line 
-        while (<$FILTER>) { 
-            last if $_ =~ /^\s*$/; 
-        } 
-        @f = <$FILTER>; 
-        close $FILTER or die "Cannot close $filt: $!";; 
-    } 
-    return ($fortran_filter) if !@f; # BUFR descriptor criteria only 
- 
-    my @allowed_operators = 
-        ('=', 
-         '<', 
-         '<=', 
-         '>', 
-         '>=', 
-         '!=', 
-         '=~', 
-         '!~', 
-        ); 
-    my @criteria; 
-    my @num_alt; # Number of alternative criteria following this, 
-                 # i.e. if line is '<cr1> | <cr2> | <cr3>' then 
-                 # corresponding values in @num_alt will be 2,1,0 
- 
-    # Read the filter criteria meant for Perl parsing, skipping blank 
-    # lines and comment lines 
-  FILTERLINE: 
-    foreach my $line (@f) { 
-        $line =~ s/^\s+//; 
-        $line =~ s/\s+$//; 
-        next FILTERLINE if !$line || $line =~ /^#/; 
-        my @crit = split /\|/, $line; 
-        my $num = scalar @crit; 
-        foreach my $criterium (@crit) { 
-            $criterium =~ s/^\s+//; 
-            $criterium =~ s/\s+$//; 
-            if ($criterium ne '') { 
-                push @criteria, $criterium; 
-                push @num_alt, --$num; 
-            } 
-        } 
-    } 
-    return ($fortran_filter) if !@criteria; 
- 
-    # Check that the criteria are properly formatted 
-    foreach my $criterium (@criteria) { 
-        # Naked parameter possibly preceded by '!' is ok 
-        next if $criterium =~ /^!?\w+$/; 
- 
-        my $op = (split / +/, $criterium)[1]; 
-        if (!defined($op) or grep(/[+*?\\]/, $op) 
-            or !grep(/^$op$/, @allowed_operators) ) { 
-            print "Error in $filt:\ncriterium is badly formatted" 
-                . " or operator not supported:\n$criterium"; 
-            exit 1; 
-        } 
-    } 
-    return ($fortran_filter, \@criteria, \@num_alt); 
-} 
- 
- 
-# Read the parameters into @params, those preceded by an exclamation 
-# mark also into %forced_params, and return references to these two. 
-sub get_params { 
-    my $params = shift; 
-    return if ! $params; 
- 
-    my @params; 
-    my %forced_params; 
- 
-    if ($params =~ /,/) { 
-        # Argument to --params is a comma separated list 
-        my @p = split /,/, $params; 
-        foreach my $name (@p) { 
-            $name =~ s/^\s+//; 
-            $name =~ s/\s+$//; 
-            if ($name =~ /^!/) { 
-                $name = substr $name, 1; 
-                $forced_params{$name} = 1; 
-            } 
-            push @params, $name; 
-        } 
-    } else { 
-        # Argument to --params is a file 
-        open my $PARAM, '<', $params 
-            or die "Cannot open file $params: $!"; 
-        while (my $name = <$PARAM>) { 
-            # Skip blank lines and comment lines 
-            $name =~ s/^\s+//; 
-            $name =~ s/\s+$//; 
-            next if !$name || $name =~ /^#/; 
-            if ($name =~ /^!/) { 
-                $name = substr $name, 1; 
-                $forced_params{$name} = 1; 
-            } 
-            push @params, $name; 
-        } 
-        close $PARAM or die "Cannot close $params: $!"; 
-    } 
-    return \@params, \%forced_params; 
-} 
- 
- 
-sub read_transformation_file { 
-    my $transform_file = shift; 
-    return if !$transform_file; 
- 
-    open my $TRANSFORM, '<', $transform_file 
-        or die "Cannot open $transform_file: $!"; 
- 
-    # Read in the transformations, skipping blank lines and comment 
-    # lines 
-    my %transform_of; 
-    while (my $line = <$TRANSFORM>) { 
-        $line =~ s/^\s+//; 
-        $line =~ s/\s+$//; 
-        next if !$line || $line =~ /^#/; 
-        my ($param, $transform) = split /=/, $line, 2; 
-        die "Invalid transformation in $transform_file:\n$line\n" 
-            unless $transform; 
-        $param =~ s/\s+$//; 
-        $transform =~ s/^\s+//; 
-        $transform_of{$param} = $transform; 
-    } 
-    close $TRANSFORM or die "Cannot close $transform_file: $!"; 
-    return \%transform_of; 
-} 
- 
-# Return true (1) if observation is to be filtered, i.e. does not 
-# comply with at least one line in filter file, where each line is one 
-# or more alternatives <param> or <param> <operator> <value> 
-sub filter_obs { 
-    my $msg_ref = shift; 
-    my $criteria_ref = shift; 
-    my $num_alt_ref = shift; # gives the number of alternative 
-                             # criteria still to be checked 
-    return unless $criteria_ref; 
- 
-    my @ascii_params = qw(aircraft call_sign icao_id name obstime type wigosid); 
- 
-    # Note that the loop counter $i might be changed in the loop 
-    for (my $i=0; $i < @{$criteria_ref}; $i++) { 
-        my $num_alt = $num_alt_ref->[$i]; 
-        my $criterium = $criteria_ref->[$i]; 
-        my ($f_param, $f_operator, $f_value) = split / +/, $criterium, 3; 
- 
-        # First check for !$par, meaning $par should not be in the observation 
-        if (substr($f_param,0,1) eq '!') { 
-            $f_param = substr($f_param,1); 
-            # If parameter is present, criterium is not fullfilled 
-            if (exists $msg_ref->{$f_param}) { 
-                next if $num_alt; # More alternatives to check 
- 
-                # Criterium not fulfilled and no more alternatives to 
-                # check. This observation should be filtered away 
-                return 1; 
-            } else { 
-                # Criterium fulfilled. No need to check alternative criteria 
-                $i += $num_alt if $num_alt; 
-                next; 
-            } 
-        } 
- 
-        # If parameter not present, criterium is obviously not fullfilled 
-        if (not exists $msg_ref->{$f_param}) { 
-            next if $num_alt; 
-            return 1; 
-        } 
- 
-        my $msg_value = $msg_ref->{$f_param}; 
-        # If a naked parameter criterium, we already know parameter is 
-        # present (as found in previous check), so criterium is fulfilled 
-        if (not defined $f_operator) { 
-            if ($num_alt) { 
-                # No need to check the alternative criteria 
-                $i += $num_alt; 
-            } 
-            next; 
-        } 
- 
-        chomp $f_value; 
-        my $op; 
-        if ($f_operator eq '<' 
-            || $f_operator eq '<=' 
-            || $f_operator eq '>' 
-            || $f_operator eq '>=' 
-            || $f_operator eq '=~' 
-            || $f_operator eq '!~') { 
-            $op = $f_operator; 
-        } elsif ($f_operator eq '=' 
-                 || $f_operator eq '!=') { 
-            if (grep {$_ eq $f_param} @ascii_params) { 
-                $msg_value =~ s/\s*$//; 
-                $op = ($f_operator eq '=') ? 'eq' : 'ne'; 
-            } else { 
-                $op = ($f_operator eq '=') ? '==' : '!='; 
-            } 
-        } else { 
-            die "Internal error: unknown operator '$f_operator'"; 
-        } 
- 
- # Some parameters might need special massaging 
-        if ($f_operator !~ /~/) { 
-            if ($f_param eq 'wmonr' || $f_param eq 'buoy_id') { 
-                # Make non octal by removing leading 0 
-                $msg_value =~ s/^0+//; 
-                $f_value =~ s/^0+// if $f_value != 0; 
-            } elsif ($f_param eq 'nationalnr') { 
-                # Convert to a pure numerical value (float). For 001101 
-                # State id only numbers between 100 and 699 are operational 
-                $msg_value =~ s/_0*/./; 
-                $f_value =~ s/_0*/./; 
-            } elsif ($f_param eq 'obstime') { 
-                # Convert to a pure numerical value (float) 
-                $msg_value =~ s/[-:]//g; 
-                $msg_value =~ s/ /./; 
-                $f_value =~ s/[-:']//g; 
-                $f_value =~ s/^ +//; 
-                $f_value =~ s/ +$//; 
-                $f_value =~ s/ /./; 
-            } elsif ($f_param eq 'name') { 
-                # Add or correct quoting to "" and ignore casing 
-                $msg_value = '"' . lc $msg_value . '"'; 
-                $f_value = lc $f_value; 
-                if ($f_value =~ /^'.*'$/) { 
-                    $f_value =~ s/^'//; 
-                    $f_value =~ s/'$//; 
-                } 
-                if ($f_value !~ /^".*"$/) { 
-                    $f_value = '"' . $f_value . '"'; 
-                } 
-            } 
-        } 
- 
- my $condition = "$msg_value $op $f_value"; 
- # Some values should be string values 
- if ($f_operator =~ /~/) { 
-     $condition = "q{$msg_value} $op $f_value"; 
- } elsif (grep {$_ eq $f_param} @ascii_params) { 
-     $condition = "q{$msg_value} $op q{$f_value}"; 
- } 
- 
-        # Finally, do the criterium check 
- if (eval $condition) { 
-     # No need to check the remaining alternative criteria 
-     $i += $num_alt if $num_alt; 
-     next; 
- } else { 
-     next if $num_alt; 
-     return 1; 
- } 
-    } 
- 
-    # All filter conditions have been fullfilled 
-    return 0; 
-} 
- 
-# Return the type of station requested, and the station 
-# identifications. Leave some leeway for how to list wmonr and 
-# nationalnr (leading 0's might be omitted - added here) 
-sub get_requested_stations { 
-    my $req_stations = shift; 
-    return if !$req_stations; 
-    die "Station list must start with 'wmonr=', 'nationalnr=', " 
-        . "'call_sign=', 'buoy_id=', 'aircraft=' or 'wigosid='" 
-        unless $req_stations 
-        =~ /^(wmonr=|nationalnr=|call_sign=|buoy_id=|aircraft=|wigosid=)/; 
- 
-    my ($id, $rest) = split /=/, $req_stations; 
-    my @stations = split /,/, $rest; 
-    if ($id eq 'wmonr') { 
-        my @req_stn; 
-        foreach my $station (@stations) { 
-            # Turn $station into a 5 digit wmonr 
-            $station =~ s/^0+//; 
-            $station += 1000 if $station < 1000; 
-            $station = sprintf("%05d", $station); 
-            push @req_stn, $station; 
-        } 
-        return ($id, \@req_stn); 
-    } elsif ($id eq 'nationalnr') { 
-        my @req_stn; 
-        foreach my $station (@stations) { 
-            # Turn national station number into 10 digits 
-            my ($state_id, $national_id) = split /_/, $station; 
-            die "Uncorrected formatted station: '$station' in station list" 
-                if !defined $national_id or $national_id eq ''; 
-            $station = $state_id . '_' . sprintf("%010d", $national_id); 
-            push @req_stn, $station; 
-        } 
-        return ($id, \@req_stn); 
-    } else { 
-        return ($id, \@stations); 
-    } 
-} 
- 
-# Return true (1) if observation does not contain one of the stations 
-# listed in @$req_stn_ref (of type $req_id), i.e. if this observation 
-# should be filtered away 
-sub filter_station { 
-    my ($msg_ref, $req_id, $req_stn_ref) = @_; 
- 
-    return 1 unless $msg_ref->{$req_id}; 
-    my $stn = $msg_ref->{$req_id}; 
- 
-    return !grep { $_ eq $stn } @$req_stn_ref; 
-} 
- 
- 
-# When --sort_on is used, need to supply the sort method. Also returns 
-# input parameter $sort_on with possible trailing '-' (or '+') 
-# stripped off 
-sub get_sort_method { 
-    my ($sort_on, $sort) = @_; 
-    return if !$sort_on; 
- 
-    my $ascending_sort = 1; 
-    # A minus sign appended to the sort parameter means descending sort 
-    if ($sort_on =~ /-$/) { 
-        $ascending_sort = 0; 
-        chop $sort_on; 
-    } 
-    # Just in case someone adds a '+' to signify ascending sort 
-    if ($sort_on =~ /[+]$/) { 
-        chop $sort_on; 
-    } 
- 
-    my @ascii_params = qw(aircraft call_sign icao_id obstime name type wigosid); 
-    my $lexical_sort = grep {$_ eq $sort_on} @ascii_params; 
- 
-    my $sort_sub; 
-    if ($sort) { 
-        $sort_sub = sub { 
-            my ($stn_a, $val_a) = split /\|/, $a; 
-            my ($stn_b, $val_b) = split /\|/, $b; 
-            # Sort on stationid is always lexical and ascending 
-            my $cmp = $stn_a cmp $stn_b; 
-            if ($cmp) { 
-                return $cmp; 
-            } else { 
-                # Stationids are equal. Sort on value of sort_on parameter 
-                if ($lexical_sort) { 
-                    if ($ascending_sort) { 
-                        return $val_a cmp $val_b; 
-                    } else { 
-                        return $val_b cmp $val_a; 
-                    } 
-                } else { 
-                    # Numerical sort 
-                    if ($ascending_sort) { 
-                        return $val_a <=> $val_b; 
-                    } else { 
-                        return $val_b <=> $val_a; 
-                    } 
-                } 
-            } 
-        } 
-    } else { 
-        # Sort restricted to sort_on parameter 
-        $sort_sub = sub { 
-            if ($lexical_sort) { 
-                if ($ascending_sort) { 
-                    return $a cmp $b; 
-                } else { 
-                    return $b cmp $a; 
-                } 
-            } else { 
-                # Numerical sort 
-                if ($ascending_sort) { 
-                    return $a <=> $b; 
-                } else { 
-                    return $b <=> $a; 
-                } 
-            } 
-        } 
-    } 
- 
-    return ($sort_on, $sort_sub); 
-} 
- 
-=pod 
- 
-=head1 SYNOPSIS 
- 
-  bufrdump.pl <bufr file(s)> 
-      [--filter <filter file | filter list>] 
-      [--param <parameter file | parameter list> [--csv [--delimiter <del>]] 
-      [--sort] 
-      [--sort_on <parameter>[-]] 
-      [--station <station list>] 
-      [--transform <transformation file>] 
-      [--lon1 <x1>] 
-      [--lat1 <y1>] 
-      [--lon2 <x2>] 
-      [--lat2 <y2>] 
-      [--obstype <amdar|ocea|surface|sounding|sounding->] 
-      [--tablepath <path to BUFR tables>] 
-      [--help] 
- 
-=head1 DESCRIPTION 
- 
-Extracts BUFR messages from BUFR file(s) and prints section 4 as 
-"parameter=value" lines. Calls the Fortran program bufrdump 
-internally, so this program must be installed at the location set in 
-variable $BUFRDUMP in source code. 
- 
-Execute without arguments for Usage, with option C<--help> for some 
-additional info. See also L</https://wiki.met.no/bufr.pm/start> for 
-examples of use. 
- 
- 
-=head1 OPTIONS 
- 
- 
-  --filter <filter file | filter list> 
-                  Decode observations meeting criteria in filter file or 
-                  filter list only 
-  --param <parameter file | parameter list> [--csv [--delimiter <del>]] 
-                  Print parameters in parameter file or comma 
-                  separated list (e.g. wmonr,TA) only, in same order 
-                  as they occur there. If using --csv possibly 
-                  followed by --delimiter <del>, the parameters vill 
-                  be printed using the CSV (comma-separated values) 
-                  format, with the delimiter del (default is ';') 
-  --sort          Sort the decoded observations on station identification; 
-                  first stations with wmonr, then stations with nationalnr, 
-                  call sign, buoy_id, aircraft or wigosid (others left out) 
-  --sort_on <parameter>[-] Sort the decoded observations on increasing 
-                  values of parameter, or decreasing values if a '-' 
-                  follows the parameter name. E.g. --sort_on TA- will 
-                  sort on decreasing temperatures. Observations not 
-                  containing the parameter at all will be printed lastly, 
-                  except when --sort_on is combined with --sort (in which 
-                  case sorting is done firstly on station identification, 
-                  secondly on parameter with missing values printed first) 
-  --station <station list> 
-                  Print observations for stations in station list only, 
-                  e.g. wmonr=01384,01492 
-  --transform <transformation file> 
-                  Do the transformations of parameter values listed in 
-                  transformation file 
-  --lon1 <x1>     Decode observations with longitude >= x1 only 
-  --lat1 <y1>     Decode observations with latitude >= y1 only 
-  --lon2 <x2>     Decode observations with longitude <= x2 only 
-  --lat2 <y2>     Decode observations with latitude <= y2 only 
-                  x1,y1,x2,y2 should be decimal degrees 
-  --obstype <amdar|ocea|surface|sounding|sounding->] 
-                  Force observation type. If this option is not set, 
-                  will make an educated guess of observation type 
-                  based on metadata in section 1 of each BUFR message 
-  --tablepath <path to BUFR tables> 
-                  Set path to BUFR tables (overrides ENV{BUFR_TABLES}) 
-  --help          Print this Usage (but you might instead prefer to use 
-                  perldoc bufrdump.pl) 
- 
-Options may be abbreviated, e.g. --h or -h for --help. 
- 
-To avoid having to use the C<--tablepath> option, you are adviced to 
-set the environment variable BUFR_TABLES to the directory where your 
-BUFR tables are located (unless the default path provided by 
-bufrdump.pl works for you). 
- 
-The lines in <parameter file>, or the comma separated values in 
-<parameter list>, should be name of the parameters you want to be 
-printed. For example, if you want only station identification and 
-temperature to be printed for a BUFR SYNOP file, either supply 
- 
-  wmonr,nationalnr,call_sign,TA 
- 
-as argument to --params, or supply a <parameter file> which should 
-look like this: 
- 
-  wmonr 
-  nationalnr 
-  call_sign 
-  TA 
- 
-If you want "parameter=value" to be printed also when value is missing 
-in BUFR message, precede the parameter name with an exclamation mark 
-(e.g. '!TA'). Missing values will then be displayed as -32767. If the 
-argument to --param is a parameter list, you must prevent the shell 
-from attaching special meaning to the exclamation mark by enclosing the 
-list in single quotes. 
- 
-If the parameter list consists of one parameter only, a comma must be 
-appended (e.g. 'wmonr,') because bufrdump.pl uses the appearence of 
-comma to signal that this is not a filename but parameter name(s). 
- 
-If --csv is used in conjunction with --param, all values will be 
-printed using the CSV format, with first line listing the parameters, 
-and with missing fields printed as -32767 if the parameter is marked 
-with '!' in parameter file or list. With the parameter file above, the 
-listing may for example start like 
- 
-  wmonr;nationalnr;call_sign;TA 
-  01001;;;-1.5 
-  ;;LF5U;9.0 
- 
-You can choose another delimiter than semicolon by use of option 
---delimiter <del>, e.g. --csv --delimiter ',' 
- 
-Using --filter will decode only those observations that meet at least 
-one of the BUFR descriptor criteria and all of the parameter criteria 
-in <filter file>, where the BUFR descriptor criteria should come first 
-in filter file followed by a blank line, then comes the parameter 
-criteria which should match <param> or !<param> or <param> <operator> 
-<value> where operator is one of =, !=, =~, !~, <, <=, > and >=. What 
-follows =~ and !~ should be a Perl match regular expression. The parameter 
-criteria may be phrased as alternatives by separating them with '|' on 
-a single line. An example filter file is 
- 
-  D: 001001 I2.2 
-  01 
-  D: 001001 I2.2 001002 I3.3 
-  03 895 
-  06 252 
-  D: 001011 A9 
-  LF5U 
- 
-  type = Manned 
-  NN != 8 
-  TA >= 5 
-  TA < 9.5 
-  RR_24 
- 
-which decodes all observations with block number 01, two other 
-specific wmo stations and one specific ship, where stations should be 
-manned and have cloud cover with a value different from 8, and have 
-temperature between 5 and 9.5 degrees Celsius, and contain 
-precipitation for last 24 hours. Comment lines starting with # will be 
-ignored. 
- 
-Another example: the filter file (starting with a blank line!) 
- 
- 
-  call_sign =~ /^L[A-N]..$/ 
-  obstime >= '2012-02-10 06:00:00' 
-  HW | HWA | PW | PWA 
-  FF > 10 | FG_010 > 10 
- 
-will print only those ship observations for which the 4 character 
-call_sign starts with 2 letters in the interval LA-LN, and having 
-obstime larger or equal to the datetime given, and containing wave 
-data (specifically: height or period of waves, manually or 
-automatically measured), and with wind or 10 minutes gust more than 10 
-m/s. 
- 
-For convenience, when there are no BUFR descriptor criteria, you might 
-provide the filter criteria on the command line. Example: 
- 
---filter 'wmonr,TA > 0,RR_12 | RR_24, !FF' 
- 
-will decode only observations with wmonr, having positive temperature 
-and containing precipitation for 12 or 24 hours and not reporting 
-wind. If (like for --param) the filter list consists of one criterium 
-only, a comma must be appended. 
- 
-To avoid the need of creating a filter file when observations for some 
-few stations are requested, you can provide the stations in a comma 
-separated list after option --station. Some examples: 
- 
-  --station wmonr=01001,01152,01492 
-  --station nationalnr=614_0050410003,637_108 
-  --station call_sign=LF5U 
-  --station buoyid=64607,64609 
-  --station aircraft=EU3421,JHCWUURA 
-  --station wigosid=0-376-0-511,0-20000-0-01492 
- 
-You cannot mix different kinds of stations this way (before '=' you 
-must choose either wmonr, nationalnr, call_sign, buoy_id, aircraft or 
-wigosid). Note also that providing the stations in the BUFR descriptor 
-part (first part) of the filter file will speed up execution time 
-considerably, compared to using option --station. It is possible to 
-combine --filter with --station if done with some care, 
-e.g. specifying WMO block 01 and the required parameters in filter 
-file, then the requested stations in station list. 
- 
-The --transform option is provided mainly to be able to use other 
-units than what is default in bufrdump.pl. The transformation file 
-should list the transformations wanted, one per line as 
- 
-  <parameter> = <perl expression involving $x> 
- 
-where $x is original value of the parameter. 
- 
-For example, the following transformation file will display wind speed 
-FF and wind gust FG in knots instead of m/s, rounded to one decimal, 
-and cloud cover NN in % (instead of the default which is using WMO 
-code table 2700, roughly counting octas): 
- 
-  FF = sprintf("%.1f", $x*1.9438) 
-  FG = sprintf("%.1f", $x*1.9438) 
-  NN = int($x*12.5 + .5) 
- 
-If --transform is combined with --filter, the filter criteria should 
-refer to the transformed values. E.g. if the above NN transform to % 
-is to be applied for sky not all covered by clouds, you should use NN 
-!= 100 instead of NN != 8 in filter file. 
- 
-The --obstype option might be handy in some special cases, like when 
-you are interested only in the surface part of oceanographic data 
-(then use '--obstype surface'), or when you want to see only levels 
-with vss>0 in high resolution radiosonde data (then use '--obstype 
-sounding-'), or when data category and/or data sub-category in the 
-BUFR messages have unusual values. 
- 
-=head1 AUTHOR 
- 
-Pål Sannes E<lt>pal.sannes@met.noE<gt> 
- 
-=head1 COPYRIGHT 
- 
-Copyright (C) 2010 met.no 
- 
-=cut 
-</code> 
  • bufr.pm/bufrdump.pl_source.txt
  • Last modified: 2022-05-31 09:29:31
  • (external edit)