File Coverage

bufrread.pl
Criterion Covered Total %
statement 209 224 93.3
branch 106 146 72.6
condition 33 42 78.5
subroutine 14 15 93.3
pod n/a
total 362 427 84.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             # (C) Copyright 2010-2025 MET Norway
4             #
5             # This program is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by
7             # the Free Software Foundation; either version 2 of the License, or
8             # (at your option) any later version.
9             #
10             # This program is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13             # General Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License
16             # along with this program; if not, write to the Free Software
17             # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18             # 02110-1301, USA.
19              
20             # pod included at end of file
21              
22 30     30   145166 use strict;
  30         52  
  30         1189  
23 30     30   132 use warnings;
  30         44  
  30         1751  
24 30     30   26645 use Getopt::Long;
  30         477400  
  30         179  
25 30     30   20915 use Pod::Usage qw(pod2usage);
  30         2131005  
  30         2687  
26 30     30   43198 use Geo::BUFR;
  30         147  
  30         1740  
27              
28             # This is actually default in BUFR.pm, but provided here to make it
29             # easier for users to change to 'ECCODES' if preferred
30 30     30   241 use constant DEFAULT_TABLE_FORMAT => 'BUFRDC';
  30         42  
  30         2531  
31              
32             # Will be used if neither --tablepath nor $ENV{BUFR_TABLES} is set
33 30     30   157 use constant DEFAULT_TABLE_PATH_BUFRDC => '/usr/local/lib/bufrtables';
  30         39  
  30         1443  
34 30     30   131 use constant DEFAULT_TABLE_PATH_ECCODES => '/usr/local/share/eccodes/definitions/bufr/tables';
  30         43  
  30         132742  
35              
36             # Parse command line options
37 30         4712274 my %option = ();
38 30 50       297 GetOptions(
39             \%option,
40             'ahl=s', # Decode BUFR messages with AHL matching only
41             'all_operators',# Show replication descriptors and all operator descriptors
42             # when printing section 4
43             'bitmap', # Display bit-mapped values on same line
44             'codetables', # Use code and flag tables to resolve values
45             'data_only', # Print section 4 (data section) only
46             'filter=s', # Decode observations meeting criteria in only
47             'help', # Print help information and exit
48             'nodata', # Do not print (nor decode) section 4 (data section)
49             'noqc', # Do not decode quality control
50             'on_error_stop', # Stop processing if an error occurs
51             'optional_section', # Display a hex dump of optional section if present
52             'outfile=s', # Print to file instead of STDOUT
53             'param=s', # Decode parameters with descriptors in only
54             'strict_checking=i', # Enable/disable strict checking of BUFR format
55             'tableformat=s', # Set BUFR table format
56             'tablepath=s', # Set BUFR table path
57             'verbose=i', # Set verbose level to n, 0<=n<=6 (default 0)
58             'width=i', # Set width of values field (default is 15 characters)
59             ) or pod2usage(-verbose => 0);
60              
61             # User asked for help
62 30 50       55425 pod2usage(-verbose => 1) if $option{help};
63              
64             # Make sure there is at least one input file
65 30 50       100 pod2usage(-verbose => 0) unless @ARGV;
66              
67             # Set verbosity level
68 30 100       142 Geo::BUFR->set_verbose($option{verbose}) if $option{verbose};
69              
70             # Set whether section 4 should be decoded for the BUFR module
71 30 100       118 Geo::BUFR->set_nodata() if ($option{nodata});
72              
73             # Set whether quality information should be decoded for the BUFR module
74 30 100       123 Geo::BUFR->set_noqc() if ($option{noqc});
75              
76 30 100       175 Geo::BUFR->set_strict_checking($option{strict_checking}) if defined $option{strict_checking};
77              
78 30 100       137 Geo::BUFR->set_show_all_operators($option{all_operators}) if defined $option{all_operators};
79              
80             # Set BUFR table format
81 30 100       129 my $tableformat = (defined $option{tableformat}) ? uc $option{tableformat} : DEFAULT_TABLE_FORMAT;
82 30         317 Geo::BUFR->set_tableformat($tableformat);
83              
84             # Set BUFR table path
85 30 50       86 if ($option{tablepath}) {
    0          
86             # Command line option --tablepath overrides all
87 30         162 Geo::BUFR->set_tablepath($option{tablepath});
88             } elsif ($ENV{BUFR_TABLES}) {
89             # If no --tablepath option, use the BUFR_TABLES environment variable
90 0         0 Geo::BUFR->set_tablepath($ENV{BUFR_TABLES});
91             } else {
92             # If all else fails, use the default tablepath in BUFRDC/ECCODES
93 0 0       0 if ($tableformat eq 'BUFRDC') {
    0          
94 0         0 Geo::BUFR->set_tablepath(DEFAULT_TABLE_PATH_BUFRDC);
95             } elsif ($tableformat eq 'ECCODES') {
96 0         0 Geo::BUFR->set_tablepath(DEFAULT_TABLE_PATH_ECCODES);
97             }
98             }
99              
100 30         54 my $ahl_regexp;
101 30 100       110 if ($option{ahl}) {
102 1         2 eval { $ahl_regexp = qr/$option{ahl}/ };
  1         24  
103 1 50       2 die "Argument to --ahl is not a valid Perl regular expression: $@" if $@;
104             }
105              
106             # Where to direct output (including verbose output, but not output to STDERR)
107 30         41 my $OUT;
108 30 100       83 if ($option{outfile}) {
109             open($OUT, '>', $option{outfile})
110 1 50       244 or die "Cannot open $option{outfile} for writing: $!";
111             } else {
112 29         95 $OUT = *STDOUT;
113             }
114              
115 30         58 my @requested_desc;
116 30 100       101 if ($option{param}) {
117 3         17 @requested_desc = read_descriptor_file($option{param});
118             }
119              
120             # Arrays over filter criteria, used if option --filter is set
121 30         172 my @fid; # Filter descriptors, e.g. $fid[1] = [ 001001, 001002 ]
122             my @fiv; # Filter values, e.g. $fiv[1] = [ [ 3, 895 ], [ 6 252 ] ]
123 30         0 my @num_desc; # Number of filter descriptors for each criterion, e.g. $num_desc[1] = 2
124 30         0 my @num_val; # Number of filter value lines for each criterion, e.g. $num_val[1] = 2
125 30         0 my @required; # 1 for required criteria (D!: in filter file), 0 for others
126 30         46 my $num_criteria = 0;
127 30         78 my $num_required_criteria = 0;
128 30 100       92 if ($option{filter}) {
129 2         14 read_filter_file($option{filter});
130             }
131              
132 30 100       95 my $width = $option{width} ? $option{width} : 15;
133              
134             # Used to display section 2 if --optional_section is set
135 30     0   186 my $sec2_code_ref = sub {return ' Hex dump:'.' 'x26 . unpack('H*',substr(shift,4))};
  0         0  
136              
137             # Loop for processing of BUFR input files
138 30         95 foreach my $inputfname ( @ARGV ) {
139 30         160 my $bufr = Geo::BUFR->new();
140 30 100       114 $bufr->set_filter_cb(\&filter_on_ahl,$ahl_regexp) if $option{ahl};
141              
142             # Open BUFR file
143 30         201 $bufr->fopen($inputfname);
144              
145             # Process input file
146 30         160 decode($bufr);
147 30         162 $bufr->fclose();
148             }
149              
150              
151             # Extract data from BUFR file. Print AHL for first message in each GTS
152             # bulletin, print message number for each new message, print subset
153             # number for each subset.
154             sub decode {
155 30     30   75 my $bufr = shift; # BUFR object
156              
157 30         63 my ($message_header, $current_message_number, $current_ahl);
158 30         52 my $section013_dumped = 0; # Used to keep track of whether sections
159             # 0-3 have been printed when --filter
160             # option has been used
161             READLOOP:
162 30         162 while (not $bufr->eof()) {
163              
164             # Read next observation. If an error is encountered during
165             # decoding, skip this observation while printing the error
166             # message to STDERR, also displaying ahl of bulletin if found
167             # (but skip error message if the message should be skipped on
168             # --ahl anyway).
169 123         221 my ($data, $descriptors);
170 123         176 eval {
171 123         444 ($data, $descriptors) = $bufr->next_observation();
172             };
173 123 100       294 if ($@) {
174 11   100     44 $current_ahl = $bufr->get_current_ahl() || '';
175 11 50 33     62 next READLOOP if $option{ahl} && $current_ahl !~ $ahl_regexp;
176              
177 11         719 warn $@;
178             # Try to extract message number and ahl of the bulletin
179             # where the error occurred
180 11         54 $current_message_number = $bufr->get_current_message_number();
181 11 50       37 if (defined $current_message_number) {
182 11         26 my $error_msg = "In message $current_message_number";
183 11 100       35 $error_msg .= " contained in bulletin with ahl $current_ahl\n"
184             if $current_ahl;
185 11 50       300 warn $error_msg if $error_msg;
186             }
187 11 50       41 exit(1) if $option{on_error_stop};
188 11         53 next READLOOP;
189             }
190              
191 112 100 100     477 next if $option{ahl} && $bufr->is_filtered();
192              
193 107 100 66     431 if ($option{codetables} && !$option{nodata}) {
194             # Load C table, trying first to use same table version as
195             # the B and D tables loaded in next_observation, or if
196             # this C table file does not exist, loads C table for latest
197             # master table in table path found instead.
198 8         33 my $table_version = $bufr->get_table_version();
199 8         79 my $tableformat = Geo::BUFR->get_tableformat();
200 8 100       22 if ($tableformat eq 'BUFRDC') {
    50          
201 5         24 $bufr->load_Ctable("C$table_version");
202             } elsif ($tableformat eq 'ECCODES') {
203 3         9 $bufr->load_Ctable("$table_version");
204             }
205             }
206              
207 107         391 my $current_subset_number = $bufr->get_current_subset_number();
208             # If next_observation() did find a BUFR message, subset number
209             # should have been set to at least 1 (even in a 0 subset message)
210 107 100       362 last READLOOP if $current_subset_number == 0;
211              
212 77 100 66     407 if ($current_subset_number == 1 || $option{nodata}) {
213 46         165 $current_message_number = $bufr->get_current_message_number();
214 46   100     174 $current_ahl = $bufr->get_current_ahl() || '';
215 46         200 $message_header = sprintf "\nMessage %d", $current_message_number;
216 46 50       308 $message_header .= (defined $current_ahl)
217             ? " $current_ahl\n" : "\n";
218              
219 46         102 $section013_dumped = 0;
220             next READLOOP if ($option{filter}
221 46 100 100     212 && filter_observation($bufr, $data, $descriptors));
222              
223 45         546 print $OUT $message_header;
224              
225 45 100       192 if (not $option{data_only}) {
226 43         226 print $OUT $bufr->dumpsection0();
227 43         206 print $OUT $bufr->dumpsection1();
228             print $OUT $bufr->dumpsection2($sec2_code_ref)
229 43 50       212 if $option{optional_section};
230 43         190 print $OUT $bufr->dumpsection3();
231 43         82 $section013_dumped = 1;
232             }
233 45 100       219 next READLOOP if $option{nodata};
234             } else { # subset number > 1
235             next READLOOP if ($option{filter}
236 31 100 100     131 && filter_observation($bufr, $data, $descriptors));
237              
238             # If subset 1 was filtered away, section 0-3 might not
239             # have been printed yet
240 29 100 100     231 if ($option{filter} and not $option{data_only}
      66        
241             and not $section013_dumped) {
242 1         9 print $OUT $bufr->dumpsection0();
243 1         6 print $OUT $bufr->dumpsection1();
244             print $OUT $bufr->dumpsection2($sec2_code_ref)
245 1 50       4 if $option{optional_section};
246 1         4 print $OUT $bufr->dumpsection3();
247 1         2 $section013_dumped = 1;
248             }
249             }
250              
251 71 100       179 if ($option{param}) {
252             # Reduce data and descriptors to those requested only
253 6         25 ($data, $descriptors)
254             = param($data, $descriptors, @requested_desc);
255             }
256              
257 71         364 printf $OUT "\nSubset %d\n", $current_subset_number;
258              
259             # If an error is encountered during dumping of section 4, skip
260             # this subset while printing the error message to STDERR, also
261             # displaying ahl of bulletin if found.
262 71         131 my $dump;
263 71         114 eval {
264             $dump = ( $option{bitmap} )
265 71 100       407 ? $bufr->dumpsection4_with_bitmaps($data, $descriptors,
266             $current_subset_number, $width)
267             : $bufr->dumpsection4($data, $descriptors, $width);
268             };
269 71 50       220 if ($@) {
270 0         0 warn $@;
271 0         0 my $error_msg = "In message $current_message_number"
272             . " and subset $current_subset_number";
273 0 0       0 $error_msg .= " contained in bulletin with ahl $current_ahl\n"
274             if $current_ahl;
275 0         0 warn $error_msg;
276 0 0       0 exit(1) if $option{on_error_stop};
277 0         0 next READLOOP;
278             } else {
279 71         4550 print $OUT $dump;
280             }
281             }
282             }
283              
284             sub read_descriptor_file {
285 3     3   7 my $descriptor_file = shift;
286              
287 3 50       211 open my $fh, '<', $descriptor_file
288             or die "Cannot open $descriptor_file: $!";
289 3         9 my @requested_desc;
290 3         112 while (<$fh>) {
291 39 50       82 next unless /^\s*(\d{6})/;
292 39         119 push @requested_desc, $1;
293             }
294 3 50       53 close $fh or die "Cannot close $descriptor_file: $!";
295 3         30 return @requested_desc;
296             }
297              
298             # Reduce the data to those corresponding to the requested descriptors
299             # only.
300             sub param {
301 6     6   49 my ($data, $descriptors, @requested_desc) = @_;
302              
303 6         10 my (@req_data, @req_desc);
304 6         10 my $i = 0;
305 6         8 foreach my $id ( @{$descriptors} ) {
  6         15  
306 856 100       764 if (grep { $id == $_ } @requested_desc) {
  11240         10702  
307 156         186 push @req_data, $data->[$i];
308 156         166 push @req_desc, $id;
309             }
310 856         795 $i++;
311             }
312 6         44 return (\@req_data, \@req_desc);
313             }
314              
315              
316             ###################################################################################
317              
318             # Filter routines
319              
320             sub filter_on_ahl {
321 8     8   8 my $obj = shift;
322 8         19 my $ahl_regexp = shift;
323 8   100     14 my $ahl = $obj->get_current_ahl() || '';
324 8 100       48 return $ahl =~ $ahl_regexp ? 0 : 1;
325             }
326              
327             # Read in contents of $filter_file into variables @fid, @fiv,
328             # @num_desc, @num_val and $num_criteria, which are defined above.
329             # Note that index 0 of the arrays is not used.
330             sub read_filter_file {
331 2     2   3 my $filter_file = shift;
332              
333 2 50       152 open my $fh, '<', $filter_file
334             or die "Cannot open $filter_file: $!";
335 2         63 while (<$fh>) {
336             # Remove comments and skip blank lines
337 29         40 s/#.*//;
338 29 100       66 next if /^\s*$/;
339              
340 26 100       73 if (s/^\s*D(!)?://) {
341 9         18 my @desc = split;
342             # Check that all descriptors are numbers
343 9         15 foreach my $desc (@desc) {
344 10 50       36 die "'$desc' cannot be a descriptor in line $. in filter file '$filter_file'"
345             if $desc !~/^\d+$/;
346             }
347             # Save the criterium
348 9         16 $num_desc[++$num_criteria] = @desc;
349 9         14 $num_val[$num_criteria] = 0;
350 9         24 $fid[$num_criteria] = \@desc;
351 9 100       23 $required[$num_criteria] = $1 ? 1 : 0;
352 9 100       29 $num_required_criteria++ if $1;
353             } else {
354 17         27 my @values = split;
355             # Check that value line contains correct number of values
356             die "Number of values doesn't match number of descriptors"
357             . " for line $. in filter file '$filter_file'"
358 17 50       35 if scalar @values != scalar @{$fid[$num_criteria]};
  17         28  
359             # Remove leading 0's in numerical values (to prepare for string comparison)
360 17         18 for $_ (@values) { s/^0+(\d+)$/$1/ };
  18         23  
361 17         69 $fiv[$num_criteria]->[++$num_val[$num_criteria]] = \@values;
362             }
363             }
364 2 50       39 close $fh or die "Cannot close $filter_file: $!";
365 2         11 return;
366             }
367              
368             # Return true (observations should be filtered) if the observation
369             # does not meet all of the D! criteria (if exists) and does not meet
370             # any one of the other criteria (if exists) in filter file.
371             sub filter_observation {
372 6     6   8 my $bufr = shift;
373 6 50       28 die "Error in filter_observation: argument not a BUFR object"
374             unless ref($bufr) eq 'Geo::BUFR';
375 6         12 my ($data, $descriptors) = @_;
376              
377 6         10 my $num_ordinary_criteria = $#fid - $num_required_criteria;
378 6         8 my $num_success_req_criteria = 0; # Number of required criteria successfully fulfilled
379 6         7 my $num_success_ord_criteria = 0; # Number of ordinary criteria successfully fulfilled
380              
381             # loop through all different criteria:
382 6         13 CRITERIA: foreach my $filter_criterion (1 .. $num_criteria) {
383 26 100       41 if ($num_val[$filter_criterion] == 0) {
384             # Enough to check that the descriptor(s) are present in observation
385 3         5 my $nmatch = 0;
386             # loop through all descriptors in criterion:
387 3         27 foreach my $idesc (0 .. $num_desc[$filter_criterion] - 1) {
388 3         8 my $filter_desc = $fid[$filter_criterion]->[$idesc];
389 3         7 for (my $j = 0; $j < @{$descriptors}; $j++) {
  339         602  
390 338 100       629 if ($descriptors->[$j] == $filter_desc) {
391 3         5 $nmatch++; # Matched!
392 3 50       12 if ($nmatch == $num_desc[$filter_criterion]) {
393             # All descriptors for this line in this criterion matched.
394             # Do we need to check more criteria?
395 3 50       8 if ($required[$filter_criterion]) {
396 3         7 $num_success_req_criteria++;
397 3 50 33     39 if ($num_success_req_criteria == $num_required_criteria
      66        
398             and ($num_ordinary_criteria == 0
399             or $num_success_ord_criteria > 0)) {
400 2         17 return 0; # Don't filter this observation
401             }
402             } else {
403 0         0 $num_success_ord_criteria++;
404 0 0       0 if ($num_success_req_criteria == $num_required_criteria) {
405 0         0 return 0; # Don't filter this observation
406             }
407             }
408             }
409             }
410             }
411             }
412             } else {
413             # loop through all filter values lines (for given) criterion:
414 23         35 LINE: foreach my $line (1 .. $num_val[$filter_criterion]) {
415 49         85 my $nmatch = 0;
416             # loop through all descriptors in criterion:
417 49         82 DESC: foreach my $idesc (0 .. $num_desc[$filter_criterion] - 1) {
418 52         82 my $filter_desc = $fid[$filter_criterion]->[$idesc];
419             # loop through all data in subset:
420 52         89 for (my $j = 0; $j < @{$descriptors}; $j++) {
  2042         2766  
421 2032 100       3014 if ($descriptors->[$j] == $filter_desc) {
422 47 50       77 next DESC if !defined $data->[$j];
423 47         503 (my $val = $data->[$j]) =~ s/^\s*(.*?)\s*$/$1/;
424 47 100       109 if ($val eq $fiv[$filter_criterion]->[$line]->[$idesc]) {
425 15         17 $nmatch++; # Matched!
426 15 100       22 if ($nmatch == $num_desc[$filter_criterion]) {
427             # All descriptors for this line in this criterion matched.
428             # Do we need to check more criteria?
429 12 100       20 if ($required[$filter_criterion]) {
430 7         8 $num_success_req_criteria++;
431 7 100 66     23 if ($num_success_req_criteria == $num_required_criteria
      100        
432             and ($num_ordinary_criteria == 0
433             or $num_success_ord_criteria > 0)) {
434 1         3 return 0; # Don't filter this observation
435             } else {
436 6         38 next DESC;
437             }
438             } else {
439 5         5 $num_success_ord_criteria++;
440 5 50       10 if ($num_success_req_criteria == $num_required_criteria) {
441 0         0 return 0; # Don't filter this observation
442             }
443             }
444             } else {
445 3         4 next DESC;
446             }
447             } else {
448             # Found the descriptor, but wrong value
449 32         151 next LINE;
450             }
451             }
452             }
453             } # End of filter descriptor loop
454             } # End of value line loop
455             }
456             } # End of criteria loop
457              
458             # One required criterion not fulfilled, or if there are no
459             # required criteria: none of the non-required criteria fulfilled
460             # (so the observation should be filtered away)
461 3         27 return 1;
462             }
463              
464             =pod
465              
466             =encoding utf8
467              
468             =head1 SYNOPSIS
469              
470             bufrread.pl
471             [--ahl ]
472             [--all_operators]
473             [--bitmap]
474             [--codetables]
475             [--data_only]
476             [--filter ]
477             [--help]
478             [--nodata]
479             [--noqc]
480             [--on_error_stop]
481             [--optional_section]
482             [--outfile ]
483             [--param ]
484             [--strict_checking n]
485             [--tableformat ]
486             [--tablepath ]
487             [--verbose n]
488             [--width n]
489              
490             =head1 DESCRIPTION
491              
492             Extract BUFR messages from BUFR file(s) and print the decoded content
493             to screen, including AHL (Abbreviated Header Line) if present.
494              
495             Execute without arguments for Usage, with option C<--help> for some
496             additional info. See also L for
497             examples of use.
498              
499              
500             =head1 OPTIONS
501              
502             --ahl
503             Decode BUFR messages with AHL matching only
504             --all_operators Show replication descriptors and all operator descriptors
505             when printing section 4
506             --bitmap Display bit-mapped values on same line
507             --codetables Use code and flag tables to resolve values when unit
508             is [CODE TABLE] or [FLAG TABLE]
509             --data_only Print section 4 (data section) only
510             --filter
511             Decode observations meeting criteria in only
512             --help Display Usage and explain the options used. For even
513             more info you might prefer to consult perldoc bufrread.pl
514             --nodata Do not print (nor decode) section 4 (data section)
515             --noqc Do not decode quality control
516             (or any descriptors following 222000)
517             --on_error_stop Stop processing as soon as an error occurs during decoding
518             --outfile
519             Will print to instead of STDOUT
520             --optional_section
521             Display a hex dump of optional section if present
522             --param
523             Display parameters with descriptors in only
524             --strict_checking n n=0 (default) Disable strict checking of BUFR format
525             n=1 Issue warning if (recoverable) error in
526             BUFR format
527             n=2 Croak if (recoverable) error in BUFR format.
528             Nothing more in this message/subset will be decoded.
529             --tableformat Currently supported are BUFRDC and ECCODES (default is BUFRDC)
530             --tablepath
531             Set path to BUFR tables (overrides ENV{BUFR_TABLES})
532             --verbose n Set verbose level to n, 0<=n<=6 (default 0). n=1 will
533             show the tables loaded.
534             --width n Set width of field used for data values to n characters
535             (default is 15)
536              
537             Options may be abbreviated, e.g. C<--h> or C<-h> for C<--help>.
538              
539             To avoid having to use the C<--tablepath> option, you are adviced to
540             set the environment variable BUFR_TABLES to the directory where your
541             BUFR tables are located (unless the default path provided by
542             bufrread.pl works for you). For tableformat ECCODES, se
543             L
544             for more info on how to set C<--tablepath> (or BUFR_TABLES).
545              
546             For option C<--ahl> the should be a Perl regular
547             expression. E.g. C<--ahl "ISS... ENMI"> will decode only BUFR SHIP
548             (ISS) from CCCC=ENMI. This is the only case where a little knowledge
549             of Perl might possibly be required when using the utility programs
550             included in Geo::BUFR.
551              
552             For option C<--param> each line in should start with
553             a BUFR descriptor (6 digits). Rest of line will be ignored.
554             bufrread.pl will display values for these descriptors only.
555              
556             Using C<--filter> will decode only those observations that meet one of
557             the criteria in marked D: and all of those criteria
558             marked D!:. Comments (starting with #) are ignored. An example of a
559             filter file is
560              
561             D: 001001
562             1
563             D: 001001 001002
564             3 895
565             6 252
566             D: 001011
567             LF5U # Ekofisk
568             D!: 004004
569             6
570             7
571              
572             which decodes all observations with block number 01, two other
573             specific WMO stations and one specific ship, all of which having hour
574             (004004) equal to 6 or 7. If there is no value line after a
575             descriptor line, it is enough that the observation contains the
576             descriptor(s), whatever the values are. So to extract all ship
577             messages from a BUFR file, the filter file should contain this single
578             line only:
579              
580             D: 001011
581              
582             If an error occurs during decoding (typically because the required
583             BUFR table is missing or message is corrupt), the BUFR message is
584             skipped with an error message printed to STDERR, and processing then
585             continues with the next BUFR message. You can change this default
586             behaviour, however, by setting C<--on_error_stop>.
587              
588             =head1 CAVEAT
589              
590             Option C<--bitmap> may not work properly for complicated BUFR messages.
591             Namely, when the first bit-map is encountered, no more data values (or
592             their descriptors) will be displayed unless they refer to the
593             preceding data values by a bit-map. And output is not to be trusted
594             if a bit-map refers to another bit-map or the bit-mapped values are
595             combined with 204YYY (add associated field operator).
596              
597             =head1 AUTHOR
598              
599             Pål Sannes Epal.sannes@met.noE
600              
601             =head1 COPYRIGHT
602              
603             Copyright (C) 2010-2025 MET Norway
604              
605             =cut