| 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 |