Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
bufr.pm:bufrencode_source [2010-03-10 09:19:34] pals |
bufr.pm:bufrencode_source [2025-11-05 09:27:00] (current) pals |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | < | + | < |
| - | # | + | # |
| - | # (C) Copyright 2010, met.no | + | # (C) Copyright 2010-2025 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 BUFR; | + | use Pod::Usage qw(pod2usage); |
| + | use Geo::BUFR; | ||
| + | |||
| + | # This is actually default in BUFR.pm, but provided here to make it | ||
| + | # easier for users to change to ' | ||
| + | use constant DEFAULT_TABLE_FORMAT => ' | ||
| # Will be used if neither --tablepath nor $ENV{BUFR_TABLES} is set | # Will be used if neither --tablepath nor $ENV{BUFR_TABLES} is set | ||
| - | my $DEFAULT_TABLE_PATH | + | use constant DEFAULT_TABLE_PATH_BUFRDC |
| + | use constant DEFAULT_TABLE_PATH_ECCODES => '/ | ||
| # Parse command line options | # Parse command line options | ||
| Line 39: | 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 default | + | # If all else fails, use the default |
| - | 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 118: | 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 152: | 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, | + | |
| - | $DEFAULT_TABLE_PATH | + | |
| - | 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 240: | 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-2025 MET Norway | ||
| + | |||
| + | =cut | ||
| </ | </ | ||