Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
bufr.pm:bufrread.pl [2010-09-21 08:07:02] pals |
bufr.pm:bufrread.pl [2023-02-05 10:14:41] (current) pals |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | < | + | < |
- | # | + | # |
- | # (C) Copyright 2010, met.no | + | # (C) Copyright 2010-2023 MET Norway |
# | # | ||
# This program is free software; you can redistribute it and/or modify | # This program is free software; you can redistribute it and/or modify | ||
Line 22: | Line 22: | ||
use strict; | use strict; | ||
+ | use warnings; | ||
use Getopt:: | use Getopt:: | ||
use Pod::Usage qw(pod2usage); | use Pod::Usage qw(pod2usage); | ||
+ | use Geo::BUFR; | ||
- | use constant | + | # This is actually default in BUFR.pm, but provided here to make it |
- | my $BUFRDUMP | + | # easier for users to change to ' |
+ | use constant | ||
+ | |||
+ | # Will be used if neither --tablepath nor $ENV{BUFR_TABLES} is set | ||
+ | use constant DEFAULT_TABLE_PATH_BUFRDC | ||
+ | use constant DEFAULT_TABLE_PATH_ECCODES | ||
+ | # Ought to be your most up-to-date code table(s) | ||
+ | use constant DEFAULT_CTABLE_BUFRDC => ' | ||
+ | use constant DEFAULT_CTABLE_ECCODES => '0/wmo/37'; | ||
# Parse command line options | # Parse command line options | ||
Line 32: | Line 42: | ||
GetOptions( | GetOptions( | ||
| | ||
- | ' | + | ' |
- | ' | + | ' |
- | ' | + | # when printing section 4 |
- | ' | + | ' |
- | ' | + | ' |
- | ' | + | ' |
- | ' | + | ' |
- | ' | + | ' |
- | ' | + | ' |
- | ' | + | ' |
- | ' | + | ' |
- | ' | + | ' |
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
) or pod2usage(-verbose => 0); | ) or pod2usage(-verbose => 0); | ||
Line 52: | Line 69: | ||
pod2usage(-verbose => 0) unless @ARGV; | pod2usage(-verbose => 0) unless @ARGV; | ||
- | # --csv can only be used together with --param | + | # Set verbosity level |
- | pod2usage(-verbose => 0) if $option{csv} && !$option{param}; | + | Geo::BUFR-> |
+ | |||
+ | # Set whether section 4 should | ||
+ | Geo:: | ||
+ | |||
+ | # Set whether quality information should be decoded for the BUFR module | ||
+ | Geo::BUFR->set_noqc() if ($option{noqc}); | ||
+ | |||
+ | Geo:: | ||
- | # --sort and --sort_on are exclusive | + | Geo::BUFR-> |
- | pod2usage(-verbose => 0) if $option{sort} && | + | |
- | # Prevent ECMWF software from printing | + | # Set BUFR table format |
- | $ENV{PRINT_TABLE_NAMES} = ' | + | my $tableformat = (defined $option{tableformat}) ? uc $option{tableformat} : DEFAULT_TABLE_FORMAT; |
+ | Geo:: | ||
- | # Set BUFR table path environment variable used by bufrdump | + | # Set BUFR table path |
if ($option{tablepath}) { | if ($option{tablepath}) { | ||
# Command line option --tablepath overrides all | # Command line option --tablepath overrides all | ||
- | | + | |
- | } elsif (!$ENV{BUFR_TABLES}) { | + | } elsif ($ENV{BUFR_TABLES}) { |
- | $ENV{BUFR_TABLES} | + | |
+ | Geo:: | ||
+ | } else { | ||
+ | # If all else fails, use the default tablepath in BUFRDC/ | ||
+ | if ($tableformat eq ' | ||
+ | Geo:: | ||
+ | } elsif ($tableformat eq ' | ||
+ | Geo:: | ||
+ | } | ||
} | } | ||
- | # ECMWF software requires trailing '/' | ||
- | $ENV{BUFR_TABLES} .= '/' | ||
- | my $filter = $option{filter} ? " | + | my $ahl_regexp; |
- | my $lon1 = $option{lon1} ? " | + | if ($option{ahl}) { |
- | my $lat1 = $option{lat1} ? "--lat1 $option{lat1}" | + | eval { $ahl_regexp |
- | my $lon2 = $option{lon2} ? "--lon2 | + | |
- | my $lat2 = $option{lat2} ? " | + | } |
- | my $criteria_ref; | + | # Where to direct output (including verbose output, but not output to STDERR) |
- | $criteria_ref = read_filter_file($option{filter}) if $filter; | + | my $OUT; |
+ | if ($option{outfile}) { | ||
+ | open($OUT, '>', | ||
+ | or die " | ||
+ | } else { | ||
+ | $OUT = *STDOUT; | ||
+ | } | ||
- | my $param_file = $option{param} | + | my @requested_desc; |
- | my ($forced_params_ref, | + | if ($option{param}) { |
- | ($forced_params_ref, | + | @requested_desc = read_descriptor_file($option{param}); |
- | = read_param_file($param_file) if $param_file; | + | } |
- | my $csv = $option{csv} ? 1 : 0; | + | # Arrays over filter criteria, used if option --filter is set |
- | # First line in CSV should be the parameters | + | my @fid; # Filter descriptors, |
- | print join(',', @$params_ref) . " | + | my @fiv; # Filter values, e.g. $fiv[1] = [ [ 3, 895 ], [ 6 252 ] ] |
+ | my @num_desc; # Number of filter descriptors for each criterion, e.g. $num_desc[1] = 2 | ||
+ | my @num_val; | ||
+ | my @required; # 1 for required criteria | ||
+ | my $num_criteria = 0; | ||
+ | my $num_required_criteria = 0; | ||
+ | if ($option{filter}) { | ||
+ | read_filter_file($option{filter}); | ||
+ | } | ||
- | my $transform_file | + | my $width = $option{width} ? $option{width} : 15; |
- | my $transform_ref; | + | |
- | $transform_ref = read_transformation_file($transform_file) if $transform_file; | + | |
- | my $sort = $option{sort} ? 1 : 0; | + | # Used to display section 2 if --optional_section is set |
- | my $sort_on | + | my $sec2_code_ref |
- | # What kind of sorting is required? | + | # Loop for processing |
- | my $sort_by; | + | foreach |
- | if ($sort_on) { | + | my $bufr = Geo:: |
- | my @ascii_params | + | $bufr-> |
- | | + | |
- | # A minus sign appended to the sort parameter means descending sort | + | # Open BUFR file |
- | | + | $bufr->fopen($inputfname); |
- | $ascending_sort = 0; | + | |
- | | + | # Process input file |
- | } | + | decode($bufr); |
- | # Just in case someone adds a ' | + | $bufr-> |
- | | + | } |
- | $ascending_sort = 1; | + | |
- | | + | |
- | } | + | # Extract data from BUFR file. Print AHL for first message |
- | if (grep {$_ eq $sort_on} @ascii_params) { | + | # bulletin, print message number for each new message, print subset |
- | if ($ascending_sort) { | + | # number for each subset. |
- | $sort_by | + | sub decode { |
- | } else { | + | |
- | $sort_by = sub {$b cmp $a}; | + | |
+ | my ($message_header, | ||
+ | my $section013_dumped | ||
+ | # 0-3 have been printed when --filter | ||
+ | # option has been used | ||
+ | READLOOP: | ||
+ | while (not $bufr-> | ||
+ | |||
+ | | ||
+ | # decoding, skip this observation while printing the error | ||
+ | # message to STDERR, also displaying ahl of bulletin if found | ||
+ | # (but skip error message if the message should be skipped on | ||
+ | # --ahl anyway). | ||
+ | my ($data, $descriptors); | ||
+ | | ||
+ | ($data, $descriptors) = $bufr-> | ||
+ | }; | ||
+ | if ($@) { | ||
+ | | ||
+ | next READLOOP if $option{ahl} && $current_ahl !~ $ahl_regexp; | ||
+ | |||
+ | warn $@; | ||
+ | # Try to extract message number and ahl of the bulletin | ||
+ | # where the error occurred | ||
+ | $current_message_number = $bufr-> | ||
+ | if (defined | ||
+ | | ||
+ | | ||
+ | if $current_ahl; | ||
+ | warn $error_msg if $error_msg; | ||
+ | | ||
+ | | ||
+ | next READLOOP; | ||
} | } | ||
- | } else { | + | |
- | if ($ascending_sort) { | + | next if $option{ahl} && $bufr-> |
- | $sort_by | + | |
- | } else { | + | if ($option{codetables} && !$option{nodata}) { |
- | $sort_by = sub {$b <=> $a}; | + | |
+ | # the B and D tables loaded in next_observation, | ||
+ | # this C table file does not exist, loads DEFAULT_CTABLE | ||
+ | # instead. | ||
+ | my $table_version | ||
+ | my $tableformat = Geo:: | ||
+ | if ($tableformat eq ' | ||
+ | $bufr-> | ||
+ | | ||
+ | | ||
+ | } | ||
} | } | ||
- | } | ||
- | } | ||
- | # Loop for processing of BUFR input files | + | |
- | my %data_of | + | # If next_observation() did find a BUFR message, subset number |
- | foreach my $inputfname | + | # should have been set to at least 1 (even in a 0 subset message) |
+ | last READLOOP if $current_subset_number == 0; | ||
- | # Dump the content of the BUFR file using the Fortran program | + | if ($current_subset_number == 1 || $option{nodata}) { |
- | | + | $current_message_number |
- | die if $?; # Reason for bufrdump failing should have been printed to STDERR | + | |
+ | | ||
+ | $message_header .= (defined $current_ahl) | ||
+ | | ||
- | # Then process the output from the dump | + | $section013_dumped |
- | my @lines | + | next READLOOP if ($option{filter} |
- | # Add an empty line to simplify processing | + | && |
- | push @lines, ''; | + | |
- | my $station = '' | + | |
- | # Skip first(blank) line | + | print $OUT $message_header; |
- | shift @lines; | + | |
- | my @lines_to_print; | + | if (not $option{data_only}) { |
- | my %msg; # Hash with parameter name as key, parameter value as value | + | print $OUT $bufr-> |
+ | print $OUT $bufr-> | ||
+ | print $OUT $bufr-> | ||
+ | if $option{optional_section}; | ||
+ | print $OUT $bufr-> | ||
+ | $section013_dumped = 1; | ||
+ | } | ||
+ | next READLOOP if $option{nodata}; | ||
+ | } else { # subset number > 1 | ||
+ | next READLOOP if ($option{filter} | ||
+ | && filter_observation($bufr, $data, $descriptors)); | ||
- | LINE:while (defined(my $line = shift @lines)) { | + | |
- | | + | # have been printed |
- | if ($line !~ /^\s*$/) { | + | |
- | # Build up the message to be (possibly) | + | and not $section013_dumped) { |
- | | + | |
- | if ($transform_file && | + | |
- | | + | |
- | my $transform = $transform_ref->{$param}; | + | |
- | $transform =~ s/\$x/$value/g; | + | |
- | $value = eval $transform; | + | $section013_dumped |
- | | + | |
- | . $transform . " | + | |
- | $line =~ s/ | + | |
} | } | ||
- | $msg{$param} = $value; | ||
- | push @lines_to_print, | ||
} | } | ||
- | if ($line =~ /^\s*$/ or @lines == 0) { | + | if ($option{param}) { |
- | # A full message has been completed. Should it be printed? | + | # Reduce data and descriptors to those requested only |
- | | + | ($data, $descriptors) |
- | | + | = param($data, $descriptors, @requested_desc); |
- | } else { | + | } |
- | # Print the message (or if --sort or --sort_on: save the message) | + | |
- | my $txt = ''; | + | printf |
- | if ($param_file) { | + | |
- | # Print the params in @$params_ref if exists in | + | # If an error is encountered during dumping of section 4, skip |
- | # message, in same order as in @$params_ref | + | # this subset while printing the error message to STDERR, also |
- | foreach my $name (@$params_ref) { | + | # displaying ahl of bulletin if found. |
- | if (exists $msg{$name}) { | + | my $dump; |
- | $txt .= $csv ? $msg{$name} . ',' | + | eval { |
- | } elsif ($forced_params_ref-> | + | $dump = ( $option{bitmap} ) |
- | $txt .= $csv ? ' | + | |
- | } elsif ($csv) { | + | |
- | $txt .= ','; | + | : |
- | } | + | }; |
- | } | + | if ($@) { |
- | } else { | + | |
- | foreach | + | |
- | $line2 =~ s/ | + | . " |
- | | + | $error_msg |
- | | + | |
- | | + | |
- | chop $txt if $csv; # removes last ',' | + | |
- | if ($txt) { | + | |
- | if ($sort) { | + | } else { |
- | # Sort wmonr before call signs before buoy_id | + | |
- | if ($msg{wmonr}) { | + | |
- | $station = ' | + | |
- | } elsif ($msg{call_sign}) { | + | |
- | | + | |
- | } elsif ($msg{buoy}) { | + | |
- | $station = ' | + | |
- | } else { | + | |
- | # Skip observation | + | |
- | next LINE; | + | |
- | } | + | |
- | $data_of{$station} = exists $data_of{$station} | + | |
- | ? $data_of{$station} . "$txt \n" : "$txt \n"; | + | |
- | } elsif ($sort_on) { | + | |
- | | + | |
- | $data_of{$val} | + | |
- | ? $data_of{$val} | + | |
- | } else { | + | |
- | # No sorting. We can print the line immediately | + | |
- | print $txt .= " | + | |
- | } | + | |
- | } | + | |
- | | + | |
- | } # Finished message | + | |
- | | + | |
- | | + | |
} | } | ||
} | } | ||
} | } | ||
- | # If sorting requested, we cannot print before now | + | sub read_descriptor_file { |
- | if ($sort) { | + | my $descriptor_file = shift; |
- | | + | |
- | | + | open my $fh, '<', |
+ | or die " | ||
+ | | ||
+ | while (<$fh>) { | ||
+ | | ||
+ | push @requested_desc, | ||
} | } | ||
- | } elsif ($sort_on) { | + | close $fh or die " |
- | # Print observations with missing value for the sort parameter lastly | + | |
- | my $data_of_missing_value = $data_of{'' | + | |
- | | + | |
- | for (sort $sort_by keys %data_of) { | + | |
- | print $data_of{$_}; | + | |
- | } | + | |
- | print $data_of_missing_value if $data_of_missing_value; | + | |
} | } | ||
+ | # Reduce the data to those corresponding to the requested descriptors | ||
+ | # only. | ||
+ | sub param { | ||
+ | my ($data, $descriptors, | ||
- | sub read_param_file { | + | my (@req_data, @req_desc); |
- | my $parameter_file | + | my $i = 0; |
- | + | | |
- | | + | |
- | or die " | + | |
- | + | | |
- | my %forced_params; | + | |
- | my @params; | + | |
- | # Read the parameters into @params, those preceded by an | + | |
- | # exclamation mark also into %forced_params, | + | |
- | # and comment lines | + | |
- | while (my $name = < | + | |
- | $name =~ s/^\s+//; | + | |
- | $name =~ s/\s+$//; | + | |
- | next if !$name || $name =~ /^#/; | + | |
- | if ($name =~ /^!/) { | + | |
- | $name = substr | + | |
- | $forced_params{$name} = 1; | + | |
} | } | ||
- | | + | $i++; |
} | } | ||
- | | + | return |
- | + | ||
- | | + | |
} | } | ||
- | sub read_transformation_file { | ||
- | my $transform_file = shift; | ||
- | open my $TRANSFORM, '<', | + | ################################################################################### |
- | or die " | + | |
- | | + | # Filter routines |
- | # lines | + | |
- | my %transform_of; | + | sub filter_on_ahl { |
- | | + | my $obj = shift; |
- | $line =~ s/^\s+//; | + | my $ahl_regexp |
- | $line =~ s/\s+$//; | + | |
- | next if !$line | + | |
- | my ($param, $transform) | + | |
- | die " | + | |
- | unless $transform; | + | |
- | $param =~ s/\s+$//; | + | |
- | $transform =~ s/^\s+//; | + | |
- | $transform_of{$param} = $transform; | + | |
- | } | + | |
- | return \%transform_of; | + | |
} | } | ||
+ | # Read in contents of $filter_file into variables @fid, @fiv, | ||
+ | # @num_desc, @num_val and $num_criteria, | ||
+ | # Note that index 0 of the arrays is not used. | ||
sub read_filter_file { | sub read_filter_file { | ||
my $filter_file = shift; | my $filter_file = shift; | ||
- | my @allowed_operators = | ||
- | (' | ||
- | '<', | ||
- | '< | ||
- | '>', | ||
- | '> | ||
- | ' | ||
- | ); | ||
- | my @criteria; | ||
- | open my $FILTER, '<', | + | open my $fh, '<', |
or die " | or die " | ||
- | | + | while (<$fh>) { |
- | # first line following a blank line | + | |
- | | + | s/#.*//; |
- | | + | next if /^\s*$/; |
- | } | + | |
- | # Read the filter criteria meant for Perl parsing, skipping blank | + | if (s/^\s*D(!)?://) { |
- | # lines and comment lines | + | |
- | while (my $line = < | + | # Check that all descriptors |
- | $line =~ s/^\s+//; | + | foreach my $desc (@desc) { |
- | $line =~ s/\s+$//; | + | die "' |
- | next if !$line || $line =~ /^#/; | + | if $desc !~/^\d+$/; |
- | | + | } |
- | } | + | # Save the criterium |
- | + | | |
- | | + | $num_val[$num_criteria] = 0; |
- | foreach my $criterium | + | $fid[$num_criteria] = \@desc; |
- | # Naked parameter is ok | + | $required[$num_criteria] = $1 ? 1 : 0; |
- | | + | |
- | + | } else { | |
- | | + | |
- | if (!defined($op) or grep(/[+*?\\]/, $op) or !grep(/^$op$/, @allowed_operators) ) { | + | # Check that value line contains correct number of values |
- | | + | die " |
- | . " | + | . " |
- | | + | if scalar @values != scalar @{$fid[$num_criteria]}; |
+ | | ||
+ | for $_ (@values) { s/ | ||
+ | $fiv[$num_criteria]-> | ||
} | } | ||
} | } | ||
- | return | + | |
+ | | ||
} | } | ||
- | # Return true (1) if observation | + | # Return true (observations should be filtered) if the observation |
- | # comply with at least one of the < | + | # does not meet all of the D! criteria (if exists) and does not meet |
- | # < | + | # any one of the other criteria |
- | sub filter_obs | + | sub filter_observation |
- | my $msg_ref | + | my $bufr = shift; |
- | my $criteria_ref | + | die "Error in filter_observation: |
+ | unless ref($bufr) eq ' | ||
+ | my ($data, $descriptors) | ||
- | my @ascii_params | + | my $num_ordinary_criteria |
+ | my $num_success_req_criteria = 0; # Number of required criteria successfully fulfilled | ||
+ | my $num_success_ord_criteria = 0; # Number of ordinary criteria successfully fulfilled | ||
- | foreach my $criterium | + | |
- | | + | CRITERIA: |
- | return 1 unless exists $msg_ref-> | + | |
- | next if not defined $f_operator; | + | # Enough to check that the descriptor(s) are present |
- | # present, so criterium fulfilled | + | |
- | | + | # loop through all descriptors in criterion: |
- | if ($f_operator eq ' | + | |
- | | + | |
- | $msg_ref->{$f_param} =~ s/\s*$//; | + | |
- | | + | if ($descriptors->[$j] == $filter_desc) { |
- | } else { | + | $nmatch++; # Matched! |
- | | + | |
- | } | + | # All descriptors for this line in this criterion matched. |
- | } elsif ($f_operator eq '<' | + | # Do we need to check more criteria? |
- | | + | if ($required[$filter_criterion]) { |
- | } elsif ($f_operator eq '<=') { | + | $num_success_req_criteria++; |
- | | + | if ($num_success_req_criteria == $num_required_criteria |
- | } elsif ($f_operator eq '>' | + | |
- | | + | or $num_success_ord_criteria |
- | } elsif ($f_operator eq '>=') { | + | return |
- | return | + | } |
- | } elsif ($f_operator eq ' | + | |
- | if (grep {$_ eq $f_param} @ascii_params) { | + | |
- | | + | |
- | | + | |
- | } else { | + | } |
- | | + | } |
+ | } | ||
+ | | ||
+ | } | ||
} | } | ||
+ | } else { | ||
+ | # loop through all filter values lines (for given) criterion: | ||
+ | LINE: foreach my $line (1 .. $num_val[$filter_criterion]) { | ||
+ | my $nmatch = 0; | ||
+ | # loop through all descriptors in criterion: | ||
+ | DESC: foreach my $idesc (0 .. $num_desc[$filter_criterion] - 1) { | ||
+ | my $filter_desc = $fid[$filter_criterion]-> | ||
+ | # loop through all data in subset: | ||
+ | for (my $j = 0; $j < @{$descriptors}; | ||
+ | if ($descriptors-> | ||
+ | next DESC if !defined $data-> | ||
+ | (my $val = $data-> | ||
+ | if ($val eq $fiv[$filter_criterion]-> | ||
+ | $nmatch++; # Matched! | ||
+ | if ($nmatch == $num_desc[$filter_criterion]) { | ||
+ | # All descriptors for this line in this criterion matched. | ||
+ | # Do we need to check more criteria? | ||
+ | if ($required[$filter_criterion]) { | ||
+ | $num_success_req_criteria++; | ||
+ | if ($num_success_req_criteria == $num_required_criteria | ||
+ | and ($num_ordinary_criteria == 0 | ||
+ | or $num_success_ord_criteria > 0)) { | ||
+ | return 0; # Don't filter this observation | ||
+ | } else { | ||
+ | next DESC; | ||
+ | } | ||
+ | } else { | ||
+ | $num_success_ord_criteria++; | ||
+ | if ($num_success_req_criteria == $num_required_criteria) { | ||
+ | return 0; # Don't filter this observation | ||
+ | } | ||
+ | } | ||
+ | } else { | ||
+ | next DESC; | ||
+ | } | ||
+ | } else { | ||
+ | # Found the descriptor, but wrong value | ||
+ | next LINE; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } # End of filter descriptor loop | ||
+ | } # End of value line loop | ||
} | } | ||
- | } | + | } # End of criteria loop |
- | # All filter conditions have been fullfilled | + | # One required criterion not fulfilled, or if there are no |
- | return | + | # required criteria: none of the non-required criteria fulfilled |
+ | # (so the observation should be filtered away) | ||
+ | return | ||
} | } | ||
=pod | =pod | ||
+ | |||
+ | =encoding utf8 | ||
=head1 SYNOPSIS | =head1 SYNOPSIS | ||
- | | + | |
+ | [--ahl < | ||
+ | [--all_operators] | ||
+ | [--bitmap] | ||
+ | [--codetables] | ||
+ | [--data_only] | ||
[--filter <filter file>] | [--filter <filter file>] | ||
- | [--param < | ||
- | [--sort | --sort_on < | ||
- | [--transform < | ||
- | [--lon1 x1] | ||
- | [--lat1 y1] | ||
- | [--lon2 x2] | ||
- | [--lat2 x2] | ||
- | [--tablepath <path to BUFR tables>] | ||
[--help] | [--help] | ||
+ | [--nodata] | ||
+ | [--noqc] | ||
+ | [--on_error_stop] | ||
+ | [--optional_section] | ||
+ | [--outfile < | ||
+ | [--param < | ||
+ | [--strict_checking n] | ||
+ | [--tableformat < | ||
+ | [--tablepath <path to BUFR tables>] | ||
+ | [--verbose n] | ||
+ | [--width n] | ||
=head1 DESCRIPTION | =head1 DESCRIPTION | ||
- | Extracts | + | Extract |
- | " | + | to screen, including AHL (Abbreviated Header Line) if present. |
- | 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< | Execute without arguments for Usage, with option C< | ||
- | additional info. See also L</https:// | + | additional info. See also L< |
examples of use. | examples of use. | ||
Line 405: | Line 504: | ||
=head1 OPTIONS | =head1 OPTIONS | ||
+ | --ahl < | ||
+ | | ||
+ | | ||
+ | when printing section 4 | ||
+ | | ||
+ | | ||
+ | is [CODE TABLE] or [FLAG TABLE] | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | more info you might prefer to consult perldoc bufrread.pl | ||
+ | | ||
+ | | ||
+ | (or any descriptors following 222000) | ||
+ | | ||
+ | | ||
+ | Will print to < | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | n=1 Issue warning if (recoverable) error in | ||
+ | BUFR format | ||
+ | n=2 Croak if (recoverable) error in BUFR format. | ||
+ | | ||
+ | | ||
+ | | ||
+ | Set path to BUFR tables (overrides ENV{BUFR_TABLES}) | ||
+ | | ||
+ | show the tables loaded. | ||
+ | | ||
+ | | ||
- | --filter <filter file> | + | Options may be abbreviated, |
- | Decode observations meeting criteria in <filter file> only | + | |
- | --param < | + | |
- | Print parameters in < | + | |
- | as they occur in < | + | |
- | vill be printed using the CSV (comma separated values) format | + | |
- | --sort | + | |
- | first stations with wmonr, then stations with call sign, | + | |
- | then stations with buoy_id (others left out) | + | |
- | --sort_on < | + | |
- | values of < | + | |
- | follows the parameter name. Observations not containing | + | |
- | the parameter at all will be printed lastly. E.g. | + | |
- | --sort_on TA- will sort on decreasing temperatures | + | |
- | --transform < | + | |
- | Do the transformations of parameter values listed in | + | |
- | < | + | |
- | --lon1 x1 | + | |
- | --lat1 y1 | + | |
- | --lon2 x2 | + | |
- | --lat2 y2 | + | |
- | x1,y1,x2,y2 should be decimal degrees | + | |
- | --tablepath <path to BUFR tables> | + | |
- | Set path to BUFR tables (overrides ENV{BUFR_TABLES}) | + | |
- | --help | + | |
- | + | ||
- | Options may be abbreviated, | + | |
To avoid having to use the C< | To avoid having to use the C< | ||
- | set the invironment | + | set the environment |
BUFR tables are located (unless the default path provided by | BUFR tables are located (unless the default path provided by | ||
- | bufrdump.pl works for you). | + | bufrread.pl works for you). For tableformat ECCODES, se |
+ | L< | ||
+ | for more info on how to set C< | ||
- | The lines in <parameter file> should be name of the parameters you | + | For option C<--ahl> the < |
- | want to be printed. For example, if you want only station | + | expression. E.g. C<--ahl " |
- | identification and temperature to be printed for a BUFR SYNOP file, | + | (ISS) from CCCC=ENMI. This is the only case where a little knowledge |
- | the < | + | of Perl might possibly be required when using the utility programs |
+ | included in Geo::BUFR. | ||
- | wmonr | + | For option C< |
- | | + | a BUFR descriptor (6 digits). |
- | TA | + | bufrread.pl will display values for these descriptors only. |
- | If you want " | + | Using C< |
- | in BUFR message, precede | + | the criteria in <filter file> marked D: and all of those criteria |
- | (e.g. '!TA'). | + | marked D!:. Comments (starting with #) are ignored. An example of a |
+ | filter file is | ||
- | 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 | + | D: 001001 001002 |
- | with ' | + | |
- | may for example start like | + | |
- | + | D: 001011 | |
- | wmonr, | + | LF5U # Ekofisk |
- | 01001,, | + | |
- | ,LF5U,9.0 | + | |
- | + | | |
- | 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 < | + | |
- | where operator is one of =, !=, <, <=, > and >=. An example filter | + | |
- | file is | + | |
- | + | ||
- | | + | |
- | | + | |
- | D: 001001 | + | |
- | | + | |
- | | + | |
- | D: 001011 | + | |
- | LF5U | + | |
- | + | ||
- | | + | |
- | NN != 8 | + | |
- | TA >= 5 | + | |
- | | + | |
- | | + | |
which decodes all observations with block number 01, two other | which decodes all observations with block number 01, two other | ||
- | specific | + | specific |
- | having cloud cover different from 8 (but NN must be part of the | + | (004004) equal to 6 or 7. If there is no value line after a |
- | message) and temperature between 5 and 9.5 degrees Celsius and | + | descriptor line, it is enough that the observation contains |
- | containing precipitation for last 24 hours. Comment lines starting | + | descriptor(s), whatever the values are. So to extract all ship |
- | with # will be ignored. | + | messages from a BUFR file, the filter file should contain this single |
- | + | line only: | |
- | Another example: | + | |
- | + | ||
- | + | ||
- | wmonr | + | |
- | + | ||
- | will print only those observations containing a wmonr (skipping | + | |
- | ships). | + | |
- | + | ||
- | The --transform option is provided mainly | + | |
- | units than what is default in bufrdump.pl. The transformation | + | |
- | should list the transformations wanted, one per line as | + | |
- | + | ||
- | < | + | |
- | where $x is original value of the parameter. | + | D: 001011 |
- | For example, | + | If an error occurs during decoding (typically because |
- | FF and wind gust FG in knots instead of m/s, rounded | + | BUFR table is missing or message is corrupt), the BUFR message is |
- | and cloud cover NN in % (instead of the default | + | skipped with an error message printed |
- | code table 2700, roughly counting octas): | + | continues with the next BUFR message. You can change this default |
+ | behaviour, however, by setting C< | ||
- | FF = sprintf(" | + | =head1 CAVEAT |
- | FG = sprintf(" | + | |
- | NN = int($x*12.5 + .5) | + | |
- | If --transform is combined with --filter, the filter criteria should | + | Option C<--bitmap> may not work properly for complicated BUFR messages. |
- | refer to the transformed | + | Namely, when the first bit-map is encountered, |
- | is to be applied for sky not all covered by clouds, you should use NN | + | their descriptors) will be displayed unless they refer to the |
- | != 100 instead of NN != 8 in filter file. | + | preceding data values |
+ | if a bit-map refers to another bit-map or the bit-mapped values are | ||
+ | combined with 204YYY (add associated field operator). | ||
=head1 AUTHOR | =head1 AUTHOR | ||
Line 527: | Line 605: | ||
=head1 COPYRIGHT | =head1 COPYRIGHT | ||
- | Copyright (C) 2010 met.no | + | Copyright (C) 2010-2023 MET Norway |
=cut | =cut | ||
</ | </ |