Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
bufr.pm:bufrencode_source [2010-02-24 12:50:40] pals created |
bufr.pm:bufrencode_source [2023-02-05 10:18:55] (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 19: | Line 19: | ||
# 02110-1301, USA. | # 02110-1301, USA. | ||
- | # Encode a BUFR message, reading data and metadata from files. | + | # pod included at end of file |
- | + | ||
- | # Author: P. Sannes met.no 2010 | + | |
use strict; | use strict; | ||
+ | use warnings; | ||
use Getopt:: | use Getopt:: | ||
+ | use Pod::Usage qw(pod2usage); | ||
+ | use Geo::BUFR; | ||
- | # metno module | + | # This is actually default in BUFR.pm, but provided here to make it |
- | use BUFR; | + | # easier for users to change to ' |
+ | use constant DEFAULT_TABLE_FORMAT => ' | ||
- | use constant | + | # Will be used if neither --tablepath nor $ENV{BUFR_TABLES} is set |
+ | use constant | ||
+ | use constant DEFAULT_TABLE_PATH_ECCODES => '/ | ||
# Parse command line options | # Parse command line options | ||
Line 41: | Line 44: | ||
' | ' | ||
' | ' | ||
+ | ' | ||
+ | ' | ||
' | ' | ||
' | ' | ||
- | ) or die "Wrong option(s), execute $0 without arguments for Usage\n" | + | ) or pod2usage(-verbose => 0); |
# User asked for help | # User asked for help | ||
- | usage_verbose() if $option{help}; | + | pod2usage(-verbose => 1) if $option{help}; |
# Data or metadata file not provided | # Data or metadata file not provided | ||
- | usage() if not $option{data} or not $option{metadata}; | + | pod2usage(-verbose => 0) if not $option{data} or not $option{metadata}; |
my $data_file | my $data_file | ||
my $metadata_file = $option{metadata}; | my $metadata_file = $option{metadata}; | ||
- | my $verbose | + | # Default is croak if (recoverable) error found in encoded BUFR format |
+ | my $strict_checking | ||
+ | | ||
+ | Geo:: | ||
- | # Set verbosity level for the BUFR module. Must be set also for each | + | # Set verbosity level |
- | # BUFR object generated | + | Geo::BUFR-> |
- | BUFR->set_verbose($verbose); | + | |
+ | # Set BUFR table format | ||
+ | my $tableformat = (defined $option{tableformat}) ? uc $option{tableformat} : DEFAULT_TABLE_FORMAT; | ||
+ | Geo::BUFR->set_tableformat($tableformat); | ||
# Set BUFR table path | # Set BUFR table path | ||
if ($option{tablepath}) { | if ($option{tablepath}) { | ||
# Command line option --tablepath overrides all | # Command line option --tablepath overrides all | ||
- | BUFR-> | + | |
} elsif ($ENV{BUFR_TABLES}) { | } elsif ($ENV{BUFR_TABLES}) { | ||
# If no --tablepath option, use the BUFR_TABLES environment variable | # If no --tablepath option, use the BUFR_TABLES environment variable | ||
- | BUFR-> | + | |
} else { | } else { | ||
- | # If all else fails, use the libemos bufrtables | + | # If all else fails, use the default tablepath in BUFRDC/ |
- | BUFR-> | + | |
+ | Geo::BUFR-> | ||
+ | } elsif ($tableformat eq ' | ||
+ | Geo:: | ||
+ | } | ||
} | } | ||
- | # Read in metadata | + | my $bufr = Geo:: |
- | my $metadata | + | |
- | my $bufr = BUFR-> | + | # Read metadata |
- | + | read_metadata($metadata_file, | |
- | # This sets object verbose level equal to class verbose level | + | |
- | $bufr-> | + | |
- | + | ||
- | my $bufr_edition = $metadata-> | + | |
- | + | ||
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | if ( $bufr_edition < 4 ) { | + | |
- | $bufr-> | + | |
- | } else { | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | } | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | if ( $bufr_edition < 4 ) { | + | |
- | $bufr-> | + | |
- | } else { | + | |
- | $bufr-> | + | |
- | } | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
- | $bufr-> | + | |
+ | # Load B and D tables (table version inferred from metadata) | ||
$bufr-> | $bufr-> | ||
- | # Read in the data (descriptors and values) | + | # Get the data |
my ($data_refs, | my ($data_refs, | ||
Line 120: | Line 101: | ||
# Print the encoded BUFR message | # Print the encoded BUFR message | ||
+ | my $buffer = $bufr-> | ||
if ($option{outfile}) { | if ($option{outfile}) { | ||
- | open(my $OUT, '>', | + | |
- | | + | |
- | | + | |
- | | + | |
} else { | } else { | ||
- | print $bufr-> | + | |
+ | | ||
} | } | ||
+ | # See OPTIONS section in pod for format of metadata file | ||
sub read_metadata { | sub read_metadata { | ||
- | my $file = shift; | + | my ($file, $bufr) = @_; |
- | open (my $fh, '<' | + | |
+ | # Read metadata from file into a hash | ||
my %metadata; | my %metadata; | ||
+ | open (my $fh, '<', | ||
while ( <$fh> ) { | while ( <$fh> ) { | ||
chomp; | chomp; | ||
next if /^\s*$/; | next if /^\s*$/; | ||
+ | s/^\s+//; | ||
my ($key, $value) = split /\s+/, $_, 2; | my ($key, $value) = split /\s+/, $_, 2; | ||
$metadata{$key} = $value; | $metadata{$key} = $value; | ||
} | } | ||
- | close $fh; | + | close $fh or die " |
+ | |||
+ | # Load the metadata into the BUFR object | ||
+ | my $m = \%metadata; | ||
+ | |||
+ | my $bufr_edition = $m-> | ||
+ | |||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | if ( $bufr_edition < 4 ) { | ||
+ | $bufr-> | ||
+ | } else { | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | } | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | if ( $bufr_edition < 4 ) { | ||
+ | $bufr-> | ||
+ | } else { | ||
+ | $bufr-> | ||
+ | } | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
+ | $bufr-> | ||
- | return | + | return; |
} | } | ||
+ | # See OPTIONS section in pod for format of data file | ||
sub readdata { | sub readdata { | ||
my $file = shift; | my $file = shift; | ||
Line 154: | Line 175: | ||
while ( <$fh> ) { | while ( <$fh> ) { | ||
s/^\s+//; | s/^\s+//; | ||
+ | # Lines not starting with a number are ignored | ||
next if not /^\d/; | next if not /^\d/; | ||
my ($n, $desc, $value) = split /\s+/, $_, 3; | my ($n, $desc, $value) = split /\s+/, $_, 3; | ||
$subset++ if $n == 1; | $subset++ if $n == 1; | ||
- | $value =~ s/\s+$//; | + | |
- | $value = undef if $value eq '' | + | # without a value |
- | $data_refs-> | + | if (!defined $desc || $desc !~ /^\d/) { |
- | $desc_refs-> | + | next unless $n >= 200000 && $n < 300000; # Better to die here? |
+ | $desc = $n; | ||
+ | $value = undef; | ||
+ | } else { | ||
+ | | ||
+ | $value = undef if $value eq '' | ||
+ | | ||
+ | push @{$data_refs-> | ||
+ | | ||
} | } | ||
- | close $fh; | + | close $fh or die " |
return ($data_refs, | return ($data_refs, | ||
} | } | ||
- | sub usage { | + | =pod |
- | print <<" | + | |
- | Usage: $0 | + | |
- | --data <data file> | + | |
- | --metadata < | + | |
- | [--tablepath <path to BUFR tables> | + | |
- | [--outfile <file to print encoded BUFR message(s) to>] | + | |
- | [--verbose n] | + | |
- | [--help] | + | |
- | Try '$0 --help' | + | |
- | EOF | + | |
- | exit 0; | + | |
- | } | + | |
- | sub usage_verbose { | + | =encoding utf8 |
- | print <<" | + | |
- | Will create a BUFR message printed to STDOUT based on data and metadata | + | =head1 SYNOPSIS |
- | in files <data file> and < | + | |
- | Usage: $0 -d <data file> -m < | + | bufrencode.pl |
- | or | + | [--outfile |
+ | [--strict_checking n] | ||
+ | [--tableformat | ||
+ | [--tablepath <path to BUFR tables> | ||
+ | [--verbose n] | ||
+ | [--help] | ||
- | Options: | + | =head1 DESCRIPTION |
- | --help | + | |
- | Will print this help message and then exit | + | |
- | --outfile < | + | |
- | Will print encoded BUFR messages to < | + | |
- | --verbose n | + | |
- | Set verbose level to n, 0<=n<=3 (default 0). Verbose output is sent STDOUT, | + | |
- | so ought to be combined with option --outfile | + | |
- | --tablepath <path to BUFR tables> | + | |
- | If used, will set path to BUFR tables. If not set, will fetch tables | + | |
- | from the environment variable BUFR_TABLES, | + | |
- | / | + | |
- | For the metadata file, use this as prototype | + | Encode a BUFR message, reading data and metadata from files. The |
+ | resulting BUFR message will be printed to STDOUT unless option | ||
+ | C< | ||
- | BUFR_EDITION | + | Execute without arguments for Usage, with option --help for some |
- | MASTER_TABLE | + | additional info. See also L< |
- | CENTRE | + | examples of use. |
- | SUBCENTRE | + | |
- | UPDATE_NUMBER | + | =head1 OPTIONS |
- | OPTIONAL_SECTION | + | |
- | DATA_CATEGORY | + | |
- | INT_DATA_SUBCATEGORY | + | the same as consulting perldoc bufrencode.pl |
- | LOC_DATA_SUBCATEGORY | + | |
- | MASTER_TABLE_VERSION | + | instead of STDOUT |
- | LOCAL_TABLE_VERSION | + | |
- | YEAR 2008 | + | n=1 Issue warning if (recoverable) error in |
- | MONTH 9 | + | BUFR format |
- | DAY 1 | + | n=2 (default) Croak if (recoverable) error in BUFR format. |
- | HOUR 6 | + | Nothing more in this message will be encoded. |
- | MINUTE | + | |
- | SECOND | + | |
- | OBSERVED_DATA | + | If used, will set path to BUFR tables. If not |
- | COMPRESSED_DATA | + | set, will fetch tables from the environment |
- | DESCRIPTORS_UNEXPANDED | + | variable BUFR_TABLES, |
+ | will use DEFAULT_TABLE_PATH_< | ||
+ | hard coded in source code. | ||
+ | | ||
+ | Verbose output is sent to STDOUT, so ought to | ||
+ | be combined with option --outfile | ||
+ | |||
+ | =head2 Required options | ||
+ | |||
+ | =head4 --metadata < | ||
+ | |||
+ | For the metadata file, use this as a prototype and change the values | ||
+ | as desired: | ||
+ | |||
+ | | ||
+ | MASTER_TABLE | ||
+ | CENTRE | ||
+ | SUBCENTRE | ||
+ | | ||
+ | OPTIONAL_SECTION | ||
+ | DATA_CATEGORY | ||
+ | INT_DATA_SUBCATEGORY | ||
+ | LOC_DATA_SUBCATEGORY | ||
+ | MASTER_TABLE_VERSION | ||
+ | LOCAL_TABLE_VERSION | ||
+ | YEAR 2008 | ||
+ | MONTH 9 | ||
+ | DAY 1 | ||
+ | HOUR 6 | ||
+ | MINUTE | ||
+ | SECOND | ||
+ | OBSERVED_DATA | ||
+ | COMPRESSED_DATA | ||
+ | DESCRIPTORS_UNEXPANDED | ||
For BUFR edition < 4, replace the lines INT_DATA_SUBCATEGORY, | For BUFR edition < 4, replace the lines INT_DATA_SUBCATEGORY, | ||
LOC_DATA_SUBCATEGORY, | LOC_DATA_SUBCATEGORY, | ||
and YEAR_OF_CENTURY (the order of lines doesn' | and YEAR_OF_CENTURY (the order of lines doesn' | ||
+ | |||
+ | =head4 --data <data file> | ||
For the data file, use the same format as would result if you did run | For the data file, use the same format as would result if you did run | ||
on the generated BUFR message | on the generated BUFR message | ||
+ | |||
bufrread.pl <bufr file> --data_only | cut -c -31 | bufrread.pl <bufr file> --data_only | cut -c -31 | ||
- | (or if you use bufrread.pl with --width n, replace 31 with n+16). | + | |
+ | or if you use bufrread.pl with C<--width n>, replace 31 with n+16. | ||
For example, the file might begin with | For example, the file might begin with | ||
Line 242: | Line 291: | ||
... | ... | ||
- | Every time a new line starting with the number 1 is met, a new subset will be | + | Every time a new line starting with the number 1 is met, a new subset |
- | generated in the BUFR message. Lines not starting with a number are ignored. | + | will be generated in the BUFR message. Lines not starting with a |
+ | number are ignored. | ||
- | For missing values, use ' | + | For missing values, use ' |
+ | descriptor. | ||
- | Associated values should use BUFR descriptor 999999. | + | Associated values should use BUFR descriptor 999999, and operator |
+ | descriptors 22[2345]000 and 23[2567]000 should not have a value, | ||
+ | neither should this line be numbered, e.g. | ||
- | To encode a NIL subset, all delayed replication factors should be set to 1, and | + | |
- | all other values set to missing | + | 222000 |
+ | | ||
+ | | ||
+ | ... | ||
- | EOF | + | To encode a NIL subset, all delayed replication factors should be |
- | exit 0; | + | nonzero, and all other values set to missing except for the |
- | } | + | descriptors defining the station. |
+ | |||
+ | Options may be abbreviated, | ||
+ | |||
+ | =head1 AUTHOR | ||
+ | |||
+ | Pål Sannes E< | ||
+ | |||
+ | =head1 COPYRIGHT | ||
+ | |||
+ | Copyright (C) 2010-2023 MET Norway | ||
+ | |||
+ | =cut | ||
</ | </ |