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 [2023-02-05 10:16:41] pals |
bufr.pm:bufrencode_source [2025-11-05 09:27:00] (current) pals |
||
|---|---|---|---|
| Line 2: | Line 2: | ||
| # | # | ||
| - | # (C) Copyright 2010-2023 MET Norway | + | # (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 34: | Line 34: | ||
| use constant DEFAULT_TABLE_PATH_BUFRDC => '/ | use constant DEFAULT_TABLE_PATH_BUFRDC => '/ | ||
| use constant DEFAULT_TABLE_PATH_ECCODES => '/ | use constant DEFAULT_TABLE_PATH_ECCODES => '/ | ||
| - | # Ought to be your most up-to-date B table | ||
| - | use constant DEFAULT_TABLE_BUFRDC => ' | ||
| - | use constant DEFAULT_TABLE_ECCODES => ' | ||
| # Parse command line options | # Parse command line options | ||
| Line 43: | Line 40: | ||
| GetOptions( | GetOptions( | ||
| | | ||
| - | ' | + | ' |
| - | ' | + | ' |
| - | ' | + | ' |
| - | ' | + | ' |
| - | ' | + | ' |
| - | ' | + | ' |
| - | # replication | + | ' |
| - | ' | + | ' |
| - | # descriptors on one line | + | |
| - | ' | + | |
| - | ' | + | |
| - | ' | + | |
| ) or pod2usage(-verbose => 0); | ) or pod2usage(-verbose => 0); | ||
| - | |||
| # User asked for help | # User asked for help | ||
| pod2usage(-verbose => 1) if $option{help}; | pod2usage(-verbose => 1) if $option{help}; | ||
| - | # No arguments if --code | + | # Data or metadata file not provided |
| - | if (defined $option{code} or defined $option{flag}) { | + | pod2usage(-verbose => 0) if not $option{data} or not $option{metadata}; |
| - | | + | |
| - | } else { | + | |
| - | pod2usage(-verbose => 0) if not @ARGV; | + | |
| - | } | + | |
| - | # If --flag is set, user must also provide code table | + | my $data_file |
| - | pod2usage(-verbose | + | my $metadata_file = |
| - | # All arguments must be integers | + | # Default is croak if (recoverable) error found in encoded BUFR format |
| - | foreach | + | my $strict_checking = defined $option{strict_checking} |
| - | | + | |
| - | } | + | Geo:: |
| - | if (defined $option{code} && $option{code} !~ /^\d+$/) { | + | |
| - | | + | |
| - | } | + | |
| - | if (defined | + | |
| - | pod2usage(" | + | |
| - | } | + | |
| - | + | ||
| - | + | ||
| - | # Set verbosity level for the BUFR module | + | |
| - | my $verbose = $option{verbose} ? 1 : 0; | + | |
| - | Geo:: | + | |
| - | # From version 1.32 a descriptor sequence ending in e.g. 106000 031001 | + | # Set verbosity level |
| - | # will be allowed unless strict checking is set, and we really want | + | Geo:: |
| - | # bufrresolve.pl to complain in this case | + | |
| - | Geo:: | + | |
| # Set BUFR table format | # Set BUFR table format | ||
| Line 111: | Line 86: | ||
| } | } | ||
| } | } | ||
| - | |||
| - | # BUFR table file to use | ||
| - | my $table = $option{bufrtable} || | ||
| - | ($tableformat eq ' | ||
| my $bufr = Geo:: | my $bufr = Geo:: | ||
| - | if (defined | + | # Read metadata into $bufr |
| - | # Resolve flag value or dump code table | + | read_metadata($metadata_file, |
| - | my $code_table | + | |
| - | | + | # Load B and D tables (table version inferred from metadata) |
| - | if ($option{flag} == 0) { | + | $bufr-> |
| - | print "No bits are set\n"; | + | |
| - | } else { | + | # Get the data |
| - | | + | my ($data_refs, |
| - | } | + | |
| + | $bufr-> | ||
| + | |||
| + | # Print the encoded BUFR message | ||
| + | my $buffer = $bufr-> | ||
| + | if ($option{outfile}) { | ||
| + | my $outfile | ||
| + | | ||
| + | binmode($fh); | ||
| + | print $fh $buffer; | ||
| + | } else { | ||
| + | binmode(STDOUT); | ||
| + | print $buffer; | ||
| + | } | ||
| + | |||
| + | # See OPTIONS section in pod for format of metadata file | ||
| + | sub read_metadata { | ||
| + | my ($file, $bufr) = @_; | ||
| + | |||
| + | # Read metadata from file into a hash | ||
| + | my %metadata; | ||
| + | open (my $fh, '<', | ||
| + | while ( < | ||
| + | | ||
| + | next if /^\s*$/; | ||
| + | s/^\s+//; | ||
| + | my ($key, $value) = split /\s+/, $_, 2; | ||
| + | $metadata{$key} = $value; | ||
| + | } | ||
| + | close $fh or die " | ||
| + | |||
| + | # Load the metadata into the BUFR object | ||
| + | my $m = \%metadata; | ||
| + | |||
| + | my $bufr_edition = $m-> | ||
| + | |||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | | ||
| + | | ||
| + | $bufr-> | ||
| + | $bufr->set_optional_section($m->{OPTIONAL_SECTION}); | ||
| + | | ||
| + | if ( $bufr_edition < 4 ) { | ||
| + | | ||
| } else { | } else { | ||
| - | | + | $bufr->set_int_data_subcategory($m-> |
| + | | ||
| } | } | ||
| - | } else { | + | |
| - | # Resolve descriptor(s) | + | $bufr->set_local_table_version($m-> |
| - | | + | |
| - | | + | $bufr->set_year_of_century($m->{YEAR_OF_CENTURY}); |
| - | print $bufr->resolve_descriptor(' | + | |
| - | | + | |
| - | | + | |
| - | } elsif ($option{noexpand}) { | + | |
| - | print $bufr-> | + | |
| } else { | } else { | ||
| - | | + | $bufr->set_year($m-> |
| } | } | ||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | $bufr-> | ||
| + | |||
| + | return; | ||
| + | } | ||
| + | |||
| + | # See OPTIONS section in pod for format of data file | ||
| + | sub readdata { | ||
| + | my $file = shift; | ||
| + | open (my $fh, '<', | ||
| + | |||
| + | my ($data_refs, | ||
| + | my $subset = 0; | ||
| + | while ( <$fh> ) { | ||
| + | s/^\s+//; | ||
| + | # Lines not starting with a number are ignored | ||
| + | next if not /^\d/; | ||
| + | my ($n, $desc, $value) = split /\s+/, $_, 3; | ||
| + | $subset++ if $n == 1; | ||
| + | # Some operator descriptors are written on unnumbered lines | ||
| + | # without a value | ||
| + | if (!defined $desc || $desc !~ /^\d/) { | ||
| + | next unless $n >= 200000 && $n < 300000; # Better to die here? | ||
| + | $desc = $n; | ||
| + | $value = undef; | ||
| + | } else { | ||
| + | $value =~ s/\s+$//; | ||
| + | $value = undef if $value eq '' | ||
| + | } | ||
| + | push @{$data_refs-> | ||
| + | push @{$desc_refs-> | ||
| + | } | ||
| + | close $fh or die " | ||
| + | |||
| + | return ($data_refs, | ||
| } | } | ||
| Line 150: | Line 203: | ||
| =head1 SYNOPSIS | =head1 SYNOPSIS | ||
| - | | + | |
| - | [--partial] | + | [--outfile |
| - | | + | [--strict_checking |
| - | | + | [--tableformat < |
| - | | + | [--tablepath <path to BUFR tables> |
| - | [--tableformat | + | [--verbose n] |
| - | | + | [--help] |
| - | | + | |
| - | [--help] | + | |
| - | + | ||
| - | 2) bufrresolve.pl --code <code or flag table> | + | |
| - | | + | |
| - | [--tableformat < | + | |
| - | | + | |
| - | | + | |
| - | + | ||
| - | 3) bufrresolve.pl --flag < | + | |
| - | [--bufrtable <name of BUFR table] | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| =head1 DESCRIPTION | =head1 DESCRIPTION | ||
| - | Utility program for fetching info from BUFR tables. | + | Encode a BUFR message, reading data and metadata |
| + | resulting | ||
| + | C< | ||
| - | Execute without arguments for Usage, with option | + | Execute without arguments for Usage, with option --help for some |
| additional info. See also L< | additional info. See also L< | ||
| examples of use. | examples of use. | ||
| - | The tables used can be selected by the user with options | + | =head1 OPTIONS |
| - | C< | + | |
| - | tableformat in Geo::BUFR is BUFRDC, while default tablepath in | + | |
| - | bufrresolve.pl will be overridden if the environment variable | + | |
| - | BUFR_TABLES is set. You should consider edit the source code of | + | |
| - | bufrresolve.pl if you are not satisfied with the defaults chosen for | + | |
| - | tablepath and bufrtable (search for ' | + | |
| - | For tableformat ECCODES, see | + | |
| - | L< | + | the same as consulting perldoc bufrencode.pl |
| - | for more info on how to set C<--tablepath> | + | --outfile < |
| + | instead of STDOUT | ||
| + | | ||
| + | n=1 Issue warning if (recoverable) error in | ||
| + | | ||
| + | n=2 (default) Croak if (recoverable) error in BUFR format. | ||
| + | Nothing more in this message will be encoded. | ||
| + | --tableformat | ||
| + | | ||
| + | If used, will set path to BUFR tables. If not | ||
| + | | ||
| + | variable BUFR_TABLES, | ||
| + | will use DEFAULT_TABLE_PATH_<tableformat> | ||
| + | hard coded in source code. | ||
| + | --verbose n Set verbose level to n, 0< | ||
| + | Verbose output is sent to STDOUT, so ought to | ||
| + | be combined with option --outfile | ||
| - | For the table name in C< | + | =head2 Required options |
| - | table, e.g. B0000000000098013001.TXT. Replacing B with D or C, or | + | |
| - | omitting this prefix altogether, or even omitting the trailing ' | + | |
| - | (i.e. 0000000000098013001) will also work. | + | |
| - | For the table name in C<--bufrtable> in ECCODES, use last significant part | + | =head4 |
| - | of table location, e.g. ' | + | |
| - | ' | + | |
| - | up local sequence descriptors, | + | |
| - | and the local table to get the full expansion, e.g. | + | |
| - | ' | + | |
| - | See also L</" | + | For the metadata file, use this as a prototype and change the values |
| + | as desired: | ||
| - | =head1 OPTIONS | + | BUFR_EDITION |
| + | MASTER_TABLE | ||
| + | CENTRE | ||
| + | SUBCENTRE | ||
| + | UPDATE_SEQUENCE_NUMBER | ||
| + | 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, |
| - | --simple | + | LOC_DATA_SUBCATEGORY, YEAR and SECOND with new lines DATA_SUBCATEGORY |
| - | descriptors on one line | + | and YEAR_OF_CENTURY (the order of lines doesn' |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | the same as consulting perldoc bufrresolve.pl | + | |
| - | Usage 1): Resolves the given descriptor(s) fully into table B | + | =head4 |
| - | descriptors, | + | |
| - | bits) written on each line (except for --simple). --partial, --simple | + | |
| - | and --noexpand are mutually exclusive (full expansion is default). | + | |
| - | Usage 2): Prints | + | For the data file, use the same format as would result if you did run |
| - | (named by the table B descriptor). | + | on the generated BUFR message |
| - | Usage 3): Displays the bits set when the data value for the requested | + | bufrread.pl |
| - | flag table is <value>. | + | |
| - | Options may be abbreviated, | + | or if you use bufrread.pl with C<--width n>, replace 31 with n+16. |
| + | For example, the file might begin with | ||
| - | =head1 CAVEAT | + | |
| + | | ||
| + | | ||
| + | | ||
| + | ... | ||
| - | The C< | + | Every time a new line starting with the number 1 is met, a new subset |
| - | is no guarantee that the same BUFR descriptor resolves the same way | + | will be generated in the BUFR message. Lines not starting with a |
| - | for different BUFR tables. However, as soon as a new BUFR descriptor | + | number are ignored. |
| - | is introduced in a BUFR table, it is extremely rare that the | + | |
| - | descriptor is redefined in later versions. So for convenience, | + | For missing values, use ' |
| - | bufrresolve.pl uses a default table (adding option C< | + | descriptor. |
| - | show you the tables used). If this is the wrong table for your purpose | + | |
| - | (most common case will be if the descriptor was added in a higher | + | Associated values should use BUFR descriptor 999999, and operator |
| - | version than that of the default table), you should definitely use | + | descriptors 22[2345]000 and 23[2567]000 should not have a value, |
| - | C<--bufrtable> with the appropriate table. | + | neither should this line be numbered, e.g. |
| + | |||
| + | | ||
| + | 222000 | ||
| + | | ||
| + | | ||
| + | ... | ||
| + | |||
| + | To encode a NIL subset, all delayed replication factors should | ||
| + | nonzero, and all other values set to missing except for the | ||
| + | descriptors defining | ||
| + | |||
| + | Options may be abbreviated, | ||
| =head1 AUTHOR | =head1 AUTHOR | ||
| Line 251: | Line 320: | ||
| =head1 COPYRIGHT | =head1 COPYRIGHT | ||
| - | Copyright (C) 2010-2023 MET Norway | + | Copyright (C) 2010-2025 MET Norway |
| =cut | =cut | ||
| </ | </ | ||