File Coverage

blib/lib/Geo/BUFR.pm
Criterion Covered Total %
statement 2437 2905 83.8
branch 1211 1942 62.3
condition 380 693 54.8
subroutine 127 147 86.3
pod 0 94 0.0
total 4155 5781 71.8


line stmt bran cond sub pod time code
1             package Geo::BUFR;
2              
3             # Copyright (C) 2010-2025 MET Norway
4             #
5             # This module is free software; you can redistribute it and/or
6             # modify it under the same terms as Perl itself.
7              
8             =begin General_remarks
9              
10             Some general remarks on variables
11             ---------------------------------
12              
13             @data = data array
14             @desc = descriptor array
15              
16             These 2 arrays are in one to one correspondence, but note that some C
17             descriptors (2.....) are included in @desc even though there is no
18             associated data value in message (the corresponding element in @data
19             is set to ''). These descriptors without value are printed in
20             dumpsection4 without line number, to distinguish them from 'real' data
21             descriptors.
22              
23             $idesc = index of descriptor in @desc (and @data)
24             $bm_idesc = index of bit mapped descriptor in @data (and @desc, see below)
25              
26             Variables related to bit maps:
27              
28             $self->{BUILD_BITMAP}
29             $self->{BITMAP_INDEX}
30             $self->{NUM_BITMAPS}
31             $self->{BACKWARD_DATA_REFERENCE}
32              
33             These are explained in sub new
34              
35             $self->{BITMAP_OPERATORS}
36              
37             Reference to an array containing operators in BUFR table C which are
38             associated with bit maps, i.e. one of 22[2-5]000 and 232000; the
39             operator being added when it is met in section 3 in message. Note that
40             an operator may occur multiple times, which is why we have to use an
41             array, not a hash.
42              
43             $self->{CURRENT_BITMAP}
44              
45             Reference to an array which contains the indexes of data values for
46             which data is marked as present in 031031 in the current used bit map.
47             E.g. [2,3,6] if bitmap = 1100110.
48              
49             $self->{BITMAP_START}
50              
51             Array containing for each bit map the index of the first element
52             descriptor for which the bit map relates.
53              
54             $self->{BITMAPS}
55              
56             Reference to an array, one element added for each bit map operator in
57             $self->{BITMAP_OPERATORS} and each subset (although for compression we
58             assume all subset have identical bitmaps and operate with subset 0
59             only, i.e. $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[0] instead of
60             ...->[$isub]), the element being a reference to an array containing
61             consecutive pairs of indexes ($idesc, $bm_idesc), used to look up in
62             @data and @desc arrays for the value/descriptor and corresponding bit
63             mapped value/descriptor.
64              
65             $self->{REUSE_BITMAP}
66              
67             Gets defined when 237000 is met, undefined if 237255 or 235000 is met.
68             Originally for each subset (but defined for subset 0 only if
69             compression) set to reference an array of the indexes of data values
70             to which the last used bitmap relates (fetched from $self->{BITMAPS}),
71             then shifted as the new element in $self->{BITMAPS} is built up.
72              
73             For operator 222000 ('Quality information follows') the bit mapped
74             descriptor should be a 033-descriptor. For 22[3-5]/232 the bit mapped
75             value should be the data value of the 22[3-5]255/232255 descriptors
76             following the operator in BUFR section 3, with bit mapped descriptor
77             $desc[bm_idesc] equal to $desc[$idesc] (with data width and reference
78             value changed for 225255)
79              
80             =end General_remarks
81              
82             =cut
83              
84             require 5.006;
85 79     79   621837 use strict;
  79         139  
  79         3061  
86 79     79   363 use warnings;
  79         137  
  79         4088  
87 79     79   475 use Carp;
  79         153  
  79         5392  
88 79     79   497 use Cwd qw(getcwd);
  79         158  
  79         3922  
89 79     79   36350 use FileHandle;
  79         837190  
  79         464  
90 79     79   63421 use File::Spec::Functions qw(catfile);
  79         65936  
  79         7182  
91 79     79   581 use Scalar::Util qw(looks_like_number);
  79         122  
  79         7069  
92 79     79   41831 use Time::Local qw(timegm);
  79         156888  
  79         3705036  
93             # Also requires Storable if sub copy_from() is called
94              
95             require DynaLoader;
96             our @ISA = qw(DynaLoader);
97             our $VERSION = '1.41';
98              
99             # This loads BUFR.so, the compiled version of BUFR.xs, which
100             # contains bitstream2dec, bitstream2ascii, dec2bitstream,
101             # ascii2bitstream and null2bitstream
102             bootstrap Geo::BUFR $VERSION;
103              
104              
105             # Some package globals
106             our $Verbose = 0;
107              
108             # $Verbose or $self->{VERBOSE} > 0 leads to the following output, all
109             # except for level 6 on lines starting with 'BUFR.pm: ':
110             # 1 -> B,C,D tables used (full path)
111             # 2 -> Identifying stages of processing, displaying length of sections
112             # and some additional data from section 1 and 3
113             # 3 -> All descriptors and values extracted
114             # 4 -> Operator specific information, including delayed replication
115             # and repetition
116             # 5 -> BUFR compression specific information
117             # 6 -> Calling dumpsection0,1,3
118              
119             our $Spew = 0; # To avoid the overhead of subroutine calls to _spew
120             # (which is called a lot), $Spew is set to 1 if global
121             # $Verbose or at least one object VERBOSE is set > 1.
122             # This should speed up execution a bit in the common
123             # situation when no verbose output (except possibly
124             # the BUFR tables used) is requested
125             our $Nodata = 0; # If set to true will prevent decoding of section 4
126             our $Noqc = 0; # If set to true will prevent decoding (or encoding) of
127             # any descriptors after 222000 is met
128             our $Reuse_current_ahl = 0;
129             # If set to true will cause cet_current_ahl() to return
130             # last AHL extracted and not undef if currently
131             # processed BUFR message has no (immediately preceding)
132             # AHL
133             our $Strict_checking = 0; # Ignore recoverable errors in BUFR format
134             # met during decoding. User might set
135             # $Strict_checking to 1: Issue warning
136             # (carp) but continue decoding, or to 2:
137             # Croak instead of carp
138              
139             # The next 2 operators are separated for readability. Public interface should
140             # provide only set_show_all_operators() to set both of these (to the same value)
141             our $Show_all_operators = 0; # = 0: show just the most informative C operators in dumpsection4
142             # = 1: show all operators (as far as possible)
143             our $Show_replication = 0; # = 0: don't include replication descriptors (F=1) in dumpsection4
144             # = 1: include replication descriptors(F=1) in dumpsection4,
145             # with X in FXY replaced with actual number X' of replicated descriptors.
146             # X' is replaced by 0 if X' > 99
147              
148             our %BUFR_table;
149             # Keys: PATH -> full path to the chosen directory of BUFR tables
150             # FORMAT -> supported formats are BUFRDC and ECCODES
151             # B$version -> hash containing the B table $BUFR_table/B$version
152             # key: element descriptor (6 digits)
153             # value: a \0 separated string containing the B table fields
154             # $name, $unit, $scale, $refval, $bits
155             # C$version -> hash containing the C table $BUFR_table/C$version
156             # key: table B descriptor (6 digits) of the code/flag table
157             # value: a new hash, with keys the possible values listed in
158             # the code table, the value the corresponding text
159             # D$version -> hash containing the D table $BUFR_table/D$version
160             # key: sequence descriptor
161             # value: a space separated string containing the element
162             # descriptors (6 digits) the sequence descriptor expands to
163             $BUFR_table{FORMAT} = 'BUFRDC'; # Default. Might in the future be changed to ECCODES
164              
165             our %Descriptors_already_expanded;
166             # Keys: Text string "$table_version $unexpanded_descriptors"
167             # Values: Space separated string of expanded descriptors
168              
169             sub _croak {
170 7     7   11 my $msg = shift;
171 7         1335 croak "BUFR.pm ERROR: $msg";
172             }
173              
174             ## Carp or croak (or ignore) according to value of $Strict_checking
175             sub _complain {
176 55     55   128 my $msg = shift;
177 55 100       175 if ($Strict_checking == 1) {
    100          
178 8         1747 carp "BUFR.pm WARNING: $msg";
179             } elsif ($Strict_checking > 1) {
180 7         1244 croak "BUFR.pm ERROR: $msg";
181             }
182 48         122 return;
183             }
184              
185             sub _spew {
186 2222     2222   3851 my $self = shift;
187 2222         2389 my $level = shift;
188 2222 100       3137 if (ref($self)) {
189             # Global $Verbose overrides object VERBOSE
190 2015 100 66     5687 return if $level > $self->{VERBOSE} && $level > $Verbose;
191             } else {
192 207 100       754 return if $level > $Verbose;
193             }
194 58         86 my $format = shift;
195 58 100       65 if (@_) {
196 30         275 printf "BUFR.pm: $format\n", @_;
197             } else {
198 28         92 print "BUFR.pm: $format\n";
199             }
200 58         128 return;
201             }
202              
203             ## Object constructor
204             sub new {
205 84     84 0 258 my $class = shift;
206 84         193 my $self = {};
207 84         264 $self->{VERBOSE} = 0;
208 84         252 $self->{CURRENT_MESSAGE} = 0;
209 84         237 $self->{CURRENT_SUBSET} = 0;
210 84         248 $self->{BUILD_BITMAP} = 0; # Will be set to 1 if a bit map needs to
211             # be built
212 84         226 $self->{BITMAP_INDEX} = 0; # Used for building up bit maps; will
213             # be incremented for each 031031
214             # encountered, then reset to 0 when bit
215             # map is finished built
216 84         246 $self->{NUM_BITMAPS} = 0; # Will be incremented each time an
217             # operator descriptor which uses a bit
218             # map is encountered in section 3
219 84         253 $self->{BACKWARD_DATA_REFERENCE} = 1; # Number the first bitmap in
220             # a possible sequence of bitmaps which
221             # relate to the same scope of data
222             # descriptors. Starts as 1 when (or
223             # rather before) the first bitmap is
224             # constructed, will then be reset to
225             # the number of the next bitmap to be
226             # constructed each time 235000 is met
227 84         224 $self->{NUM_CHANGE_OPERATORS} = 0; # Will be incremented for
228             # each of the operators CHANGE_WIDTH,
229             # CHANGE_CCITTIA5_WIDTH, CHANGE_SCALE,
230             # CHANGE_REFERENCE_VALUE (actually
231             # NEW_REFVAL_OF), CHANGE_SRW and
232             # DIFFERENCE_STATISTICAL_VALUE in effect
233              
234             # If number of arguments is odd, first argument is expected to be
235             # a string containing the BUFR message(s)
236 84 100       414 if (@_ % 2) {
237 5         23 $self->{IN_BUFFER} = shift;
238             }
239              
240             # This part is not documented in the POD. Better to remove it?
241 84         442 while (@_) {
242 0         0 my $parameter = shift;
243 0         0 my $value = shift;
244 0         0 $self->{$parameter} = $value;
245             }
246 84   33     571 bless $self, ref($class) || $class;
247 84         278 return $self;
248             }
249              
250             ## Copy contents of the bufr object in first argument. With no extra
251             ## arguments, will copy (clone) everything. With 'metadata' as second
252             ## argument, will copy just the metadata in section 0, 1 and 3 (and
253             ## all of section 2 if present)
254             sub copy_from {
255 5     5 0 15 my $self = shift;
256 5         8 my $bufr = shift;
257 5 50       18 _croak("First argument to copy_from must be a Geo::BUFR object")
258             unless ref($bufr) eq 'Geo::BUFR';
259 5   50     16 my $what = shift || 'all';
260 5 50       19 if ($what eq 'metadata') {
    0          
261 5         16 for (qw(
262             BUFR_EDITION
263             MASTER_TABLE CENTRE SUBCENTRE UPDATE_NUMBER OPTIONAL_SECTION
264             DATA_CATEGORY INT_DATA_SUBCATEGORY LOC_DATA_SUBCATEGORY
265             MASTER_TABLE_VERSION LOCAL_TABLE_VERSION YEAR MONTH DAY
266             HOUR MINUTE SECOND LOCAL_USE DATA_SUBCATEGORY YEAR_OF_CENTURY
267             NUM_SUBSETS OBSERVED_DATA COMPRESSED_DATA DESCRIPTORS_UNEXPANDED
268             SEC2_STREAM
269             )) {
270 125 50       208 if (exists $bufr->{$_}) {
271 125         230 $self->{$_} = $bufr->{$_};
272             } else {
273             # This cleanup might be necessary if BUFR edition changes
274 0 0       0 delete $self->{$_} if exists $self->{$_};
275             }
276             }
277             } elsif ($what eq 'all') {
278 0         0 %$self = ();
279 0         0 while (my ($key, $value) = each %{$bufr}) {
  0         0  
280 0 0 0     0 if ($key eq 'FILEHANDLE') {
    0          
281             # If a file has been associated with the copied
282             # object, make a new filehandle rather than just
283             # copying the reference
284 0         0 $self->fopen($bufr->{FILENAME});
285             } elsif (ref($value) and $key !~ /[BCD]_TABLE/) {
286             # Copy the whole structure, not merely the reference.
287             # Using Clone would be cheaper, but unfortunately
288             # Clone is not a core module, while Storable is
289 0         0 require Storable;
290 0         0 import Storable qw(dclone);
291 0         0 $self->{$key} = dclone($value);
292             } else {
293 0         0 $self->{$key} = $value;
294             }
295             }
296             } else {
297 0         0 _croak("Don't recognize second argument '$what' to copy_from()");
298             }
299 5         14 return 1;
300             }
301              
302              
303             ## Set debug level. Also set $Spew to true if debug level > 1 is set
304             ## (we don't bother to reset $Spew to 0 if all debug levels later are
305             ## reset to 0 or 1)
306             sub set_verbose {
307 14     14 0 36 my $self = shift;
308 14         30 my $verbose = shift;
309 14 50       45 if (ref($self)) {
310             # Just myself
311 0         0 $self->{VERBOSE} = $verbose;
312 0         0 $self->_spew(2, "Verbosity level for object set to %d", $verbose);
313             } else {
314             # Whole class
315 14         77 $Verbose = $verbose;
316 14         120 Geo::BUFR->_spew(2, "Verbosity level for class set to %d", $verbose);
317             }
318 14 100       51 $Spew = $verbose if $verbose > 1;
319 14         51 return 1;
320             }
321              
322             ## Turn off (or on) decoding of section 4
323             sub set_nodata {
324 9     9 0 22538350 my $self = shift;
325 9         26 my $n = shift;
326 9 100       46 $Nodata = defined $n ? $n : 1; # Default is 1
327 9         68 Geo::BUFR->_spew(2, "Nodata set to %d", $Nodata);
328 9         31 return 1;
329             }
330              
331             ## Turn off (or on) decoding of quality information
332             sub set_noqc {
333 1     1 0 3 my $self = shift;
334 1         3 my $n = shift;
335 1 50       6 $Noqc = defined $n ? $n : 1; # Default is 1
336 1         9 Geo::BUFR->_spew(2, "Noqc set to %d", $Noqc);
337 1         17 return 1;
338             }
339              
340             ## Require strict checking of BUFR format
341             sub set_strict_checking {
342 40     40 0 92 my $self = shift;
343 40         107 my $n = shift;
344 40 50       147 _croak "Value for strict checking not provided"
345             unless defined $n;
346 40         89 $Strict_checking = $n;
347 40         284 Geo::BUFR->_spew(2, "Strict_checking set to %d", $Strict_checking);
348 40         104 return 1;
349             }
350              
351             ## Show replication descriptors (with X in FXY replaced by actual
352             ## number of descriptors replicated, adjusted to 0 if > 99) and all
353             ## data description operators when calling dumpsection4
354             sub set_show_all_operators {
355 3     3 0 8 my $self = shift;
356 3         6 my $n = shift;
357 3 50       15 $Show_all_operators = defined $n ? $n : 1; # Default is 1
358 3         7 $Show_replication = $Show_all_operators;
359 3         45 Geo::BUFR->_spew(2, "Show_all_operators set to %d", $Show_all_operators);
360 3         7 return 1;
361             }
362              
363             ## Accessor methods for BUFR sec0-3 ##
364             sub get_bufr_length {
365 0     0 0 0 my $self = shift;
366 0 0       0 return defined $self->{BUFR_LENGTH} ? $self->{BUFR_LENGTH} : undef;
367             }
368             sub set_bufr_edition {
369 7     7 0 21 my ($self, $bufr_edition) = @_;
370 7 50       31 _croak "BUFR edition number not provided in set_bufr_edition"
371             unless defined $bufr_edition;
372 7 50       43 _croak "BUFR edition number must be an integer, is '$bufr_edition'"
373             unless $bufr_edition =~ /^\d+$/;
374 7 50 33     59 _croak "Not an allowed value for BUFR edition number: $bufr_edition"
375             unless $bufr_edition >= 0 and $bufr_edition < 5;
376             # BUFR edition 0 is in fact in use in ECMWF MARS archive
377 7         54 $self->{BUFR_EDITION} = $bufr_edition;
378 7         21 return 1;
379             }
380             sub get_bufr_edition {
381 3     3 0 6 my $self = shift;
382 3 50       14 return defined $self->{BUFR_EDITION} ? $self->{BUFR_EDITION} : undef;
383             }
384             sub set_master_table {
385 4     4 0 15 my ($self, $master_table) = @_;
386 4 50       19 _croak "BUFR master table not provided in set_master_table"
387             unless defined $master_table;
388 4 50       23 _croak "BUFR master table must be an integer, is '$master_table'"
389             unless $master_table =~ /^\d+$/;
390             # Max value that can be stored in 1 byte is 255
391 4 50       19 _croak "BUFR master table exceeds limit 255, is '$master_table'"
392             if $master_table > 255;
393 4         12 $self->{MASTER_TABLE} = $master_table;
394 4         9 return 1;
395             }
396             sub get_master_table {
397 0     0 0 0 my $self = shift;
398 0 0       0 return defined $self->{MASTER_TABLE} ? $self->{MASTER_TABLE} : undef;
399             }
400             sub set_centre {
401 7     7 0 20 my ($self, $centre) = @_;
402 7 50       23 _croak "Originating/generating centre not provided in set_centre"
403             unless defined $centre;
404 7 50       65 _croak "Originating/generating centre must be an integer, is '$centre'"
405             unless $centre =~ /^\d+$/;
406             # Max value that can be stored in 2 bytes is 65535
407 7 50       29 _croak "Originating/generating centre exceeds limit 65535, is '$centre'"
408             if $centre > 65535;
409 7         20 $self->{CENTRE} = $centre;
410 7         17 return 1;
411             }
412             sub get_centre {
413 0     0 0 0 my $self = shift;
414 0 0       0 return defined $self->{CENTRE} ? $self->{CENTRE} : undef;
415             }
416             sub set_subcentre {
417 7     7 0 19 my ($self, $subcentre) = @_;
418 7 50       42 _croak "Originating/generating subcentre not provided in set_subcentre"
419             unless defined $subcentre;
420 7 50       35 _croak "Originating/generating subcentre must be an integer, is '$subcentre'"
421             unless $subcentre =~ /^\d+$/;
422 7 50       32 _croak "Originating/generating subcentre exceeds limit 65535, is '$subcentre'"
423             if $subcentre > 65535;
424 7         20 $self->{SUBCENTRE} = $subcentre;
425 7         18 return 1;
426             }
427             sub get_subcentre {
428 0     0 0 0 my $self = shift;
429 0 0       0 return defined $self->{SUBCENTRE} ? $self->{SUBCENTRE} : undef;
430             }
431             sub set_update_sequence_number {
432 7     7 0 21 my ($self, $update_number) = @_;
433 7 50       25 _croak "Update sequence number not provided in set_update_sequence_number"
434             unless defined $update_number;
435 7 50       35 _croak "Update sequence number must be a nonnegative integer, is '$update_number'"
436             unless $update_number =~ /^\d+$/;
437 7 50       28 _croak "Update sequence number exceeds limit 255, is '$update_number'"
438             if $update_number > 255;
439 7         19 $self->{UPDATE_NUMBER} = $update_number;
440 7         17 return 1;
441             }
442             sub get_update_sequence_number {
443 2     2 0 3 my $self = shift;
444 2 50       7 return defined $self->{UPDATE_NUMBER} ? $self->{UPDATE_NUMBER} : undef;
445             }
446             sub set_optional_section {
447 4     4 0 13 my ($self, $optional_section) = @_;
448 4 50       24 _croak "Optional section (0 or 1) not provided in set_optional_section"
449             unless defined $optional_section;
450 4 50 33     89 _croak "Optional section must be 0 or 1, is '$optional_section'"
451             unless $optional_section eq '0' or $optional_section eq '1';
452 4         43 $self->{OPTIONAL_SECTION} = $optional_section;
453 4         13 return 1;
454             }
455             sub get_optional_section {
456 0     0 0 0 my $self = shift;
457 0 0       0 return defined $self->{OPTIONAL_SECTION} ? $self->{OPTIONAL_SECTION} : undef;
458             }
459             sub set_data_category {
460 7     7 0 20 my ($self, $data_category) = @_;
461 7 50       22 _croak "Data category not provided in set_data_category"
462             unless defined $data_category;
463 7 50       109 _croak "Data category must be an integer, is '$data_category'"
464             unless $data_category =~ /^\d+$/;
465 7 50       26 _croak "Data category exceeds limit 255, is '$data_category'"
466             if $data_category > 255;
467 7         20 $self->{DATA_CATEGORY} = $data_category;
468 7         16 return 1;
469             }
470             sub get_data_category {
471 24     24 0 158 my $self = shift;
472 24 50       60 return defined $self->{DATA_CATEGORY} ? $self->{DATA_CATEGORY} : undef;
473             }
474             sub set_int_data_subcategory {
475 3     3 0 11 my ($self, $int_data_subcategory) = @_;
476 3 50       18 _croak "International data subcategory not provided in set_int_data_subcategory"
477             unless defined $int_data_subcategory;
478 3 50       17 _croak "International data subcategory must be an integer, is '$int_data_subcategory'"
479             unless $int_data_subcategory =~ /^\d+$/;
480 3 50       23 _croak "International data subcategory exceeds limit 255, is '$int_data_subcategory'"
481             if $int_data_subcategory > 255;
482 3         8 $self->{INT_DATA_SUBCATEGORY} = $int_data_subcategory;
483 3         7 return 1;
484             }
485             sub get_int_data_subcategory {
486 1     1 0 2 my $self = shift;
487 1 50       6 return defined $self->{INT_DATA_SUBCATEGORY} ? $self->{INT_DATA_SUBCATEGORY} : undef;
488             }
489             sub set_loc_data_subcategory {
490 3     3 0 9 my ($self, $loc_data_subcategory) = @_;
491 3 50       12 _croak "Local subcategory not provided in set_loc_data_subcategory"
492             unless defined $loc_data_subcategory;
493 3 50       20 _croak "Local data subcategory must be an integer, is '$loc_data_subcategory'"
494             unless $loc_data_subcategory =~ /^\d+$/;
495 3 50       25 _croak "Local data subcategory exceeds limit 255, is '$loc_data_subcategory'"
496             if $loc_data_subcategory > 255;
497 3         8 $self->{LOC_DATA_SUBCATEGORY} = $loc_data_subcategory;
498 3         8 return 1;
499             }
500             sub get_loc_data_subcategory {
501 1     1 0 6 my $self = shift;
502 1 50       17 return defined $self->{LOC_DATA_SUBCATEGORY} ? $self->{LOC_DATA_SUBCATEGORY} : undef;
503             }
504             sub set_data_subcategory {
505 5     5 0 16 my ($self, $data_subcategory) = @_;
506 5 50       15 _croak "Data subcategory not provided in set_data_subcategory"
507             unless defined $data_subcategory;
508 5 50       22 _croak "Data subcategory must be an integer, is '$data_subcategory'"
509             unless $data_subcategory =~ /^\d+$/;
510 5 50       39 _croak "Data subcategory exceeds limit 255, is '$data_subcategory'"
511             if $data_subcategory > 255;
512 5         13 $self->{DATA_SUBCATEGORY} = $data_subcategory;
513 5         12 return 1;
514             }
515             sub get_data_subcategory {
516 3     3 0 5 my $self = shift;
517 3 50       19 return defined $self->{DATA_SUBCATEGORY} ? $self->{DATA_SUBCATEGORY} : undef;
518             }
519             sub set_master_table_version {
520 6     6 0 19 my ($self, $master_table_version) = @_;
521 6 50       31 _croak "Master table version not provided in set_master_table_version"
522             unless defined $master_table_version;
523 6 50       32 _croak "BUFR master table version must be an integer, is '$master_table_version'"
524             unless $master_table_version =~ /^\d+$/;
525 6 50       26 _croak "BUFR master table version exceeds limit 255, is '$master_table_version'"
526             if $master_table_version > 255;
527 6         23 $self->{MASTER_TABLE_VERSION} = $master_table_version;
528 6         15 return 1;
529             }
530             sub get_master_table_version {
531 0     0 0 0 my $self = shift;
532             return defined $self->{MASTER_TABLE_VERSION}
533 0 0       0 ? $self->{MASTER_TABLE_VERSION} : undef;
534             }
535             sub set_local_table_version {
536 6     6 0 19 my ($self, $local_table_version) = @_;
537 6 50       19 _croak "Local table version not provided in set_local_table_version"
538             unless defined $local_table_version;
539 6 50       30 _croak "Local table version must be an integer, is '$local_table_version'"
540             unless $local_table_version =~ /^\d+$/;
541 6 50       21 _croak "Local table version exceeds limit 255, is '$local_table_version'"
542             if $local_table_version > 255;
543 6         22 $self->{LOCAL_TABLE_VERSION} = $local_table_version;
544 6         15 return 1;
545             }
546             sub get_local_table_version {
547 0     0 0 0 my $self = shift;
548             return defined $self->{LOCAL_TABLE_VERSION}
549 0 0       0 ? $self->{LOCAL_TABLE_VERSION} : undef;
550             }
551             sub set_year_of_century {
552 4     4 0 9 my ($self, $year_of_century) = @_;
553 4 50       23 _croak "Year of century not provided in set_year_of_century"
554             unless defined $year_of_century;
555 4 50       18 _croak "Year of century must be an integer, is '$year_of_century'"
556             unless $year_of_century =~ /^\d+$/;
557 4 50       21 _complain "year_of_century > 100 in set_year_of_century: $year_of_century"
558             if $year_of_century > 100;
559             # A common mistake is to set year_of_century for year 2000 to 0, should be 100
560 4 50       28 $self->{YEAR_OF_CENTURY} = $year_of_century == 0 ? 100 : $year_of_century;
561 4         8 return 1;
562             }
563             sub get_year_of_century {
564 2     2 0 3 my $self = shift;
565 2 50       16 if (defined $self->{YEAR_OF_CENTURY}) {
    50          
566 0         0 return $self->{YEAR_OF_CENTURY};
567             } elsif (defined $self->{YEAR}) {
568 2         3 my $yy = $self->{YEAR} % 100;
569 2 50       10 return $yy == 0 ? 100 : $yy;
570             } else {
571 0         0 return undef;
572             }
573             }
574             sub set_year {
575 4     4 0 11 my ($self, $year) = @_;
576 4 50       124 _croak "Year not provided in set_year"
577             unless defined $year;
578 4 50       28 _croak "Year must be an integer, is '$year'"
579             unless $year =~ /^\d+$/;
580 4 50       15 _croak "Year exceeds limit 65535, is '$year'"
581             if $year > 65535;
582 4         13 $self->{YEAR} = $year;
583 4         10 return 1;
584             }
585             sub get_year {
586 1     1 0 2 my $self = shift;
587 1 50       6 return defined $self->{YEAR} ? $self->{YEAR} : undef;
588             }
589             sub set_month {
590 7     7 0 17 my ($self, $month) = @_;
591 7 50       32 _croak "Month not provided in set_month"
592             unless defined $month;
593 7 50       33 _croak "Month must be an integer, is '$month'"
594             unless $month =~ /^\d+$/;
595 7 50 33     52 _complain "Month must be 1-12 in set_month, is '$month'"
596             if $month == 0 || $month > 12;
597 7         20 $self->{MONTH} = $month;
598 7         15 return 1;
599             }
600             sub get_month {
601 0     0 0 0 my $self = shift;
602 0 0       0 return defined $self->{MONTH} ? $self->{MONTH} : undef;
603             }
604             sub set_day {
605 7     7 0 20 my ($self, $day) = @_;
606 7 50       64 _croak "Day not provided in set_day"
607             unless defined $day;
608 7 50       36 _croak "Day must be an integer, is '$day'"
609             unless $day =~ /^\d+$/;
610 7 50 33     54 _complain "Day must be 1-31 in set_day, is '$day'"
611             if $day == 0 || $day > 31;
612 7         30 $self->{DAY} = $day;
613 7         21 return 1;
614             }
615             sub get_day {
616 0     0 0 0 my $self = shift;
617 0 0       0 return defined $self->{DAY} ? $self->{DAY} : undef;
618             }
619             sub set_hour {
620 7     7 0 19 my ($self, $hour) = @_;
621 7 50       90 _croak "Hour not provided in set_hour"
622             unless defined $hour;
623 7 50       33 _croak "Hour must be an integer, is '$hour'"
624             unless $hour =~ /^\d+$/;
625 7 50       28 _complain "Hour must be 0-23 in set_hour, is '$hour'"
626             if $hour > 23;
627 7         93 $self->{HOUR} = $hour;
628 7         19 return 1;
629             }
630             sub get_hour {
631 0     0 0 0 my $self = shift;
632 0 0       0 return defined $self->{HOUR} ? $self->{HOUR} : undef;
633             }
634             sub set_minute {
635 7     7 0 29 my ($self, $minute) = @_;
636 7 50       20 _croak "Minute not provided in set_minute"
637             unless defined $minute;
638 7 50       60 _croak "Minute must be an integer, is '$minute'"
639             unless $minute =~ /^\d+$/;
640 7 50       23 _complain "Minute must be 0-59 in set_minute, is '$minute'"
641             if $minute > 59;
642 7         20 $self->{MINUTE} = $minute;
643 7         16 return 1;
644             }
645             sub get_minute {
646 0     0 0 0 my $self = shift;
647 0 0       0 return defined $self->{MINUTE} ? $self->{MINUTE} : undef;
648             }
649             sub set_second {
650 3     3 0 10 my ($self, $second) = @_;
651 3 50       12 _croak "Second not provided in set_second"
652             unless defined $second;
653 3 50       24 _croak "Second must be an integer, is '$second'"
654             unless $second =~ /^\d+$/;
655 3 50       45 _complain "Second must be 0-59 in set_second, is '$second'"
656             if $second > 59;
657 3         10 $self->{SECOND} = $second;
658 3         10 return 1;
659             }
660             sub get_second {
661 1     1 0 2 my $self = shift;
662 1 50       5 return defined $self->{SECOND} ? $self->{SECOND} : undef;
663             }
664             sub set_local_use {
665 0     0 0 0 my ($self, $local_use) = @_;
666 0 0       0 _croak "Local use not provided in set_local use"
667             unless defined $local_use;
668 0         0 $self->{LOCAL_USE} = $local_use;
669 0         0 return 1;
670             }
671             sub get_local_use {
672 0     0 0 0 my $self = shift;
673 0 0       0 return defined $self->{LOCAL_USE} ? $self->{LOCAL_USE} : undef;
674             }
675             sub set_number_of_subsets {
676 6     6 0 22 my ($self, $number_of_subsets) = @_;
677 6 50       17 _croak "Number of subsets not provided in set_number_of_subsets"
678             unless defined $number_of_subsets;
679 6 50       60 _croak "Number of subsets must be an integer, is '$number_of_subsets'"
680             unless $number_of_subsets =~ /^\d+$/;
681 6 50       23 _croak "Number of subsets exceeds limit 65535, is '$number_of_subsets'"
682             if $number_of_subsets > 65535;
683 6         21 $self->{NUM_SUBSETS} = $number_of_subsets;
684 6         17 return 1;
685             }
686             sub get_number_of_subsets {
687 5     5 0 9 my $self = shift;
688 5 50       18 return defined $self->{NUM_SUBSETS} ? $self->{NUM_SUBSETS} : undef;
689             }
690             sub set_observed_data {
691 5     5 0 17 my ($self, $observed_data) = @_;
692 5 50       33 _croak "Observed data (0 or 1) not provided in set_observed_data"
693             unless defined $observed_data;
694 5 50 66     35 _croak "Observed data must be 0 or 1, is '$observed_data'"
695             unless $observed_data eq '0' or $observed_data eq '1';
696 5         33 $self->{OBSERVED_DATA} = $observed_data;
697 5         12 return 1;
698             }
699             sub get_observed_data {
700 0     0 0 0 my $self = shift;
701 0 0       0 return defined $self->{OBSERVED_DATA} ? $self->{OBSERVED_DATA} : undef;
702             }
703             sub set_compressed_data {
704 7     7 0 37 my ($self, $compressed_data) = @_;
705 7 50       22 _croak "Compressed data (0 or 1) not provided in set_compressed_data"
706             unless defined $compressed_data;
707 7 50 33     34 _croak "Compressed data must be 0 or 1, is '$compressed_data'"
708             unless $compressed_data eq '0' or $compressed_data eq '1';
709             _complain "Not allowed to use compression for one subset messages!"
710             if $compressed_data
711 7 0 33     43 and defined $self->{NUM_SUBSETS} and $self->{NUM_SUBSETS} == 1;
      33        
712 7         19 $self->{COMPRESSED_DATA} = $compressed_data;
713 7         15 return 1;
714             }
715             sub get_compressed_data {
716 0     0 0 0 my $self = shift;
717 0 0       0 return defined $self->{COMPRESSED_DATA} ? $self->{COMPRESSED_DATA} : undef;
718             }
719             sub set_descriptors_unexpanded {
720 4     4 0 12 my ($self, $descriptors_unexpanded) = @_;
721 4 50       13 _croak "Unexpanded descriptors not provided in set_descriptors_unexpanded"
722             unless defined $descriptors_unexpanded;
723 4         12 $self->{DESCRIPTORS_UNEXPANDED} = $descriptors_unexpanded;
724 4         9 return 1;
725             }
726             sub get_descriptors_unexpanded {
727 0     0 0 0 my $self = shift;
728             return defined $self->{DESCRIPTORS_UNEXPANDED}
729 0 0       0 ? $self->{DESCRIPTORS_UNEXPANDED} : undef;
730             }
731             #############################################
732             ## End of accessor methods for BUFR sec0-3 ##
733             #############################################
734              
735             sub get_current_subset_number {
736 182     182 0 336 my $self = shift;
737 182 50       671 return defined $self->{CURRENT_SUBSET} ? $self->{CURRENT_SUBSET} : undef;
738             }
739              
740             sub get_current_message_number {
741 117     117 0 373 my $self = shift;
742 117 50       361 return defined $self->{CURRENT_MESSAGE} ? $self->{CURRENT_MESSAGE} : undef;
743             }
744              
745             sub get_current_ahl {
746 168     168 0 318 my $self = shift;
747 168 100       749 return defined $self->{CURRENT_AHL} ? $self->{CURRENT_AHL} : undef;
748             }
749              
750             sub get_current_gts_starting_line {
751 7     7 0 8 my $self = shift;
752 7 50       34 return defined $self->{CURRENT_GTS_STARTING_LINE} ? $self->{CURRENT_GTS_STARTING_LINE} : undef;
753             }
754              
755             sub get_current_gts_eom {
756 7     7 0 7 my $self = shift;
757 7 100       22 return defined $self->{CURRENT_GTS_EOM} ? $self->{CURRENT_GTS_EOM} : undef;
758             }
759              
760             sub set_filter_cb {
761 7     7 0 42 my $self = shift;
762 7         11 my $cb = shift;
763              
764 7 50       29 if (ref $cb eq 'CODE') {
765 7         47 $self->{FILTER_CB} = $cb;
766 7         15 @{$self->{FILTER_ARGS}} = ($self, @_);
  7         24  
767             } else {
768 0         0 $self->{FILTER_CB} = undef;
769 0         0 delete $self->{FILTER_ARGS};
770             }
771 7         14 return 1;
772             }
773              
774             sub is_filtered {
775 57     57 0 147 my $self = shift;
776 57 100       214 return defined $self->{IS_FILTERED} ? $self->{IS_FILTERED} : undef;
777             }
778              
779             sub bad_bufrlength {
780 46     46 0 108 my $self = shift;
781 46 50       143 return defined $self->{BAD_LENGTH} ? $self->{BAD_LENGTH} : undef;
782             }
783              
784             sub set_tableformat {
785 67     67 0 147 my $self = shift;
786              
787 67         150 my $format = shift;
788 67 50       220 _croak "Table format not provided. Possible values are BUFRDC and ECCODES"
789             unless defined $format;
790 67 50 66     365 _croak "Supported table formats are BUFRDC and ECCODES"
791             unless uc($format) eq 'BUFRDC' || uc($format) eq 'ECCODES';
792 67         459 $BUFR_table{FORMAT} = uc($format);
793 67         381 Geo::BUFR->_spew(2, "BUFR table format set to %s", $BUFR_table{FORMAT});
794 67         131 return 1;
795             }
796              
797             sub get_tableformat {
798 8     8 0 15 my $self = shift;
799 8 50       36 return exists $BUFR_table{FORMAT} ? $BUFR_table{FORMAT} : '';
800             }
801              
802             ## Set the path for BUFR table files
803             ## Usage: Geo::BUFR->set_tablepath(directory_list)
804             ## where directory_list is a list of colon-separated strings.
805             ## Example: Geo::BUFR->set_tablepath("/foo/bar:/foo/baz", "/some/where/else")
806             sub set_tablepath {
807 70     70 0 34064761 my $self = shift;
808              
809 70         296 $BUFR_table{PATH} = join ":", map {split /:/} @_;
  70         484  
810 70         397 Geo::BUFR->_spew(2, "BUFR table path set to %s", $BUFR_table{PATH});
811 70         160 return 1;
812             }
813              
814             sub get_tablepath {
815 0     0 0 0 my $self = shift;
816              
817 0 0       0 if (exists $BUFR_table{PATH}) {
818 0 0       0 return wantarray ? split(/:/, $BUFR_table{PATH}) : $BUFR_table{PATH};
819             } else {
820 0         0 return '';
821             }
822             }
823              
824             ## Return table version from table if provided, or else from section 1
825             ## information in BUFR message. For BUFRDC, this is a stripped down
826             ## version of table name. For ECCODES, this is last path of table
827             ## location (e.g. '0/wmo/29'), and a stringified list of two such
828             ## paths (master and local) if local tables are used
829             ## (e.g. '0/wmo/29,0/local/8/78/236'). Returns undef/empty list if
830             ## impossible to determine table version.
831             sub get_table_version {
832 127     127 0 243 my $self = shift;
833 127         256 my $table = shift;
834              
835 127 100       366 if ($table) {
836 25 100       99 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
    50          
837             # First check if this actually is an attempt to load an ECCODES table
838 16 50 33     132 if ($table =~ /wmo/ || $table =~ /local/) {
839 0         0 _croak("$table cannot be a BUFRDC table. "
840             . "Did you forget to set tableformat to ECCODES?");
841             }
842 16         192 (my $version = $table) =~ s/^(?:[BCD]?)(.*?)(?:\.TXT)?$/$1/;
843 16         175 return $version;
844             } elsif ($BUFR_table{FORMAT} eq 'ECCODES') {
845             # Mainly meant to catch attempts to load a BUFRDC table
846             # with tableformat mistakingly set to ECCODES
847 9 50 66     38 _croak("$table cannot be an ecCodes table")
848             unless ($table =~ /wmo/ || $table =~ /local/);
849 9         40 return $table;
850             }
851             }
852              
853             # No table provided. Decide version from section 1 information.
854             # First check that the necessary metadata exist
855 102         240 foreach my $metadata (qw(MASTER_TABLE LOCAL_TABLE_VERSION
856             CENTRE SUBCENTRE)) {
857 408 50       1050 return undef if ! defined $self->{$metadata};
858             }
859              
860             # If master table version, use centre 0 and subcentre 0 (in ECMWF
861             # BUFRDC this is the convention from version 320 onwards)
862 102         227 my $centre = $self->{CENTRE};
863 102         233 my $subcentre = $self->{SUBCENTRE};
864 102         212 my $local_table_version = $self->{LOCAL_TABLE_VERSION};
865 102 100 66     771 if ($local_table_version == 0 || $local_table_version == 255) {
866 26         54 $centre = 0;
867 26         33 $subcentre = 0;
868 26         36 $local_table_version = 0;
869             }
870              
871 102         206 my $master_table = $self->{MASTER_TABLE};
872 102         194 my $master_table_version = $self->{MASTER_TABLE_VERSION};
873 102 100       430 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
    50          
874             # naming convention used in BUFRDC version >= 000270
875 93         1007 return sprintf "%03d%05d%05d%03d%03d",
876             $master_table,$subcentre,$centre,$master_table_version,$local_table_version;
877             } elsif ($BUFR_table{FORMAT} eq 'ECCODES') {
878 9 50       20 if ($local_table_version == 0) {
879 0         0 return catfile($master_table,'wmo',$master_table_version);
880             } else {
881 9         142 return catfile($master_table,'wmo',$master_table_version) . ',' .
882             catfile($master_table,'local',$local_table_version,$centre,$subcentre);
883             }
884             }
885             }
886              
887             # Return table version for the latest master table found in tablepath,
888             # in the same format as get_table_version. For use when master table
889             # version is not known, e.g. in bufrresolve.pl.
890             sub get_max_table_version {
891 2     2 0 4 my ($self) = @_;
892              
893             _croak "BUFR table path not set, did you forget to call set_tablepath()?"
894 2 50       5 unless $BUFR_table{PATH};
895              
896 2         3 my $max_version = 0;
897 2 50       9 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
    50          
898 0         0 foreach my $tabledir (split /:/, $BUFR_table{PATH}) {
899 0 0       0 opendir(my $dh, $tabledir) || _croak "Couldn't open $tabledir: $!";
900 0         0 my @btables = sort grep { /^B0000000000000\d{3}000.TXT/ } readdir $dh;
  0         0  
901 0         0 closedir $dh;
902 0   0     0 my $version = substr($btables[-1], 14, 3)
903             || _croak "Wrong tablepath or tableformat?";
904 0 0       0 if ($version > $max_version) {
905 0         0 $max_version = $version;
906             }
907             }
908              
909 0 0       0 _croak "No WMO master table found" unless $max_version;
910              
911 0         0 return '0000000000000' . $max_version . '000';
912              
913             } elsif ($BUFR_table{FORMAT} eq 'ECCODES') {
914 2         5 foreach my $tabledir (split /:/, $BUFR_table{PATH}) {
915 2         13 my $dir = catfile($tabledir,'0/wmo');
916 2 50       142 opendir(my $dh, $dir) || _croak "Cannot open $dir: $!";
917 2         78 my @files = sort {$a <=> $b} grep { /^[0-9]+$/ } readdir $dh;
  0         0  
  6         20  
918 2         18 closedir $dh;
919              
920 2         5 my $version = $files[-1];
921 2 50       8 if ($version > $max_version) {
922 2         20 $max_version = $version;
923             }
924             }
925              
926 2 50       6 _croak "No WMO master table found" unless $max_version;
927              
928 2         36 return catfile('0', 'wmo', $max_version);
929             }
930             }
931              
932             # Search through $BUFR_table{PATH} to find first path for which $fname
933             # exists, or (for BUFRDC) if no such path exists, first path for which the
934             # corresponding master file exists, in which case
935             # $self->{LOCAL_TABLES_NOT_FOUND} is set to the local table initially
936             # searched for (this variable should be undefined as soon as the
937             # message is finished processing). Returns empty list if no such path
938             # could be found, else returns the path and the table name for which
939             # path was found.
940             sub _locate_table {
941 163     163   450 my ($self,$fname) = @_;
942              
943             _croak "BUFR table path not set, did you forget to call set_tablepath()?"
944 163 50       689 unless $BUFR_table{PATH};
945              
946 163         377 my $path;
947 163         841 foreach (split /:/, $BUFR_table{PATH}) {
948 163 100       5846 if (-e catfile($_, $fname)) {
949 146         447 $path = $_;
950 146         487 $path =~ s|/$||;
951 146         1192 return ($path,$fname);
952             }
953             }
954              
955 17 100       84 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
956             # Path couldn't be found for $fname. Then try again for master table
957 8         43 my $master_table;
958 8         53 ($master_table,$path) = $self->_locate_master_table($fname);
959 8 50       26 if ($path) {
960 8         35 $self->{LOCAL_TABLES_NOT_FOUND} = $fname;
961 8         45 return ($path,$master_table);
962             }
963             }
964              
965             # No table found
966 9         26 return;
967             }
968              
969             # Return master table and path corresponding to local table $fname, or
970             # empty list if $fname actually is a master table or if no path for the
971             # master table could be found.
972             sub _locate_master_table {
973 8     8   53 my ($self,$fname) = @_;
974              
975 8         18 my $master_table;
976 8 50       48 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
    0          
977 8 50       39 _croak("$fname is not a valid name for BUFRDC tables")
978             if length($fname) < 20;
979 8         37 $master_table = substr($fname,0,4) . '00000' . '00000'
980             . substr($fname,14,3) . '000.TXT';
981             } elsif ($BUFR_table{FORMAT} eq 'ECCODES') {
982 0         0 foreach my $metadata (qw(MASTER_TABLE MASTER_TABLE_VERSION)) {
983 0 0       0 return if ! defined $self->{$metadata};
984             }
985 0         0 $master_table = catfile($self->{MASTER_TABLE},'wmo',$self->{MASTER_TABLE_VERSION});
986             }
987 8 50       39 return if ($master_table eq $fname); # Already tried
988              
989 8         50 my $path;
990 8         32 foreach (split /:/, $BUFR_table{PATH}) {
991 8 50       209 if (-e catfile($_, $master_table)) {
992 8         23 $path = $_;
993 8         23 $path =~ s|/$||;
994 8         40 return ($master_table,$path);
995             }
996             }
997 0         0 return;
998             }
999              
1000             ## Read in a B table file into a hash, e.g.
1001             ## $B_table{'001001'} = "WMO BLOCK NUMBER\0NUMERIC\0 0\0 0\0 7"
1002             ## where the B table values for 001001 are \0 (NUL) separated
1003             sub _read_B_table_bufrdc {
1004 61     61   223 my ($self,$version) = @_;
1005              
1006 61         150 my $fname = "B$version.TXT";
1007 61 50       329 my ($path,$tname) = $self->_locate_table($fname)
1008             or _croak "Couldn't find BUFR table $fname in $BUFR_table{PATH}."
1009             . " Wrong tablepath?";
1010              
1011             # If we are forced to try master table because local table
1012             # couldn't be found, check if this might already have been loaded
1013 61 100       421 if ($tname ne $fname) {
1014 4         12 my $master_version = substr($tname,1,-4);
1015 4 50       33 return $BUFR_table{"B$master_version"} if exists $BUFR_table{"B$master_version"};
1016             }
1017              
1018 61         450 my $tablefile = catfile($path,$tname);
1019 61 50       3135 open(my $TABLE, '<', $tablefile)
1020             or _croak "Couldn't open BUFR table B $tablefile: $!";
1021 61         247 my $txt = "Reading table $tablefile";
1022             $txt .= " (since local table " . $self->{LOCAL_TABLES_NOT_FOUND}
1023 61 100       487 . " couldn't be found)" if $self->{LOCAL_TABLES_NOT_FOUND};
1024 61         357 $self->_spew(1, "%s", $txt);
1025              
1026 61         145 my %B_table;
1027 61         2687 while (<$TABLE>) {
1028 83814         365073 my ($s1,$fxy,$s2,$name,$s3,$unit,$s4,$scale,$s5,$refval,$s6,$bits)
1029             = unpack('AA6AA64AA24AA3AA12AA3', $_);
1030 83814 50       168958 next unless defined $bits;
1031 83814         191769 $name =~ s/\s+$//;
1032 83814         117789 $refval =~ s/-\s+(\d+)/-$1/; # Remove blanks between minus sign and value
1033 83814         381938 $B_table{$fxy} = join "\0", $name, $unit, $scale, $refval, $bits;
1034             }
1035             # When installing Geo::BUFR on Windows Vista with Strawberry Perl,
1036             # close sometimes returned an empty string. Therefore removed
1037             # check on return value for close.
1038 61         942 close $TABLE; # or _croak "Closing $tablefile failed: $!";
1039              
1040 61         416 $BUFR_table{"B$version"} = \%B_table;
1041 61         1006 return \%B_table;
1042             }
1043              
1044             sub _read_B_table_eccodes {
1045 14     14   34 my ($self,$version) = @_;
1046              
1047 14         105 my ($path,$tname) = $self->_locate_table(catfile($version,'element.table'));
1048              
1049 14 100       41 if (! $path) {
1050 4 50       13 if ($version =~ /wmo/) {
1051 0         0 _croak "Couldn't find BUFR table " . catfile($version,'element.table')
1052             . " in $BUFR_table{PATH}. Wrong tablepath?";
1053             } else {
1054             # This might actually not be an error, since local table
1055             # might be provided for D only. But if later a local
1056             # element descriptor is requested, we should complain
1057 4         10 $self->{LOCAL_TABLES_NOT_FOUND} = $version;
1058 4         9 return;
1059             }
1060             }
1061 10         47 my $tablefile = catfile($path,$tname);
1062              
1063 10 50       422 open(my $TABLE, '<', $tablefile)
1064             or _croak "Couldn't open BUFR table B $tablefile: $!";
1065 10         51 $self->_spew(1, "Reading table %s", $tablefile);
1066              
1067 10         23 my %B_table;
1068 10         459 while (<$TABLE>) {
1069             # Skip comments (expexted to be in first line only)
1070 3902 100       6416 next if /^#/;
1071              
1072             # $rest is crex_unit|crex_scale|crex_width
1073 3892         12934 my ($code,$abbreviation,$type,$name,$unit,$scale,$reference,$width,$rest)
1074             = split /[|]/;
1075 3892 50       7236 next unless defined $width; # shouldn't happen
1076 3892 100       5883 $unit = 'CCITTIA5' if $unit eq 'CCITT IA5';
1077 3892         13746 $B_table{$code} = join "\0", $name, $unit, $scale, $reference, $width;
1078             }
1079 10         121 close $TABLE;
1080              
1081 10         65 $BUFR_table{"B$version"} = \%B_table;
1082 10         105 return \%B_table;
1083             }
1084              
1085             ## Reads a D table file into a hash, e.g.
1086             ## $D_table->{307080} = '301090 302031 ...'
1087             ## There are two different types of lines in D*.TXT, e.g.
1088             ## 307080 13 301090 BUFR template for synoptic reports
1089             ## 302031
1090             ## We choose to ignore the number of lines in expansion (here 13)
1091             ## because this number is sometimes in error. Instead we consider a
1092             ## line starting with 5 spaces to be of the second type above, else of
1093             ## the first type
1094             sub _read_D_table_bufrdc {
1095 61     61   214 my ($self,$version) = @_;
1096              
1097 61         160 my $fname = "D$version.TXT";
1098 61 50       332 my ($path,$tname) = $self->_locate_table($fname)
1099             or _croak "Couldn't find BUFR table $fname in $BUFR_table{PATH}."
1100             . "Wrong tablepath?";
1101              
1102             # If we are forced to try master table because local table
1103             # couldn't be found, check if this might already have been loaded
1104 61 100       264 if ($tname ne $fname) {
1105 4         14 my $master_version = substr($tname,1,-4);
1106 4 50       23 return $BUFR_table{"D$master_version"} if exists $BUFR_table{"D$master_version"};
1107             }
1108              
1109 61         417 my $tablefile = catfile($path,$tname);
1110 61 50       2689 open(my $TABLE, '<', $tablefile)
1111             or _croak "Couldn't open BUFR table D $tablefile: $!";
1112 61         226 my $txt = "Reading table $tablefile";
1113             $txt .= " (since local table " . $self->{LOCAL_TABLES_NOT_FOUND}
1114 61 100       281 . " couldn't be found)" if $self->{LOCAL_TABLES_NOT_FOUND};
1115 61         464 $self->_spew(1, "%s", $txt);
1116              
1117 61         144 my (%D_table, $alias);
1118 61         2199 while (my $line = <$TABLE>) {
1119 226371         580143 $line =~ s/\s+$//;
1120 226371 50       460036 next if $line =~ /^\s*$/; # Blank line
1121              
1122 226371 100       348149 if (substr($line,0,5) eq ' ' x 5) {
1123 201117         366830 $line =~ s/^\s+//;
1124 201117         565489 $D_table{$alias} .= " $line";
1125             } else {
1126 25254         50775 $line =~ s/^\s+//;
1127             # In table version 17 a descriptor with more than 100
1128             # entries occurs, causing no space between alias and
1129             # number of entries (so split /\s+/ doesn't work)
1130 25254         68181 my ($ali, $skip, $desc) = unpack('A6A4A6', $line);
1131 25254         34836 $alias = $ali;
1132 25254         80461 $D_table{$alias} = $desc;
1133             }
1134             }
1135 61         1040 close $TABLE; # or _croak "Closing $tablefile failed: $!";
1136              
1137 61         422 $BUFR_table{"D$version"} = \%D_table;
1138 61         1090 return \%D_table;
1139             }
1140              
1141             sub _read_D_table_eccodes {
1142 14     14   42 my ($self,$version) = @_;
1143              
1144 14         116 my ($path,$tname) = $self->_locate_table(catfile($version,'sequence.def'));
1145              
1146 14 100       100 if (! $path) {
1147 5 50       17 if ($version =~ /wmo/) {
1148 0         0 _croak "Couldn't find BUFR table " . catfile($version,'sequence.def')
1149             . " in $BUFR_table{PATH}. Wrong tablepath?";
1150             } else {
1151             # This might actually not be an error, since local table
1152             # might be provided for B only. But if later a local
1153             # sequence descriptor is requested, we should complain
1154 5         13 $self->{LOCAL_TABLES_NOT_FOUND} = $version;
1155             }
1156 5         11 return;
1157             }
1158 9         43 my $tablefile = catfile($path,$tname);
1159              
1160 9 50       354 open(my $TABLE, '<', $tablefile)
1161             or _croak "Couldn't open BUFR table B $tablefile: $!";
1162 9         50 $self->_spew(1, "Reading table %s", $tablefile);
1163              
1164             ## sequence.def is expected to contain lines like
1165             #"301196" = [ 301011, 301013, 301021 ]
1166             ## which should be converted to
1167             # 301196 3 301011
1168             # 301013
1169             # 301021
1170             ## Must also handle descriptors spanning more than one line, like
1171             #"301046" = [ 001007, 001012, 002048, 021119, 025060, 202124, 002026, 002027, 202000, 005040
1172             # ]
1173             ## and
1174             #"301058" = [ 301011, 301012, 201152, 202135, 004006, 202000, 201000, 301021, 020111, 020112,
1175             # 020113, 020114, 020115, 020116, 020117, 020118, 020119, 025035, 020121, 020122,
1176             # 020123, 020124, 025175, 020023, 025063, 202136, 201136, 002121, 201000, 202000,
1177             # 025061, 002184, 002189, 025036, 101000, 031002, 301059 ]
1178 9         33 my %D_table;
1179             my $txt;
1180 9         284 while (<$TABLE>) {
1181 1698 100       2858 if (substr($_,0,1) eq '"') {
1182             # New sequence descriptor, parse and store the previous
1183 1410 100       3133 _parse_sequence(\%D_table,$txt) if $txt;
1184 1410         1913 chomp;
1185 1410         3377 $txt = $_;
1186             } else {
1187 288         359 chomp;
1188 288         769 $txt .= $_;
1189             }
1190             }
1191 9 50       37 _parse_sequence(\%D_table,$txt) if $txt;
1192              
1193 9         107 close $TABLE; # or _croak "Closing $tablefile failed: $!";
1194              
1195 9         44 $BUFR_table{"D$version"} = \%D_table;
1196 9         75 return \%D_table;
1197             }
1198              
1199             sub _parse_sequence {
1200 1410     1410   2100 my ($Dtable, $txt) = @_;
1201              
1202 1410         4523 my ($seq, $rest) = ($txt =~ /^"(\d{6})" = \[(.*)\]/);
1203 1410         3461 my @list = split(/,/, $rest);
1204 1410         2047 foreach (@list) {
1205 9486         18933 s/^ +//;
1206 9486         14797 s/ +$//;
1207             }
1208 1410         5770 $Dtable->{$seq} = join(' ', @list);
1209             }
1210              
1211             ## Read the flag and code tables, which in ECMWF BUFRDC tables are
1212             ## put in tables C$version.TXT (not to be confused with BUFR C tables,
1213             ## which contain the operator descriptors). Note that even though
1214             ## number of code values and number of lines are included in the
1215             ## tables, we choose to ignore them, because these values are often
1216             ## found to be in error. Instead we trust that the text starts at
1217             ## fixed positions in file. Returns reference to the C table, or undef
1218             ## if failing to open table file.
1219             sub _read_C_table {
1220 9     9   22 my ($self,$version) = @_;
1221              
1222             # For ECCODES loading 2 different codetables directories might be necessary
1223 9 100       24 if ($BUFR_table{FORMAT} eq 'ECCODES') {
1224 3 50       7 if ($version =~ /,/) {
1225 0         0 my ($master, $local) = (split /,/, $version);
1226 0         0 $self->_read_C_table_eccodes($master);
1227 0         0 return $self->_read_C_table_eccodes($local);
1228             } else {
1229 3         6 return $self->_read_C_table_eccodes($version);
1230             }
1231             }
1232              
1233             # Rest of code is for BUFRDC
1234 6         14 my $fname = "C$version.TXT";
1235 6         21 my ($path,$tname) = $self->_locate_table($fname);
1236 6 50       23 return undef unless $path;
1237              
1238             # If we are forced to try master table because local table
1239             # couldn't be found, check if this might already have been loaded
1240 6 50       52 if ($tname ne $fname) {
1241 0         0 my $master_version = substr($tname,1,-4);
1242 0 0       0 return $BUFR_table{"C$master_version"} if exists $BUFR_table{"C$master_version"};
1243             }
1244              
1245 6         30 my $tablefile = catfile($path,$tname);
1246 6 50       267 open(my $TABLE, '<', $tablefile)
1247             or _croak "Couldn't open BUFR table C $tablefile: $!";
1248 6         92 my $txt = "Reading table $tablefile";
1249             $txt .= " (since local table " . $self->{LOCAL_TABLES_NOT_FOUND}
1250 6 50       142 . " couldn't be found)" if $self->{LOCAL_TABLES_NOT_FOUND};
1251 6         54 $self->_spew(1, "%s", $txt);
1252              
1253 6         13 my (%C_table, $table, $value);
1254 6         252 while (my $line = <$TABLE>) {
1255 28506         107283 $line =~ s/\s+$//;
1256 28506 50       58521 next if $line =~ /^\s*$/; # Blank line
1257              
1258 28506 100       53506 if (substr($line,0,15) eq ' ' x 15) {
    100          
1259 3210         6719 $line =~ s/^\s+//;
1260 3210 50 33     8775 next if $line eq 'NOT DEFINED' || $line eq 'RESERVED';
1261 3210         11823 $C_table{$table}{$value} .= $line . "\n";
1262             } elsif (substr($line,0,10) eq ' ' x 10) {
1263 23292         47382 $line =~ s/^\s+//;
1264 23292         53384 my ($val, $nlines, $txt) = split /\s+/, $line, 3;
1265 23292         33719 $value = $val+0;
1266 23292 100 66     82035 next if !defined $txt || $txt eq 'NOT DEFINED' || $txt eq 'RESERVED';
      100        
1267 22974         99748 $C_table{$table}{$value} .= $txt . "\n";
1268             } else {
1269 2004         6627 my ($tbl, $nval, $val, $nlines, $txt) = split /\s+/, $line, 5;
1270 2004         5791 $table = sprintf "%06d", $tbl;
1271             # For tables listed 2 or more times, use last instance only.
1272             # This prevents $txt to be duplicated in $C_table{$table}{$value}
1273 2004 100       4177 undef $C_table{$table} if defined $C_table{$table};
1274 2004         2727 $value = $val+0;
1275 2004 100 33     7919 next if !defined $txt || $txt eq 'NOT DEFINED' || $txt eq 'RESERVED';
      66        
1276 1830         7941 $C_table{$table}{$value} = $txt . "\n";
1277             }
1278             }
1279 6         120 close $TABLE; # or _croak "Closing $tablefile failed: $!";
1280              
1281 6         59 $BUFR_table{"C$version"} = \%C_table;
1282 6         127 return \%C_table;
1283             }
1284              
1285             sub _read_C_table_eccodes {
1286 7     7   11 my ($self,$version) = @_;
1287              
1288 7         48 my ($path,$tname) = $self->_locate_table(catfile($version,'codetables'));
1289              
1290 7 50       20 if (! $path) {
1291 0 0       0 if ($version =~ /wmo/) {
1292 0 0 0     0 _croak "Couldn't find BUFR table " . catfile($version,'element.table')
1293             . " in $BUFR_table{PATH}. Wrong tablepath?"
1294             if (! $path && $version =~ /wmo/);
1295             } else {
1296             # This might actually not be an error, if none of the
1297             # local descriptors are of type code or flag table. So
1298             # prefer to keep silent in this case.
1299 0         0 return;
1300             }
1301             }
1302              
1303 7         23 my $tabledir = catfile($path,$tname);
1304              
1305 7 50       229 opendir(my $dh, $tabledir) || _croak "Couldn't open $tabledir: $!";
1306 7         200 my @table_files = grep { /table$/ } readdir $dh;
  35         82  
1307 7         63 closedir($dh);
1308 7 50       30 $self->_spew(1, "Reading tables in %s", $tabledir) if @table_files;
1309              
1310 7         10 my %C_table;
1311 7         30 foreach my $table_file (@table_files) {
1312 21         99 my ($table) = ($table_file =~ /(\d+)\.table$/);
1313 21 50       35 die "Unexpected name of table file: $table_file" unless $table;
1314 21         62 $table = sprintf "%06d", $table;
1315              
1316 21 50       725 open my $IN, '<', catfile($tabledir, $table_file)
1317             or _croak "Couldn't open $table_file: $!";
1318 21         300 while (<$IN>) {
1319 443         385 chomp;
1320 443         686 my ($num, $val, $txt) = split(/ /, $_, 3);
1321 443 50 66     682 _complain("Unexpected: first 2 fields in $table_file in $tabledir are unequal: $num $val")
1322             if ($Strict_checking and $num ne $val);
1323              
1324             # Fix a common problem in ecCodes codetables with long
1325             # lines, hopefully not changing valid use of '"' in local
1326             # tables (e.g. 8/78/0/codetables/8198.table: ""Nebenamtliche"" measurement
1327 443         414 $txt =~ s/(?
1328             ## $txt =~ s/" +//;
1329              
1330 443         896 $C_table{$table}{$val} = $txt . "\n";
1331             }
1332              
1333             _complain("$table_file in $tabledir is empty!")
1334 21 50 66     60 if ($Strict_checking and not $C_table{$table});
1335 21         152 close $IN;
1336             }
1337              
1338 7         23 $BUFR_table{"C$version"} = \%C_table;
1339 7         50 return \%C_table;
1340             }
1341              
1342             sub _get_tableid_eccodes {
1343 0     0   0 my $table_file = shift;
1344 0         0 my ($id) = ($table_file =~ /(\d+)\.table$/);
1345 0         0 return $id;
1346             }
1347              
1348              
1349             sub load_BDtables {
1350 104     104 0 247 my $self = shift;
1351 104   100     569 my $table = shift || '';
1352              
1353 104 50       531 my $version = $self->{TABLE_VERSION} = $self->get_table_version($table)
1354             or _croak "Not enough info to decide which tables to load";
1355              
1356 104 100       460 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
    50          
1357 95   66     959 $self->{B_TABLE} = $BUFR_table{"B$version"} || $self->_read_B_table_bufrdc($version);
1358 95   66     856 $self->{D_TABLE} = $BUFR_table{"D$version"} || $self->_read_D_table_bufrdc($version);
1359             } elsif ($BUFR_table{FORMAT} eq 'ECCODES') {
1360 9 100       42 if ($version =~ /,/) {
1361 7         46 my ($master, $local) = (split /,/, $version);
1362 7   66     70 $self->{B_TABLE} = $BUFR_table{"B$master"} || $self->_read_B_table_eccodes($master);
1363 7   66     51 $self->{D_TABLE} = $BUFR_table{"D$master"} || $self->_read_D_table_eccodes($master);
1364              
1365             # Append local table to the master table (should work even if empty)
1366 7 50       60 my $local_Btable = (exists($BUFR_table{"B$local"})) ? $BUFR_table{"B$local"}
1367             : $self->_read_B_table_eccodes($local);
1368 7         232 @{$self->{B_TABLE}}{ keys %$local_Btable } = values %$local_Btable;
  7         502  
1369 7 50       104 my $local_Dtable = (exists($BUFR_table{"D$local"})) ? $BUFR_table{"D$local"}
1370             : $self->_read_D_table_eccodes($local);
1371 7         22 @{$self->{D_TABLE}}{ keys %$local_Dtable } = values %$local_Dtable;;
  7         28  
1372              
1373             } else {
1374 2   33     16 $self->{B_TABLE} = $BUFR_table{"B$version"} || $self->_read_B_table_eccodes($version);
1375 2   66     16 $self->{D_TABLE} = $BUFR_table{"D$version"} || $self->_read_D_table_eccodes($version);
1376             }
1377             }
1378 104         424 return $version;
1379             }
1380              
1381             sub load_Ctable {
1382 15     15 0 23 my $self = shift;
1383 15   50     60 my $table = shift || '';
1384              
1385 15   50     49 my $version = $self->get_table_version($table) || '';
1386              
1387 15 50       50 if ($version) {
1388 15 100       79 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
    50          
1389 9   66     69 $self->{C_TABLE} = $BUFR_table{"C$version"} || $self->_read_C_table($version);
1390             } elsif ($BUFR_table{FORMAT} eq 'ECCODES') {
1391 6 100       23 if ($version =~ /,/) {
1392 3         7 my ($master, $local) = (split /,/, $version);
1393 3   33     19 $self->{C_TABLE} = $BUFR_table{"$master"} || $self->_read_C_table($master);
1394              
1395             # Append local table to the master table (should work even if empty)
1396 3 100       12 my $local_Ctable = (exists($BUFR_table{"C$local"})) ? $BUFR_table{"C$local"}
1397             : $self->_read_C_table_eccodes($local);
1398 3         9 @{$self->{C_TABLE}}{ keys %$local_Ctable } = values %$local_Ctable;
  3         9  
1399              
1400             } else {
1401 3   33     19 $self->{C_TABLE} = $BUFR_table{"C$version"} || $self->_read_C_table_eccodes($version);
1402             }
1403             }
1404             }
1405              
1406 15 50       70 if (not $self->{C_TABLE}) {
1407             # Was not able to load $table. Try latest master table instead.
1408 0   0     0 $version = $self->get_max_table_version()
1409             || _croak "No master tables found in tablepath";
1410 0 0       0 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
1411 0   0     0 $self->{C_TABLE} = $BUFR_table{"C$version"} || $self->_read_C_table($version);
1412             } else {
1413 0   0     0 $self->{C_TABLE} = $BUFR_table{"C$version"} || $self->_read_C_table_eccodes($version);
1414             }
1415             }
1416 15 50       46 if (not $self->{C_TABLE}) {
1417 0 0       0 if ($BUFR_table{FORMAT} eq 'BUFRDC') {
1418 0         0 _croak "Unable to load C table (C$version.TXT)";
1419             } else {
1420 0         0 _croak "Unable to load codetables for $version";
1421             }
1422             }
1423              
1424 15         51 return $version;
1425             }
1426              
1427              
1428             ## Specify BUFR file to read
1429             sub fopen {
1430 39     39 0 97 my $self = shift;
1431 39 50       146 my $filename = shift
1432             or _croak "fopen() called without an argument";
1433 39 50       1800 _croak "File $filename doesn't exist!" unless -e $filename;
1434 39 50       457 _croak "$filename is not a plain file" unless -f $filename;
1435              
1436             # Open file for reading
1437 39         484 $self->{FILEHANDLE} = new FileHandle;
1438 39 50       4823 open $self->{FILEHANDLE}, '<', $filename
1439             or _croak "Couldn't open file $filename for reading";
1440              
1441 39         289 $self->_spew(2, "File %s opened for reading", $filename);
1442              
1443             # For some OS this is necessary
1444 39         156 binmode $self->{FILEHANDLE};
1445              
1446 39         128 $self->{FILENAME} = $filename;
1447 39         127 return 1;
1448             }
1449              
1450             sub fclose {
1451 40     40 0 189 my $self = shift;
1452 40 100       263 if ($self->{FILEHANDLE}) {
1453             close $self->{FILEHANDLE}
1454 37 50       957 or _croak "Couldn't close BUFR file opened by fopen()";
1455 37         273 $self->_spew(2, "Closed file %s", $self->{FILENAME});
1456             }
1457 40         377 delete $self->{FILEHANDLE};
1458 40         93 delete $self->{FILENAME};
1459             # Much more might be considered deleted here, but usually the bufr
1460             # object goes out of scope immediately after a fclose anyway
1461 40         6 return 1;
1462             }
1463              
1464             sub eof {
1465 236     236 0 700 my $self = shift;
1466 236   100     1271 return ($self->{EOF} || 0);
1467             }
1468              
1469             # Go to start of input buffer or start of file associated with the object
1470             sub rewind {
1471 6     6 0 18 my $self = shift;
1472 6 100       13 if (exists $self->{FILEHANDLE}) {
    50          
1473 4 50       25 seek $self->{FILEHANDLE}, 0, 0 or _croak "Cannot seek: $!";
1474             } elsif (! $self->{IN_BUFFER}) {
1475 0         0 _croak "Cannot rewind: no file or input buffer associated with this object";
1476             }
1477 6         9 $self->{CURRENT_MESSAGE} = 0;
1478 6         8 $self->{CURRENT_SUBSET} = 0;
1479 6         16 delete $self->{START_POS};
1480 6         6 delete $self->{POS};
1481 6         12 delete $self->{EOF};
1482 6         54 return 1;
1483             }
1484              
1485             ## Read in next BUFR message from file if $self->{FILEHANDLE} is set,
1486             ## else from $self->{IN_BUFFER} (string argument to
1487             ## constructor). Decodes section 0 and sets $self->{START_POS} to
1488             ## start of message and $self->{POS} to end of BUFR message (or after
1489             ## first 8 bytes of truncated/corrupt BUFR message for which we still
1490             ## want to attempt decoding). $self->{CURRENT_AHL} is updated if a GTS
1491             ## ahl is found (implemented for file reading only) and similarly for
1492             ## $self->{CURRENT_GTS_STARTING_LINE}, and $self->{EOF} is set if no
1493             ## more 'BUFR' in file/buffer. Croaks if an error occurs when reading
1494             ## BUFR message.
1495              
1496             ## Returns BUFR message from section 1 on, or undef if no BUFR message
1497             ## is found.
1498             sub _read_message {
1499 199     199   287 my $self = shift;
1500              
1501 199 100       594 my $filehandle = $self->{FILEHANDLE} ? $self->{FILEHANDLE} : undef;
1502 199 100       583 my $in_buffer = $self->{IN_BUFFER} ? $self->{IN_BUFFER} : undef;
1503 199 50 66     577 _croak "_read_message: Neither BUFR file nor BUFR text is given"
1504             unless $filehandle or $in_buffer;
1505              
1506             # Locate next 'BUFR' and set $pos to this position in file/string,
1507             # also finding corresponding GTS ahl and starting line if exists
1508             # (for file only). Possibly sets $self->{EOF}
1509 199 100       533 my $pos = defined $self->{POS} ? $self->{POS} : 0;
1510 199         301 my ($ahl, $gts_start);
1511 199         638 ($pos, $ahl, $gts_start) = $self->_find_next_BUFR($filehandle, $in_buffer, $pos, '');
1512 199 100       559 return if $pos < 0;
1513 156 100       318 if ($ahl) {
1514 99         198 $self->{CURRENT_AHL} = $ahl;
1515 99 100       223 if ($gts_start) {
1516 60         105 $self->{CURRENT_GTS_STARTING_LINE} = $gts_start;
1517 60         103 $self->{GTS_CURRENT_EOM} = undef;
1518             }
1519             } else {
1520 57         158 $self->{CURRENT_AHL} = undef;
1521 57         137 $self->{CURRENT_GTS_STARTING_LINE} = undef;
1522             }
1523              
1524             # Remember start position of BUFR message in case we need to
1525             # rewind later because length of BUFR cannot be trusted
1526 156         269 $self->{START_POS} = $pos;
1527              
1528             # Report (if verbose setting) where we found the BUFR message
1529 156 100       360 $self->_spew(2, "BUFR message at position %d", $pos) if $Spew;
1530              
1531             # Read (rest) of Section 0 (length of BUFR message and edition number)
1532 156         202 my $sec0; # Section 0 is BUFR$sec0
1533 156 100       303 if ($filehandle) {
1534 144 50       1171 if ((read $filehandle, $sec0, 8) != 8) {
1535 0         0 $self->{EOF} = 1;
1536 0         0 _croak "Error reading section 0 in file '$self->{FILENAME}', position "
1537             . tell($filehandle);
1538             }
1539 144         394 $sec0 = substr $sec0, 4;
1540             } else {
1541 12 50       37 if (length($in_buffer) < $pos+8) {
1542 0         0 $self->{EOF} = 1;
1543 0         0 _croak "Error reading section 0: this is not a BUFR message?"
1544             }
1545 12         69 $sec0 = substr $in_buffer, $pos+4, 4;
1546             }
1547 156         335 $self->{SEC0_STREAM} = "BUFR$sec0";
1548              
1549             # Extract length and edition number
1550 156         642 my ($length, $edition) = unpack 'NC', "\0$sec0";
1551 156         317 $self->{BUFR_LENGTH} = $length;
1552 156         314 $self->{BUFR_EDITION} = $edition;
1553 156 100       393 $self->_spew(2, "Message length: %d, Edition: %d", $length, $edition) if $Spew;
1554 156 100 66     852 _croak "Cannot handle BUFR edition $edition" if $edition < 2 || $edition > 4;
1555              
1556             # Read rest of BUFR message (section 1-5)
1557 155         220 my $msg;
1558 155         208 my $msgisOK = 1;
1559 155 100       315 if ($filehandle) {
1560 143 100       655 if ((read $filehandle, $msg, $length-8) != $length-8) {
1561             # Probably a corrupt or truncated BUFR message. We choose
1562             # to decode as much as possible (maybe the length in
1563             # section 0 is all that is wrong), but obviously we cannot
1564             # trust the stated length of BUFR message, so reset
1565             # position of filehandle to just after section 0
1566 1         3 $self->{BAD_LENGTH} = 1;
1567 1         25 $msgisOK = 0;
1568 1         10 seek $filehandle, $pos+8, 0;
1569             _complain("File %s not big enough to contain the stated"
1570 1         6 . "length of BUFR message", $self->{FILENAME});
1571 1         2 $pos += 8;
1572             } else {
1573 142         230 $pos = tell($filehandle);
1574 142 100       470 if (substr($msg, -4) ne '7777') {
1575 36         56 $self->{BAD_LENGTH} = 1;
1576 36         101 _complain("BUFR length in sec 0 can't be correct, "
1577             . "last 4 bytes are not '7777'");
1578             }
1579             }
1580             } else {
1581 12 50       57 if (length($in_buffer) < $pos+$length) {
1582 0         0 $self->{BAD_LENGTH} = 1;
1583 0         0 $msgisOK = 0;
1584 0         0 _complain("Buffer not big enough to contain the stated "
1585             . "length of BUFR message");
1586 0         0 $msg = substr $in_buffer, $pos+8, $length-8;
1587 0         0 $pos += 8;
1588             } else {
1589 12         38 $msg = substr $in_buffer, $pos+8, $length-8;
1590 12         23 $pos += $length;
1591 12 100       38 if (substr($msg, -4) ne '7777') {
1592 3         7 $self->{BAD_LENGTH} = 1;
1593 3         35 _complain("BUFR length in sec 0 can't be correct, "
1594             . "last 4 bytes are not '7777'");
1595             }
1596             }
1597             }
1598 155 100       382 if ($Spew) {
1599 2 50       6 if ($msgisOK) {
1600 2         4 $self->_spew(2, "Successfully read BUFR message; position now %d", $pos);
1601             } else {
1602 0         0 $self->_spew(2, "Resetting position to %d", $pos);
1603             }
1604             }
1605              
1606             # Reset $self->{POS} to end of BUFR message (or after first 8
1607             # bytes of truncated/corrupt BUFR message)
1608 155         294 $self->{POS} = $pos;
1609              
1610             # And then advance past GTS end of message if found
1611 155         257 my $gts_eom;
1612 155 100 100     635 if ($filehandle && ! $self->{BAD_LENGTH}) {
1613 106 100 100     744 if ((read $filehandle, $gts_eom, 4) == 4 && $gts_eom eq "\r\r\n\003") {
1614 33         143 $self->{CURRENT_GTS_EOM} = $gts_eom;
1615 33         48 $self->{POS} +=4;
1616             } else {
1617             # return to end of message position
1618 73         535 seek $filehandle, $pos, 0;
1619             }
1620             }
1621              
1622 155         458 return $msg;
1623             }
1624              
1625             # Note that our definition av AHL and GTS starting line differs
1626             # slightly from that of the Manual on the GTS PART II. OPERATIONAL
1627             # PROCEDURES FOR THE GLOBAL TELECOMMUNICATION SYSTEM (2.3.1 and 2.3.2)
1628             # in that the Abbreviated heading in the Manual starts with \r\r\n
1629             # which we have chosen to consider belonging to (the end of) the GTS
1630             # starting line.
1631              
1632             my $gts_start_regexp = qr{\001\r\r\n\d{3,5}\r\r\n};
1633             # Allow both 3 and 5 digits channel sequence number
1634              
1635             my $ahl_regex = qr{[A-Z]{4}\d\d [A-Z]{4} \d{6}(?: (?:(?:RR|CC|AA|PA)[A-Z])| COR| RTD)?};
1636             # BBB=Pxx (segmentation) was allowed until 2007, but at least one
1637             # centre still uses PAA as of 2014. COR and RTD shouldn't be
1638             # allowed (from ?), but are still used
1639              
1640             ## Advance to first occurrence of 'BUFR', or to the possibly preceding
1641             ## GTS envelope if this is requested in $at. Returns the new position
1642             ## and (if called in array context) the possibly preceding ahl and gts
1643             ## starting line. If no 'BUFR' is found, sets $self->{EOF} and returns
1644             ## -1 for the new position.
1645             sub _find_next_BUFR {
1646 213     213   321 my $self = shift;
1647 213         524 my ($filehandle, $in_buffer, $pos, $at) = @_;
1648              
1649 213         364 my ($new_pos, $ahl, $gts_start);
1650 213 100       458 if ($filehandle) {
1651 199         507 my $oldeol = $/;
1652 199         553 $/ = "BUFR";
1653 199   100     4614 my $slurp = <$filehandle> || ' ';
1654 199         487 $/ = $oldeol;
1655 199 100 66     999 if (CORE::eof($filehandle) or substr($slurp,-4) ne 'BUFR') {
1656 41         239 $self->{EOF} = 1;
1657             } else {
1658             # Get the GTS ahl (TTAAii CCCC DTG [BBB]) before 'BUFR',
1659             # if present. Use '\n+' not '\n' since adding an extra
1660             # '\n' in bulletin has been seen. Allow also for not
1661             # including \r\r (which might be how the bulletin file was
1662             # prepared originally, or might catch cases where ahl is
1663             # mistakingly included twice)
1664 158         234 my $reset = 4;
1665 158 100       8889 if ($slurp =~ /(${gts_start_regexp}?)(${ahl_regex})((?:\r\r)?\n+BUFR)$/) {
1666 102   100     437 $gts_start = $1 || '';
1667 102         169 $ahl = $2;
1668 102 100       306 $reset = length($gts_start) + length($2) + length($3) if $at eq 'at_ahl';
1669              
1670 102 50       200 $self->_spew(2,"GTS ahl found: %s%s", $gts_start, $ahl) if $Spew;
1671             }
1672             # Reset position of filehandle to just before 'BUFR', or
1673             # if requested, before possible preceding AHL
1674 158         1356 seek($filehandle, -$reset, 1);
1675 158         363 $new_pos = tell $filehandle;
1676             }
1677             } else {
1678 14         48 $new_pos = index($in_buffer, 'BUFR', $pos);
1679 14 100       33 if ($new_pos < 0) {
1680 2         6 $self->{EOF} = 1;
1681             } else {
1682 12 100       1208 if (substr($in_buffer, $pos, $new_pos - $pos)
1683             =~ /(${gts_start_regexp}?)(${ahl_regex})((?:\r\r)?\n+)$/) {
1684 6   100     50 $gts_start = $1 || '';
1685 6         15 $ahl = $2;
1686 6 50       16 $self->_spew(2,"GTS ahl found: %s%s", $gts_start, $ahl) if $Spew;
1687 6 50       19 if ($at eq 'at_ahl') {
1688 0         0 $new_pos -= length($gts_start) + length($2) + length($3);
1689             }
1690             }
1691             }
1692             }
1693              
1694 213 100       556 if ($self->{EOF}) {
1695 43 50       151 if ($pos == 0) {
1696 0 0       0 if ($filehandle) {
1697             $self->_spew(2,"No BUFR message in file %s", $self->{FILENAME})
1698 0 0       0 if $Spew;
1699             } else {
1700 0 0       0 $self->_spew(2, "No BUFR message found") if $Spew;
1701             }
1702             }
1703 43         147 return -1;
1704             }
1705              
1706 170 100       853 return wantarray ? ($new_pos, $ahl, $gts_start) : $new_pos;
1707             }
1708              
1709             ## Returns the BUFR message in raw (binary) form, '' if errors encountered
1710             sub get_bufr_message {
1711 26     26 0 53 my $self = shift;
1712              
1713 26 50 33     105 if ($self->{BAD_LENGTH} || $self->{ERROR_IN_MESSAGE}) {
1714 0         0 $self->_spew(2, "Skipping erroneous BUFR message");
1715 0         0 return '';
1716             }
1717 26 50 66     68 if (!$self->{FILEHANDLE} && !$self->{IN_BUFFER}) {
1718 0         0 $self->_spew(2, "No file or input buffer associated with this object");
1719 0         0 return '';
1720             }
1721 26 50 33     120 if (!exists $self->{START_POS} || !$self->{BUFR_LENGTH}) {
1722 0         0 $self->_spew(2, "No bufr message to return");
1723 0         0 return '';
1724             }
1725              
1726 26         37 my $msg;
1727 26 100       77 if (exists $self->{FILEHANDLE}) {
    50          
1728 21         44 my $fh = $self->{FILEHANDLE};
1729 21         27 my $old_pos = tell($fh);
1730 21         143 seek($fh, $self->{START_POS}, 0);
1731 21         151 read($fh, $msg, $self->{BUFR_LENGTH});
1732 21         122 seek($fh, $old_pos, 0);
1733 21         37 $self->_spew(2, "BUFR message extracted from file");
1734             } elsif (exists $self->{IN_BUFFER}) {
1735 5         19 $msg = substr $self->{IN_BUFFER}, $self->{START_POS}, $self->{BUFR_LENGTH};
1736 5         17 $self->_spew(2, "BUFR message extracted");
1737             }
1738              
1739 26         116 return $msg;
1740             }
1741              
1742             ## Decode section 1 to 5. Section 0 is already decoded in _read_message.
1743             sub _decode_sections {
1744 155     155   270 my $self = shift;
1745 155         271 my $msg = shift;
1746              
1747 155         390 $self->{BUFR_STREAM} = $msg;
1748 155         290 $self->{SEC1_STREAM} = undef;
1749 155         266 $self->{SEC2_STREAM} = undef;
1750 155         320 $self->{SEC3_STREAM} = undef;
1751 155         275 $self->{SEC4_STREAM} = undef;
1752 155         274 $self->{SEC5_STREAM} = undef;
1753              
1754             # Breaking the rule that all debugging should be on lines starting
1755             # with 'BUFR.pm:', therefore using $verbose=6
1756 155 100       364 $self->_spew(6, "%s", $self->dumpsection0()) if $Spew;
1757              
1758             ## Decode Section 1 (Identification Section) ##
1759              
1760 155 100       330 $self->_spew(2, "Decoding section 1") if $Spew;
1761              
1762             # Extract Section 1 information
1763 155 100       484 if ($self->{BUFR_EDITION} < 4) {
    50          
1764             # N means 4 byte integer, so put an extra null byte ('\0') in
1765             # front of string to get first 3 bytes as integer
1766 38         248 my @sec1 = unpack 'NC14', "\0" . $self->{BUFR_STREAM};
1767              
1768             # Check that stated length of section 1 makes sense
1769 38 50       125 _croak "Length of section 1 too small (< 17): $sec1[0]"
1770             if $sec1[0] < 17;
1771             _croak "Rest of BUFR message shorter (" . length($self->{BUFR_STREAM})
1772             . " bytes) than stated length of section 1 ($sec1[0] bytes)"
1773 38 50       136 if $sec1[0] > length($self->{BUFR_STREAM});
1774              
1775 38         195 push @sec1, (unpack 'a*', substr $self->{BUFR_STREAM},17,$sec1[0]-17);
1776 38         109 $self->{SEC1_STREAM} = substr $self->{BUFR_STREAM}, 0, $sec1[0];
1777 38         151 $self->{BUFR_STREAM} = substr $self->{BUFR_STREAM}, $sec1[0];
1778 38         142 $self->{SEC1} = \@sec1;
1779 38         82 $self->{MASTER_TABLE} = $sec1[1];
1780 38         72 $self->{SUBCENTRE} = $sec1[2];
1781 38         99 $self->{CENTRE} = $sec1[3];
1782 38         108 $self->{UPDATE_NUMBER} = $sec1[4];
1783 38         163 $self->{OPTIONAL_SECTION} = vec($sec1[5] & 0x80,0,1); # 1. bit
1784 38         86 $self->{DATA_CATEGORY} = $sec1[6];
1785 38         87 $self->{DATA_SUBCATEGORY} = $sec1[7];
1786 38         91 $self->{MASTER_TABLE_VERSION} = $sec1[8];
1787 38         64 $self->{LOCAL_TABLE_VERSION} = $sec1[9];
1788 38         87 $self->{YEAR_OF_CENTURY} = $sec1[10];
1789 38         69 $self->{MONTH} = $sec1[11];
1790 38         76 $self->{DAY} = $sec1[12];
1791 38         67 $self->{HOUR} = $sec1[13];
1792 38         104 $self->{MINUTE} = $sec1[14];
1793 38         99 $self->{LOCAL_USE} = $sec1[15];
1794             # In case previous message was edition 4
1795 38         90 foreach my $key (qw(INT_DATA_SUBCATEGORY LOC_DATA_SUBCATEGORY
1796             YEAR SECOND)) {
1797 152         350 undef $self->{$key};
1798             }
1799             } elsif ($self->{BUFR_EDITION} == 4) {
1800 117         636 my @sec1 = unpack 'NCnnC7nC5', "\0" . $self->{BUFR_STREAM};
1801              
1802             # Check that stated length of section 1 makes sense
1803 117 50       266 _croak "Length of section 1 too small (< 22): $sec1[0]"
1804             if $sec1[0] < 22;
1805             _croak "Rest of BUFR message shorter (" . length($self->{BUFR_STREAM})
1806             . " bytes) than stated length of section 1 ($sec1[0] bytes)"
1807 117 50       305 if $sec1[0] > length($self->{BUFR_STREAM});
1808              
1809 117         476 push @sec1, (unpack 'a*', substr $self->{BUFR_STREAM},22,$sec1[0]-22);
1810 117         260 $self->{SEC1_STREAM} = substr $self->{BUFR_STREAM}, 0, $sec1[0];
1811 117         300 $self->{BUFR_STREAM} = substr $self->{BUFR_STREAM}, $sec1[0];
1812 117         288 $self->{SEC1} = \@sec1;
1813 117         271 $self->{MASTER_TABLE} = $sec1[1];
1814 117         206 $self->{CENTRE} = $sec1[2];
1815 117         241 $self->{SUBCENTRE} = $sec1[3];
1816 117         198 $self->{UPDATE_NUMBER} = $sec1[4];
1817 117         320 $self->{OPTIONAL_SECTION} = vec($sec1[5] & 0x80,0,1); # 1. bit
1818 117         181 $self->{DATA_CATEGORY} = $sec1[6];
1819 117         185 $self->{INT_DATA_SUBCATEGORY} = $sec1[7];
1820 117         238 $self->{LOC_DATA_SUBCATEGORY} = $sec1[8];
1821 117         182 $self->{MASTER_TABLE_VERSION} = $sec1[9];
1822 117         178 $self->{LOCAL_TABLE_VERSION} = $sec1[10];
1823 117         248 $self->{YEAR} = $sec1[11];
1824 117         170 $self->{MONTH} = $sec1[12];
1825 117         175 $self->{DAY} = $sec1[13];
1826 117         169 $self->{HOUR} = $sec1[14];
1827 117         194 $self->{MINUTE} = $sec1[15];
1828 117         242 $self->{SECOND} = $sec1[16];
1829 117 50       275 $self->{LOCAL_USE} = ($sec1[0] > 22) ? $sec1[17] : undef;
1830             # In case previous message was edition 3 or lower
1831 117         259 foreach my $key (qw(DATA_SUBCATEGORY YEAR_OF_CENTURY)) {
1832 234         519 undef $self->{$key};
1833             }
1834             }
1835             $self->_spew(2, "BUFR edition: %d Optional section: %d Update sequence number: %d",
1836 155 100       369 $self->{BUFR_EDITION}, $self->{OPTIONAL_SECTION}, $self->{UPDATE_NUMBER}) if $Spew;
1837 155 100       322 $self->_spew(6, "%s", $self->dumpsection1()) if $Spew;
1838              
1839 155 100       432 $self->_validate_datetime() if ($Strict_checking);
1840              
1841             ## Decode Section 2 (Optional Section) if present ##
1842              
1843 155 100       316 $self->_spew(2, "Decoding section 2") if $Spew;
1844              
1845 155 50       342 if ($self->{OPTIONAL_SECTION}) {
1846 0         0 my @sec2 = unpack 'N', "\0" . $self->{BUFR_STREAM};
1847              
1848             # Check that stated length of section 2 makes sense
1849 0 0       0 _croak "Length of section 2 too small (< 4): $sec2[0]"
1850             if $sec2[0] < 4;
1851             _croak "Rest of BUFR message shorter (" . length($self->{BUFR_STREAM})
1852             . " bytes) than stated length of section 2 ($sec2[0] bytes)"
1853 0 0       0 if $sec2[0] > length($self->{BUFR_STREAM});
1854              
1855 0         0 push @sec2, substr $self->{BUFR_STREAM}, 4, $sec2[0]-4;
1856 0         0 $self->{SEC2_STREAM} = substr $self->{BUFR_STREAM}, 0, $sec2[0];
1857 0         0 $self->{BUFR_STREAM} = substr $self->{BUFR_STREAM}, $sec2[0];
1858 0         0 $self->{SEC2} = \@sec2;
1859 0 0       0 $self->_spew(2, "Length of section 2: %d", $sec2[0]) if $Spew;
1860             } else {
1861 155         294 $self->{SEC2} = undef;
1862 155         308 $self->{SEC2_STREAM} = undef;
1863             }
1864              
1865             ## Decode Section 3 (Data Description Section) ##
1866              
1867 155 100       298 $self->_spew(2, "Decoding section 3") if $Spew;
1868              
1869 155         599 my @sec3 = unpack 'NCnC', "\0".$self->{BUFR_STREAM};
1870              
1871             # Check that stated length of section 3 makes sense
1872 155 50       450 _croak "Length of section 3 too small (< 8): $sec3[0]"
1873             if $sec3[0] < 8;
1874             _croak "Rest of BUFR message shorter (" . length($self->{BUFR_STREAM})
1875             . " bytes) than stated length of section 3 ($sec3[0] bytes)"
1876 155 50       424 if $sec3[0] > length($self->{BUFR_STREAM});
1877              
1878 155         448 push @sec3, substr $self->{BUFR_STREAM},7,($sec3[0]-7)&0x0ffe; # $sec3[0]-7 will be reduced by one if odd integer,
1879             # so will not push last byte if length of sec3 is even,
1880             # which might happen for BUFR edition < 4 (padding byte)
1881 155         355 $self->{SEC3_STREAM} = substr $self->{BUFR_STREAM}, 0, $sec3[0];
1882 155         396 $self->{BUFR_STREAM} = substr $self->{BUFR_STREAM}, $sec3[0];
1883              
1884 155         347 $self->{SEC3} = \@sec3;
1885 155         295 $self->{NUM_SUBSETS} = $sec3[2];
1886 155         446 $self->{OBSERVED_DATA} = vec($sec3[3] & 0x80,0,1); # extract 1. bit
1887 155         317 $self->{COMPRESSED_DATA} = vec($sec3[3] & 0x40,1,1); # extract 2. bit
1888 155 100       342 $self->_spew(2, "Length of section 3: %d", $sec3[0]) if $Spew;
1889             $self->_spew(2, "Number of subsets: %d Observed data: %d Compressed data: %d",
1890 155 100       1549 $self->{NUM_SUBSETS}, $self->{OBSERVED_DATA}, $self->{COMPRESSED_DATA}) if $Spew;
1891             _complain("0 subsets in BUFR message")
1892 155 50 66     454 if ($Strict_checking and $self->{NUM_SUBSETS} == 0);
1893 155 50 66     470 _complain("Bits 3-8 in octet 7 in section 3 are not 0 (octet 7 = $sec3[3])")
1894             if ($Strict_checking and ($sec3[3] & 0x3f) != 0);
1895 155 100 66     688 if ($Spew == 6 || $Nodata) {
1896 67         223 my @unexpanded = _int2fxy(unpack 'n*', $self->{SEC3}[4]);
1897 67 50       264 $self->{DESCRIPTORS_UNEXPANDED} = @unexpanded ?
1898             join(' ', @unexpanded) : '';
1899 67         224 $self->_spew(6, "%s", $self->dumpsection3());
1900             }
1901              
1902             $self->{IS_FILTERED} = defined $self->{FILTER_CB}
1903 155 100       570 ? $self->{FILTER_CB}->(@{$self->{FILTER_ARGS}}) : 0;
  56         213  
1904 155 100 100     865 return if $self->{IS_FILTERED} || $Nodata;
1905              
1906             ## Decode Section 4 (Data Section) ##
1907              
1908 69 100       228 $self->_spew(2, "Decoding section 4") if $Spew;
1909              
1910 69         251 my $sec4_len = unpack 'N', "\0$self->{BUFR_STREAM}";
1911 69 100       182 $self->_spew(2, "Length of section 4: %d", $sec4_len) if $Spew;
1912              
1913             # Check that stated length of section 4 makes sense
1914 69 50       365 _croak "Length of section 4 too small (< 4): $sec4_len"
1915             if $sec4_len < 4;
1916             _croak "Rest of BUFR message (" . length($self->{BUFR_STREAM}) . " bytes)"
1917             . " shorter than stated length of section 4 ($sec4_len bytes)."
1918             . " Probably the BUFR message is truncated"
1919 69 100       342 if $sec4_len > length($self->{BUFR_STREAM});
1920              
1921 68         278 $self->{SEC4_STREAM} = substr $self->{BUFR_STREAM}, 0, $sec4_len;
1922 68         228 $self->{SEC4_RAWDATA} = substr $self->{BUFR_STREAM}, 4, $sec4_len-4;
1923 68         184 $self->{BUFR_STREAM} = substr $self->{BUFR_STREAM}, $sec4_len;
1924              
1925             ## Decode Section 5 (End Section) ##
1926              
1927 68 100       199 $self->_spew(2, "Decoding section 5") if $Spew;
1928              
1929             # Next 4 characters should be '7777' and these should be end of
1930             # message, but allow more characters (i.e. length of message in
1931             # section 0 has been set too big) if $Strict_checking not set
1932 68         121 my $str = $self->{BUFR_STREAM};
1933 68         129 my $len = length($str);
1934 68 100 66     548 if ($len > 4
      66        
1935             || ($len == 4 && substr($str,0,4) ne '7777')) {
1936             my $err_msg = "Section 5 is not '7777' but the $len"
1937             . " characters (in hex): "
1938 5         16 . join(' ', map {sprintf "0x%02X", $_} unpack('C*', $str));
  20         49  
1939 5 50 33     50 if ($len > 4 && substr($str,0,4) eq '7777') {
    50 33        
1940 0         0 _complain($err_msg);
1941             } elsif ($len == 4 && substr($str,0,4) ne '7777') {
1942 5         14 _croak($err_msg);
1943             }
1944             }
1945              
1946 63         228 return;
1947             }
1948              
1949             ## Read next BUFR message and decode. Set $self->{ERROR_IN_MESSAGE} if
1950             ## anything goes seriously wrong, so that sub next_observation can use
1951             ## this to skip to next message if user chooses to trap the call to
1952             ## next_observation in an eval and then calls next_observation again.
1953             sub _next_message {
1954 199     199   361 my $self = shift;
1955              
1956 199 100       466 $self->_spew(2, "Reading next BUFR message") if $Spew;
1957              
1958 199         408 $self->{ERROR_IN_MESSAGE} = 0;
1959 199         379 $self->{BAD_LENGTH} = 0;
1960              
1961 199         258 my $msg;
1962 199         326 eval {
1963             # Read BUFR message and decode section 0 (needed to get length
1964             # of message)
1965 199         645 $msg = $self->_read_message();
1966              
1967             # Unpack section 1-5
1968 198 100       839 $self->_decode_sections($msg) if $msg;
1969             };
1970 199 100       569 if ($@) {
1971 7         15 $self->{ERROR_IN_MESSAGE} = 1;
1972 7         14 $self->{CURRENT_MESSAGE}++;
1973 7         29 die $@; # Could use croak, but then 2 "at ... line ..." will
1974             # be printed to STDERR
1975             }
1976 192 100       403 if (!$msg) {
1977             # Nothing to decode. $self->{EOF} should have been set
1978 43 100       227 $self->_spew(2, "No more BUFR messages found") if $Spew;
1979 43         93 return;
1980             }
1981              
1982 149         279 $self->{CURRENT_MESSAGE}++;
1983              
1984 149 100 100     694 return if $Nodata || $self->{IS_FILTERED};
1985              
1986             # Load the relevant code tables
1987 63         108 my $table_version;
1988 63         177 eval { $table_version = $self->load_BDtables() };
  63         298  
1989 63 50       219 if ($@) {
1990 0         0 $self->{ERROR_IN_MESSAGE} = 1;
1991 0         0 die $@;
1992             }
1993              
1994             # Get the data descriptors and expand them
1995 63         528 my @unexpanded = _int2fxy(unpack 'n*', $self->{SEC3}[4]);
1996 63 50       240 _croak "No data description in section 3" if !defined $unexpanded[0];
1997             # Using master table because local tables couldn't be found is
1998             # risky, so catch missing descriptors here to be able to give
1999             # informative error messages
2000 63 100       253 $self->_check_descriptors(\@unexpanded) if $self->{LOCAL_TABLES_NOT_FOUND};
2001 63         341 $self->{DESCRIPTORS_UNEXPANDED} = join ' ', @unexpanded;
2002 63 100       202 $self->_spew(2, "Unexpanded data descriptors: %s", $self->{DESCRIPTORS_UNEXPANDED}) if $Spew;
2003              
2004 63 100       173 $self->_spew(2, "Expanding data descriptors") if $Spew;
2005 63         183 my $alias = "$table_version " . $self->{DESCRIPTORS_UNEXPANDED};
2006 63 100       208 if (exists $Descriptors_already_expanded{$alias}) {
2007 24         54 $self->{DESCRIPTORS_EXPANDED} = $Descriptors_already_expanded{$alias};
2008             } else {
2009 39         80 eval {
2010             $Descriptors_already_expanded{$alias} = $self->{DESCRIPTORS_EXPANDED}
2011 39         242 = join " ", _expand_descriptors($self->{D_TABLE}, @unexpanded);
2012             };
2013 39 50       182 if ($@) {
2014 0         0 $self->{ERROR_IN_MESSAGE} = 1;
2015 0         0 die $@;
2016             }
2017             }
2018              
2019             # Unpack data from bitstream
2020 63 100       169 $self->_spew(2, "Unpacking data") if $Spew;
2021 63         131 eval {
2022 63 100       207 if ($self->{COMPRESSED_DATA}) {
2023 23         115 $self->_decompress_bitstream();
2024             } else {
2025 40         231 $self->_decode_bitstream();
2026             }
2027             };
2028 63 100       192 if ($@) {
2029 7         13 $self->{ERROR_IN_MESSAGE} = 1;
2030 7         62 die $@;
2031             }
2032              
2033 56         299 return;
2034             }
2035              
2036             ## Check if all element and sequence descriptors given are found in
2037             ## B/D-tables (but skip check for those preceded by 206-operator)
2038             sub _check_descriptors {
2039 4     4   13 my ($self,$unexpanded) = @_;
2040              
2041 4         13 my $B_table = $self->{B_TABLE};
2042 4         9 my $D_table = $self->{D_TABLE};
2043 4         10 my $skip_next = 0;
2044 4         8 foreach my $id (@{$unexpanded}) {
  4         13  
2045             # Skip descriptors preceded by 206-operator
2046 30 100 66     202 if ($skip_next) {
    100 66        
    50 33        
2047 2         8 $skip_next = 0;
2048             } elsif (substr($id,0,3) eq '206') {
2049 2         6 $skip_next = 1;
2050             } elsif ( (substr($id,0,1) eq '0' && ! exists $B_table->{$id})
2051             || (substr($id,0,1) eq '3' && ! exists $D_table->{$id}) ) {
2052             my $version = ($BUFR_table{FORMAT} eq 'BUFRDC')
2053             ? substr($self->{LOCAL_TABLES_NOT_FOUND},1,-4)
2054 0 0       0 : $self->{LOCAL_TABLES_NOT_FOUND};
2055 0         0 undef $BUFR_table{"B$version"};
2056 0         0 undef $BUFR_table{"D$version"};
2057 0         0 $self->{ERROR_IN_MESSAGE} = 1;
2058 0         0 _croak("Data descriptor $id is not in master table."
2059             . " You need to get the local tables B/D$version.TXT");
2060             }
2061             }
2062 4         10 return;
2063             }
2064              
2065             ## Get next observation, i.e. next subset in current BUFR message or
2066             ## first subset in next message. Returns (reference to) data and
2067             ## descriptors, or empty list if either no observation is found (in
2068             ## which case $self->{EOF} should have been set) or if decoding of
2069             ## section 4 is not requested (in which case all of sections 0-3 have
2070             ## been decoded in next message).
2071             sub next_observation {
2072 234     234 0 603 my $self = shift;
2073              
2074 234 100       598 $self->_spew(2, "Fetching next observation") if $Spew;
2075              
2076             # If an error occurred during decoding of previous message, we
2077             # don't know if stated length in section 0 is to be trusted,
2078             # so rewind to next 'BUFR', or setting EOF if no such exists
2079 234 100       589 if ($self->{ERROR_IN_MESSAGE}) {
2080             # First rewind to right after 'BUFR' in previous (faulty)
2081             # message. We cannot go further if file/buffer starts as
2082             # 'BUFRBUFR'
2083 14         26 my $pos = $self->{START_POS} + 4;
2084 14 50       172 seek($self->{FILEHANDLE}, $pos, 0) if $self->{FILEHANDLE};
2085 14 50       69 $self->_spew(2, "Error in processing BUFR message (check STDERR for "
2086             . "details), rewinding to next 'BUFR'") if $Spew;
2087             # Prepare for (a possible) next call to _read_message by
2088             # advancing to next 'BUFR', not skipping a preceding ahl
2089             my $new_pos = $self->_find_next_BUFR($self->{FILEHANDLE},
2090 14         70 $self->{IN_BUFFER},$pos,'at_ahl');
2091 14 50       57 if ($self->{EOF}) {
2092 0 0       0 $self->_spew(2, "Last BUFR message (reached end of file)") if $Spew;
2093 0         0 return;
2094             } else {
2095 14         31 $self->{POS} = $new_pos;
2096             }
2097             }
2098              
2099             # Read next BUFR message
2100 234 100 100     1365 if ($self->{CURRENT_MESSAGE} == 0
      100        
2101             or $self->{ERROR_IN_MESSAGE}
2102             or $self->{CURRENT_SUBSET} >= $self->{NUM_SUBSETS}) {
2103              
2104 199         362 $self->{CURRENT_SUBSET} = 0;
2105             # The bit maps must be rebuilt for each message
2106 199         448 undef $self->{BITMAPS};
2107 199         367 undef $self->{BITMAP_OPERATORS};
2108 199         303 undef $self->{BITMAP_START};
2109 199         384 undef $self->{REUSE_BITMAP};
2110 199         325 $self->{NUM_BITMAPS} = 0;
2111 199         305 $self->{BACKWARD_DATA_REFERENCE} = 1;
2112             # Some more tidying after decoding of previous message might
2113             # be necessary
2114 199         342 $self->{NUM_CHANGE_OPERATORS} = 0;
2115 199         323 undef $self->{CHANGE_WIDTH};
2116 199         302 undef $self->{CHANGE_CCITTIA5_WIDTH};
2117 199         354 undef $self->{CHANGE_SCALE};
2118 199         329 undef $self->{CHANGE_REFERENCE_VALUE};
2119 199         337 undef $self->{NEW_REFVAL_OF};
2120 199         350 undef $self->{CHANGE_SRW};
2121 199         308 undef $self->{ADD_ASSOCIATED_FIELD};
2122 199         417 undef $self->{LOCAL_TABLES_NOT_FOUND};
2123 199         972 undef $self->{DATA};
2124 199         777 undef $self->{DESC};
2125             # Note that we should NOT undef metadata in section 1-3 here,
2126             # since if the next call (_next_message) finds no more
2127             # messages, we don't want to lose the metadata of the last
2128             # valid message extracted. sub join_subsets is based on this
2129             # assumption
2130              
2131 199         906 $self->_next_message();
2132 185 100       553 return if $self->{EOF};
2133              
2134 142 100 100     623 if ($Nodata || $self->{IS_FILTERED}) {
2135             # Make a simple check that section 4 and 5 are complete
2136 86 100       203 if ($self->{BAD_LENGTH}) {
2137             # We could have set $self->{ERROR_IN_MESSAGE} here and
2138             # let next_observation() take care of the rewinding.
2139             # But we don't want error messages to be displayed if
2140             # e.g. message is to be filtered
2141 34         112 $self->{POS} = $self->{START_POS} + 4;
2142 34 100       313 seek($self->{FILEHANDLE}, $self->{POS}, 0) if $self->{FILEHANDLE};
2143             $self->_spew(2, "Possibly truncated message found (last 4 bytes"
2144             . " are not '7777'), so rewinding to position %d",
2145 34 50       135 $self->{POS}) if $Spew;
2146             }
2147             # This will ensure next call to next_observation to read next message
2148 86         151 $self->{CURRENT_SUBSET} = $self->{NUM_SUBSETS};
2149 86         193 return;
2150             }
2151             }
2152              
2153 91         197 $self->{CURRENT_SUBSET}++;
2154              
2155             # Return references to data and descriptor arrays
2156 91 100       271 if ($self->{COMPRESSED_DATA}) {
2157             return ($self->{DATA}[$self->{CURRENT_SUBSET}],
2158 40         198 $self->{DESC});
2159             } else {
2160             return ($self->{DATA}[$self->{CURRENT_SUBSET}],
2161 51         323 $self->{DESC}[$self->{CURRENT_SUBSET}]);
2162             }
2163             }
2164              
2165             # Dumping contents of a subset (including section 0, 1 and 3 if this is
2166             # first subset) in a BUFR message, also displaying message number and
2167             # ahl (if found) and subset number
2168             sub dumpsections {
2169 0     0 0 0 my $self = shift;
2170 0         0 my $data = shift;
2171 0         0 my $descriptors = shift;
2172 0   0     0 my $options = shift || {};
2173              
2174 0   0     0 my $width = $options->{width} || 15;
2175 0 0       0 my $bitmap = exists $options->{bitmap} ? $options->{bitmap} : 1;
2176              
2177 0         0 my $current_subset_number = $self->get_current_subset_number();
2178 0         0 my $current_message_number = $self->get_current_message_number();
2179 0   0     0 my $current_ahl = $self->get_current_ahl() || '';
2180              
2181 0         0 my $txt;
2182 0 0       0 if ($current_subset_number == 1) {
2183 0         0 $txt = "\nMessage $current_message_number";
2184 0 0       0 $txt .= defined $current_ahl ? " $current_ahl\n" : "\n";
2185 0         0 $txt .= $self->dumpsection0() . $self->dumpsection1() . $self->dumpsection3();
2186             }
2187              
2188             # If this is last message and there is a BUFR formatting error
2189             # caught by user with eval, we might end up here with current
2190             # subset number 0 (and no section 4 to dump)
2191 0 0       0 if ($current_subset_number > 0) {
2192 0         0 $txt .= "\nSubset $current_subset_number\n";
2193 0 0       0 $txt .= $bitmap ? $self->dumpsection4_with_bitmaps($data,$descriptors,
2194             $current_subset_number,$width)
2195             : $self->dumpsection4($data,$descriptors,$width);
2196             }
2197              
2198 0         0 return $txt;
2199             }
2200              
2201             sub dumpsection0 {
2202 46     46 0 93 my $self = shift;
2203             _croak "BUFR object not properly initialized to call dumpsection0. "
2204 46 50       165 . "Did you forget to call next_observation()?" unless $self->{BUFR_LENGTH};
2205              
2206 46         214 my $txt = <<"EOT";
2207              
2208             Section 0:
2209             Length of BUFR message: $self->{BUFR_LENGTH}
2210             BUFR edition: $self->{BUFR_EDITION}
2211             EOT
2212 46         141 return $txt;
2213             }
2214              
2215             sub dumpsection1 {
2216 46     46 0 79 my $self = shift;
2217             _croak "BUFR object not properly initialized to call dumpsection1. "
2218 46 50       189 . "Did you forget to call next_observation()?" unless $self->{SEC1_STREAM};
2219              
2220 46         89 my $txt;
2221 46 100       154 if ($self->{BUFR_EDITION} < 4) {
2222 28         56 $txt = <<"EOT";
2223              
2224             Section 1:
2225 28         476 Length of section: @{[ length $self->{SEC1_STREAM} ]}
2226             BUFR master table: $self->{MASTER_TABLE}
2227             Originating subcentre: $self->{SUBCENTRE}
2228             Originating centre: $self->{CENTRE}
2229             Update sequence number: $self->{UPDATE_NUMBER}
2230             Optional section present: $self->{OPTIONAL_SECTION}
2231             Data category (table A): $self->{DATA_CATEGORY}
2232             Data subcategory: $self->{DATA_SUBCATEGORY}
2233             Master table version number: $self->{MASTER_TABLE_VERSION}
2234             Local table version number: $self->{LOCAL_TABLE_VERSION}
2235             Year of century: $self->{YEAR_OF_CENTURY}
2236             Month: $self->{MONTH}
2237             Day: $self->{DAY}
2238             Hour: $self->{HOUR}
2239             Minute: $self->{MINUTE}
2240             EOT
2241             } else {
2242 18         89 $txt = <<"EOT";
2243              
2244             Section 1:
2245 18         280 Length of section: @{[ length $self->{SEC1_STREAM} ]}
2246             BUFR master table: $self->{MASTER_TABLE}
2247             Originating centre: $self->{CENTRE}
2248             Originating subcentre: $self->{SUBCENTRE}
2249             Update sequence number: $self->{UPDATE_NUMBER}
2250             Optional section present: $self->{OPTIONAL_SECTION}
2251             Data category (table A): $self->{DATA_CATEGORY}
2252             International data subcategory: $self->{INT_DATA_SUBCATEGORY}
2253             Local data subcategory: $self->{LOC_DATA_SUBCATEGORY}
2254             Master table version number: $self->{MASTER_TABLE_VERSION}
2255             Local table version number: $self->{LOCAL_TABLE_VERSION}
2256             Year: $self->{YEAR}
2257             Month: $self->{MONTH}
2258             Day: $self->{DAY}
2259             Hour: $self->{HOUR}
2260             Minute: $self->{MINUTE}
2261             Second: $self->{SECOND}
2262             EOT
2263             }
2264             # Last part of section 1: "Reserved for local use by ADP centres"
2265             # is considered so uninteresting (and rare), that it is displayed
2266             # only if verbose >= 2, in a _spew statement. Note that for BUFR
2267             # edition < 4 there is always one byte here (to make an even
2268             # number of bytes in section 1).
2269 0         0 $self->_spew(2, "Reserved for local use: 0x@{[unpack('H*', $self->{LOCAL_USE})]}")
2270 46 50 66     363 if $self->{LOCAL_USE} and length $self->{LOCAL_USE} > 1;
2271              
2272 46         259 return $txt;
2273             }
2274              
2275             sub dumpsection2 {
2276 0     0 0 0 my $self = shift;
2277 0 0       0 return '' if not defined $self->{SEC2};
2278              
2279 0         0 my $sec2_code_ref = shift;
2280 0 0 0     0 _croak "dumpsection2: no code ref provided"
2281             unless defined $sec2_code_ref && ref($sec2_code_ref) eq 'CODE';
2282              
2283 0         0 my $txt = <<"EOT";
2284              
2285             Section 2:
2286 0         0 Length of section: @{[ length $self->{SEC2_STREAM} ]}
2287             EOT
2288              
2289 0         0 return $txt . $sec2_code_ref->($self->{SEC2_STREAM}) . "\n";
2290             }
2291              
2292             sub dumpsection3 {
2293 111     111 0 185 my $self = shift;
2294             _croak "BUFR object not properly initialized to call dumpsection3. "
2295 111 50       345 . "Did you forget to call next_observation()?" unless $self->{SEC3_STREAM};
2296 111   50     312 $self->{DESCRIPTORS_UNEXPANDED} ||= '';
2297              
2298 111         178 my $txt = <<"EOT";
2299              
2300             Section 3:
2301 111         720 Length of section: @{[ length $self->{SEC3_STREAM} ]}
2302             Number of data subsets: $self->{NUM_SUBSETS}
2303             Observed data: $self->{OBSERVED_DATA}
2304             Compressed data: $self->{COMPRESSED_DATA}
2305             Data descriptors unexpanded: $self->{DESCRIPTORS_UNEXPANDED}
2306             EOT
2307 111         336 return $txt;
2308             }
2309              
2310             sub dumpsection4 {
2311 64     64 0 112 my $self = shift;
2312 64         172 my $data = shift;
2313 64         138 my $descriptors = shift;
2314 64   50     201 my $width = shift || 15; # Optional argument
2315             # Since last (optional) argument to dumpsection() is an anonymous
2316             # hash, check that this is not mistakenly applied here also
2317 64 50 33     610 _croak "Last optional argument to dumpsection4 should be integer"
2318             if ref($width) || $width !~ /^\d+$/;
2319              
2320 64         186 my $txt = "\n";
2321 64         195 my $B_table = $self->{B_TABLE};
2322             # Add the artificial descriptor for associated field
2323 64         146 $B_table->{999999} = "ASSOCIATED FIELD\0NUMERIC";
2324 64   100     482 my $C_table = $self->{C_TABLE} || '';
2325 64         118 my $idx = 0;
2326 64         99 my $line_no = 0; # Precede each line with a line number, except
2327             # for replication descriptors and for operator
2328             # descriptors with no data value in section 4
2329             ID:
2330 64         92 foreach my $id (@{$descriptors}) {
  64         169  
2331 7341 100       11093 my $value = defined $data->[$idx] ? $data->[$idx] : 'missing';
2332 7341         7540 $idx++;
2333 7341         8454 my $f = substr($id, 0, 1);
2334 7341 100 100     19134 if ($f == 1) {
    100          
    100          
    100          
2335 16         44 $txt .= sprintf " %6d\n", $id;
2336 16         38 next ID;
2337             } elsif ($f == 2) {
2338 149 50       323 if ($id =~ /^205/) { # Character information operator
2339 0         0 $txt .= sprintf "%6d %06d %${width}.${width}s %s\n",
2340             ++$line_no, $id, $value, "CHARACTER INFORMATION";
2341 0         0 next ID;
2342             } else {
2343 149         316 my $operator_name = _get_operator_name($id);
2344 149 50       312 if ($operator_name) {
2345 149         483 $txt .= sprintf " %06d %${width}.${width}s %s\n",
2346             $id, "", $operator_name;
2347             }
2348 149         345 next ID;
2349             }
2350             } elsif ($f == 9 && $id != 999999) {
2351 4         21 $txt .= sprintf "%6d %06d %${width}.${width}s %s %06d\n",
2352             ++$line_no, $id, $value, 'NEW REFERENCE VALUE FOR', $id - 900000;
2353 4         7 next ID;
2354             } elsif ($id == 31031) { # This is the only data descriptor
2355             # where all bits set to one should
2356             # not be rendered as missing value
2357             # (for replication/repetition factors in
2358             # class 31 $value has been adjusted already)
2359 439 100       728 $value = 1 if $value eq 'missing';
2360             }
2361             _croak "Data descriptor $id is not present in BUFR table B"
2362 7172 50       11468 unless exists $B_table->{$id};
2363 7172         16889 my ($name, $unit, $bits) = (split /\0/, $B_table->{$id})[0,1,4];
2364             # Code or flag table number equals $id, so no need to display this in [unit]
2365 7172         9762 my $short_unit = $unit;
2366 7172         8682 my $unit_start = uc(substr($unit, 0, 4));
2367 7172 100       11948 if ($unit_start eq 'CODE') {
    100          
2368 1322         1482 $short_unit = 'CODE TABLE';
2369             } elsif ($unit_start eq 'FLAG') {
2370 626         892 $short_unit = 'FLAG TABLE';
2371             }
2372 7172         19196 $txt .= sprintf "%6d %06d %${width}.${width}s %s\n",
2373             ++$line_no, $id, $value, "$name [$short_unit]";
2374              
2375             # Check for illegal flag value
2376 7172 100 100     14092 if ($Strict_checking && $short_unit eq 'FLAG TABLE' && $bits > 1) {
      66        
2377 27 50 33     58 if ($value ne 'missing' && $value % 2) {
2378 0         0 $bits += 0; # get rid of spaces
2379 0         0 my $max_value = 2**$bits - 1;
2380 0         0 _complain("$id - $value: rightmost bit $bits is set indicating missing value"
2381             . " but then value should be $max_value");
2382             }
2383             }
2384              
2385             # Resolve flag and code table values if code table is loaded
2386             # (but don't bother about 031031 - too much uninformative output)
2387 7172 100 100     14011 if ($C_table && $id != 31031 && $value ne 'missing') {
      100        
2388 329         558 my $num_spaces = $width + 18;
2389 329         630 $txt .= _get_code_table_txt($id,$value,$unit,$B_table,$C_table,$num_spaces)
2390             }
2391             }
2392 64         1169 return $txt;
2393             }
2394              
2395             # Operators which should always be displayed in dumpsection4
2396             my %OPERATOR_NAME_A =
2397             ( 222000 => 'QUALITY INFORMATION FOLLOW',
2398             223000 => 'SUBSTITUTED VALUES FOLLOW',
2399             224000 => 'FIRST ORDER STATISTICS FOLLOW',
2400             225000 => 'DIFFERENCE STATISTICAL VALUES FOLLOW',
2401             232000 => 'REPLACE/RETAINED VALUES FOLLOW',
2402             235000 => 'CANCEL BACKWARD DATA REFERENCE',
2403             236000 => 'DEFINE DATA PRESENT BIT MAP',
2404             237000 => 'USE PREVIOUSLY DEFINED BIT MAP',
2405             );
2406             # Operators which should normally not be displayed in dumpsection4
2407             my %OPERATOR_NAME_B =
2408             ( 201000 => 'CANCEL CHANGE DATA WIDTH',
2409             202000 => 'CANCEL CHANGE SCALE',
2410             203000 => 'CANCEL CHANGE REFERENCE VALUES',
2411             207000 => 'CANCEL INCREASE SCALE, REFERENCE VALUE AND DATA WIDTH',
2412             208000 => 'CANCEL CHANGE WIDTH OF CCITT IA5 FIELD',
2413             203255 => 'STOP CHANGING REFERENCE VALUES',
2414             223255 => 'SUBSTITUTED VALUES MARKER OPERATOR',
2415             224255 => 'FIRST ORDER STATISTICAL VALUES MARKER OPERATOR',
2416             225255 => 'DIFFERENCE STATISTICAL STATISTICAL VALUES MARKER OPERATOR',
2417             232255 => 'REPLACED/RETAINED VALUES MARKER OPERATOR',
2418             237255 => 'CANCEL DEFINED DATA PRESENT BIT MAP',
2419             );
2420             # Operator classes which should normally not be displayed in dumpsection4
2421             my %OPERATOR_NAME_C =
2422             ( 201 => 'CHANGE DATA WIDTH',
2423             202 => 'CHANGE SCALE',
2424             203 => 'CHANGE REFERENCE VALUES',
2425             204 => 'ADD ASSOCIATED FIELD',
2426             # This one is displayed, treated specially (and named CHARACTER INFORMATION)
2427             ## 205 => 'SIGNIFY CHARACTER',
2428             206 => 'SIGNIFY DATA WIDTH FOR THE IMMEDIATELY FOLLOWING LOCAL DESCRIPTOR',
2429             207 => 'INCREASE SCALE, REFERENCE VALUE AND DATA WIDTH',
2430             208 => 'CHANGE WIDTH OF CCITT IA5 FIELD',
2431             221 => 'DATA NOT PRESENT',
2432             );
2433             sub _get_operator_name {
2434 149     149   332 my $id = shift;
2435 149         237 my $operator_name = '';
2436 149 100       468 if ($OPERATOR_NAME_A{$id}) {
    50          
2437 35         54 $operator_name = $OPERATOR_NAME_A{$id}
2438             } elsif ($Show_all_operators) {
2439 114 100       259 if ($OPERATOR_NAME_B{$id}) {
2440 36         64 $operator_name = $OPERATOR_NAME_B{$id}
2441             } else {
2442 78         147 my $fx = substr $id, 0, 3;
2443 78 50       188 if ($OPERATOR_NAME_C{$fx}) {
2444 78         143 $operator_name = $OPERATOR_NAME_C{$fx};
2445             }
2446             }
2447             }
2448 149         282 return $operator_name;
2449             }
2450              
2451             ## Display bit mapped values on same line as the original value. This
2452             ## offer a much shorter and easier to read dump of section 4 when bit
2453             ## maps has been used (i.e. for 222000 quality information, 223000
2454             ## substituted values, 224000 first order statistics, 225000
2455             ## difference statistics, 232000 replaced/retained values). '*******'
2456             ## is displayed if data is not present in bit map (bit set to 1 in
2457             ## 031031 or data not covered by the 031031 descriptors), 'missing' is
2458             ## displayed if value is missing. But note that we miss other
2459             ## descriptors like 001031 and 001032 if these come after 222000 etc
2460             ## with the current implementation. And there are more shortcomings,
2461             ## described in CAVEAT section in POD for bufrread.pl
2462             sub dumpsection4_with_bitmaps {
2463 7     7 0 25 my $self = shift;
2464 7         14 my $data = shift;
2465 7         11 my $descriptors = shift;
2466 7         12 my $isub = shift;
2467 7   50     21 my $width = shift || 15; # Optional argument
2468              
2469             # If no bit maps call the ordinary dumpsection4
2470 7 50       82 if (not defined $self->{BITMAPS}) {
2471 0         0 return $self->dumpsection4($data, $descriptors, $width);
2472             }
2473              
2474             # $Show_all_operators must be turned off for this sub to work correctly
2475 7 50       19 _croak "Cannot dump section 4 properly with bitmaps"
2476             . " when Show_all_operators is set" if $Show_all_operators;
2477              
2478             # The kind of bit maps (i.e. the operator descriptors) used in BUFR message
2479 7         10 my @bitmap_desc = @{ $self->{BITMAP_OPERATORS} };
  7         31  
2480              
2481 7         66 my @bitmap_array; # Will contain for each bit map a reference to a hash with
2482             # key: index (in data and descriptor arrays) for data value
2483             # value: index for bit mapped value
2484              
2485             # For compressed data all subsets use same bit map (we assume)
2486 7 100       21 $isub = 0 if $self->{COMPRESSED_DATA};
2487              
2488 7         13 my $txt = "\n";
2489 7         13 my $space = ' ';
2490 7         23 my $line = $space x (17 + $width);
2491 7         25 foreach my $bitmap_num (0..$#bitmap_desc) {
2492 16         36 $line .= " $bitmap_desc[$bitmap_num]";
2493             # Convert the sequence of ($data_idesc,$bitmapped_idesc) pairs into a hash
2494 16         22 my %hash = @{ $self->{BITMAPS}->[$bitmap_num + 1]->[$isub] };
  16         149  
2495 16         41 $bitmap_array[$bitmap_num] = \%hash;
2496             }
2497             # First make a line showing the operator descriptors using bit maps
2498 7         17 $txt .= "$line\n";
2499              
2500 7         20 my $B_table = $self->{B_TABLE};
2501             # Add the artificial descriptor for associated field
2502 7         20 $B_table->{999999} = "ASSOCIATED FIELD\0Numeric";
2503 7   50     36 my $C_table = $self->{C_TABLE} || '';
2504              
2505 7         12 my $idx = 0;
2506             # Loop over data descriptors
2507             ID:
2508 7         21 foreach my $id (@{$descriptors}) {
  7         18  
2509             # Stop printing when the bit map part starts
2510 641 50 33     1230 last ID if (substr($id,0,1) eq '2'
      66        
2511             and ($id =~ /^22[2-5]/ || $id =~ /^232/));
2512              
2513             # Get the data value
2514 634 100       1054 my $value = defined $data->[$idx] ? $data->[$idx] : 'missing';
2515             _croak "Data descriptor $id is not present in BUFR table B"
2516 634 50       1215 unless exists $B_table->{$id};
2517 634         1696 my ($name, $unit, $bits) = (split /\0/, $B_table->{$id})[0,1,4];
2518 634         2151 $line = sprintf "%6d %06d %${width}.${width}s ",
2519             $idx+1, $id, $value;
2520              
2521             # Then get the corresponding bit mapped values, using '*******'
2522             # if 'data not present' in bit map
2523 634         773 my $max_len = 7;
2524 634         1022 foreach my $bitmap_num (0..$#bitmap_desc) {
2525 1618         1759 my $val;
2526 1618 100       2470 if ($bitmap_array[$bitmap_num]->{$idx}) {
2527             # data marked as 'data present' in bitmap
2528 268         319 my $bitmapped_idesc = $bitmap_array[$bitmap_num]->{$idx};
2529 268 50       385 $val = defined $data->[$bitmapped_idesc]
2530             ? $data->[$bitmapped_idesc] : 'missing';
2531 268 100       567 $max_len = length($val) if length($val) > $max_len;
2532             } else {
2533 1350         1610 $val = '*******';
2534             }
2535             # If $max_len has been increased, this might not always
2536             # print very pretty, but at least there is no truncation
2537             # of digits in value
2538 1618         2865 $line .= sprintf " %${max_len}.${max_len}s", $val;
2539             }
2540             # Code or flag table number equals $id, so no need to display this in [unit]
2541 634         839 my $short_unit = $unit;
2542 634         936 my $unit_start = uc(substr($unit, 0, 4));
2543 634 100       1187 if ($unit_start eq 'CODE') {
    100          
2544 129         270 $short_unit = 'CODE TABLE';
2545             } elsif ($unit_start eq 'FLAG') {
2546 24         25 $short_unit = 'FLAG TABLE';
2547             }
2548 634         1002 $line .= sprintf " %s\n", "$name [$short_unit]";
2549 634         871 $txt .= $line;
2550              
2551             # Check for illegal flag value
2552 634 0 33     1163 if ($Strict_checking && $short_unit eq 'FLAG TABLE' && $bits > 1) {
      33        
2553 0 0 0     0 if ($value ne 'missing' and $value % 2) {
2554 0         0 my $max_value = 2**$bits - 1;
2555 0         0 $bits += 0; # get rid of spaces
2556 0         0 _complain("$id - $value: rightmost bit $bits is set indicating missing value"
2557             . " but then value should be $max_value");
2558             }
2559             }
2560              
2561             # Resolve flag and code table values if code table is loaded
2562 634 50 33     1056 if ($C_table && $value ne 'missing') {
2563 0         0 my $num_spaces = $width + 19 + 7*@bitmap_desc;
2564 0         0 $txt .= _get_code_table_txt($id,$value,$unit,$B_table,$C_table,$num_spaces)
2565             }
2566 634         908 $idx++;
2567             }
2568 7         182 return $txt;
2569             }
2570              
2571             ## Return the text found in flag or code tables for value $value of
2572             ## descriptor $id. The empty string is returned if $unit is neither
2573             ## CODE TABLE nor FLAG TABLE, or if $unit is CODE TABLE but for this
2574             ## $value there is no text in C table. Returns a "... does not exist!"
2575             ## message if flag/code table is not found. If $check_illegal is
2576             ## defined, an 'Illegal value' message is returned if $value is bigger
2577             ## than allowed or has highest bit set without having all other bits
2578             ## set.
2579             sub _get_code_table_txt {
2580 332     332   748 my ($id,$value,$unit,$B_table,$C_table,$num_spaces,$check_illegal) = @_;
2581              
2582 332         477 my $txt = '';
2583             # Need case insensitive matching, since local tables from at least
2584             # DWD use 'Code table', not 'CODE TABLE', in the ECMWF ecCodes
2585             # distribution
2586 332 100       1065 if ($unit =~ m/^CODE[ ]?TABLE/i) {
    100          
2587 32         71 my $code_table = sprintf "%06d", $id;
2588             return "Code table $code_table does not exist!\n"
2589 32 50       84 if ! exists $C_table->{$code_table};
2590 32 100       188 if ($C_table->{$code_table}{$value}) {
2591 31         133 my @lines = split "\n", $C_table->{$code_table}{$value};
2592 31         60 foreach (@lines) {
2593 39         143 $txt .= sprintf "%s %s\n", ' ' x ($num_spaces), lc $_;
2594             }
2595             }
2596             } elsif ($unit =~ m/^FLAG[ ]?TABLE/i) {
2597 39         105 my $flag_table = sprintf "%06d", $id;
2598             return "Flag table $flag_table does not exist!\n"
2599 39 50       118 if ! exists $C_table->{$flag_table};
2600              
2601 39         142 my $width = (split /\0/, $B_table->{$flag_table})[4];
2602 39         101 $width += 0; # Get rid of spaces
2603             # Cannot handle more than 32 bits flags with current method
2604 39 50       85 _croak "Unable to handle > 32 bits flag; $id has width $width"
2605             if $width > 32;
2606              
2607 39         98 my $max_value = 2**$width - 1;
2608              
2609 39 50 66     139 if (defined $check_illegal and $value > $max_value) {
    50          
2610 0         0 $txt = "Illegal value: $value is bigger than maximum allowed ($max_value)\n";
2611             } elsif ($value == $max_value) {
2612 0         0 $txt = sprintf "%s=> %s", ' ' x ($num_spaces), "bit $width set:"
2613             . sprintf "%s %s\n", ' ' x ($num_spaces), "missing value\n";
2614             } else {
2615             # Convert to bitstring and localize the 1 bits
2616 39         116 my $binary = pack "N", $value; # Packed as 32 bits in big-endian order
2617 39         147 my $bitstring = substr unpack('B*',$binary), 32-$width;
2618 39         141 for my $i (1..$width) {
2619 750 100       1557 if (substr($bitstring, $i-1, 1) == 1) {
2620 52         198 $txt .= sprintf "%s=> %s", ' ' x ($num_spaces),
2621             "bit $i set";
2622 52 100       157 if ($C_table->{$flag_table}{$i}) {
2623 50         135 my @lines = split "\n", $C_table->{$flag_table}{$i};
2624 50         146 $txt .= ': ' . lc (shift @lines) . "\n";
2625 50         146 foreach (@lines) {
2626 0         0 $txt .= sprintf "%s %s\n", ' ' x ($num_spaces), lc $_;
2627             }
2628             } else {
2629 2         6 $txt .= "\n";
2630             }
2631             }
2632             }
2633 39 100 100     193 if (defined $check_illegal and $txt =~ /bit $width set/) {
2634 1         7 $txt = "Illegal value ($value): bit $width is set indicating missing value,"
2635             . " but then value should be $max_value\n";
2636             }
2637             }
2638             }
2639 332         941 return $txt;
2640             }
2641              
2642             ## Convert from integer to descriptor
2643             sub _int2fxy {
2644 130     130   346 my @fxy = map {sprintf("%1d%02d%03d", ($_>>14)&0x3, ($_>>8)&0x3f, $_&0xff)} @_;
  2699         5748  
2645 130 100       1206 return @_ > 1 ? @fxy : $fxy[0];
2646             }
2647              
2648             ## Expand a list of descriptors using BUFR table D, also expanding
2649             ## simple replication but not delayed replication
2650             sub _expand_descriptors {
2651 1280     1280   1658 my $D_table = shift;
2652 1280         1518 my @expanded = ();
2653              
2654 1280         2344 for (my $di = 0; $di < @_; $di++) {
2655 7005         8847 my $descriptor = $_[$di];
2656 7005 50       14590 _croak "$descriptor is not a BUFR descriptor"
2657             if $descriptor !~ /^\d{6}$/;
2658 7005         9219 my $f = int substr($descriptor, 0, 1);
2659 7005 100       11121 if ($f == 1) {
    100          
2660 282         547 my $x = substr $descriptor, 1, 2; # Replicate next $x descriptors
2661 282         468 my $y = substr $descriptor, 3; # Number of replications
2662 282 100       662 if ($y > 0) {
2663             # Simple replication (replicate next x descriptors y times)
2664 173 50       538 _croak "Cannot expand: Not enough descriptors following "
2665             . "replication descriptor $descriptor (or there is "
2666             . "a problem in nesting of replication)" if $di+$x+1 > @_;
2667 173         282 my @r = ();
2668 173         1851 push @r, @_[($di+1)..($di+$x)] for (1..$y);
2669             # Recursively expand replicated descriptors $y times
2670 173         270 my @s = ();
2671 173 50       646 @s = _expand_descriptors($D_table, @r) if @r;
2672 173 100       429 if ($Show_replication) {
2673             # Adjust x since replicated descriptors might have been expanded
2674             # Unfortunately _spew is not available here to report the x>99 -> x=0 hack
2675 5 50       24 my $z = @s/$y > 99 ? 0 : @s/$y;
2676 5         31 substr($_[$di], 1, 2) = sprintf "%02d", $z;
2677 5         18 push @expanded, $_[$di];
2678             }
2679 173 50       803 push @expanded, @s if @s;
2680 173         513 $di += $x;
2681             } else {
2682             # Delayed replication. Next descriptor ought to be the
2683             # delayed descriptor replication (and data repetition)
2684             # factor, i.e. one of 0310(00|01|02|11|12), followed
2685             # by the x descriptors to be replicated
2686 109 100 66     558 if ($di+2 == @_ && $_[$di+1] =~ /^0310(00|01|02|11|12)$/) {
2687 1         8 _complain "Missing the $x descriptors which should follow"
2688             . " $descriptor $_[$di+1]";
2689 1         4 push @expanded, @_[$di,$di+1];
2690 1         3 last;
2691             }
2692 108 50       378 _croak "Cannot expand: Not enough descriptors following delayed"
2693             . " replication descriptor $descriptor (or there is "
2694             . "a problem in nesting of replication)" if $di+$x+1 > @_;
2695 108 50       534 _croak "Cannot expand: Delayed replication descriptor "
2696             . "$descriptor is not followed by one of "
2697             . "0310(00|01|02|11|12) but by $_[$di+1]"
2698             if $_[$di+1] !~ /^0310(00|01|02|11|12)$/;
2699 108         449 my @r = @_[($di+2)..($di+$x+1)];
2700             # Here we just expand the D descriptors in the
2701             # descriptors to be replicated. The final expansion
2702             # using delayed replication factor has to wait until
2703             # data part is decoded
2704 108         176 my @s = ();
2705 108 50       457 @s = _expand_descriptors($D_table, @r) if @r;
2706             # Must adjust x since replicated descriptors might have been expanded
2707 108         446 substr($_[$di], 1, 2) = sprintf "%02d", scalar @s;
2708 108         376 push @expanded, @_[$di,$di+1], @s;
2709 108         309 $di += 1+$x; # NOTE: 1 is added to $di on next iteration
2710             }
2711 281         724 next;
2712             } elsif ($f == 3) {
2713             _croak "No sequence descriptor $descriptor in BUFR table D"
2714 932 50       1858 if not exists $D_table->{$descriptor};
2715             # Expand recursively, if necessary
2716             push @expanded,
2717 932         5150 _expand_descriptors($D_table, split /\s/, $D_table->{$descriptor});
2718             } else { # f=0,2
2719 5791         10957 push @expanded, $descriptor;
2720             }
2721             }
2722              
2723 1280         8745 return @expanded;
2724             }
2725              
2726             ## Return a text string suitable for printing information about the given
2727             ## BUFR table descriptors
2728             ##
2729             ## $how = 'fully': Expand all D descriptors fully into B descriptors,
2730             ## with name, unit, scale, reference value and width (each on a
2731             ## numbered line, except for replication operators which are not
2732             ## numbered).
2733             ##
2734             ## $how = 'partially': Like 'fully, but expand D descriptors only once
2735             ## and ignore replication.
2736             ##
2737             ## $how = 'noexpand': Like 'partially', but do not expand D
2738             ## descriptors at all.
2739             ##
2740             ## $how = 'simply': Like 'partially', but list the descriptors on one
2741             ## single line with no extra information provided.
2742             sub resolve_descriptor {
2743 6     6 0 17 my $self = shift;
2744 6         18 my $how = shift;
2745 6         50 foreach (@_) {
2746 18 50       102 _croak("'$_' is not an integer argument to resolve_descriptor!")
2747             unless /^\d+$/;
2748             }
2749 6         34 my @desc = map { sprintf "%06d", $_ } @_;
  18         77  
2750              
2751 6         26 my @allowed_hows = qw( simply fully partially noexpand );
2752             _croak "First argument in resolve_descriptor must be one of"
2753             . " '@allowed_hows', is: '$how'"
2754 6 50       14 unless grep { $how eq $_ } @allowed_hows;
  24         66  
2755              
2756 6 50       29 if (! $self->{B_TABLE}) {
2757 0 0 0     0 if ($BUFR_table{FORMAT} eq 'ECCODES' && $self->{LOCAL_TABLES_NOT_FOUND}) {
2758 0         0 _croak "Local table " . $self->{LOCAL_TABLES_NOT_FOUND} . " couldn't be found,"
2759             . " or you might need to load WMO master table also?";
2760             } else {
2761 0         0 _croak "No B table is loaded - did you forget to call load_BDtables?";
2762             }
2763             }
2764 6         17 my $B_table = $self->{B_TABLE};
2765              
2766             # Some local tables are provided only for element descriptors, and
2767             # we might in fact not need the sequence descriptors for resolving
2768 6         11 my $D_table;
2769 6         13 my $need_Dtable = 0;
2770 6         18 foreach my $id (@desc) {
2771 18 100       39 if (substr($id,0,1) eq '3') {
2772 8         31 $need_Dtable = 1;
2773             }
2774             }
2775 6 50 66     65 if ($need_Dtable && ! $self->{D_TABLE}) {
2776 0 0 0     0 if ($BUFR_table{FORMAT} eq 'ECCODES' && $self->{LOCAL_TABLES_NOT_FOUND}) {
2777 0         0 _croak "Local table " . $self->{LOCAL_TABLES_NOT_FOUND} . " couldn't be found,"
2778             . " or you might need to load WMO master table also?";
2779             } else {
2780 0         0 _croak "No D table is loaded - did you forget to call load_BDtables?";
2781             }
2782             } else {
2783             # Could consider omitting this if $need_Dtable = 0 ...
2784 6         15 $D_table = $self->{D_TABLE};
2785             }
2786              
2787 6         16 my $txt = '';
2788              
2789 6 100 100     35 if ($how eq 'simply' or $how eq 'partially') {
2790 2         5 my @expanded;
2791 2         6 foreach my $id (@desc) {
2792 2         7 my $f = substr $id, 0, 1;
2793 2 50       8 if ($f == 3) {
2794             _croak "$id is not in table D, unable to expand"
2795 2 50       10 unless $D_table->{$id};
2796 2         32 push @expanded, split /\s/, $D_table->{$id};
2797             } else {
2798 0         0 push @expanded, $id;
2799             }
2800             }
2801 2 100       8 if ($how eq 'simply') {
2802 1         0 return $txt = "@expanded\n";
2803             } else {
2804 1         10 @desc = @expanded;
2805             }
2806             }
2807 5 100       28 if ($how eq 'fully') {
2808 3 50 33     25 if (@desc == 1 and $desc[0] =~ /^1/) {
2809             # This is simply a replication descriptor; do not try to expand
2810             } else {
2811 3         16 @desc = _expand_descriptors($D_table, @desc);
2812             }
2813             }
2814              
2815 5         12 my $count = 0;
2816 5         11 foreach my $id (@desc) {
2817 166 100       367 if ($id =~ /^[123]/) {
    50          
2818 21         86 $txt .= sprintf " %06d\n", $id;
2819             } elsif ($B_table->{$id}) {
2820 145         305 my ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$id};
2821 145         413 $txt .= sprintf "%3d %06d %s [%s] %d %d %d\n",
2822             ++$count,$id,$name,$unit,$scale,$refval,$width;
2823             } else {
2824 0         0 $txt .= sprintf "%3d %06d Not in table B\n",
2825             ++$count,$id;
2826             }
2827             }
2828 5         0 return $txt;
2829             }
2830              
2831             ## Return BUFR table B information for an element descriptor for the
2832             ## last table loaded, as an array of name, unit, scale, reference
2833             ## value and data width in bits. Returns false if the descriptor is
2834             ## not found or no data width is defined, or croaks if no table B has
2835             ## been loaded.
2836             sub element_descriptor {
2837 2     2 0 956 my $self = shift;
2838 2         7 my $desc = shift;
2839 2 50       24 _croak "Argument to element_descriptor must be an integer\n"
2840             unless $desc =~ /^\d+$/;
2841 2         16 $desc = sprintf "%06d", $desc;
2842 2 50       8 _croak "No BUFR B table loaded\n" unless defined $self->{B_TABLE};
2843 2 100       11 return unless defined $self->{B_TABLE}->{$desc};
2844             my ($name, $unit, $scale, $refval, $width)
2845 1         7 = split /\0/, $self->{B_TABLE}->{$desc};
2846 1 50 33     15 return unless defined $width && $width =~ /\d+$/;
2847 1         9 return ($name, $unit, $scale+0, $refval+0, $width+0);
2848             }
2849              
2850             ## Return BUFR table D information for a sequence descriptor for the
2851             ## last table loaded, as a space separated string of the descriptors
2852             ## in the direct (nonrecursive) lookup in table D. Returns false if
2853             ## the sequence descriptor is not found, or croaks if no table D has
2854             ## been loaded.
2855             sub sequence_descriptor {
2856 3     3 0 3147 my $self = shift;
2857 3         31 my $desc = shift;
2858 3 50       26 _croak "Argument to element_descriptor must be an integer\n"
2859             unless $desc =~ /^\d+$/;
2860 3 50       10 _croak "No BUFR D table loaded\n" unless defined $self->{D_TABLE};
2861 3 100       13 return unless defined $self->{D_TABLE}->{$desc};
2862 2 100       7 if (wantarray) {
2863 1         9 return split / /, $self->{D_TABLE}->{$desc};
2864             } else {
2865 1         5 return $self->{D_TABLE}->{$desc};
2866             }
2867             }
2868              
2869             ## Return a text string telling which bits are set and the meaning of
2870             ## the bits set when $value is interpreted as a flag value, also
2871             ## checking for illegal values. The empty string is returned if $value=0.
2872             sub resolve_flagvalue {
2873 3     3 0 4 my $self = shift;
2874 3         9 my ($value, $flag_table, $table, $num_leading_spaces) = @_;
2875 3 50       8 _croak "Flag value can't be negative!\n" if $value < 0;
2876 3   50     21 $num_leading_spaces ||= 0; # Default value
2877              
2878 3         13 $self->load_Ctable($table);
2879 3         10 my $C_table = $self->{C_TABLE};
2880              
2881             # Number of bits used for the flag is hard to extract from C
2882             # table; it is much easier to obtain from B table
2883 3         19 $self->load_BDtables($table);
2884 3         12 my $B_table = $self->{B_TABLE};
2885              
2886 3         8 my $unit = 'FLAG TABLE';
2887 3         31 return _get_code_table_txt($flag_table,$value,$unit,
2888             $B_table,$C_table,$num_leading_spaces,'check_illegal');
2889             }
2890              
2891             ## Return the contents of code table $code_table, or empty string if
2892             ## code table is not found
2893             sub dump_codetable {
2894 4     4 0 964 my $self = shift;
2895 4         15 my ($code_table, $table) = @_;
2896 4 50       27 _croak("code_table '$code_table' is not a (positive) integer in dump_codetable()")
2897             unless $code_table =~ /^\d+$/;
2898 4         20 $code_table = sprintf "%06d", $code_table;
2899              
2900 4         22 $self->load_Ctable($table);
2901 4         9 my $C_table = $self->{C_TABLE};
2902              
2903 4 100       29 return '' unless $C_table->{$code_table};
2904              
2905 3         4 my $dump;
2906 3         14 foreach my $value (sort {$a <=> $b} keys %{ $C_table->{$code_table} }) {
  123         115  
  3         25  
2907 42         43 my $txt = $C_table->{$code_table}{$value};
2908 42         39 chomp $txt;
2909 42         48 $txt =~ s/\n/\n /g;
2910 42         64 $dump .= sprintf "%3d -> %s\n", $value, $txt;
2911             }
2912 3         0 return $dump;
2913             }
2914              
2915             ## Decode bitstream (data part of section 4) while working through the
2916             ## (expanded) descriptors in section 3. The final data and
2917             ## corresponding descriptors are put in $self->{DATA} and
2918             ## $self->{DESC} (indexed by subset number)
2919             sub _decode_bitstream {
2920 40     40   73 my $self = shift;
2921 40         113 $self->{CODING} = 'DECODE';
2922 40         117 my $bitstream = $self->{SEC4_RAWDATA} . "\0\0\0\0";
2923 40         99 my $maxpos = 8*length($self->{SEC4_RAWDATA});
2924 40         63 my $pos = 0;
2925 40         367 my @operators;
2926             my $ref_values_ref; # Hash ref to reference values with descriptors as keys;
2927             # to be implemented later (not used yet)
2928 40         0 my @subset_data; # Will contain data values for subset 1,2...
2929 40         0 my @subset_desc; # Will contain the set of descriptors for subset 1,2...
2930             # expanded to be in one to one correspondance with the data
2931 40         0 my $repeat_X; # Set to number of descriptors to be repeated if
2932             # delayed descriptor and data repetition factor is
2933             # in effect
2934 40         0 my $repeat_factor; # Set to number of times descriptors (and data)
2935             # are to be repeated if delayed descriptor and
2936             # data repetition factor is in effect
2937 40         0 my @repeat_desc; # The descriptors to be repeated
2938 40         0 my @repeat_data; # The data to be repeated
2939 40         85 my $B_table = $self->{B_TABLE};
2940              
2941             # Has to fully expand @desc for each subset in turn, as delayed
2942             # replication factors might be different for each subset,
2943             # resulting in different full expansions. During the expansion the
2944             # effect of operator descriptors are taken into account, causing
2945             # most of them to be eliminated (unless $Show_all_operators is
2946             # set), so that @desc and the equivalent $subset_desc[$isub] ends
2947             # up being in one to one correspondence with the data values in
2948             # $subset_data[$isub] (the operators included having data value
2949             # '')
2950 40         153 S_LOOP: foreach my $isub (1..$self->{NUM_SUBSETS}) {
2951 53 100       154 $self->_spew(2, "Decoding subset number %d", $isub) if $Spew;
2952              
2953             # Bit maps might vary from subset to subset, so must be rebuilt
2954 53         116 undef $self->{BITMAP_OPERATORS};
2955 53         105 undef $self->{BITMAP_START};
2956 53         107 undef $self->{REUSE_BITMAP};
2957 53         101 $self->{NUM_BITMAPS} = 0;
2958 53         104 $self->{BACKWARD_DATA_REFERENCE} = 1;
2959 53         115 $self->{NUM_CHANGE_OPERATORS} = 0;
2960              
2961 53         2007 my @desc = split /\s/, $self->{DESCRIPTORS_EXPANDED};
2962              
2963             # Note: @desc as well as $idesc may be changed during this loop,
2964             # so we cannot use a foreach loop instead
2965 53         224 D_LOOP: for (my $idesc = 0; $idesc < @desc; $idesc++) {
2966 7666         9419 my $id = $desc[$idesc];
2967 7666         9151 my $f = substr($id,0,1);
2968 7666         10786 my $x = substr($id,1,2)+0;
2969 7666         9632 my $y = substr($id,3,3)+0;
2970              
2971 7666 100       13569 if ($f == 1) {
    100          
2972 76 100       153 if ($Show_replication) {
2973 2         4 push @{$subset_desc[$isub]}, $id;
  2         4  
2974 2         4 push @{$subset_data[$isub]}, '';
  2         5  
2975 2 50 33     32 $self->_spew(4, "X=0 in $id for F=1, might have been > 99 in expansion")
2976             if $Spew && $x == 0;
2977             }
2978 76 100       174 next D_LOOP if $y > 0; # Nothing more to do for normal replication
2979              
2980 74 50       163 if ($x == 0) {
2981 0         0 _complain("Nonsensical replication of zero descriptors ($id)");
2982 0         0 $idesc++;
2983 0         0 next D_LOOP;
2984             }
2985              
2986 74         161 $_ = $desc[$idesc+1];
2987             _croak "$id Erroneous replication factor"
2988 74 50 33     540 unless /^0310(00|01|02|11|12)/ && exists $B_table->{$_};
2989              
2990 74         270 my $width = (split /\0/, $B_table->{$_})[-1];
2991 74         259 my $factor = bitstream2dec($bitstream, $pos, $width);
2992 74         87 $pos += $width;
2993             # Delayed descriptor replication factors (and
2994             # associated fields) are the only values in section 4
2995             # where all bits being 1 is not to be interpreted as a
2996             # missing value
2997 74 50       166 if (not defined $factor) {
2998 0         0 $factor = 2**$width - 1;
2999             }
3000 74 100       162 if ($Spew) {
3001 8 50 33     35 if ($_ eq '031011' || $_ eq '031012') {
3002 0         0 $self->_spew(4, "$_ Delayed repetition factor: %s", $factor);
3003             } else {
3004 8         20 $self->_spew(4, "$_ Delayed replication factor: %s", $factor);
3005             }
3006             }
3007             # Include the delayed replication in descriptor and data list
3008 74         178 splice @desc, $idesc++, 0, $_;
3009 74         89 push @{$subset_desc[$isub]}, $_;
  74         142  
3010 74         103 push @{$subset_data[$isub]}, $factor;
  74         154  
3011              
3012 74 50 33     386 if ($_ eq '031011' || $_ eq '031012') {
3013             # For delayed repetition, descriptor *and* data are
3014             # to be repeated
3015 0         0 $repeat_X = $x;
3016 0         0 $repeat_factor = $factor;
3017             }
3018 74         129 my @r = ();
3019 74         1629 push @r, @desc[($idesc+2)..($idesc+$x+1)] while $factor--;
3020 74         831 splice @desc, $idesc, 2+$x, @r;
3021              
3022 74 50       169 if ($repeat_factor) {
3023             # Skip to the last set to be repeated, which will
3024             # then be included $repeat_factor times
3025 0         0 $idesc += $x * ($repeat_factor - 1);
3026 0 0       0 $self->_spew(4, "Delayed repetition ($id $_ -> @r)") if $Spew;
3027             } else {
3028 74 100       197 $self->_spew(4, "Delayed replication ($id $_ -> @r)") if $Spew;
3029             }
3030 74 100       152 if ($idesc < @desc) {
3031 69         297 redo D_LOOP;
3032             } else {
3033 5         63 last D_LOOP; # Might happen if delayed factor is 0
3034             }
3035              
3036             } elsif ($f == 2) {
3037 114         196 my $flow;
3038             my $bm_idesc;
3039 114         383 ($pos, $flow, $bm_idesc, @operators)
3040             = $self->_apply_operator_descriptor($id, $x, $y, $pos, $isub,
3041             $desc[$idesc+1], @operators);
3042 114 100       401 if ($flow eq 'redo_bitmap') {
    50          
    100          
3043             # Data value is associated with the descriptor
3044             # defined by bit map. Remember original and new
3045             # index in descriptor array for the bit mapped
3046             # values ('dr' = data reference)
3047 22         24 my $dr_idesc;
3048 22 50       36 if (!defined $bm_idesc) {
    50          
3049 0         0 $dr_idesc = shift @{$self->{REUSE_BITMAP}->[$isub]};
  0         0  
3050             } elsif (!$Show_all_operators) {
3051 22         28 $dr_idesc = $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
3052             + $bm_idesc;
3053             } else {
3054 0         0 $dr_idesc = $self->{BITMAP_START}[$self->{NUM_BITMAPS}];
3055             # Skip operator descriptors
3056 0         0 while ($bm_idesc-- > 0) {
3057 0         0 $dr_idesc++;
3058 0         0 $dr_idesc++ while ($desc[$dr_idesc] >= 200000);
3059             }
3060             }
3061 22         21 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[$isub] },
  22         48  
3062             $dr_idesc, $idesc;
3063 22 50       47 if ($Show_all_operators) {
3064 0         0 push @{$subset_desc[$isub]}, $id;
  0         0  
3065 0         0 push @{$subset_data[$isub]}, '';
  0         0  
3066             }
3067 22         26 $desc[$idesc] = $desc[$dr_idesc];
3068 22         31 redo D_LOOP;
3069             } elsif ($flow eq 'signify_character') {
3070 0         0 push @{$subset_desc[$isub]}, $id;
  0         0  
3071             # Extract ASCII string
3072 0         0 my $value = bitstream2ascii($bitstream, $pos, $y);
3073 0         0 $pos += 8*$y;
3074             # Trim string, also removing nulls
3075 0         0 $value = _trim($value, $id);
3076 0         0 push @{$subset_data[$isub]}, $value;
  0         0  
3077 0         0 next D_LOOP;
3078             } elsif ($flow eq 'no_value') {
3079             # Some operator descriptors ought to be included
3080             # in expanded descriptors even though they have no
3081             # corresponding data value, because they contain
3082             # valuable information to be displayed in
3083             # dumpsection4 (e.g. 222000 'Quality information follows')
3084 41         55 push @{$subset_desc[$isub]}, $id;
  41         94  
3085 41         55 push @{$subset_data[$isub]}, '';
  41         72  
3086 41         204 next D_LOOP;
3087             }
3088              
3089 51 100       129 if ($Show_all_operators) {
3090 8         13 push @{$subset_desc[$isub]}, $id;
  8         21  
3091 8         10 push @{$subset_data[$isub]}, '';
  8         18  
3092             } else {
3093             # Remove operator descriptor from @desc
3094 43         94 splice @desc, $idesc--, 1;
3095             }
3096              
3097 51 100       257 next D_LOOP if $flow eq 'next';
3098 9 100       29 last D_LOOP if $flow eq 'last';
3099 8 50       17 if ($flow eq 'skip') {
3100 8         15 $idesc++;
3101 8         37 next D_LOOP;
3102             }
3103             }
3104              
3105 7476 100       11560 if ($self->{CHANGE_REFERENCE_VALUE}) {
3106             # The data descriptor is to be associated with a new
3107             # reference value, which is fetched from data stream
3108 2 50       6 _croak "Change reference operator 203Y is not followed by element"
3109             . " descriptor, but $id" if $f > 0;
3110 2         6 my $num_bits = $self->{CHANGE_REFERENCE_VALUE};
3111 2         11 my $new_refval = bitstream2dec($bitstream, $pos, $num_bits);
3112 2         3 $pos += $num_bits;
3113             # Negative value if most significant bit is set (one's complement)
3114 2 50       48 $new_refval = $new_refval & (1<<$num_bits-1)
3115             ? -($new_refval & ((1<<$num_bits-1)-1))
3116             : $new_refval;
3117 2 0       6 $self->_spew(4, "$id * Change reference value: ".
    50          
3118             ($new_refval > 0 ? "+" : "")."$new_refval") if $Spew;
3119 2         11 $self->{NEW_REFVAL_OF}{$id}{$isub} = $new_refval;
3120             # Identify new reference values by setting f=9
3121 2         5 push @{$subset_desc[$isub]}, $id + 900000;
  2         8  
3122 2         5 push @{$subset_data[$isub]}, $new_refval;
  2         5  
3123 2         7 next D_LOOP;
3124             }
3125              
3126             # If operator 204$y 'Add associated field is in effect',
3127             # each data value is preceded by $y bits which should be
3128             # decoded separately. We choose to provide a descriptor
3129             # 999999 in this case (like the ECMWF BUFRDC software)
3130 7474 100 100     12249 if ($self->{ADD_ASSOCIATED_FIELD} and $id ne '031021') {
3131             # First extract associated field
3132 2         3 my $width = $self->{ADD_ASSOCIATED_FIELD};
3133 2         4 my $value = bitstream2dec($bitstream, $pos, $width);
3134             # All bits set to 1 for associated field is NOT
3135             # interpreted as missing value
3136 2 100       4 $value = 2**$width - 1 if ! defined $value;
3137 2         2 $pos += $width;
3138 2         2 push @{$subset_desc[$isub]}, 999999;
  2         3  
3139 2         3 push @{$subset_data[$isub]}, $value;
  2         2  
3140 2 50       4 $self->_spew(4, "Added associated field: %s", $value) if $Spew;
3141             }
3142              
3143             # We now have a "real" data descriptor
3144 7474         7771 push @{$subset_desc[$isub]}, $id;
  7474         11432  
3145              
3146             # For quality information, if this relates to a bit map we
3147             # need to store index of the data ($data_idesc) for which
3148             # the quality information applies, as well as the new
3149             # index ($idesc) in the descriptor array for the bit
3150             # mapped values
3151 7474 100 100     13732 if (substr($id,0,3) eq '033'
      66        
3152             && defined $self->{BITMAP_OPERATORS}
3153             && $self->{BITMAP_OPERATORS}->[-1] eq '222000') {
3154 306 100       512 if (defined $self->{REUSE_BITMAP}) {
3155 120         154 my $data_idesc = shift @{ $self->{REUSE_BITMAP}->[$isub] };
  120         239  
3156 120 50       282 _croak "$id: Not enough quality values provided"
3157             if not defined $data_idesc;
3158 120         168 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[$isub] },
  120         371  
3159             $data_idesc, $idesc;
3160             } else {
3161 186         205 my $data_idesc = shift @{ $self->{CURRENT_BITMAP} };
  186         266  
3162 186 50       316 _croak "$id: Not enough quality values provided"
3163             if not defined $data_idesc;
3164 186         403 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[$isub] },
3165 186         193 $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
3166             + $data_idesc, $idesc;
3167             }
3168             }
3169              
3170             # Find the relevant entry in BUFR table B
3171             _croak "Data descriptor $id is not present in BUFR table B"
3172 7474 50       12360 unless exists $B_table->{$id};
3173 7474         20919 my ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$id};
3174 7474 100       12610 $self->_spew(3, "%6s %-20s %s", $id, $unit, $name) if $Spew;
3175              
3176             # Override Table B values if Data Description Operators are in effect
3177 7474 100       11447 if ($self->{NUM_CHANGE_OPERATORS} > 0) {
3178 126 100 100     327 if ($unit ne 'CCITTIA5' && $unit !~ /^(CODE|FLAG)/) {
    100 66        
3179 102 50       139 if (defined $self->{CHANGE_SRW}) {
3180 0         0 $scale += $self->{CHANGE_SRW};
3181 0         0 $width += int((10*$self->{CHANGE_SRW}+2)/3);
3182 0         0 $refval *= 10*$self->{CHANGE_SRW};
3183             } else {
3184 102 100       183 $scale += $self->{CHANGE_SCALE} if defined $self->{CHANGE_SCALE};
3185 102 100       168 $width += $self->{CHANGE_WIDTH} if defined $self->{CHANGE_WIDTH};
3186             }
3187             } elsif ($unit eq 'CCITTIA5' && defined $self->{CHANGE_CCITTIA5_WIDTH}) {
3188             $width = $self->{CHANGE_CCITTIA5_WIDTH}
3189 1         2 }
3190             # To prevent autovivification (see perldoc -f exists) we
3191             # need this laborious test for defined
3192             $refval = $self->{NEW_REFVAL_OF}{$id}{$isub} if defined $self->{NEW_REFVAL_OF}{$id}
3193 126 100 66     189 && defined $self->{NEW_REFVAL_OF}{$id}{$isub};
3194             # Difference statistical values use different width and reference value
3195 126 50       165 if ($self->{DIFFERENCE_STATISTICAL_VALUE}) {
3196 0         0 $width += 1;
3197 0         0 $refval = -2**$width;
3198 0         0 undef $self->{DIFFERENCE_STATISTICAL_VALUE};
3199 0         0 $self->{NUM_CHANGE_OPERATORS}--;
3200             }
3201             }
3202 7474 50       11534 _croak "$id Data width <= 0" if $width <= 0;
3203              
3204 7474         7715 my $value;
3205 7474 100       9729 if ($unit eq 'CCITTIA5') {
3206             # Extract ASCII string
3207 55 50       177 _croak "Width for unit CCITTIA5 must be integer bytes\n"
3208             . "is $width bits for descriptor $id" if $width % 8;
3209 55         244 $value = bitstream2ascii($bitstream, $pos, $width/8);
3210 55 50       122 $self->_spew(3, " %s", defined $value ? $value : 'missing') if $Spew;
    100          
3211             # Trim string, also removing nulls
3212 55         198 $value = _trim($value, $id);
3213             } else {
3214 7419         11859 $value = bitstream2dec($bitstream, $pos, $width);
3215 7419 100       10618 if (defined $value) {
3216             # Compute and format decoded value
3217 4519         12882 ($scale) = $scale =~ /(-?\d+)/; # untaint
3218 4519 100       12653 $value = $scale <= 0 ? ($value + $refval)/10**$scale
3219             : sprintf "%.${scale}f", ($value + $refval)/10**$scale;
3220             }
3221 7419 100       10938 $self->_spew(3, " %s", defined $value ? $value : 'missing') if $Spew;
    100          
3222             }
3223 7474         7872 $pos += $width;
3224 7474         7650 push @{$subset_data[$isub]}, $value;
  7474         11605  
3225             # $value = undef if missing value
3226              
3227 7474 50       10854 if ($repeat_X) {
3228             # Delayed repetition factor (030011/030012) is in
3229             # effect, so descriptors and data are to be repeated
3230 0         0 push @repeat_desc, $id;
3231 0         0 push @repeat_data, $value;
3232 0 0       0 if (--$repeat_X == 0) {
3233             # Store $repeat_factor repetitions of data and descriptors
3234             # (one repetition has already been included)
3235 0         0 while (--$repeat_factor) {
3236 0         0 push @{$subset_desc[$isub]}, @repeat_desc;
  0         0  
3237 0         0 push @{$subset_data[$isub]}, @repeat_data;
  0         0  
3238             }
3239 0         0 @repeat_desc = ();
3240 0         0 @repeat_data = ();
3241             }
3242             }
3243              
3244 7474 100 66     24962 if ($id eq '031031' and $self->{BUILD_BITMAP}) {
    100 100        
3245             # Store the index of expanded descriptors if data is
3246             # marked as present in data present indicator: 0 is
3247             # 'present', 1 (undef value) is 'not present'. E.g.
3248             # bitmap = 1100110 => (2,3,6) is stored in $self->{CURRENT_BITMAP}
3249 949 100       1618 if (defined $value) {
3250 208         260 push @{$self->{CURRENT_BITMAP}}, $self->{BITMAP_INDEX};
  208         426  
3251             }
3252 949         1314 $self->{BITMAP_INDEX}++;
3253 949 100       1876 if ($self->{BACKWARD_DATA_REFERENCE} == $self->{NUM_BITMAPS}) {
3254 789         1109 my $numb = $self->{NUM_BITMAPS};
3255 789 100       1552 if (!defined $self->{BITMAP_START}[$numb]) {
3256             # Look up the element descriptor immediately
3257             # preceding the bitmap operator
3258 8         39 my $i = $idesc;
3259 8   66     93 $i-- while ($desc[$i] ne $self->{BITMAP_OPERATORS}->[-1]
3260             && $i >=0);
3261 8   66     72 $i-- while ($desc[$i] > 100000 && $i >=0);
3262 8 50       51 _croak "No element descriptor preceding bitmap" if $i < 0;
3263 8         40 $self->{BITMAP_START}[$numb] = $i;
3264             } else {
3265 781         1161 $self->{BITMAP_START}[$numb]--;
3266             _croak "Bitmap too big"
3267 781 50       2319 if $self->{BITMAP_START}[$numb] < 0;
3268             }
3269             }
3270             } elsif ($self->{BUILD_BITMAP} and $self->{BITMAP_INDEX} > 0) {
3271             # We have finished building the bit map
3272 9         46 $self->{BUILD_BITMAP} = 0;
3273 9         20 $self->{BITMAP_INDEX} = 0;
3274 9 100       35 if ($self->{BACKWARD_DATA_REFERENCE} != $self->{NUM_BITMAPS}) {
3275             $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
3276 1         4 = $self->{BITMAP_START}[$self->{BACKWARD_DATA_REFERENCE}];
3277             }
3278             }
3279             } # End D_LOOP
3280             } # END S_LOOP
3281              
3282             # Check that length of section 4 corresponds to what expected from section 3
3283 40         248 $self->_check_section4_length($pos,$maxpos);
3284              
3285 40         142 $self->{DATA} = \@subset_data;
3286 40         82 $self->{DESC} = \@subset_desc;
3287 40         141 return;
3288             }
3289              
3290             ## Decode bitstream (data part of section 4 encoded using BUFR
3291             ## compression) while working through the (expanded) descriptors in
3292             ## section 3. The final data and corresponding descriptors are put in
3293             ## $self->{DATA} and $self->{DESC} (the data indexed by subset number)
3294             sub _decompress_bitstream {
3295 23     23   55 my $self = shift;
3296 23         67 $self->{CODING} = 'DECODE';
3297 23         98 my $bitstream = $self->{SEC4_RAWDATA}."\0\0\0\0";
3298 23         48 my $nsubsets = $self->{NUM_SUBSETS};
3299 23         53 my $B_table = $self->{B_TABLE};
3300 23         48 my $maxpos = 8*length($self->{SEC4_RAWDATA});
3301 23         44 my $pos = 0;
3302 23         190 my @operators;
3303             my @subset_data; # Will contain data values for subset 1,2...,
3304             # i.e. $subset[$i] is a reference to an array
3305             # containing the data values for subset $i
3306 23         0 my @desc_exp; # Will contain the set of descriptors for one
3307             # subset, expanded to be in one to one
3308             # correspondance with the data, i.e. element
3309             # descriptors only
3310 23         0 my $repeat_X; # Set to number of descriptors to be repeated if
3311             # delayed descriptor and data repetition factor is
3312             # in effect. Will be decremented while (repeated)
3313             # data sets are extracted
3314 23         0 my $repeat_XX; # Like $repeat_X, but will not be decremented
3315 23         0 my $repeat_factor; # Set to number of times descriptors (and data)
3316             # are to be repeated if delayed descriptor and
3317             # data repetition factor is in effect
3318 23         0 my @repeat_desc; # The descriptors to be repeated
3319 23         0 my @repeat_data; # The data to be repeated (reference to an array
3320             # containing the data values for subset $i)
3321              
3322 23 100       72 _complain("Compression set in section 1 for one subset message")
3323             if $nsubsets == 1;
3324              
3325 18         98 $#subset_data = $nsubsets;
3326              
3327 18         682 my @desc = split /\s/, $self->{DESCRIPTORS_EXPANDED};
3328             # This will be further expanded to be in one to one correspondance
3329             # with the data, taking replication and table C operators into account
3330              
3331             # All subsets in a compressed BUFR message must have exactly the same
3332             # fully expanded section 3, i.e. all replications factors must be the same
3333             # in all subsets. So, as opposed to noncompressed messages, it is enough
3334             # to run through the set of descriptors once.
3335 18         73 D_LOOP: for (my $idesc = 0; $idesc < @desc; $idesc++) {
3336 1769         2200 my $id = $desc[$idesc];
3337 1769         2160 my $f = substr($id,0,1);
3338 1769         2638 my $x = substr($id,1,2)+0;
3339 1769         2128 my $y = substr($id,3,3)+0;
3340              
3341 1769 100       3112 if ($f == 1) {
    100          
3342 33 100       95 if ($Show_replication) {
3343 7         14 push @desc_exp, $id;
3344 7         26 foreach my $isub (1..$nsubsets) {
3345 14         20 push @{$subset_data[$isub]}, '';
  14         63  
3346             }
3347 7 50 33     21 $self->_spew(4, "X=0 in $id for F=1, might have been > 99 in expansion")
3348             if $Spew && $x == 0;
3349             }
3350 33 100       97 next D_LOOP if $y > 0; # Nothing more to do for normal replication
3351              
3352 29 50       103 if ($x == 0) {
3353 0         0 _complain("Nonsensical replication of zero descriptors ($id)");
3354 0         0 $idesc++;
3355 0         0 next D_LOOP;
3356             }
3357              
3358 29         63 $_ = $desc[$idesc+1];
3359             _croak "$id Erroneous replication factor"
3360 29 50 33     200 unless /^0310(00|01|02|11|12)/ && exists $B_table->{$_};
3361              
3362 29         117 my $width = (split /\0/, $B_table->{$_})[-1];
3363 29         89 my $factor = bitstream2dec($bitstream, $pos, $width);
3364 29         42 $pos += $width + 6; # 6 bits for the bit count (which we
3365             # skip because we know it has to be 0
3366             # for delayed replication)
3367             # Delayed descriptor replication factors (and associated
3368             # fields) are the only values in section 4 where all bits
3369             # being 1 is not interpreted as a missing value
3370 29 50       67 if (not defined $factor) {
3371 0         0 $factor = 2**$width - 1;
3372             }
3373             # Include the delayed replication in descriptor and data list
3374 29         60 push @desc_exp, $_;
3375 29         84 splice @desc, $idesc++, 0, $_;
3376 29         50 foreach my $isub (1..$nsubsets) {
3377 74         78 push @{$subset_data[$isub]}, $factor;
  74         178  
3378             }
3379              
3380 29 50 33     137 if ($_ eq '031011' || $_ eq '031012') {
3381             # For delayed repetition, descriptor *and* data is
3382             # to be repeated
3383 0         0 $repeat_X = $repeat_XX = $x;
3384 0         0 $repeat_factor = $factor;
3385 0 0       0 $self->_spew(4, "$_ Delayed repetition factor: $factor") if $Spew;
3386             } else {
3387 29 50       70 $self->_spew(4, "$_ Delayed replication factor: $factor") if $Spew;
3388             }
3389 29         46 my @r = ();
3390 29         242 push @r, @desc[($idesc+2)..($idesc+$x+1)] while $factor--;
3391 29         123 splice @desc, $idesc, 2+$x, @r;
3392 29 50       70 if ($Spew) {
3393 0 0       0 if ($repeat_factor) {
3394 0         0 $self->_spew(4, "$_ Delayed repetition ($id $_ -> @r)");
3395             } else {
3396 0         0 $self->_spew(4, "$_ Delayed replication ($id $_ -> @r)");
3397             }
3398             }
3399              
3400 29 50       73 if ($idesc < @desc) {
3401 29         85 redo D_LOOP;
3402             } else {
3403 0         0 last D_LOOP; # Might happen if delayed factor is 0
3404             }
3405              
3406             } elsif ($f == 2) {
3407 80         136 my $flow;
3408             my $bm_idesc;
3409 80         267 ($pos, $flow, $bm_idesc, @operators)
3410             = $self->_apply_operator_descriptor($id, $x, $y, $pos, 0,
3411             $desc[$idesc+1], @operators);
3412 80 100       326 if ($flow eq 'redo_bitmap') {
    50          
    100          
3413             # Data value is associated with the descriptor
3414             # defined by bit map. Remember original and new
3415             # index in descriptor array for the bit mapped
3416             # values ('dr' = data reference)
3417 3         6 my $dr_idesc;
3418 3 50       23 if (!defined $bm_idesc) {
    100          
3419 0         0 $dr_idesc = shift @{ $self->{REUSE_BITMAP}->[0] };
  0         0  
3420             } elsif (!$Show_all_operators) {
3421 2         5 $dr_idesc = $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
3422             + $bm_idesc;
3423             } else {
3424 1         2 $dr_idesc = $self->{BITMAP_START}[$self->{NUM_BITMAPS}];
3425             # Skip operator descriptors
3426 1         4 while ($bm_idesc-- > 0) {
3427 2         3 $dr_idesc++;
3428 2         6 $dr_idesc++ while ($desc[$dr_idesc] >= 200000);
3429             }
3430             }
3431 3         20 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[0] },
  3         30  
3432             $dr_idesc, $idesc;
3433 3 100       59 if ($Show_all_operators) {
3434 1         2 push @desc_exp, $id;
3435 1         3 foreach my $isub (1..$nsubsets) {
3436 2         1 push @{$subset_data[$isub]}, '';
  2         5  
3437             }
3438             }
3439 3         6 $desc[$idesc] = $desc[$dr_idesc];
3440 3         8 redo D_LOOP;
3441             } elsif ($flow eq 'signify_character') {
3442 0         0 push @desc_exp, $id;
3443 0         0 $pos = $self->_extract_compressed_value($id, $idesc, $pos, $bitstream,
3444             $nsubsets, \@subset_data);
3445 0         0 next D_LOOP;
3446             } elsif ($flow eq 'no_value') {
3447             # Some operator descriptors ought to be included
3448             # in expanded descriptors even though they have no
3449             # corresponding data value, because they contain
3450             # valuable information to be displayed in
3451             # dumpsection4 (e.g. 222000 'Quality information follows')
3452 12         22 push @desc_exp, $id;
3453 12         36 foreach my $isub (1..$nsubsets) {
3454 24         36 push @{$subset_data[$isub]}, '';
  24         38  
3455             }
3456 12         30 next D_LOOP;
3457             }
3458              
3459 65 100       146 if ($Show_all_operators) {
3460 52         105 push @desc_exp, $id;
3461 52         116 foreach my $isub (1..$nsubsets) {
3462 104         143 push @{$subset_data[$isub]}, '';
  104         273  
3463             }
3464             } else {
3465             # Remove operator descriptor from @desc
3466 13         24 splice @desc, $idesc--, 1;
3467             }
3468              
3469 65 50       278 next D_LOOP if $flow eq 'next';
3470 0 0       0 last D_LOOP if $flow eq 'last';
3471 0 0       0 if ($flow eq 'skip') {
3472 0         0 $idesc++;
3473 0         0 next D_LOOP;
3474             }
3475             }
3476              
3477 1656 100       2527 if ($self->{CHANGE_REFERENCE_VALUE}) {
3478             # The data descriptor is to be associated with a new
3479             # reference value, which is fetched from data stream
3480 1 50       4 _croak "Change reference operator 203Y is not followed by element"
3481             . " descriptor, but $id" if $f > 0;
3482 1         2 my $num_bits = $self->{CHANGE_REFERENCE_VALUE};
3483 1         7 my $new_refval = bitstream2dec($bitstream, $pos, $num_bits);
3484 1         2 $pos += $num_bits + 6;
3485             # Negative value if most significant bit is set (one's complement)
3486 1 50       7 $new_refval = $new_refval & (1<<$num_bits-1)
3487             ? -($new_refval & ((1<<$num_bits-1)-1))
3488             : $new_refval;
3489 1 0       3 $self->_spew(4, "$id * Change reference value: ".
    50          
3490             ($new_refval > 0 ? "+" : "")."$new_refval") if $Spew;
3491 1         5 $self->{NEW_REFVAL_OF}{$id} = $new_refval;
3492             # Identify new reference values by setting f=9
3493 1         4 push @desc_exp, $id + 900000;
3494 1         5 foreach my $isub (1..$nsubsets) {
3495 2         3 push @{$subset_data[$isub]}, $new_refval;
  2         7  
3496             }
3497 1         4 next D_LOOP;
3498             }
3499              
3500             # If operator 204$y 'Add associated field is in effect',
3501             # each data value is preceded by $y bits which should be
3502             # decoded separately. We choose to provide a descriptor
3503             # 999999 in this case (like the ECMWF BUFRDC software)
3504 1655 100 100     2694 if ($self->{ADD_ASSOCIATED_FIELD} and $id ne '031021') {
3505             # First extract associated field
3506 9         17 push @desc_exp, 999999;
3507 9         29 $pos = $self->_extract_compressed_value(999999, $idesc, $pos, $bitstream,
3508             $nsubsets, \@subset_data);
3509             }
3510              
3511             # We now have a "real" data descriptor, so add it to the descriptor list
3512 1655         2388 push @desc_exp, $id;
3513              
3514 1655         2877 $pos = $self->_extract_compressed_value($id, $idesc, $pos, $bitstream,
3515             $nsubsets, \@subset_data, \@desc);
3516 1653 50       3692 if ($repeat_X) {
3517             # Delayed repetition factor (030011/030012) is in
3518             # effect, so descriptors and data are to be repeated
3519 0         0 push @repeat_desc, $id;
3520 0         0 foreach my $isub (1..$nsubsets) {
3521 0         0 push @{$repeat_data[$isub]}, $subset_data[$isub]->[-1];
  0         0  
3522             }
3523 0 0       0 if (--$repeat_X == 0) {
3524             # Store $repeat_factor repetitions of data and descriptors
3525             # (one repetition has already been included)
3526 0         0 while (--$repeat_factor) {
3527 0         0 push @desc_exp, @repeat_desc;
3528 0         0 foreach my $isub (1..$nsubsets) {
3529 0         0 push @{$subset_data[$isub]}, @{$repeat_data[$isub]};
  0         0  
  0         0  
3530             }
3531 0         0 $idesc += $repeat_XX;
3532             }
3533 0         0 @repeat_desc = ();
3534 0         0 @repeat_data = ();
3535 0         0 $repeat_XX = 0;
3536             }
3537             }
3538             }
3539              
3540             # Check that length of section 4 corresponds to what expected from section 3
3541 16         87 $self->_check_section4_length($pos,$maxpos);
3542              
3543 16         35 $self->{DATA} = \@subset_data;
3544 16         45 $self->{DESC} = \@desc_exp;
3545 16         154 return;
3546             }
3547              
3548             ## Extract the data values for descriptor $id (with index $idesc in
3549             ## the final expanded descriptor array) for each subset, into
3550             ## $subset_data_ref->[$isub], $isub = 1...$nsubsets (number of
3551             ## subsets). Extraction starts at position $pos in $bitstream.
3552             sub _extract_compressed_value {
3553 1664     1664   1801 my $self = shift;
3554 1664         2771 my ($id, $idesc, $pos, $bitstream, $nsubsets, $subset_data_ref, $desc_ref) = @_;
3555 1664         1975 my $B_table = $self->{B_TABLE};
3556              
3557             # For quality information, if this relates to a bit map we
3558             # need to store index of the data ($data_idesc) for which
3559             # the quality information applies, as well as the new
3560             # index ($idesc) in the descriptor array for the bit
3561             # mapped values
3562 1664 100 100     3021 if (substr($id,0,3) eq '033'
      66        
3563             && defined $self->{BITMAP_OPERATORS}
3564             && $self->{BITMAP_OPERATORS}->[-1] eq '222000') {
3565 30 100       34 if (defined $self->{REUSE_BITMAP}) {
3566 20         16 my $data_idesc = shift @{ $self->{REUSE_BITMAP}->[0] };
  20         24  
3567 20 50       28 _croak "$id: Not enough quality values provided"
3568             if not defined $data_idesc;
3569 20         15 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[0] },
  20         33  
3570             $data_idesc, $idesc;
3571             } else {
3572 10         8 my $data_idesc = shift @{ $self->{CURRENT_BITMAP} };
  10         12  
3573 10 50       12 _croak "$id: Not enough quality values provided"
3574             if not defined $data_idesc;
3575 10         28 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[0] },
3576 10         10 $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
3577             + $data_idesc, $idesc;
3578             }
3579             }
3580              
3581             # Find the relevant entry in BUFR table B
3582 1664         1930 my ($name,$unit,$scale,$refval,$width);
3583 1664 100       3158 if ($id == 999999) {
    50          
3584 9         17 $name = 'ASSOCIATED FIELD';
3585 9         12 $unit = 'NUMERIC';
3586 9         18 $scale = 0;
3587 9         12 $refval = 0;
3588 9         17 $width = $self->{ADD_ASSOCIATED_FIELD};
3589             } elsif ($id =~ /^205(\d\d\d)/) { # Signify character
3590 0         0 $name = 'CHARACTER INFORMATION';
3591 0         0 $unit = 'CCITTIA5';
3592 0         0 $scale = 0;
3593 0         0 $refval = 0;
3594 0         0 $width = 8*$1;
3595             } else {
3596             _croak "Data descriptor $id is not present in BUFR table B"
3597 1655 50       2865 if not exists $B_table->{$id};
3598 1655         5156 ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$id};
3599              
3600             # Override Table B values if Data Description Operators are in effect
3601 1655 100       3049 if ($self->{NUM_CHANGE_OPERATORS} > 0) {
3602 22 50 33     98 if ($unit ne 'CCITTIA5' && $unit !~ /^(CODE|FLAG)/) {
    0 0        
3603 22 50       66 if (defined $self->{CHANGE_SRW}) {
3604 0         0 $scale += $self->{CHANGE_SRW};
3605 0         0 $width += int((10*$self->{CHANGE_SRW}+2)/3);
3606 0         0 $refval *= 10*$self->{CHANGE_SRW};
3607             } else {
3608 22 100       89 $scale += $self->{CHANGE_SCALE} if defined $self->{CHANGE_SCALE};
3609 22 100       72 $width += $self->{CHANGE_WIDTH} if defined $self->{CHANGE_WIDTH};
3610             }
3611             } elsif ($unit eq 'CCITTIA5' && defined $self->{CHANGE_CCITTIA5_WIDTH}) {
3612             $width = $self->{CHANGE_CCITTIA5_WIDTH}
3613 0         0 }
3614 22 100       66 $refval = $self->{NEW_REFVAL_OF}{$id} if defined $self->{NEW_REFVAL_OF}{$id};
3615             # Difference statistical values use different width and reference value
3616 22 50       80 if ($self->{DIFFERENCE_STATISTICAL_VALUE}) {
3617 0         0 $width += 1;
3618 0         0 $refval = -2**$width;
3619 0         0 undef $self->{DIFFERENCE_STATISTICAL_VALUE};
3620 0         0 $self->{NUM_CHANGE_OPERATORS}--;
3621             }
3622             }
3623             }
3624 1664 50       2344 $self->_spew(3, "%6s %-20s %s", $id, $unit, $name) if $Spew;
3625 1664 50       2581 _croak "$id Data width <= 0" if $width <= 0;
3626              
3627 1664 100       2203 if ($unit eq 'CCITTIA5') {
3628             # Extract ASCII string ('minimum value')
3629 12 50       36 _croak "Width for unit CCITTIA5 must be integer bytes\n"
3630             . "is $width bits for descriptor $id" if $width % 8;
3631 12         68 my $minval = bitstream2ascii($bitstream, $pos, $width/8);
3632 12 50       29 if ($Spew) {
3633 0 0       0 if ($minval eq "\0" x ($width/8)) {
3634 0         0 $self->_spew(5, " Local reference value has all bits zero");
3635             } else {
3636 0         0 $self->_spew(5, " Local reference value: %s", $minval);
3637             }
3638             }
3639 12         33 $pos += $width;
3640             # Extract number of bytes for next subsets
3641 12         34 my $deltabytes = bitstream2dec($bitstream, $pos, 6);
3642 12 50       36 $self->_spew(5, " Increment width (bytes): %d", $deltabytes) if $Spew;
3643 12         23 $pos += 6;
3644 12 100 66     96 if ($deltabytes && defined $minval) {
3645             # Extract compressed data for all subsets. According
3646             # to 94.6.3 (2) (i) in FM 94 BUFR, the first value for
3647             # character data shall be set to all bits zero
3648 7         45 my $nbytes = $width/8;
3649 7 100 66     63 _complain("Local reference value for compressed CCITTIA5 data "
3650             . "hasn't all bits set to zero, but is '$minval'")
3651             if $Strict_checking and $minval ne "\0" x $nbytes;
3652 5         11 my $incr_values;
3653 5         15 foreach my $isub (1..$nsubsets) {
3654 23         62 my $string = bitstream2ascii($bitstream, $pos, $deltabytes);
3655 23 50       52 if ($Spew) {
3656 0 0       0 $incr_values .= defined $string ? "$string," : ',';
3657             }
3658             # Trim string, also removing nulls
3659 23         47 $string = _trim($string, $id);
3660 23         28 push @{$subset_data_ref->[$isub]}, $string;
  23         40  
3661 23         46 $pos += 8*$deltabytes;
3662             }
3663 5 50       22 if ($Spew) {
3664 0         0 chop $incr_values;
3665 0         0 $self->_spew(5, " Increment values: %s", $incr_values);
3666             }
3667             } else {
3668             # If min value is defined => All subsets set to min value
3669             # If min value is undefined => Data in all subsets are undefined
3670 5 50       13 my $value = defined $minval ? $minval : undef;
3671             # Trim string, also removing nulls
3672 5         15 $value = _trim($value, $id);
3673 5         10 foreach my $isub (1..$nsubsets) {
3674 5         6 push @{$subset_data_ref->[$isub]}, $value;
  5         8  
3675             }
3676 5         8 $pos += $nsubsets*8*$deltabytes;
3677             }
3678             $self->_spew(3, " %s", join ',',
3679 10 0       29 map { defined($subset_data_ref->[$_][-1]) ?
  0 50       0  
3680             $subset_data_ref->[$_][-1] : 'missing'} 1..$nsubsets) if $Spew;
3681             } else {
3682             # Extract minimum value
3683 1652         2805 my $minval = bitstream2dec($bitstream, $pos, $width);
3684 1652 100       2761 $minval += $refval if defined $minval;
3685 1652         1804 $pos += $width;
3686 1652 50 33     2430 $self->_spew(5, " Local reference value: %d", $minval) if $Spew && defined $minval;
3687              
3688             # Extract number of bits for next subsets
3689 1652         2297 my $deltabits = bitstream2dec($bitstream, $pos, 6);
3690 1652         1751 $pos += 6;
3691 1652 50       2172 $self->_spew(5, " Increment width (bits): %d", $deltabits) if $Spew;
3692              
3693 1652 100 66     3053 if ($deltabits && defined $minval) {
3694             # Extract compressed data for all subsets
3695 372         419 my $incr_values;
3696 372         644 foreach my $isub (1..$nsubsets) {
3697 1611         2459 my $value = bitstream2dec($bitstream, $pos, $deltabits);
3698 1611 50 100     4080 _complain("value " . ($value + $minval) . " in subset $isub for "
      66        
3699             . "$id too big to be encoded without compression")
3700             if ($Strict_checking && defined $value &&
3701             ($value + $minval) > 2**$width);
3702 1611 0       2326 $incr_values .= defined $value ? "$value," : ',' if $Spew;
    50          
3703 1611 100       2426 if (defined $value) {
3704             # Compute and format decoded value
3705 1310         3616 ($scale) = $scale =~ /(-?\d+)/; # untaint
3706 1310 100       4114 $value = $scale <= 0 ? ($value + $minval)/10**$scale
3707             : sprintf "%.${scale}f", ($value + $minval)/10**$scale;
3708             }
3709             # All bits set to 1 for associated field is NOT
3710             # interpreted as missing value
3711 1611 50 33     2670 if ($id == 999999 and ! defined $value) {
3712 0         0 $value = 2**$width - 1;
3713             }
3714 1611         1722 push @{$subset_data_ref->[$isub]}, $value;
  1611         2908  
3715 1611         2280 $pos += $deltabits;
3716             }
3717 372 50       714 if ($Spew) {
3718 0         0 chop $incr_values;
3719 0         0 $self->_spew(5, " Increment values: %s", $incr_values);
3720             }
3721             } else {
3722             # If minimum value is defined => All subsets set to minimum value
3723             # If minimum value is undefined => Data in all subsets are undefined
3724 1280         1287 my $value;
3725 1280 100       1659 if (defined $minval) {
3726             # Compute and format decoded value
3727 572         1933 ($scale) = $scale =~ /(-?\d+)/; # untaint
3728 572 100       1365 $value = $scale <= 0 ? $minval/10**$scale
3729             : sprintf "%.${scale}f", $minval/10**$scale;
3730             }
3731             # Exception: all bits set to 1 for associated field is NOT
3732             # interpreted as missing value
3733 1280 100 66     1920 if ($id == 999999 and ! defined $value) {
3734 9         24 $value = 2**$width - 1;
3735             }
3736 1280         1779 foreach my $isub (1..$nsubsets) {
3737 3101         3071 push @{$subset_data_ref->[$isub]}, $value;
  3101         6624  
3738             }
3739 1280 50       2046 $pos += $nsubsets*$deltabits if defined $deltabits;
3740             }
3741              
3742             # Bit maps need special treatment. We are only able to
3743             # handle those where all subsets have exactly the same
3744             # bit map with the present method.
3745 1652 100 66     4629 if ($id eq '031031' and $self->{BUILD_BITMAP}) {
    100 100        
3746 115 50       134 _croak "$id: Unable to handle bit maps which differ between subsets"
3747             . " in compressed data" if $deltabits;
3748             # Store the index of expanded descriptors if data is
3749             # marked as present in data present indicator: 0 is
3750             # 'present', 1 (undef value) is 'not present'
3751 115 100       130 if (defined $minval) {
3752 13         15 push @{$self->{CURRENT_BITMAP}}, $self->{BITMAP_INDEX};
  13         26  
3753             }
3754 115         122 $self->{BITMAP_INDEX}++;
3755 115 50       152 if ($self->{BACKWARD_DATA_REFERENCE} == $self->{NUM_BITMAPS}) {
3756 115         112 my $numb = $self->{NUM_BITMAPS};
3757 115 100       149 if (!defined $self->{BITMAP_START}[$numb]) {
3758             # Look up the element descriptor immediately
3759             # preceding the bitmap operator
3760 4         7 my $i = $idesc;
3761 4   66     53 $i-- while ($desc_ref->[$i] ne $self->{BITMAP_OPERATORS}->[-1]
3762             && $i >=0);
3763 4   66     55 $i-- while ($desc_ref->[$i] > 100000 && $i >=0);
3764 4 50       14 _croak "No element descriptor preceding bitmap" if $i < 0;
3765 4         9 $self->{BITMAP_START}[$numb] = $i;
3766             } else {
3767 111 100       116 if ($Show_all_operators) {
3768 3         5 my $i = $self->{BITMAP_START}[$numb] - 1;
3769 3   66     13 $i-- while ($desc_ref->[$i] > 100000 && $i >=0);
3770 3         14 $self->{BITMAP_START}[$numb] = $i;
3771             } else {
3772 108         92 $self->{BITMAP_START}[$numb]--;
3773             }
3774             _croak "Bitmap too big"
3775 111 50       176 if $self->{BITMAP_START}[$numb] < 0;
3776             }
3777             }
3778             } elsif ($self->{BUILD_BITMAP} and $self->{BITMAP_INDEX} > 0) {
3779             # We have finished building the bit map
3780 4         9 $self->{BUILD_BITMAP} = 0;
3781 4         8 $self->{BITMAP_INDEX} = 0;
3782 4 50       40 if ($self->{BACKWARD_DATA_REFERENCE} != $self->{NUM_BITMAPS}) {
3783             $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
3784 0         0 = $self->{BITMAP_START}[$self->{BACKWARD_DATA_REFERENCE}];
3785             }
3786             }
3787             $self->_spew(3, " %s", join ' ',
3788 1652 0       2345 map { defined($subset_data_ref->[$_][-1]) ?
  0 50       0  
3789             $subset_data_ref->[$_][-1] : 'missing'} 1..$nsubsets) if $Spew;
3790             }
3791 1662         2467 return $pos;
3792             }
3793              
3794             ## Takes a text $decoded_message as argument and returns BUFR messages
3795             ## which would give the same output as $decoded_message when running
3796             ## dumpsection0(), dumpsection1(), dumpsection3() and dumpsection4() in
3797             ## turn on each of the reencoded BUFR messages
3798             sub reencode_message {
3799 18     18 0 138 my $self = shift;
3800 18         112 my $decoded_message = shift;
3801 18   50     72 my $width = shift || 15; # Optional argument
3802             # Data values usually start at column 31, but if a $width
3803             # different from 15 was used in dumpsection4 you should use the
3804             # same value here
3805              
3806 18         1581 my @lines = split /\n/, $decoded_message;
3807 18         54 my $bufr_messages = '';
3808 18         36 my $i = 0;
3809              
3810 18         105 MESSAGE: while ($i < @lines) {
3811             # Some tidying after decoding of previous message might be
3812             # necessary
3813 36         321 $self->{NUM_CHANGE_OPERATORS} = 0;
3814 36         119 undef $self->{CHANGE_WIDTH};
3815 36         92 undef $self->{CHANGE_CCITTIA5_WIDTH};
3816 36         101 undef $self->{CHANGE_SCALE};
3817 36         91 undef $self->{CHANGE_REFERENCE_VALUE};
3818 36         104 undef $self->{NEW_REFVAL_OF};
3819 36         83 undef $self->{CHANGE_SRW};
3820 36         72 undef $self->{ADD_ASSOCIATED_FIELD};
3821 36         104 undef $self->{BITMAPS};
3822 36         77 undef $self->{BITMAP_OPERATORS};
3823 36         69 undef $self->{REUSE_BITMAP};
3824 36         70 $self->{NUM_BITMAPS} = 0;
3825             # $self->{LOCAL_USE} is always set for BUFR edition < 4 in _encode_sec1
3826 36         78 undef $self->{LOCAL_USE};
3827              
3828             # Extract section 0 info
3829 36   100     423 $i++ while $lines[$i] !~ /^Section 0/ and $i < @lines-1;
3830 36 100       143 last MESSAGE if $i >= @lines-1; # Not containing any decoded BUFR message
3831 18         43 $i++; # Skip length of BUFR message
3832 18         159 ($self->{BUFR_EDITION}) = $lines[++$i]
3833             =~ /BUFR edition:\s+(\d+)/;
3834             _croak "BUFR edition number not provided or is not a number"
3835 18 50       115 unless defined $self->{BUFR_EDITION};
3836              
3837             # Extract section 1 info
3838 18         115 $i++ while $lines[$i] !~ /^Section 1/;
3839 18 50       64 _croak "reencode_message: Don't find decoded section 1" if $i >= @lines;
3840 18         33 $i++; # Skip length of section 1
3841 18 100       115 if ($self->{BUFR_EDITION} < 4 ) {
    50          
3842 11         73 ($self->{MASTER_TABLE}) = $lines[++$i]
3843             =~ /BUFR master table:\s+(\d+)/;
3844 11         70 ($self->{SUBCENTRE}) = $lines[++$i]
3845             =~ /Originating subcentre:\s+(\d+)/;
3846 11         114 ($self->{CENTRE}) = $lines[++$i]
3847             =~ /Originating centre:\s+(\d+)/;
3848 11         63 ($self->{UPDATE_NUMBER}) = $lines[++$i]
3849             =~ /Update sequence number:\s+(\d+)/;
3850 11         76 ($self->{OPTIONAL_SECTION}) = $lines[++$i]
3851             =~ /Optional section present:\s+(\d+)/;
3852 11         67 ($self->{DATA_CATEGORY}) = $lines[++$i]
3853             =~ /Data category \(table A\):\s+(\d+)/;
3854 11         56 ($self->{DATA_SUBCATEGORY}) = $lines[++$i]
3855             =~ /Data subcategory:\s+(\d+)/;
3856 11         56 ($self->{MASTER_TABLE_VERSION}) = $lines[++$i]
3857             =~ /Master table version number:\s+(\d+)/;
3858 11         61 ($self->{LOCAL_TABLE_VERSION}) = $lines[++$i]
3859             =~ /Local table version number:\s+(\d+)/;
3860 11         60 ($self->{YEAR_OF_CENTURY}) = $lines[++$i]
3861             =~ /Year of century:\s+(\d+)/;
3862 11         58 ($self->{MONTH}) = $lines[++$i]
3863             =~ /Month:\s+(\d+)/;
3864 11         51 ($self->{DAY}) = $lines[++$i]
3865             =~ /Day:\s+(\d+)/;
3866 11         70 ($self->{HOUR}) = $lines[++$i]
3867             =~ /Hour:\s+(\d+)/;
3868 11         151 ($self->{MINUTE}) = $lines[++$i]
3869             =~ /Minute:\s+(\d+)/;
3870             _croak "reencode_message: Something seriously wrong in decoded section 1"
3871 11 50       69 unless defined $self->{MINUTE};
3872             } elsif ($self->{BUFR_EDITION} == 4) {
3873 7         45 ($self->{MASTER_TABLE}) = $lines[++$i]
3874             =~ /BUFR master table:\s+(\d+)/;
3875 7         46 ($self->{CENTRE}) = $lines[++$i]
3876             =~ /Originating centre:\s+(\d+)/;
3877 7         36 ($self->{SUBCENTRE}) = $lines[++$i]
3878             =~ /Originating subcentre:\s+(\d+)/;
3879 7         39 ($self->{UPDATE_NUMBER}) = $lines[++$i]
3880             =~ /Update sequence number:\s+(\d+)/;
3881 7         49 ($self->{OPTIONAL_SECTION}) = $lines[++$i]
3882             =~ /Optional section present:\s+(\d+)/;
3883 7         42 ($self->{DATA_CATEGORY}) = $lines[++$i]
3884             =~ /Data category \(table A\):\s+(\d+)/;
3885 7         34 ($self->{INT_DATA_SUBCATEGORY}) = $lines[++$i]
3886             =~ /International data subcategory:\s+(\d+)/;
3887 7         62 ($self->{LOC_DATA_SUBCATEGORY}) = $lines[++$i]
3888             =~ /Local data subcategory:\s+(\d+)/;
3889 7         35 ($self->{MASTER_TABLE_VERSION}) = $lines[++$i]
3890             =~ /Master table version number:\s+(\d+)/;
3891 7         30 ($self->{LOCAL_TABLE_VERSION}) = $lines[++$i]
3892             =~ /Local table version number:\s+(\d+)/;
3893 7         35 ($self->{YEAR}) = $lines[++$i]
3894             =~ /Year:\s+(\d+)/;
3895 7         30 ($self->{MONTH}) = $lines[++$i]
3896             =~ /Month:\s+(\d+)/;
3897 7         59 ($self->{DAY}) = $lines[++$i]
3898             =~ /Day:\s+(\d+)/;
3899 7         78 ($self->{HOUR}) = $lines[++$i]
3900             =~ /Hour:\s+(\d+)/;
3901 7         40 ($self->{MINUTE}) = $lines[++$i]
3902             =~ /Minute:\s+(\d+)/;
3903 7         100 ($self->{SECOND}) = $lines[++$i]
3904             =~ /Second:\s+(\d+)/;
3905             _croak "reencode_message: Something seriously wrong in decoded section 1"
3906 7 50       34 unless defined $self->{SECOND};
3907             }
3908              
3909             # Extract section 3 info
3910 18         123 $i++ while $lines[$i] !~ /^Section 3/;
3911 18 50       86 _croak "reencode_message: Don't find decoded section 3" if $i >= @lines;
3912 18         29 $i++; # Skip length of section 3
3913              
3914 18         109 ($self->{NUM_SUBSETS}) = $lines[++$i]
3915             =~ /Number of data subsets:\s+(\d+)/;
3916             _croak "Don't support reencoding of 0 subset message"
3917 18 50       149 if $self->{NUM_SUBSETS} == 0;
3918 18         93 ($self->{OBSERVED_DATA}) = $lines[++$i]
3919             =~ /Observed data:\s+(\d+)/;
3920 18         114 ($self->{COMPRESSED_DATA}) = $lines[++$i]
3921             =~ /Compressed data:\s+(\d+)/;
3922 18         261 ($self->{DESCRIPTORS_UNEXPANDED}) = $lines[++$i]
3923             =~ /Data descriptors unexpanded:\s+(\d+.*)/;
3924             _croak "reencode_message: Something seriously wrong in decoded section 3"
3925 18 50       90 unless defined $self->{DESCRIPTORS_UNEXPANDED};
3926              
3927             # Extract data values to use in section 4
3928 18         42 my ($data_refs, $desc_refs);
3929 18         36 my $subset = 0;
3930 18         80 SUBSET: while ($i < @lines-1) {
3931 3999         5690 $_ = $lines[++$i];
3932 3999 100 100     11781 next SUBSET if /^$/ or /^Subset/;
3933 3906 50       6380 last SUBSET if /^Message/;
3934 3906         6113 $_ = substr $_, 0, $width + 16;
3935 3906         9324 s/^\s+//;
3936 3906 50       8235 next SUBSET if not /^\d/;
3937 3906         9173 my ($n, $desc, $value) = split /\s+/, $_, 3;
3938 3906 100       7278 $subset++ if $n == 1;
3939 3906 100       5559 if (defined $value) {
3940 3861         5519 $value =~ s/\s+$//;
3941 3861 100 66     10030 $value = undef if $value eq '' or $value eq 'missing';
3942             } else {
3943             # Some descriptors are not numbered (like 222000)
3944 45         69 $desc = $n;
3945 45         71 $value = '';
3946             }
3947 3906         4586 push @{$data_refs->[$subset]}, $value;
  3906         6787  
3948 3906         4497 push @{$desc_refs->[$subset]}, $desc;
  3906         9001  
3949             }
3950              
3951             # If optional section is present, pretend it is not, because we
3952             # are not able to encode this section
3953 18 50       173 if ($self->{OPTIONAL_SECTION}) {
3954 0         0 $self->{OPTIONAL_SECTION} = 0;
3955 0         0 carp "Warning: 'Optional section present' changed from 1 to 0'\n";
3956             }
3957              
3958 18         146 $bufr_messages .= $self->encode_message($data_refs, $desc_refs);
3959             }
3960              
3961 18         700 return $bufr_messages;
3962             }
3963              
3964              
3965             ## Encode a new BUFR message. All relevant metadata
3966             ## ($self->{BUFR_EDITION} etc) must have been initialized already or
3967             ## else the _encode_sec routines will croak.
3968             sub encode_message {
3969 26     26 0 73 my $self = shift;
3970 26         74 my ($data_refs, $desc_refs) = @_;
3971              
3972 26 50       134 _croak "encode_message: No data/descriptors provided" unless $desc_refs;
3973              
3974 26         74 $self->{MESSAGE_NUMBER}++;
3975 26 100       121 $self->_spew(2, "Encoding message number %d", $self->{MESSAGE_NUMBER}) if $Spew;
3976              
3977 26         152 $self->load_BDtables();
3978              
3979 26 100       131 $self->_spew(2, "Encoding section 1-3") if $Spew;
3980 26         172 my $sec1_stream = $self->_encode_sec1();
3981 26         149 my $sec2_stream = $self->_encode_sec2();
3982 26         178 my $sec3_stream = $self->_encode_sec3();
3983 26 100       123 $self->_spew(2, "Encoding section 4") if $Spew;
3984 26         138 my $sec4_stream = $self->_encode_sec4($data_refs, $desc_refs);
3985              
3986             # Compute length of whole message and encode section 0
3987 26         112 my $msg_len = 8 + length($sec1_stream) + length($sec2_stream)
3988             + length($sec3_stream) + length($sec4_stream) + 4;
3989 26         121 my $msg_len_binary = pack("N", $msg_len);
3990 26         98 my $bufr_edition_binary = pack('n', $self->{BUFR_EDITION});
3991 26         98 my $sec0_stream = 'BUFR' . substr($msg_len_binary,1,3)
3992             . substr($bufr_edition_binary,1,1);
3993              
3994 26         95 my $new_message = $sec0_stream . $sec1_stream . $sec2_stream
3995             . $sec3_stream . $sec4_stream . '7777';
3996 26         804 return $new_message;
3997             }
3998              
3999             ## Encode and return section 1
4000             sub _encode_sec1 {
4001 27     27   67 my $self = shift;
4002              
4003             my $bufr_edition = $self->{BUFR_EDITION} or
4004 27 50       185 _croak "_encode_sec1: BUFR edition not defined";
4005              
4006 27         241 my @keys = qw( MASTER_TABLE CENTRE SUBCENTRE UPDATE_NUMBER
4007             OPTIONAL_SECTION DATA_CATEGORY MASTER_TABLE_VERSION
4008             LOCAL_TABLE_VERSION MONTH DAY HOUR MINUTE );
4009 27 100       141 if ($bufr_edition < 4) {
    50          
4010 15         56 push @keys, qw( DATA_SUBCATEGORY YEAR_OF_CENTURY );
4011             } elsif ($bufr_edition == 4) {
4012 12         57 push @keys, qw( INT_DATA_SUBCATEGORY LOC_DATA_SUBCATEGORY YEAR SECOND );
4013             }
4014              
4015             # Check that the required variables for section 1 are provided
4016 27         71 foreach my $key (@keys) {
4017             _croak "_encode_sec1: $key not given"
4018 402 50       10488 unless defined $self->{$key};
4019             }
4020              
4021 27 100       260 $self->_validate_datetime() if ($Strict_checking);
4022              
4023 27         336 my $sec1_stream;
4024             # Byte 4-
4025 27 100       193 if ($bufr_edition < 4) {
    50          
4026 15 50       235 $self->{LOCAL_USE} = "\0" if !defined $self->{LOCAL_USE};
4027             $sec1_stream = pack 'C14a*',
4028             $self->{MASTER_TABLE},
4029             $self->{SUBCENTRE},
4030             $self->{CENTRE},
4031             $self->{UPDATE_NUMBER},
4032             $self->{OPTIONAL_SECTION} ? 128 : 0,
4033             $self->{DATA_CATEGORY},
4034             $self->{DATA_SUBCATEGORY},
4035             $self->{MASTER_TABLE_VERSION},
4036             $self->{LOCAL_TABLE_VERSION},
4037             $self->{YEAR_OF_CENTURY},
4038             $self->{MONTH},
4039             $self->{DAY},
4040             $self->{HOUR},
4041             $self->{MINUTE},
4042 15 50       154 $self->{LOCAL_USE};
4043             } elsif ($bufr_edition == 4) {
4044             $sec1_stream = pack 'CnnC7nC5',
4045             $self->{MASTER_TABLE},
4046             $self->{CENTRE},
4047             $self->{SUBCENTRE},
4048             $self->{UPDATE_NUMBER},
4049             $self->{OPTIONAL_SECTION} ? 128 : 0,
4050             $self->{DATA_CATEGORY},
4051             $self->{INT_DATA_SUBCATEGORY},
4052             $self->{LOC_DATA_SUBCATEGORY},
4053             $self->{MASTER_TABLE_VERSION},
4054             $self->{LOCAL_TABLE_VERSION},
4055             $self->{YEAR},
4056             $self->{MONTH},
4057             $self->{DAY},
4058             $self->{HOUR},
4059             $self->{MINUTE},
4060 12 50       184 $self->{SECOND};
4061             $sec1_stream .= pack 'a*', $self->{LOCAL_USE}
4062 12 100       81 if defined $self->{LOCAL_USE};
4063             }
4064              
4065 27         126 my $sec1_len = 3 + length $sec1_stream;
4066 27 100       107 if ($bufr_edition < 4) {
4067             # Each section should be an even number of octets
4068 15 50       90 if ($sec1_len % 2) {
4069 0         0 $sec1_stream .= "\0";
4070 0         0 $sec1_len++;
4071             }
4072             }
4073              
4074             # Byte 1-3
4075 27         132 my $sec1_len_binary = substr pack("N", $sec1_len), 1, 3;
4076              
4077 27         160 return $sec1_len_binary . $sec1_stream;
4078             }
4079              
4080             ## Encode and return section 2 (empty string if no optional section)
4081             sub _encode_sec2 {
4082 26     26   53 my $self = shift;
4083 26 50       86 if ($self->{OPTIONAL_SECTION}) {
4084             _croak "_encode_sec2: No optional section provided"
4085 0 0       0 unless defined $self->{SEC2_STREAM};
4086 0         0 return $self->{SEC2_STREAM};
4087             } else {
4088 26         79 return '';
4089             }
4090             }
4091              
4092             ## Encode and return section 3
4093             sub _encode_sec3 {
4094 27     27   57 my $self = shift;
4095              
4096             # Check that the required variables for section 3 are provided
4097 27         81 foreach my $key (qw(NUM_SUBSETS OBSERVED_DATA COMPRESSED_DATA
4098             DESCRIPTORS_UNEXPANDED)) {
4099             _croak "_encode_sec3: $key not given"
4100 108 50       348 unless defined $self->{$key};
4101             }
4102              
4103 27         149 my @desc = split / /, $self->{DESCRIPTORS_UNEXPANDED};
4104              
4105             # Byte 5-6
4106 27         107 my $nsubsets_binary = pack "n", $self->{NUM_SUBSETS};
4107              
4108             # Byte 7
4109             my $flag = pack 'C', $self->{OBSERVED_DATA}*128 +
4110 27         146 $self->{COMPRESSED_DATA}*64;
4111              
4112             # Byte 8-
4113 27         101 my $desc_binary = "\0\0" x @desc;
4114 27         59 my $pos = 0;
4115 27         77 foreach my $desc (@desc) {
4116 229         329 my $f = substr($desc,0,1);
4117 229         372 my $x = substr($desc,1,2)+0;
4118 229         385 my $y = substr($desc,3,3)+0;
4119 229         4222 dec2bitstream($f, $desc_binary, $pos, 2);
4120 229         283 $pos += 2;
4121 229         440 dec2bitstream($x, $desc_binary, $pos, 6);
4122 229         255 $pos += 6;
4123 229         496 dec2bitstream($y, $desc_binary, $pos, 8);
4124 229         335 $pos += 8;
4125             }
4126              
4127 27         66 my $sec3_len = 7 + length $desc_binary;
4128 27 100       110 if ($self->{BUFR_EDITION} < 4) {
4129             # Each section should be an even number of octets
4130 15 50       114 if ($sec3_len % 2) {
4131 15         36 $desc_binary .= "\0";
4132 15         47 $sec3_len++;
4133             }
4134             }
4135              
4136             # Byte 1-4
4137 27         84 my $sec3_len_binary = pack("N", $sec3_len);
4138 27         140 my $sec3_start = substr($sec3_len_binary, 1, 3) . "\0";
4139              
4140 27         178 return $sec3_start . $nsubsets_binary . $flag . $desc_binary;
4141             }
4142              
4143             ## Encode and return section 4
4144             sub _encode_sec4 {
4145 26     26   143 my $self = shift;
4146 26         160 my ($data_refs, $desc_refs) = @_;
4147              
4148             # Check that dimension of argument arrays agrees with number of
4149             # subsets in section 3
4150 26         210 my $nsubsets = $self->{NUM_SUBSETS};
4151 26 50       240 _croak "Wrong number of subsets ($nsubsets) in section 3?\n"
4152             . "Disagrees with dimension of descriptor array used as argument "
4153             . "to encode_message()"
4154             unless @$desc_refs == $nsubsets + 1;
4155              
4156             my ($bitstream, $byte_len) = $self->{COMPRESSED_DATA}
4157 26 100       389 ? $self->_encode_compressed_bitstream($data_refs, $desc_refs)
4158             : $self->_encode_bitstream($data_refs, $desc_refs);
4159              
4160 26         80 my $sec4_len = $byte_len + 4;
4161 26         137 my $sec4_len_binary = pack("N", $sec4_len);
4162 26         115 my $sec4_stream = substr($sec4_len_binary, 1, 3) . "\0" . $bitstream;
4163              
4164 26         103 return $sec4_stream;
4165             }
4166              
4167             ## Encode a nil message, i.e. all values set to missing except delayed
4168             ## replication factors and the (descriptor, value) pairs in the hash
4169             ## ref $stationid_ref. Delayed replication factors will all be set to
4170             ## 1 unless $delayed_repl_ref is provided, in which case the
4171             ## descriptors 031001 and 031002 will get the values contained in
4172             ## @$delayed_repl_ref. Note that data in section 1 and 3 must have
4173             ## been set before calling this method.
4174             sub encode_nil_message {
4175 1     1 0 15 my $self = shift;
4176 1         3 my ($stationid_ref, $delayed_repl_ref) = @_;
4177              
4178 1 50       3 _croak "encode_nil_message: No station descriptors provided"
4179             unless $stationid_ref;
4180              
4181             my $bufr_edition = $self->{BUFR_EDITION} or
4182 1 50       2 _croak "encode_nil_message: BUFR edition not defined";
4183              
4184             # Since a nil message necessarily is a one subset message, some
4185             # metadata might need to be adjusted (saving the user for having
4186             # to remember this)
4187 1         11 $self->set_number_of_subsets(1);
4188 1         4 $self->set_compressed_data(0);
4189              
4190 1         4 $self->load_BDtables();
4191              
4192 1 50       3 $self->_spew(2, "Encoding NIL message") if $Spew;
4193 1         18 my $sec1_stream = $self->_encode_sec1();
4194 1         8 my $sec3_stream = $self->_encode_sec3();
4195 1         5 my $sec4_stream = $self->_encode_nil_sec4($stationid_ref,
4196             $delayed_repl_ref);
4197              
4198             # Compute length of whole message and encode section 0
4199 1         3 my $msg_len = 8 + length($sec1_stream) + length($sec3_stream)
4200             + length($sec4_stream) + 4;
4201 1         2 my $msg_len_binary = pack("N", $msg_len);
4202 1         1 my $bufr_edition_binary = pack('n', $bufr_edition);
4203 1         11 my $sec0_stream = 'BUFR' . substr($msg_len_binary,1,3)
4204             . substr($bufr_edition_binary,1,1);
4205              
4206 1         4 my $new_message = $sec0_stream . $sec1_stream . $sec3_stream . $sec4_stream
4207             . '7777';
4208 1         4 return $new_message;
4209             }
4210              
4211             ## Encode and return section 4 with all values set to missing except
4212             ## delayed replication factors and the (descriptor, value) pairs in
4213             ## the hash ref $stationid_ref. Delayed replication factors will all
4214             ## be set to 1 unless $delayed_repl_ref is provided, in which case the
4215             ## descriptors 031001 and 031002 will get the values contained in
4216             ## @$delayed_repl_ref (in that order).
4217             sub _encode_nil_sec4 {
4218 1     1   2 my $self = shift;
4219 1         3 $self->{CODING} = 'ENCODE';
4220 1         3 my ($stationid_ref, $delayed_repl_ref) = @_;
4221 1 50       3 my @delayed_repl = defined $delayed_repl_ref ? @$delayed_repl_ref : ();
4222              
4223             # Get the expanded list of descriptors (i.e. expanded with table D)
4224 1 50       15 if (not $self->{DESCRIPTORS_EXPANDED}) {
4225             _croak "_encode_nil_sec4: DESCRIPTORS_UNEXPANDED not given"
4226 1 50       10 unless $self->{DESCRIPTORS_UNEXPANDED};
4227 1         2 my @unexpanded = split / /, $self->{DESCRIPTORS_UNEXPANDED};
4228             _croak "_encode_nil_sec4: D_TABLE not given"
4229 1 50       3 unless $self->{D_TABLE};
4230 1         2 my $alias = "$self->{TABLE_VERSION} " . $self->{DESCRIPTORS_UNEXPANDED};
4231 1 50       3 if (exists $Descriptors_already_expanded{$alias}) {
4232 1         3 $self->{DESCRIPTORS_EXPANDED} = $Descriptors_already_expanded{$alias};
4233             } else {
4234             $Descriptors_already_expanded{$alias} = $self->{DESCRIPTORS_EXPANDED}
4235 0         0 = join " ", _expand_descriptors($self->{D_TABLE}, @unexpanded);
4236             }
4237             }
4238              
4239             # The rest is very similar to sub _decode_bitstream, except that we
4240             # now are encoding, not decoding a bitstream, with most values set
4241             # to missing value, and we do not need to fully expand the
4242             # descriptors.
4243 1         16 my $B_table = $self->{B_TABLE};
4244 1         1 my @operators;
4245 1         4 my $bitstream = chr(255) x 65536; # one bits only
4246 1         1 my $pos = 0;
4247              
4248 1         56 my @desc = split /\s/, $self->{DESCRIPTORS_EXPANDED};
4249 1         4 D_LOOP: for (my $idesc = 0; $idesc < @desc; $idesc++) {
4250              
4251 130         116 my $id = $desc[$idesc];
4252 130         113 my $f = substr($id,0,1);
4253 130         124 my $x = substr($id,1,2)+0;
4254 130         135 my $y = substr($id,3,3)+0;
4255              
4256 130 100       165 if ($f == 1) {
    50          
4257             # Delayed replication
4258 2 50       5 if ($x == 0) {
4259 0         0 _complain("Nonsensical replication of zero descriptors ($id)");
4260 0         0 $idesc++;
4261 0         0 next D_LOOP;
4262             }
4263 2 50       18 _croak "$id _expand_descriptors() did not do its job"
4264             if $y > 0;
4265              
4266 2         6 $_ = $desc[$idesc+1];
4267             _croak "$id Erroneous replication factor"
4268 2 50 33     18 unless /^0310(00|01|02|11|12)/ && exists $B_table->{$_};
4269 2         13 my $factor = 1;
4270 2 50 33     13 if (@delayed_repl && /^03100(1|2)/) {
4271 2         2 $factor = shift @delayed_repl;
4272 2 50 33     13 _croak "Delayed replication factor must be positive integer in "
4273             . "encode_nil_message, is '$factor'"
4274             if ($factor !~ /^\d+$/ || $factor == 0);
4275             }
4276 2         7 my ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$_};
4277 2 50       5 if ($Spew) {
4278 0         0 $self->_spew(3, "%6s %-20s %s", $id, $unit, $name);
4279 0         0 $self->_spew(3, " %s", $factor);
4280             }
4281 2         5 dec2bitstream($factor, $bitstream, $pos, $width);
4282 2         2 $pos += $width;
4283             # Include the delayed replication in descriptor list
4284 2         5 splice @desc, $idesc++, 0, $_;
4285              
4286 2         3 my @r = ();
4287 2         13 push @r, @desc[($idesc+2)..($idesc+$x+1)] while $factor--;
4288 2 50       3 $self->_spew(4, "Delayed replication ($id $_ -> @r)") if $Spew;
4289 2         13 splice @desc, $idesc, 2+$x, @r;
4290              
4291 2 50       3 if ($idesc < @desc) {
4292 2         5 redo D_LOOP;
4293             } else {
4294 0         0 last D_LOOP; # Might happen if delayed factor is 0
4295             }
4296              
4297             } elsif ($f == 2) {
4298 0         0 my $next_id = $desc[$idesc+1];
4299 0         0 my $flow;
4300             my $bm_idesc;
4301 0         0 ($pos, $flow, $bm_idesc, @operators)
4302             = $self->_apply_operator_descriptor($id, $x, $y, $pos, 0,
4303             $next_id, @operators);
4304 0 0       0 next D_LOOP if $flow eq 'next';
4305             }
4306              
4307             # We now have a "real" data descriptor
4308              
4309             # Find the relevant entry in BUFR table B
4310             _croak "Data descriptor $id is not present in BUFR table B"
4311 128 50       171 unless exists $B_table->{$id};
4312 128         248 my ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$id};
4313 128 50       158 $self->_spew(3, "%6s %-20s %s", $id, $unit, $name) if $Spew;
4314              
4315             # Override Table B values if Data Description Operators are in effect
4316 128 50       151 if ($self->{NUM_CHANGE_OPERATORS} > 0) {
4317 0 0 0     0 if ($unit ne 'CCITTIA5' && $unit !~ /^(CODE|FLAG)/) {
    0 0        
4318 0 0       0 if (defined $self->{CHANGE_SRW}) {
4319 0         0 $scale += $self->{CHANGE_SRW};
4320 0         0 $width += int((10*$self->{CHANGE_SRW}+2)/3);
4321 0         0 $refval *= 10*$self->{CHANGE_SRW};
4322             } else {
4323 0 0       0 $scale += $self->{CHANGE_SCALE} if defined $self->{CHANGE_SCALE};
4324 0 0       0 $width += $self->{CHANGE_WIDTH} if defined $self->{CHANGE_WIDTH};
4325             }
4326             } elsif ($unit eq 'CCITTIA5' && defined $self->{CHANGE_CCITTIA5_WIDTH}) {
4327             $width = $self->{CHANGE_CCITTIA5_WIDTH}
4328 0         0 }
4329 0 0       0 $refval = $self->{NEW_REFVAL_OF}{$id} if defined $self->{NEW_REFVAL_OF}{$id};
4330             }
4331 128 50       159 _croak "$id Data width <= 0" if $width <= 0;
4332              
4333 128 100       159 if ($stationid_ref->{$id}) {
4334 3         8 my $value = $stationid_ref->{$id};
4335 3 50       7 $self->_spew(3, " %s", $value) if $Spew;
4336 3 100       15 if ($unit eq 'CCITTIA5') {
4337             # Encode ASCII string in $width bits (left justified,
4338             # padded with spaces)
4339 1         4 my $num_bytes = int($width/8);
4340 1 50       4 _croak "Ascii string too long to fit in $width bits: $value"
4341             if length($value) > $num_bytes;
4342 1         8 $value .= ' ' x ($num_bytes - length($value));
4343 1         4 ascii2bitstream($value, $bitstream, $pos, $num_bytes);
4344             } else {
4345             # Encode value as integer in $width bits
4346 2         8 $value = int($value * 10**$scale - $refval + 0.5);
4347 2 50       4 _croak "Data value no $id is negative: $value"
4348             if $value < 0;
4349 2         9 dec2bitstream($value, $bitstream, $pos, $width);
4350             }
4351             } else {
4352             # Missing value is encoded as 1 bits
4353             }
4354 128         156 $pos += $width;
4355             }
4356              
4357             # Pad with 0 bits if necessary to get an even or integer number of
4358             # octets, depending on bufr edition
4359 1 50       19 my $padnum = $self->{BUFR_EDITION} < 4 ? (16-($pos%16)) % 16 : (8-($pos%8)) % 8;
4360 1 50       3 if ($padnum > 0) {
4361 1         4 null2bitstream($bitstream, $pos, $padnum);
4362             }
4363 1         2 my $len = ($pos + $padnum)/8;
4364 1         3 $bitstream = substr $bitstream, 0, $len;
4365              
4366             # Encode section 4
4367 1         3 my $sec4_len_binary = pack("N", $len + 4);
4368 1         3 my $sec4_stream = substr($sec4_len_binary, 1, 3) . "\0" . $bitstream;
4369              
4370 1         7 return $sec4_stream;
4371             }
4372              
4373             ## Encode bitstream using the data values in $data_refs, first
4374             ## expanding section 3 fully (and comparing with $desc_refs to check
4375             ## for consistency). This sub is very similar to sub _decode_bitstream
4376             sub _encode_bitstream {
4377 20     20   86 my $self = shift;
4378 20         94 $self->{CODING} = 'ENCODE';
4379 20         72 my ($data_refs, $desc_refs) = @_;
4380              
4381             # Expand section 3 except for delayed replication and operator descriptors
4382 20         130 my @unexpanded = split / /, $self->{DESCRIPTORS_UNEXPANDED};
4383 20         84 my $alias = "$self->{TABLE_VERSION} " . $self->{DESCRIPTORS_UNEXPANDED};
4384 20 100       107 if (exists $Descriptors_already_expanded{$alias}) {
4385 1         2 $self->{DESCRIPTORS_EXPANDED} = $Descriptors_already_expanded{$alias};
4386             } else {
4387             $Descriptors_already_expanded{$alias} = $self->{DESCRIPTORS_EXPANDED}
4388 19         116 = join " ", _expand_descriptors($self->{D_TABLE}, @unexpanded);
4389             }
4390              
4391 20         180 my $nsubsets = $self->{NUM_SUBSETS};
4392 20         55 my $B_table = $self->{B_TABLE};
4393 20         42 my $maxlen = 1024;
4394 20         146 my $bitstream = chr(255) x $maxlen; # one bits only
4395 20         47 my $pos = 0;
4396 20         40 my @operators;
4397              
4398 20         103 S_LOOP: foreach my $isub (1..$nsubsets) {
4399 33 100       131 $self->_spew(2, "Encoding subset number %d", $isub) if $Spew;
4400              
4401             # Bit maps might vary from subset to subset, so must be rebuilt
4402 33         86 undef $self->{BITMAP_OPERATORS};
4403 33         82 undef $self->{BITMAP_START};
4404 33         68 undef $self->{REUSE_BITMAP};
4405 33         74 $self->{NUM_BITMAPS} = 0;
4406 33         67 $self->{BACKWARD_DATA_REFERENCE} = 1;
4407 33         81 $self->{NUM_CHANGE_OPERATORS} = 0;
4408              
4409             # The data values to use for this subset
4410 33         83 my $data_ref = $data_refs->[$isub];
4411             # The descriptors from expanding section 3
4412 33         1442 my @desc = split /\s/, $self->{DESCRIPTORS_EXPANDED};
4413             # The descriptors to compare with for this subset
4414 33         82 my $desc_ref = $desc_refs->[$isub];
4415              
4416             # Note: @desc as well as $idesc may be changed during this loop,
4417             # so we cannot use a foreach loop instead
4418 33         128 D_LOOP: for (my $idesc = 0; $idesc < @desc; $idesc++) {
4419 5172   33     9507 my $id = $desc[$idesc]
4420             || _croak("No descriptor no. $idesc defined. Consider using --strict_checking 2"
4421             . " or --verbose 4 to explore what went wrong in the encoding");
4422 5172         7109 my $f = substr($id,0,1);
4423 5172         8325 my $x = substr($id,1,2)+0;
4424 5172         7293 my $y = substr($id,3,3)+0;
4425              
4426 5172 100       10670 if ($f == 1) {
    100          
4427             # Delayed replication
4428 54 50       202 if ($x == 0) {
4429 0         0 _complain("Nonsensical replication of zero descriptors ($id)");
4430 0         0 $idesc++;
4431 0         0 next D_LOOP;
4432             }
4433 54 50       220 _croak "$id _expand_descriptors() did not do its job"
4434             if $y > 0;
4435              
4436 54         123 my $next_id = $desc[$idesc+1];
4437             _croak "$id Erroneous replication factor"
4438 54 50 33     489 unless $next_id =~ /^0310(00|01|02|11|12)/ && exists $B_table->{$next_id};
4439 54 50       241 _croak "Descriptor no $idesc is $desc_ref->[$idesc], expected $next_id"
4440             if $desc_ref->[$idesc] != $next_id;
4441 54         103 my $factor = $data_ref->[$idesc];
4442 54         242 my ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$next_id};
4443 54 100       159 if ($Spew) {
4444 6         12 $self->_spew(3, "%6s %-20s %s", $next_id, $unit, $name);
4445 6         7 $self->_spew(3, " %s", $factor);
4446             }
4447 54         280 ($bitstream, $pos, $maxlen)
4448             = $self->_encode_value($factor,$isub,$unit,$scale,$refval,
4449             $width,$next_id,$bitstream,$pos,$maxlen);
4450             # Include the delayed replication/repetition in descriptor list
4451 54         204 splice @desc, $idesc++, 0, $next_id;
4452              
4453 54         91 my @r = ();
4454 54         1549 push @r, @desc[($idesc+2)..($idesc+$x+1)] while $factor--;
4455 54         602 splice @desc, $idesc, 2+$x, @r;
4456              
4457 54 100 66     255 if ($next_id eq '031011' || $next_id eq '031012') {
4458             # For delayed repetition we should include data just
4459             # once, so skip to the last set in data array
4460 1         3 $idesc += $x * ($data_ref->[$idesc-1] - 1);
4461             # We ought to check that the data sets we skipped are
4462             # indeed equal to the last set!
4463 1 50       2 $self->_spew(4, "Delayed repetition ($id $next_id -> @r)") if $Spew;
4464             } else {
4465 53 100       194 $self->_spew(4, "Delayed replication ($id $next_id -> @r)") if $Spew;
4466             }
4467 54 50       147 if ($idesc < @desc) {
4468 54         253 redo D_LOOP;
4469             } else {
4470 0         0 last D_LOOP; # Might happen if delayed factor is 0
4471             }
4472              
4473             } elsif ($f == 2) {
4474 155         264 my $flow;
4475             my $bm_idesc;
4476 155         642 ($pos, $flow, $bm_idesc, @operators)
4477             = $self->_apply_operator_descriptor($id, $x, $y, $pos, $isub,
4478             $desc[$idesc+1], @operators);
4479 155 100       516 if ($flow eq 'redo_bitmap') {
    50          
    100          
4480             # Data value is associated with the descriptor
4481             # defined by bit map. Remember original and new
4482             # index in descriptor array for the bit mapped
4483             # values ('dr' = data reference)
4484 42         71 my $dr_idesc;
4485 42 50       138 if (!defined $bm_idesc) {
    50          
4486 0         0 $dr_idesc = shift @{ $self->{REUSE_BITMAP}->[$isub]};
  0         0  
4487             } elsif (!$Show_all_operators) {
4488 42         113 $dr_idesc = $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
4489             + $bm_idesc;
4490             } else {
4491 0         0 $dr_idesc = $self->{BITMAP_START}[$self->{NUM_BITMAPS}];
4492             # Skip operator descriptors
4493 0         0 while ($bm_idesc-- > 0) {
4494 0         0 $dr_idesc++;
4495 0         0 $dr_idesc++ while ($desc[$dr_idesc] >= 200000);
4496             }
4497             }
4498 42         145 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[$isub] },
  42         200  
4499             $dr_idesc, $idesc;
4500 42         106 $desc[$idesc] = $desc[$dr_idesc];
4501 42         117 redo D_LOOP;
4502             } elsif ($flow eq 'signify_character') {
4503 0 0       0 _croak "Descriptor no $idesc is $desc_ref->[$idesc], expected $id"
4504             if $desc_ref->[$idesc] != $id;
4505             # Get ASCII string
4506 0         0 my $value = $data_ref->[$idesc];
4507 0         0 my $name = 'SIGNIFY CHARACTER';
4508 0         0 my $unit = 'CCITTIA5';
4509 0         0 my ($scale, $refval, $width) = (0, 0, 8*$y);
4510 0         0 ($bitstream, $pos, $maxlen)
4511             = $self->_encode_value($value,$isub,$unit,$scale,$refval,$width,"205$y",$bitstream,$pos,$maxlen);
4512 0         0 next D_LOOP;
4513             } elsif ($flow eq 'no_value') {
4514 31         141 next D_LOOP;
4515             }
4516              
4517             # Remove operator descriptor from @desc
4518 82         161 splice @desc, $idesc--, 1;
4519              
4520 82 50       392 next D_LOOP if $flow eq 'next';
4521 0 0       0 last D_LOOP if $flow eq 'last';
4522             }
4523              
4524 4963 100       8527 if ($self->{CHANGE_REFERENCE_VALUE}) {
4525             # The data descriptor is to be associated with a new
4526             # reference value, which is fetched from data stream,
4527             # possibly with f=9 instead of f=0 for descriptor
4528 2 50       7 $id -= 900000 if $id =~ /^9/;
4529 2 50       25 _croak "Change reference operator 203Y is not followed by element"
4530             . " descriptor, but $id" if $f > 0;
4531 2         6 my $new_refval = $data_ref->[$idesc];
4532 2         28 $self->{NEW_REFVAL_OF}{$id}{$isub} = $new_refval;
4533 2         11 ($bitstream, $pos, $maxlen)
4534             = $self->_encode_reference_value($new_refval,$id,$bitstream,$pos,$maxlen);
4535 2         8 next D_LOOP;
4536             }
4537              
4538             # If operator 204$y 'Add associated field' is in effect,
4539             # each data value is preceded by $y bits which should be
4540             # encoded separately. We choose to provide a descriptor
4541             # 999999 in this case (like the ECMWF BUFRDC software)
4542 4961 100 100     9189 if ($self->{ADD_ASSOCIATED_FIELD} and $id ne '031021') {
4543             # First encode associated field
4544 2 50       7 _croak "Descriptor no $idesc is $desc_ref->[$idesc], expected 999999"
4545             if $desc_ref->[$idesc] != 999999;
4546 2         5 my $value = $data_ref->[$idesc];
4547 2         5 my $name = 'ASSOCIATED FIELD';
4548 2         3 my $unit = 'NUMERIC';
4549 2         5 my ($scale, $refval) = (0, 0);
4550 2         4 my $width = $self->{ADD_ASSOCIATED_FIELD};
4551 2 50       6 $self->_spew(4, "Added associated field: %s", $value) if $Spew;
4552 2         10 ($bitstream, $pos, $maxlen)
4553             = $self->_encode_value($value,$isub,$unit,$scale,$refval,$width,999999,$bitstream,$pos,$maxlen);
4554             # Insert the artificial 999999 descriptor for the
4555             # associated value and increment $idesc to prepare for
4556             # handling the 'real' value below
4557 2         20 splice @desc, $idesc++, 0, 999999;
4558             }
4559              
4560              
4561              
4562             # For quality information, if this relates to a bit map we
4563             # need to store index of the data ($data_idesc) for which
4564             # the quality information applies, as well as the new
4565             # index ($idesc) in the descriptor array for the bit
4566             # mapped values
4567 4961 100 100     10138 if (substr($id,0,3) eq '033'
      66        
4568             && defined $self->{BITMAP_OPERATORS}
4569             && $self->{BITMAP_OPERATORS}->[-1] eq '222000') {
4570 372 100       562 if (defined $self->{REUSE_BITMAP}) {
4571 80         93 my $data_idesc = shift @{ $self->{REUSE_BITMAP}->[$isub] };
  80         140  
4572 80 50       149 _croak "$id: Not enough quality values provided"
4573             if not defined $data_idesc;
4574 80         87 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[$isub] },
  80         251  
4575             $data_idesc, $idesc;
4576             } else {
4577 292         299 my $data_idesc = shift @{ $self->{CURRENT_BITMAP} };
  292         415  
4578 292 50       437 _croak "$id: Not enough quality values provided"
4579             if not defined $data_idesc;
4580 292         581 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[$isub] },
4581 292         307 $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
4582             + $data_idesc, $idesc;
4583             }
4584             }
4585              
4586 4961         7313 my $value = $data_ref->[$idesc];
4587              
4588 4961 100 66     14727 if ($id eq '031031' and $self->{BUILD_BITMAP}) {
    100 100        
4589             # Store the index of expanded descriptors if data is
4590             # marked as present in data present indicator: 0 is
4591             # 'present', 1 (undef value) is 'not present'. E.g.
4592             # bitmap = 1100110 => (2,3,6) is stored in $self->{CURRENT_BITMAP}
4593 1063 100 66     3036 if (defined $value and $value == 0) {
4594 334         411 push @{$self->{CURRENT_BITMAP}}, $self->{BITMAP_INDEX};
  334         671  
4595             }
4596 1063         1476 $self->{BITMAP_INDEX}++;
4597 1063 100       2065 if ($self->{BACKWARD_DATA_REFERENCE} == $self->{NUM_BITMAPS}) {
4598 743         1001 my $numb = $self->{NUM_BITMAPS};
4599 743 100       1121 if (!defined $self->{BITMAP_START}[$numb]) {
4600             # Look up the element descriptor immediately
4601             # preceding the bitmap operator
4602 7         10 my $i = $idesc;
4603 7   66     82 $i-- while ($desc[$i] ne $self->{BITMAP_OPERATORS}->[-1]
4604             && $i >=0);
4605 7   66     77 $i-- while ($desc[$i] > 100000 && $i >=0);
4606 7 50       38 _croak "No element descriptor preceding bitmap" if $i < 0;
4607 7         20 $self->{BITMAP_START}[$numb] = $i;
4608             } else {
4609 736         884 $self->{BITMAP_START}[$numb]--;
4610             _croak "Bitmap too big"
4611 736 50       1257 if $self->{BITMAP_START}[$numb] < 0;
4612             }
4613             }
4614             } elsif ($self->{BUILD_BITMAP} and $self->{BITMAP_INDEX} > 0) {
4615             # We have finished building the bit map
4616 9         21 $self->{BUILD_BITMAP} = 0;
4617 9         17 $self->{BITMAP_INDEX} = 0;
4618 9 100       30 if ($self->{BACKWARD_DATA_REFERENCE} != $self->{NUM_BITMAPS}) {
4619             $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
4620 2         13 = $self->{BITMAP_START}[$self->{BACKWARD_DATA_REFERENCE}];
4621             }
4622             }
4623              
4624 4961 50       8265 _croak "Not enough descriptors provided (expected no $idesc to be $id)"
4625             unless exists $desc_ref->[$idesc];
4626 4961 50       9909 _croak "Descriptor no $idesc is $desc_ref->[$idesc], expected $id"
4627             if $desc_ref->[$idesc] != $id;
4628              
4629             # Find the relevant entry in BUFR table B
4630             _croak "Error: Data descriptor $id is not present in BUFR table B"
4631 4961 50       9368 unless exists $B_table->{$id};
4632 4961         15641 my ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$id};
4633             $refval = $self->{NEW_REFVAL_OF}{$id}{$isub} if defined $self->{NEW_REFVAL_OF}{$id}
4634 4961 100 66     10807 && defined $self->{NEW_REFVAL_OF}{$id}{$isub};
4635 4961 100       7779 if ($Spew) {
4636 346         461 $self->_spew(3, "%6s %-20s %s", $id, $unit, $name);
4637 346 100       445 $self->_spew(3, " %s", defined $value ? $value : 'missing');
4638             }
4639             ########### call to_encode_value inlined for speed
4640             # Override Table B values if Data Description Operators are in
4641             # effect (except for associated fields)
4642 4961 100 66     11248 if ($self->{NUM_CHANGE_OPERATORS} > 0 && $id != 999999) {
4643 144 100 100     435 if ($unit ne 'CCITTIA5' && $unit !~ /^(CODE|FLAG)/) {
    100 66        
4644 120 100       179 if (defined $self->{CHANGE_SRW}) {
4645 2         6 $scale += $self->{CHANGE_SRW};
4646 2         8 $width += int((10*$self->{CHANGE_SRW}+2)/3);
4647 2         6 $refval *= 10*$self->{CHANGE_SRW};
4648             } else {
4649 118 100       173 $scale += $self->{CHANGE_SCALE} if defined $self->{CHANGE_SCALE};
4650 118 100       191 $width += $self->{CHANGE_WIDTH} if defined $self->{CHANGE_WIDTH};
4651             }
4652             } elsif ($unit eq 'CCITTIA5' && defined $self->{CHANGE_CCITTIA5_WIDTH}) {
4653             $width = $self->{CHANGE_CCITTIA5_WIDTH}
4654 1         1 }
4655 144 50       243 _croak "$id Data width is $width which is <= 0" if $width <= 0;
4656             $refval = $self->{NEW_REFVAL_OF}{$id}{$isub} if defined $self->{NEW_REFVAL_OF}{$id}
4657 144 100 66     236 && defined $self->{NEW_REFVAL_OF}{$id}{$isub};
4658             # Difference statistical values use different width and reference value
4659 144 50       215 if ($self->{DIFFERENCE_STATISTICAL_VALUE}) {
4660 0         0 $width += 1;
4661 0         0 $refval = -2**$width;
4662 0         0 undef $self->{DIFFERENCE_STATISTICAL_VALUE};
4663 0         0 $self->{NUM_CHANGE_OPERATORS}--;
4664             }
4665             }
4666              
4667             # Ensure that bitstream is big enough to encode $value
4668 4961         9842 while ($pos + $width > $maxlen*8) {
4669 1         7 $bitstream .= chr(255) x $maxlen;
4670 1         2 $maxlen *= 2;
4671             }
4672              
4673 4961 100       8920 if (not defined($value)) {
    100          
4674             # Missing value is encoded as 1 bits
4675 1085         2223 $pos += $width;
4676             } elsif ($unit eq 'CCITTIA5') {
4677             # Encode ASCII string in $width bits (left justified,
4678             # padded with spaces)
4679 31         85 my $num_bytes = int ($width/8);
4680 31 50       235 _croak "Ascii string too long to fit in $width bits: $value"
4681             if length($value) > $num_bytes;
4682 31         133 $value .= ' ' x ($num_bytes - length($value));
4683 31         119 ascii2bitstream($value, $bitstream, $pos, $num_bytes);
4684 31         84 $pos += $width;
4685             } else {
4686             # Encode value as integer in $width bits
4687 3845 50       8544 _croak "Value '$value' is not a number for descriptor $id"
4688             unless looks_like_number($value);
4689 3845         9209 $value = int($value * 10**$scale - $refval + 0.5);
4690 3845 50       6329 _croak "Encoded data value for $id is negative: $value" if $value < 0;
4691 3845         5502 my $max_value = 2**$width - 1;
4692 3845 50       5965 _croak "Encoded data value for $id is too big to fit in $width bits: $value"
4693             if $value > $max_value;
4694             # Check for illegal flag value
4695 3845 50 100     14443 if ($Strict_checking && $unit =~ /^FLAG[ ]?TABLE/ && $width > 1
      100        
      66        
      66        
4696             && $value < $max_value && $value % 2) {
4697 0         0 _complain("$id - $value: rightmost bit $width is set indicating missing value"
4698             . " but then value should be $max_value");
4699             }
4700 3845         8612 dec2bitstream($value, $bitstream, $pos, $width);
4701 3845         8493 $pos += $width;
4702             }
4703             ########### end inlining of_encode_value
4704             } # End D_LOOP
4705             } # END S_LOOP
4706              
4707              
4708              
4709              
4710             # Pad with 0 bits if necessary to get an even or integer number of
4711             # octets, depending on bufr edition
4712 20 100       215 my $padnum = $self->{BUFR_EDITION} < 4 ? (16-($pos%16)) % 16 : (8-($pos%8)) % 8;
4713 20 100       138 if ($padnum > 0) {
4714 18         101 null2bitstream($bitstream, $pos, $padnum);
4715             }
4716 20         68 my $len = ($pos + $padnum)/8;
4717 20         105 $bitstream = substr $bitstream, 0, $len;
4718              
4719 20         158 return ($bitstream, $len);
4720             }
4721              
4722             sub _encode_reference_value {
4723 2     2   3 my $self = shift;
4724 2         7 my ($refval,$id,$bitstream,$pos,$maxlen) = @_;
4725              
4726 2         5 my $width = $self->{CHANGE_REFERENCE_VALUE};
4727              
4728             # Ensure that bitstream is big enough to encode $value
4729 2         8 while ($pos + $width > $maxlen*8) {
4730 0         0 $bitstream .= chr(255) x $maxlen;
4731 0         0 $maxlen *= 2;
4732             }
4733              
4734 2 50       6 $self->_spew(4, "Encoding new reference value %d for %6s in %d bits",
4735             $refval, $id, $width) if $Spew;
4736 2 50       7 if ($refval >= 0) {
4737 0 0       0 _croak "Encoded reference value for $id is too big to fit "
4738             . "in $width bits: $refval"
4739             if $refval > 2**$width - 1;
4740 0         0 dec2bitstream($refval, $bitstream, $pos, $width);
4741             } else {
4742             # Negative reference values should be encoded by setting first
4743             # bit to 1 and then encoding absolute value
4744 2 50       9 _croak "Encoded reference value for $id is too big to fit "
4745             . "in $width bits: $refval"
4746             if -$refval > 2**($width-1) - 1;
4747 2         10 dec2bitstream(-$refval, $bitstream, $pos+1, $width-1);
4748             }
4749 2         5 $pos += $width;
4750              
4751 2         7 return ($bitstream, $pos, $maxlen);
4752             }
4753              
4754             sub _encode_value {
4755 56     56   108 my $self = shift;
4756 56         225 my ($value,$isub,$unit,$scale,$refval,$width,$id,$bitstream,$pos,$maxlen) = @_;
4757              
4758             # Override Table B values if Data Description Operators are in
4759             # effect (except for associated fields)
4760 56 100 66     186 if ($self->{NUM_CHANGE_OPERATORS} > 0 && $id != 999999) {
4761 2 50 33     13 if ($unit ne 'CCITTIA5' && $unit !~ /^(CODE|FLAG)/) {
    0 0        
4762 2 50       3 if (defined $self->{CHANGE_SRW}) {
4763 0         0 $scale += $self->{CHANGE_SRW};
4764 0         0 $width += int((10*$self->{CHANGE_SRW}+2)/3);
4765 0         0 $refval *= 10*$self->{CHANGE_SRW};
4766             } else {
4767 2 50       4 $scale += $self->{CHANGE_SCALE} if defined $self->{CHANGE_SCALE};
4768 2 50       5 $width += $self->{CHANGE_WIDTH} if defined $self->{CHANGE_WIDTH};
4769             }
4770             } elsif ($unit eq 'CCITTIA5' && defined $self->{CHANGE_CCITTIA5_WIDTH}) {
4771             $width = $self->{CHANGE_CCITTIA5_WIDTH}
4772 0         0 }
4773 2 50       4 _croak "$id Data width is $width which is <= 0" if $width <= 0;
4774             $refval = $self->{NEW_REFVAL_OF}{$id}{$isub} if defined $self->{NEW_REFVAL_OF}{$id}
4775 2 50 33     4 && defined $self->{NEW_REFVAL_OF}{$id}{$isub};
4776             # Difference statistical values use different width and reference value
4777 2 50       6 if ($self->{DIFFERENCE_STATISTICAL_VALUE}) {
4778 0         0 $width += 1;
4779 0         0 $refval = -2**$width;
4780 0         0 undef $self->{DIFFERENCE_STATISTICAL_VALUE};
4781 0         0 $self->{NUM_CHANGE_OPERATORS}--;
4782             }
4783             }
4784              
4785             # Ensure that bitstream is big enough to encode $value
4786 56         189 while ($pos + $width > $maxlen*8) {
4787 0         0 $bitstream .= chr(255) x $maxlen;
4788 0         0 $maxlen *= 2;
4789             }
4790              
4791 56 50       192 if (not defined($value)) {
    50          
4792             # Missing value is encoded as 1 bits
4793 0         0 $pos += $width;
4794             } elsif ($unit eq 'CCITTIA5') {
4795             # Encode ASCII string in $width bits (left justified,
4796             # padded with spaces)
4797 0         0 my $num_bytes = int ($width/8);
4798 0 0       0 _croak "Ascii string too long to fit in $width bits: $value"
4799             if length($value) > $num_bytes;
4800 0         0 $value .= ' ' x ($num_bytes - length($value));
4801 0         0 ascii2bitstream($value, $bitstream, $pos, $num_bytes);
4802 0         0 $pos += $width;
4803             } else {
4804             # Encode value as integer in $width bits
4805 56 50       199 _croak "Value '$value' is not a number for descriptor $id"
4806             unless looks_like_number($value);
4807 56         168 $value = int($value * 10**$scale - $refval + 0.5);
4808 56 50       134 _croak "Encoded data value for $id is negative: $value" if $value < 0;
4809 56         130 my $max_value = 2**$width - 1;
4810 56 50       136 _croak "Encoded data value for $id is too big to fit in $width bits: $value"
4811             if $value > $max_value;
4812             # Check for illegal flag value
4813 56 0 66     250 if ($Strict_checking && $unit =~ /^FLAG[ ]?TABLE/ && $width > 1
      33        
      33        
      0        
4814             && $value < $max_value && $value % 2) {
4815 0         0 _complain("$id - $value: rightmost bit $width is set indicating missing value"
4816             . " but then value should be $max_value");
4817             }
4818 56         172 dec2bitstream($value, $bitstream, $pos, $width);
4819 56         83 $pos += $width;
4820             }
4821              
4822 56         181 return ($bitstream, $pos, $maxlen);
4823             }
4824              
4825             # Encode reference value using BUFR compression, assuming all subsets
4826             # have same reference value
4827             sub _encode_compressed_reference_value {
4828 1     1   3 my $self = shift;
4829 1         4 my ($refval,$id,$nsubsets,$bitstream,$pos,$maxlen) = @_;
4830              
4831 1         3 my $width = $self->{CHANGE_REFERENCE_VALUE};
4832              
4833             # Ensure that bitstream is big enough to encode $value
4834 1         5 while ($pos + ($nsubsets+1)*$width + 6 > $maxlen*8) {
4835 0         0 $bitstream .= chr(255) x $maxlen;
4836 0         0 $maxlen *= 2;
4837             }
4838              
4839 1 50       4 $self->_spew(4, "Encoding new reference value %d for %6s in %d bits",
4840             $refval, $id, $width) if $Spew;
4841             # Encode value as integer in $width bits
4842 1 50       5 if ($refval >= 0) {
4843 0 0       0 _croak "Encoded reference value for $id is too big to fit "
4844             . "in $width bits: $refval" if $refval > 2**$width - 1;
4845 0         0 dec2bitstream($refval, $bitstream, $pos, $width);
4846             } else {
4847             # Negative reference values should be encoded by setting first
4848             # bit to 1 and then encoding absolute value
4849 1 50       5 _croak "Encoded reference value for $id is too big to fit "
4850             . "in $width bits: $refval" if -$refval > 2**($width-1) - 1;
4851 1         6 dec2bitstream(-$refval, $bitstream, $pos+1, $width-1);
4852             }
4853 1         2 $pos += $width;
4854              
4855             # Increment width set to 0
4856 1         4 dec2bitstream(0, $bitstream, $pos, 6);
4857 1         2 $pos += 6;
4858              
4859 1         4 return ($bitstream, $pos, $maxlen);
4860             }
4861              
4862             sub _encode_compressed_value {
4863 479     479   523 my $self = shift;
4864 479         872 my ($bitstream,$pos,$maxlen,$unit,$scale,$refval,$width,$id,$data_refs,$idesc,$nsubsets) = @_;
4865              
4866             # Override Table B values if Data Description Operators are in
4867             # effect (except for associated fields)
4868 479 100 66     801 if ($self->{NUM_CHANGE_OPERATORS} > 0 && $id != 999999) {
4869 12 50 33     54 if ($unit ne 'CCITTIA5' && $unit !~ /^(CODE|FLAG)/) {
    0 0        
4870 12 100       22 if (defined $self->{CHANGE_SRW}) {
4871 1         3 $scale += $self->{CHANGE_SRW};
4872 1         3 $width += int((10*$self->{CHANGE_SRW}+2)/3);
4873 1         3 $refval *= 10*$self->{CHANGE_SRW};
4874             } else {
4875 11 100       26 $scale += $self->{CHANGE_SCALE} if defined $self->{CHANGE_SCALE};
4876 11 50       36 $width += $self->{CHANGE_WIDTH} if defined $self->{CHANGE_WIDTH};
4877             }
4878             } elsif ($unit eq 'CCITTIA5' && defined $self->{CHANGE_CCITTIA5_WIDTH}) {
4879             $width = $self->{CHANGE_CCITTIA5_WIDTH}
4880 0         0 }
4881 12 50       44 _croak "$id Data width <= 0" if $width <= 0;
4882 12 100       25 $refval = $self->{NEW_REFVAL_OF}{$id} if defined $self->{NEW_REFVAL_OF}{$id};
4883             # Difference statistical values use different width and reference value
4884 12 50       20 if ($self->{DIFFERENCE_STATISTICAL_VALUE}) {
4885 0         0 $width += 1;
4886 0         0 $refval = -2**$width;
4887 0         0 undef $self->{DIFFERENCE_STATISTICAL_VALUE};
4888 0         0 $self->{NUM_CHANGE_OPERATORS}--;
4889             }
4890             }
4891              
4892             # Ensure that bitstream is big enough to encode $value
4893 479         895 while ($pos + ($nsubsets+1)*$width + 6 > $maxlen*8) {
4894 0         0 $bitstream .= chr(255) x $maxlen;
4895 0         0 $maxlen *= 2;
4896             }
4897              
4898             # Get all values for this descriptor
4899 479         478 my @values;
4900 479         530 my $first_value = $data_refs->[1][$idesc];
4901 479         495 my $all_equal = 1; # Set to 0 if at least 2 elements differ
4902 479         622 foreach my $value (map { $data_refs->[$_][$idesc] } 2..$nsubsets) {
  603         1081  
4903 603 50 100     1816 if (defined $value && $unit ne 'CCITTIA5' && !looks_like_number($value)) {
      66        
4904 0         0 _croak "Value '$value' is not a number for descriptor $id"
4905             }
4906             # This used to be a sub (_check_equality), but inlined for speed
4907 603 100       743 if ($all_equal) {
4908 544 100 100     1223 if (defined $value && defined $first_value) {
    100 100        
4909 390 100       470 if ($unit eq 'CCITTIA5') {
4910 1 50       2 $all_equal = 0 if $value ne $first_value;
4911             } else {
4912 389 100       722 $all_equal = 0 if $value != $first_value;
4913             }
4914             } elsif (defined $value || defined $first_value) {
4915 23         21 $all_equal = 0;
4916             }
4917             }
4918 603 100       826 if (not defined $value) {
    100          
4919 140         160 push @values, undef;
4920             } elsif ($unit eq 'CCITTIA5') {
4921 2         3 push @values, $value;
4922             } else {
4923 461         914 push @values, int($value * 10**$scale - $refval + 0.5);
4924             }
4925             # Check for illegal flag value
4926 603 100 66     1861 if ($Strict_checking and $unit =~ /^FLAG[ ]?TABLE/ and $width > 1) {
      100        
4927 11 50 33     55 if (defined $value and $value ne 'missing' and $value % 2) {
      33        
4928 0         0 my $max_value = 2**$width - 1;
4929 0         0 _complain("$id - value $value in subset $_:\n"
4930             . "rightmost bit $width is set indicating missing value"
4931             . " but then value should be $max_value");
4932             }
4933             }
4934             }
4935              
4936 479 100       678 if ($all_equal) {
4937             # Same value in all subsets. No need to calculate or store increments
4938 366 100       423 if (defined $first_value) {
4939 270 50       323 if ($unit eq 'CCITTIA5') {
4940             # Encode ASCII string in $width bits (left justified,
4941             # padded with spaces)
4942 0         0 my $num_bytes = int ($width/8);
4943 0 0       0 _croak "Ascii string too long to fit in $width bits: $first_value"
4944             if length($first_value) > $num_bytes;
4945 0         0 $first_value .= ' ' x ($num_bytes - length($first_value));
4946 0         0 ascii2bitstream($first_value, $bitstream, $pos, $num_bytes);
4947             } else {
4948             # Encode value as integer in $width bits
4949 270 50       407 _croak "First value '$first_value' is not a number for descriptor $id"
4950             unless looks_like_number($first_value);
4951 270         363 $first_value = int($first_value * 10**$scale - $refval + 0.5);
4952 270 50       364 _croak "Encoded data value for $id is negative: $first_value"
4953             if $first_value < 0;
4954 270 50       393 _croak "Encoded data value for $id is too big to fit "
4955             . "in $width bits: $first_value"
4956             if $first_value > 2**$width - 1;
4957 270         418 dec2bitstream($first_value, $bitstream, $pos, $width);
4958             }
4959             } else {
4960             # Missing value is encoded as 1 bits, but bitstream is
4961             # padded with 1 bits already
4962             }
4963 366         340 $pos += $width;
4964             # Increment width set to 0
4965 366         599 dec2bitstream(0, $bitstream, $pos, 6);
4966 366         369 $pos += 6;
4967             } else {
4968 113 100       159 if ($unit eq 'CCITTIA5') {
4969 1         2 unshift @values, $first_value;
4970             # Local reference value set to 0 bits
4971 1         3 null2bitstream($bitstream, $pos, $width);
4972 1         2 $pos += $width;
4973             # Do not store more characters than needed: remove leading
4974             # and trailing spaces, then right pad with spaces so that
4975             # all strings has same length as largest string
4976 1         3 my $largest_length = _trimpad(\@values);
4977 1         3 dec2bitstream($largest_length, $bitstream, $pos, 6);
4978 1         1 $pos += 6;
4979             # Store the character values
4980 1         2 foreach my $value (@values) {
4981 3 50       4 if (defined $value) {
4982             # Encode ASCII string in $largest_length bytes
4983 3         4 ascii2bitstream($value, $bitstream, $pos, $largest_length);
4984             } else {
4985             # Missing value is encoded as 1 bits, but
4986             # bitstream is padded with 1 bits already
4987             }
4988 3         4 $pos += $largest_length * 8;
4989             }
4990             } else {
4991 112 50 66     289 _croak "First value '$first_value' is not a number for descriptor $id"
4992             if defined($first_value) && !looks_like_number($first_value);
4993 112 100       283 unshift @values, defined $first_value
4994             ? int($first_value * 10**$scale - $refval + 0.5)
4995             : undef;
4996             # Numeric data. First find minimum value
4997 112         190 my ($min_value, $isub) = _minimum(\@values);
4998 112 50       169 _croak "Encoded data value for $id and subset $isub is negative: $min_value"
4999             if $min_value < 0;
5000             my @inc_values =
5001 112 100       138 map { defined $_ ? $_ - $min_value : undef } @values;
  286         440  
5002             # Find how many bits are required to hold the increment
5003             # values (or rather: the highest increment value pluss one
5004             # (except for associated values), to be able to store
5005             # missing values also)
5006 112         197 my $max_inc = _maximum(\@inc_values);
5007 112 50       214 my $deltabits = ($id eq '999999')
5008             ?_get_number_of_bits_to_store($max_inc)
5009             : _get_number_of_bits_to_store($max_inc + 1);
5010             # Store local reference value
5011 112 50       183 $self->_spew(5, " Local reference value: %d", $min_value) if $Spew;
5012 112         244 dec2bitstream($min_value, $bitstream, $pos, $width);
5013 112         123 $pos += $width;
5014             # Store increment width
5015 112 50       152 $self->_spew(5, " Increment width (bits): %d", $deltabits) if $Spew;
5016 112         202 dec2bitstream($deltabits, $bitstream, $pos, 6);
5017 112         105 $pos += 6;
5018             # Store values
5019             $self->_spew(5, " Increment values: %s",
5020 112 0       141 join(',', map { defined $inc_values[$_]
  0 50       0  
5021             ? $inc_values[$_] : ''} 0..$#inc_values))
5022             if $Spew;
5023 112         154 foreach my $value (@inc_values) {
5024 286 100       322 if (defined $value) {
5025 256 50 33     593 _complain("value " . ($value + $min_value) . " for $id too big"
5026             . " to be encoded without compression")
5027             if ($Strict_checking && ($value + $min_value) > 2**$width -1);
5028 256         316 dec2bitstream($value, $bitstream, $pos, $deltabits);
5029             } else {
5030             # Missing value is encoded as 1 bits, but
5031             # bitstream is padded with 1 bits already
5032             }
5033 286         325 $pos += $deltabits;
5034             }
5035             }
5036             }
5037              
5038 479         1363 return ($bitstream, $pos, $maxlen);
5039             }
5040              
5041             ## Encode bitstream using the data values in $data_refs, first
5042             ## expanding section 3 fully (and comparing with $desc_refs to check
5043             ## for consistency). This sub is very similar to sub
5044             ## _decompress_bitstream
5045             sub _encode_compressed_bitstream {
5046 6     6   14 my $self = shift;
5047 6         36 $self->{CODING} = 'ENCODE';
5048 6         14 my ($data_refs, $desc_refs) = @_;
5049              
5050             # Expand section 3 except for delayed replication and operator
5051             # descriptors. This expansion is the same for all subsets, since
5052             # delayed replication has to be the same (this needs to be
5053             # checked) for compression to be possible
5054 6         29 my @unexpanded = split / /, $self->{DESCRIPTORS_UNEXPANDED};
5055 6         20 my $alias = "$self->{TABLE_VERSION} " . $self->{DESCRIPTORS_UNEXPANDED};
5056 6 50       39 if (exists $Descriptors_already_expanded{$alias}) {
5057 0         0 $self->{DESCRIPTORS_EXPANDED} = $Descriptors_already_expanded{$alias};
5058             } else {
5059             $Descriptors_already_expanded{$alias} = $self->{DESCRIPTORS_EXPANDED}
5060 6         35 = join " ", _expand_descriptors($self->{D_TABLE}, @unexpanded);
5061             }
5062 6         200 my @desc = split /\s/, $self->{DESCRIPTORS_EXPANDED};
5063              
5064 6         20 my $nsubsets = $self->{NUM_SUBSETS};
5065 6         15 my $B_table = $self->{B_TABLE};
5066 6         33 my $maxlen = 1024;
5067 6         55 my $bitstream = chr(255) x $maxlen; # one bits only
5068 6         12 my $pos = 0;
5069 6         10 my @operators;
5070              
5071 6         15 my $desc_ref = $desc_refs->[1];
5072              
5073             # All subsets should have same set of expanded descriptors. This
5074             # is checked later, but we also need to check that the number of
5075             # descriptors in each subset is the same for all subsets
5076 6         10 my $num_desc = @{$desc_ref};
  6         13  
5077 6         25 foreach my $isub (2..$nsubsets) {
5078 7         10 my $num_d = @{$desc_refs->[$isub]};
  7         16  
5079 7 50       27 _croak "Compression impossible: Subset 1 contains $num_desc descriptors,"
5080             . " while subset $isub contains $num_d descriptors"
5081             if $num_d != $num_desc;
5082             }
5083              
5084              
5085 6         26 D_LOOP: for (my $idesc = 0; $idesc < @desc; $idesc++) {
5086 518         598 my $id = $desc[$idesc];
5087 518         607 my $f = substr($id,0,1);
5088 518         740 my $x = substr($id,1,2)+0;
5089 518         655 my $y = substr($id,3,3)+0;
5090              
5091 518 100       872 if ($f == 1) {
    100          
5092             # Delayed replication
5093 7 50       18 if ($x == 0) {
5094 0         0 _complain("Nonsensical replication of zero descriptors ($id)");
5095 0         0 $idesc++;
5096 0         0 next D_LOOP;
5097             }
5098 7 50       22 _croak "$id _expand_descriptors() did not do its job"
5099             if $y > 0;
5100              
5101 7         14 my $next_id = $desc[$idesc+1];
5102             _croak "$id Erroneous replication factor"
5103 7 50 33     68 unless $next_id =~ /^0310(00|01|02|11|12)/ && exists $B_table->{$next_id};
5104 7 50       21 _croak "Descriptor no $idesc is $desc_ref->[$idesc], expected $next_id"
5105             if $desc_ref->[$idesc] != $next_id;
5106 7         11 my $factor = $data_refs->[1][$idesc];
5107 7         40 my ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$next_id};
5108 7 50       29 if ($Spew) {
5109 0         0 $self->_spew(3, "%6s %-20s %s", $next_id, $unit, $name);
5110 0         0 $self->_spew(3, " %s", $factor);
5111             }
5112 7         39 ($bitstream, $pos, $maxlen)
5113             = $self->_encode_compressed_value($bitstream,$pos,$maxlen,
5114             $unit,$scale,$refval,$width,
5115             $next_id,$data_refs,$idesc,$nsubsets);
5116             # Include the delayed replication/repetition in descriptor list
5117 7         19 splice @desc, $idesc++, 0, $next_id;
5118              
5119 7         16 my @r = ();
5120 7         56 push @r, @desc[($idesc+2)..($idesc+$x+1)] while $factor--;
5121 7         38 splice @desc, $idesc, 2+$x, @r;
5122              
5123 7 100 66     26 if ($next_id eq '031011' || $next_id eq '031012') {
5124             # For delayed repetition we should include data just
5125             # once, so skip to the last set in data array
5126 1         4 $idesc += $x * ($data_refs->[1][$idesc-1] - 1);
5127             # We ought to check that the data sets we skipped are
5128             # indeed equal to the last set!
5129 1 50       4 $self->_spew(4, "Delayed repetition ($id $next_id -> @r)") if $Spew;
5130             } else {
5131 6 50       9 $self->_spew(4, "Delayed replication ($id $next_id -> @r)") if $Spew;
5132             }
5133 7 50       21 if ($idesc < @desc) {
5134 7         30 redo D_LOOP;
5135             } else {
5136 0         0 last D_LOOP; # Might happen if delayed factor is 0
5137             }
5138              
5139             } elsif ($f == 2) {
5140 38         51 my $flow;
5141             my $bm_idesc;
5142 38         95 ($pos, $flow, $bm_idesc, @operators)
5143             = $self->_apply_operator_descriptor($id, $x, $y, $pos, 0,
5144             $desc[$idesc+1], @operators);
5145 38 100       143 if ($flow eq 'redo_bitmap') {
    50          
    100          
5146             # Data value is associated with the descriptor
5147             # defined by bit map. Remember original and new
5148             # index in descriptor array for the bit mapped
5149             # values ('dr' = data reference)
5150 1         1 my $dr_idesc;
5151 1 50       3 if (!defined $bm_idesc) {
    50          
5152 0         0 $dr_idesc = shift @{ $self->{REUSE_BITMAP}->[0] };
  0         0  
5153             } elsif (!$Show_all_operators) {
5154 1         3 $dr_idesc = $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
5155             + $bm_idesc;
5156             } else {
5157 0         0 $dr_idesc = $self->{BITMAP_START}[$self->{NUM_BITMAPS}];
5158             # Skip operator descriptors
5159 0         0 while ($bm_idesc-- > 0) {
5160 0         0 $dr_idesc++;
5161 0         0 $dr_idesc++ while ($desc[$dr_idesc] >= 200000);
5162             }
5163             }
5164 1         1 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[0] },
  1         10  
5165             $dr_idesc, $idesc;
5166 1         2 $desc[$idesc] = $desc[$dr_idesc];
5167 1         2 redo D_LOOP;
5168             } elsif ($flow eq 'signify_character') {
5169 0 0       0 _croak "Descriptor no $idesc is $desc_ref->[$idesc], expected $id"
5170             if $desc_ref->[$idesc] != $id;
5171             # Get ASCII string
5172 0         0 my @values = map { $data_refs->[$_][$idesc] } 1..$nsubsets;
  0         0  
5173 0         0 my $name = 'SIGNIFY CHARACTER';
5174 0         0 my $unit = 'CCITTIA5';
5175 0         0 my ($scale, $refval, $width) = (0, 0, 8*$y);
5176 0         0 ($bitstream, $pos, $maxlen)
5177             = $self->_encode_compressed_value($bitstream,$pos,$maxlen,
5178             $unit,$scale,$refval,$width,
5179             "205$y",$data_refs,$idesc,$nsubsets);
5180 0         0 next D_LOOP;
5181             } elsif ($flow eq 'no_value') {
5182 8         19 next D_LOOP;
5183             }
5184              
5185             # Remove operator descriptor from @desc
5186 29         95 splice @desc, $idesc--, 1;
5187              
5188 29 50       100 next D_LOOP if $flow eq 'next';
5189 0 0       0 last D_LOOP if $flow eq 'last';
5190             }
5191              
5192 473 100       704 if ($self->{CHANGE_REFERENCE_VALUE}) {
5193             # The data descriptor is to be associated with a new
5194             # reference value, which is fetched from data stream,
5195             # possibly with f=9 instead of f=0 for descriptor
5196 1 50       12 $id -= 900000 if $id =~ /^9/;
5197 1 50       5 _croak "Change reference operator 203Y is not followed by element"
5198             . " descriptor, but $id" if $f > 0;
5199 1         4 my @new_ref_values = map { $data_refs->[$_][$idesc] } 1..$nsubsets;
  2         7  
5200 1         10 my $new_refval = $new_ref_values[0];
5201             # Check that they are all the same
5202 1         6 foreach my $val (@new_ref_values[1..$#new_ref_values]) {
5203 1 50       6 _croak "Change reference value differ between subsets"
5204             . " which cannot be combined with BUFR compression"
5205             if $val != $new_refval;
5206             }
5207 1         4 $self->{NEW_REFVAL_OF}{$id} = $new_refval;
5208 1         7 ($bitstream, $pos, $maxlen)
5209             = $self->_encode_compressed_reference_value($new_refval,$id,$nsubsets,$bitstream,$pos,$maxlen);
5210 1         13 next D_LOOP;
5211             }
5212              
5213             # If operator 204$y 'Add associated field' is in effect,
5214             # each data value is preceded by $y bits which should be
5215             # encoded separately. We choose to provide a descriptor
5216             # 999999 in this case (like the ECMWF BUFRDC software)
5217 472 50 33     693 if ($self->{ADD_ASSOCIATED_FIELD} and $id ne '031021') {
5218             # First encode associated field
5219 0 0       0 _croak "Descriptor no $idesc is $desc_ref->[$idesc], expected 999999"
5220             if $desc_ref->[$idesc] != 999999;
5221 0         0 my @values = map { $data_refs->[$_][$idesc] } 1..$nsubsets;
  0         0  
5222 0         0 my $name = 'ASSOCIATED FIELD';
5223 0         0 my $unit = 'NUMERIC';
5224 0         0 my ($scale, $refval) = (0, 0);
5225 0         0 my $width = $self->{ADD_ASSOCIATED_FIELD};
5226 0 0       0 if ($Spew) {
5227 0         0 $self->_spew(3, "%6s %-20s %s", $id, $unit, $name);
5228 0         0 $self->_spew(3, " %s", 999999);
5229             }
5230 0         0 ($bitstream, $pos, $maxlen)
5231             = $self->_encode_compressed_value($bitstream,$pos,$maxlen,
5232             $unit,$scale,$refval,$width,
5233             999999,$data_refs,$idesc,$nsubsets);
5234             # Insert the artificial 999999 descriptor for the
5235             # associated value and increment $idesc to prepare for
5236             # handling the 'real' value below
5237 0         0 splice @desc, $idesc++, 0, 999999;
5238             }
5239              
5240              
5241              
5242             # For quality information, if this relates to a bit map we
5243             # need to store index of the data ($data_idesc) for which
5244             # the quality information applies, as well as the new
5245             # index ($idesc) in the descriptor array for the bit
5246             # mapped values
5247 472 100 100     846 if (substr($id,0,3) eq '033'
      66        
5248             && defined $self->{BITMAP_OPERATORS}
5249             && $self->{BITMAP_OPERATORS}->[-1] eq '222000') {
5250 30 100       42 if (defined $self->{REUSE_BITMAP}) {
5251 20         20 my $data_idesc = shift @{ $self->{REUSE_BITMAP}->[0] };
  20         22  
5252 20 50       39 _croak "$id: Not enough quality values provided"
5253             if not defined $data_idesc;
5254 20         18 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[0] },
  20         42  
5255             $data_idesc, $idesc;
5256             } else {
5257 10         27 my $data_idesc = shift @{ $self->{CURRENT_BITMAP} };
  10         11  
5258 10 50       15 _croak "$id: Not enough quality values provided"
5259             if not defined $data_idesc;
5260 10         21 push @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}]->[0] },
5261 10         10 $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
5262             + $data_idesc, $idesc;
5263             }
5264             }
5265              
5266 472 100 66     1245 if ($id eq '031031' and $self->{BUILD_BITMAP}) {
    100 100        
5267             # Store the index of expanded descriptors if data is
5268             # marked as present in data present indicator: 0 is
5269             # 'present', 1 (undef value) is 'not present'. E.g.
5270             # bitmap = 1100110 => (2,3,6) is stored in $self->{CURRENT_BITMAP}
5271              
5272             # NB: bit map might vary betwen subsets!!!!????
5273 107 100       199 if ($data_refs->[1][$idesc] == 0) {
5274 11         12 push @{$self->{CURRENT_BITMAP}}, $self->{BITMAP_INDEX};
  11         19  
5275             }
5276 107         120 $self->{BITMAP_INDEX}++;
5277 107 50       143 if ($self->{BACKWARD_DATA_REFERENCE} == $self->{NUM_BITMAPS}) {
5278 107         109 my $numb = $self->{NUM_BITMAPS};
5279 107 100       169 if (!defined $self->{BITMAP_START}[$numb]) {
5280             # Look up the element descriptor immediately
5281             # preceding the bitmap operator
5282 2         4 my $i = $idesc;
5283 2   66     26 $i-- while ($desc[$i] ne $self->{BITMAP_OPERATORS}->[-1]
5284             && $i >=0);
5285 2   66     16 $i-- while ($desc[$i] > 100000 && $i >=0);
5286 2 50       17 _croak "No element descriptor preceding bitmap" if $i < 0;
5287 2         7 $self->{BITMAP_START}[$numb] = $i;
5288             } else {
5289 105         105 $self->{BITMAP_START}[$numb]--;
5290             _croak "Bitmap too big"
5291 105 50       162 if $self->{BITMAP_START}[$numb] < 0;
5292             }
5293             }
5294             } elsif ($self->{BUILD_BITMAP} and $self->{BITMAP_INDEX} > 0) {
5295             # We have finished building the bit map
5296 2         3 $self->{BUILD_BITMAP} = 0;
5297 2         11 $self->{BITMAP_INDEX} = 0;
5298 2 50       6 if ($self->{BACKWARD_DATA_REFERENCE} != $self->{NUM_BITMAPS}) {
5299             $self->{BITMAP_START}[$self->{NUM_BITMAPS}]
5300 0         0 = $self->{BITMAP_START}[$self->{BACKWARD_DATA_REFERENCE}];
5301             }
5302             }
5303              
5304             # We now have a "real" data descriptor
5305 472 50       859 _croak "Descriptor no $idesc is $desc_ref->[$idesc], expected $id"
5306             if $desc_ref->[$idesc] != $id;
5307              
5308             # Find the relevant entry in BUFR table B
5309             _croak "Data descriptor $id is not present in BUFR table B"
5310 472 50       795 unless exists $B_table->{$id};
5311 472         1286 my ($name,$unit,$scale,$refval,$width) = split /\0/, $B_table->{$id};
5312 472 50       738 if ($Spew) {
5313 0         0 $self->_spew(3, "%6s %-20s %s", $id, $unit, $name);
5314             $self->_spew(3, " %s", join ' ',
5315 0 0       0 map { defined($data_refs->[$_][$idesc]) ?
  0         0  
5316             $data_refs->[$_][$idesc] : 'missing'} 1..$nsubsets );
5317             }
5318 472         747 ($bitstream, $pos, $maxlen)
5319             = $self->_encode_compressed_value($bitstream,$pos,$maxlen,
5320             $unit,$scale,$refval,$width,
5321             $id,$data_refs,$idesc,$nsubsets);
5322             } # End D_LOOP
5323              
5324             # Pad with 0 bits if necessary to get an even or integer number of
5325             # octets, depending on bufr edition
5326 6 100       41 my $padnum = $self->{BUFR_EDITION} < 4 ? (16-($pos%16)) % 16 : (8-($pos%8)) % 8;
5327 6 100       22 if ($padnum > 0) {
5328 4         18 null2bitstream($bitstream, $pos, $padnum);
5329             }
5330 6         17 my $len = ($pos + $padnum)/8;
5331 6         20 $bitstream = substr $bitstream, 0, $len;
5332              
5333 6         67 return ($bitstream, $len);
5334             }
5335              
5336             ## Check that the length of data section computed from expansion of
5337             ## section 3 ($comp_len) equals actual length of data part of section
5338             ## 4, allowing for padding zero bits according to BUFR Regulation 94.1.3
5339             ## Strict checking should also check that padding actually consists of
5340             ## zero bits only.
5341             sub _check_section4_length {
5342 56     56   102 my $self = shift;
5343 56         170 my ($comp_len, $actual_len) = @_;
5344              
5345 56 50       170 if ($comp_len > $actual_len) {
5346 0         0 _croak "More descriptors in expansion of section 3"
5347             . " than what can fit in the given length of section 4"
5348             . " ($comp_len versus $actual_len bits)";
5349             } else {
5350 56 100       256 return if not $Strict_checking; # Excessive bytes in section 4
5351             # does not prevent further decoding
5352 14 50       34 return if $Noqc; # No more sensible checks to do in this case
5353              
5354 14         58 my $bufr_edition = $self->{BUFR_EDITION};
5355 14         28 my $actual_bytes = $actual_len/8; # This is sure to be an integer
5356 14 50 33     82 if ($bufr_edition < 4 and $actual_bytes % 2) {
5357 0         0 _complain("Section 4 is odd number ($actual_bytes) of bytes,"
5358             . " which is an error in BUFR edition $bufr_edition");
5359             }
5360 14         36 my $comp_bytes = int($comp_len/8);
5361 14 100       35 $comp_bytes++ if $comp_len % 8; # Need to pad with zero bits
5362 14 100 66     55 $comp_bytes++ if $bufr_edition < 4 and $comp_bytes % 2; # Need to pad with an extra byte of zero bits
5363 14 50       39 if ($actual_bytes > $comp_bytes) {
5364 0         0 _complain("Binary data part of section 4 longer ($actual_bytes bytes)"
5365             . " than expected from section 3 ($comp_bytes bytes)");
5366             }
5367             }
5368 14         25 return;
5369             }
5370              
5371             # Trim string, also removing nulls (and _complain if nulls found).
5372             # If strict_checking, checks also for bit 1 set in each character
5373             sub _trim {
5374 83     83   218 my ($str, $id) = @_;
5375 83 50       220 return unless defined $str;
5376 83 50 66     446 if ($str =~ /\0/) {
    50          
5377 0         0 (my $str2 = $str) =~ s|\0|\\0|g;
5378 0         0 _complain("Nulls (" . '\0'
5379             . ") found in string '$str2' for descriptor $id");
5380 0         0 $str =~ s/\0//g;
5381             } elsif ($Strict_checking && $str =~/^ +$/) {
5382 0         0 _complain("Only spaces ('$str') found for descriptor $id, "
5383             . "ought to have been encoded as missing value ");
5384             }
5385              
5386 83         397 $str =~ s/\s+$//;
5387 83         202 $str =~ s/^\s+//;
5388              
5389 83 100 66     222 if ($Strict_checking && $str ne '') {
5390 27         87 foreach my $char (split //, $str) {
5391 135 50       198 if (ord($char) > 127) {
5392 0         0 _complain("Character $char (ascii value " . ord($char) .
5393             ") in string '$str' is not allowed in CCITTIA5");
5394 0         0 last; # Don't want to warn for every bad character
5395             }
5396             }
5397             }
5398 83         197 return $str;
5399             }
5400              
5401             ## Remove leading and trailing spaces in the strings provided, then add
5402             ## spaces if necessary so that all strings have same length as largest
5403             ## trimmed string. This length (in bytes) is returned
5404             sub _trimpad {
5405 1     1   1 my $string_ref = shift;
5406 1         1 my $largest_length = 0;
5407 1         1 foreach my $string (@{$string_ref}) {
  1         2  
5408 3 50       4 if (defined $string) {
5409 3         5 $string =~ s/^\s+//;
5410 3         4 $string =~ s/\s+$//;
5411 3 100       4 if (length $string > $largest_length) {
5412 2         3 $largest_length = length $string;
5413             }
5414             }
5415             }
5416 1         2 foreach my $string (@{$string_ref}) {
  1         1  
5417 3 50       20 if (defined $string) {
5418 3         6 $string .= ' ' x ($largest_length - length $string);
5419             }
5420             }
5421 1         2 return $largest_length;
5422             }
5423              
5424             ## Use timegm in Time::Local to validate date and time in section 1
5425             sub _validate_datetime {
5426 43     43   96 my $self = shift;
5427 43         116 my $bufr_edition = $self->{BUFR_EDITION};
5428             my $year = $bufr_edition < 4 ? $self->{YEAR_OF_CENTURY} + 2000
5429 43 100       252 : $self->{YEAR};
5430 43         119 my $month = $self->{MONTH} - 1;
5431 43 100       207 my $second = $bufr_edition == 4 ? $self->{SECOND} : 0;
5432              
5433             # All datetime variables set to 0 should be considered ok
5434             return if ($self->{MINUTE} == 0 && $self->{HOUR} == 0
5435 43 0 100     328 && $self->{DAY} == 0 && $self->{MONTH} == 0
      66        
      33        
      33        
      0        
      0        
5436             && $second == 0 && ($year == 0 || $year == 2000));
5437              
5438 43         109 eval {
5439             my $dummy = timegm($second,$self->{MINUTE},$self->{HOUR},
5440 43         319 $self->{DAY},$month,$year);
5441             };
5442              
5443 43 50       2384 _complain("Invalid date in section 1: $@") if $@;
5444             }
5445              
5446             ## Return number of bits necessary to store the nonnegative number $n
5447             ## (1 for 0,1, 2 for 2,3, 3 for 4,5,6,7 etc)
5448             sub _get_number_of_bits_to_store {
5449 112     112   103 my $n = shift;
5450 112 50       151 return 1 if $n == 0;
5451 112         107 my $x = 1;
5452 112         124 my $i = 0;
5453 112         136 while ($x < $n) {
5454 654         566 $i++;
5455 654         824 $x *= 2;
5456             }
5457 112 100       177 return $x==$n ? $i+1 : $i;
5458             }
5459              
5460             ## Find minimum value among set of numbers (undefined values
5461             ## permitted, but at least one value must be defined). Also returns
5462             ## for which number the minimum occurs (counting from 1).
5463             sub _minimum {
5464 112     112   104 my $v_ref = shift;
5465 112         130 my $min = 2**63;
5466 112         106 my $idx = 0;
5467 112         96 my $i=0;
5468 112         157 foreach my $v (@{$v_ref}) {
  112         178  
5469 286         235 $i++;
5470 286 100       344 next if not defined $v;
5471 256 100       384 if ($v < $min) {
5472 165         159 $min = $v;
5473 165         171 $idx = $i;
5474             }
5475             }
5476 112         230 return ($min, $idx);
5477             }
5478              
5479             ## Find maximum value among set of nonnegative numbers or undefined values
5480             sub _maximum {
5481 112     112   106 my $v_ref = shift;
5482 112         109 my $max = 0;
5483 112         122 foreach my $v (@{$v_ref}) {
  112         126  
5484 286 100       367 next if not defined $v;
5485 256 100       330 if ($v > $max) {
5486 115         131 $max = $v;
5487             }
5488             }
5489 112 50       162 _croak "Internal error: Found no maximum value" if $max < 0;
5490 112         138 return $max;
5491             }
5492              
5493             ## Return index of first occurrence av $value in $list, undef if no match
5494             sub _get_index_in_list {
5495 3     3   4 my ($list, $value) = @_;
5496 3         6 for (my $i=0; $i <= $#{$list}; $i++) {
  6         8  
5497 5 100       12 if ($list->[$i] eq $value) { # Match
5498 2         3 return $i;
5499             }
5500             }
5501             # No match
5502 1         3 return undef;
5503             }
5504              
5505             ## Apply the operator descriptor $id, adjusting $pos and
5506             ## @operators. Also returning $bm_idesc (explained in start of module)
5507             ## and a hint of what to do next in $flow
5508             sub _apply_operator_descriptor {
5509 387     387   586 my $self = shift;
5510 387         1015 my ($id, $x, $y, $pos, $isub, $next_id, @operators) = @_;
5511             # $isub should be 0 for compressed messages, else subset number
5512              
5513 387         633 my $flow = '';
5514 387         636 my $bm_idesc = '';
5515              
5516 387 100 100     4587 if ($y == 0 && $x =~ /^[12378]$/) {
    100 100        
    100 66        
    100 100        
    100 66        
    100 100        
    50 66        
    100 33        
    100 33        
    100 100        
    50 66        
    50 66        
    100 66        
    100 33        
    100 0        
    100 0        
    100 0        
    50 0        
    50 0        
    100 0        
    100 0        
    100          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
5517             # 20[12378]000 Cancellation of a data descriptor operator
5518             _complain("$id Cancelling unused operator")
5519 87 50 66     267 if $Strict_checking and !grep {$_ == $x} @operators;
  61         199  
5520 87         177 @operators = grep {$_ != $x} @operators;
  110         292  
5521 87 100       311 if ($x == 1) {
    100          
    100          
    100          
    50          
5522 52 50       186 $self->{NUM_CHANGE_OPERATORS}-- if $self->{CHANGE_WIDTH};
5523 52         152 undef $self->{CHANGE_WIDTH};
5524             } elsif ($x == 2) {
5525 24 50       95 $self->{NUM_CHANGE_OPERATORS}-- if $self->{CHANGE_SCALE};
5526 24         44 undef $self->{CHANGE_SCALE};
5527             } elsif ($x == 3) {
5528 6 50       37 $self->{NUM_CHANGE_OPERATORS}-- if $self->{NEW_REFVAL_OF};
5529 6         20 undef $self->{NEW_REFVAL_OF};
5530             } elsif ($x == 7) {
5531 3 50       14 $self->{NUM_CHANGE_OPERATORS}-- if $self->{CHANGE_SRW};
5532 3         6 undef $self->{CHANGE_SRW};
5533             } elsif ($x == 8) {
5534 2 50       16 $self->{NUM_CHANGE_OPERATORS}-- if $self->{CHANGE_CCITTIA5_WIDTH};
5535 2         4 undef $self->{CHANGE_CCITTIA5_WIDTH};
5536             }
5537 87 50       214 $self->_spew(4, "$id * Reset %s",
5538             ("width of CCITTIA5 field","data width","scale","reference values",0,0,0,
5539             "increase of scale, reference value and data width")[$x % 8]) if $Spew;
5540 87         160 $flow = 'next';
5541             } elsif ($x == 1) {
5542             # ^201 Change data width
5543             _croak "201 operator cannot be nested within 207 operator"
5544 52 50       164 if grep {$_ == 7} @operators;
  3         9  
5545 52 50       202 $self->{NUM_CHANGE_OPERATORS}++ if !$self->{CHANGE_WIDTH};
5546 52         102 $self->{CHANGE_WIDTH} = $y-128;
5547 52 50       187 $self->_spew(4, "$id * Change data width: %d", $self->{CHANGE_WIDTH}) if $Spew;
5548 52         100 push @operators, $x;
5549 52         117 $flow = 'next';
5550             } elsif ($x == 2) {
5551             # ^202 Change scale
5552             _croak "202 operator cannot be nested within 207 operator"
5553 24 50       66 if grep {$_ == 7} @operators;
  14         59  
5554 24 50       81 $self->{NUM_CHANGE_OPERATORS}++ if !$self->{CHANGE_SCALE};
5555 24         51 $self->{CHANGE_SCALE} = $y-128;
5556 24 50       78 $self->_spew(4, "$id * Change scale: %d", $self->{CHANGE_SCALE}) if $Spew;
5557 24         47 push @operators, $x;
5558 24         41 $flow = 'next';
5559             } elsif ($x == 3 && $y == 255) {
5560             # 203255 Terminate change reference value definition
5561             $self->_spew(4, "$id * Terminate reference value definition %s",
5562             '203' . (defined $self->{CHANGE_REFERENCE_VALUE}
5563 6 0       20 ? sprintf("%03d", $self->{CHANGE_REFERENCE_VALUE}) : '???')) if $Spew;
    50          
5564             _complain("$id no current change reference value to terminate")
5565 6 50       23 unless defined $self->{CHANGE_REFERENCE_VALUE};
5566 6         25 undef $self->{CHANGE_REFERENCE_VALUE};
5567 6         14 $flow = 'next';
5568             } elsif ($x == 3) {
5569             # ^203 Change reference value
5570             _croak "203 operator cannot be nested within 207 operator"
5571 6 50       16 if grep {$_ == 7} @operators;
  6         27  
5572 6 50       20 $self->_spew(4, "$id * Change reference value") if $Spew;
5573             # Get reference value from data stream ($y == number of bits)
5574 6 50       32 $self->{NUM_CHANGE_OPERATORS}++ if !$self->{CHANGE_REFERENCE_VALUE};
5575 6         10 $self->{CHANGE_REFERENCE_VALUE} = $y;
5576 6         13 push @operators, $x;
5577 6         16 $flow = 'next';
5578             } elsif ($x == 4) {
5579             # ^204 Add associated field
5580 22 100       51 if ($y > 0) {
5581             _croak "$id Nesting of Add associated field is not implemented"
5582 11 50       31 if $self->{ADD_ASSOCIATED_FIELD};
5583 11         21 $self->{ADD_ASSOCIATED_FIELD} = $y;
5584 11         71 $flow = 'next';
5585             } else {
5586             _complain "$id No previous Add associated field"
5587 11 50       47 unless defined $self->{ADD_ASSOCIATED_FIELD};
5588 11         25 undef $self->{ADD_ASSOCIATED_FIELD};
5589 11         20 $flow = 'next';
5590             }
5591             } elsif ($x == 5) {
5592             # ^205 Signify character (i.e. the following $y bytes is
5593             # character information)
5594 0         0 $flow = 'signify_character';
5595             } elsif ($x == 6) {
5596             # ^206 Signify data width for the immediately following local
5597             # descriptor. If we find this local descriptor in BUFR table B
5598             # with data width $y bits, we assume we can use this table
5599             # entry to decode/encode the value properly, and can just
5600             # ignore the operator descriptor. Else we skip the local
5601             # descriptor and the corresponding value if decoding, or have
5602             # to give up if encoding
5603 24         52 my $ff = substr($next_id,0,1);
5604 24 50       64 _croak("Descriptor $next_id following Signify data width"
5605             . " operator $_ is not an element descriptor")
5606             if $ff != 0;
5607 24 100       54 if ($Strict_checking) {
5608 8         17 my $xx = substr($next_id,1,2);
5609 8         15 my $yy = substr($next_id,3,3);
5610 8 50 33     53 _complain("Descriptor $next_id following Signify data width"
5611             . " operator $id is not a local descriptor")
5612             if ($xx < 48 && $yy < 192);
5613             }
5614 24 100 66     168 if (exists $self->{B_TABLE}->{$next_id}
5615             and (split /\0/, $self->{B_TABLE}->{$next_id})[-1] == $y) {
5616 16 50       53 $self->_spew(4, "Found $next_id with data width $y, ignoring $id") if $Spew;
5617 16         34 $flow = 'next';
5618             } else {
5619             _croak "Cannot encode descriptor $next_id (following $id), not found in table B"
5620 8 50       26 if $self->{CODING} eq 'ENCODE';
5621 8 50       17 $self->_spew(4, "$_: Did not find $next_id in table B."
5622             . " Skipping $id and $next_id.") if $Spew;
5623 8         31 $pos += $y; # Skip next $y bits in bitstream if decoding
5624 8         15 $flow = 'skip';
5625             }
5626              
5627             } elsif ($x == 7) {
5628             # ^207 Increase scale, reference value and data width
5629             _croak "207 operator cannot be nested within 201/202/203 operators"
5630 3 0 0     12 if grep {$_ == 1 || $_ == 2 || $_ == 3} @operators;
  0 50       0  
5631 3 50       14 $self->{NUM_CHANGE_OPERATORS}++ if !$self->{CHANGE_SRW};
5632 3         7 $self->{CHANGE_SRW} = $y;
5633 3 50       9 $self->_spew(4, "$id * Increase scale, reference value and data width: %d", $y) if $Spew;
5634 3         14 push @operators, $x;
5635 3         8 $flow = 'next';
5636             } elsif ($x == 8) {
5637             # ^208 Change data width for ascii data
5638 2 50       6 $self->{NUM_CHANGE_OPERATORS}++ if !$self->{CHANGE_CCITTIA5_WIDTH};
5639 2         32 $self->{CHANGE_CCITTIA5_WIDTH} = $y*8;
5640 2 50       8 $self->_spew(4, "$id * Change width for CCITTIA5 field: %d bytes", $y) if $Spew;
5641 2         6 push @operators, $x;
5642 2         5 $flow = 'next';
5643             } elsif ($x == 9) {
5644             # ^209 IEEE floating point representation
5645 0         0 _croak "$id IEEE floating point representation (not implemented)";
5646             } elsif ($x == 21) {
5647             # ^221 Data not present
5648 0         0 _croak "$id Data not present (not implemented)";
5649             } elsif ($x == 22 && $y == 0) {
5650             # 222000 Quality information follows
5651 40         70 push @{ $self->{BITMAP_OPERATORS} }, '222000';
  40         134  
5652 40         111 $self->{NUM_BITMAPS}++;
5653             # Mark that a bit map probably needs to be built
5654 40         81 $self->{BUILD_BITMAP} = 1;
5655 40         78 $self->{BITMAP_INDEX} = 0;
5656 40 100       121 $flow = $Noqc ? 'last' : 'no_value';
5657             } elsif ($x == 23 && $y == 0) {
5658             # 223000 Substituted values follow, each one following a
5659             # descriptor 223255. Which value they are a substitute for is
5660             # defined by a bit map, which already may have been defined
5661             # (if descriptor 23700 is encountered), or will shortly be
5662             # defined by data present indicators (031031)
5663 3         8 push @{ $self->{BITMAP_OPERATORS} }, '223000';
  3         9  
5664 3         10 $self->{NUM_BITMAPS}++;
5665             # Mark that a bit map probably needs to be built
5666 3         7 $self->{BUILD_BITMAP} = 1;
5667 3         8 $self->{BITMAP_INDEX} = 0;
5668 3         9 $flow = 'no_value';
5669             } elsif ($x == 23 && $y == 255) {
5670             # 223255 Substituted values marker operator
5671             _croak "$id No bit map defined"
5672             unless (defined $self->{CURRENT_BITMAP} || defined $self->{REUSE_BITMAP})
5673 60 50 33     292 && $self->{BITMAP_OPERATORS}[-1] eq '223000';
      33        
5674 60 50       115 if (defined $self->{REUSE_BITMAP}) {
5675             _croak "More 223255 encountered than current bit map allows"
5676 0 0       0 unless @{ $self->{REUSE_BITMAP}->[$isub] };
  0         0  
5677 0         0 $bm_idesc = undef;
5678             } else {
5679             _croak "More 223255 encountered than current bit map allows"
5680 60 50       82 unless @{$self->{CURRENT_BITMAP}};
  60         135  
5681 60         82 $bm_idesc = shift @{$self->{CURRENT_BITMAP}};
  60         123  
5682             }
5683 60         101 $flow = 'redo_bitmap';
5684             } elsif ($x == 24 && $y == 0) {
5685             # 224000 First order statistical values follow
5686 4         13 push @{ $self->{BITMAP_OPERATORS} }, '224000';
  4         15  
5687 4         46 $self->{NUM_BITMAPS}++;
5688             # Mark that a bit map probably needs to be built
5689 4         11 $self->{BUILD_BITMAP} = 1;
5690 4         9 $self->{BITMAP_INDEX} = 0;
5691 4         13 $flow = 'no_value';
5692             } elsif ($x == 24 && $y == 255) {
5693             # 224255 First order statistical values marker operator
5694             _croak "$id No bit map defined"
5695             unless (defined $self->{CURRENT_BITMAP} || defined $self->{REUSE_BITMAP})
5696 4 50 33     43 && $self->{BITMAP_OPERATORS}[-1] eq '224000';
      33        
5697 4 50       11 if (defined $self->{REUSE_BITMAP}) {
5698             _croak "More 224255 encountered than current bit map allows"
5699 0 0       0 unless @{ $self->{REUSE_BITMAP}->[$isub] };
  0         0  
5700 0         0 $bm_idesc = undef;
5701             } else {
5702             _croak "More 224255 encountered than current bit map allows"
5703 4 50       8 unless @{$self->{CURRENT_BITMAP}};
  4         17  
5704 4         8 $bm_idesc = shift @{$self->{CURRENT_BITMAP}};
  4         9  
5705             }
5706 4         8 $flow = 'redo_bitmap';
5707             } elsif ($x == 25 && $y == 0) {
5708             # 225000 Difference statistical values follow
5709 0         0 push @{ $self->{BITMAP_OPERATORS} }, '225000';
  0         0  
5710 0         0 $self->{NUM_BITMAPS}++;
5711             # Mark that a bit map probably needs to be built
5712 0         0 $self->{BUILD_BITMAP} = 1;
5713 0         0 $self->{BITMAP_INDEX} = 0;
5714 0         0 $flow = 'no_value';
5715             } elsif ($x == 25 && $y == 255) {
5716             # 225255 Difference statistical values marker operator
5717             _croak "$id No bit map defined\n"
5718             unless (defined $self->{CURRENT_BITMAP} || defined $self->{REUSE_BITMAP})
5719 0 0 0     0 && $self->{BITMAP_OPERATORS}[-1] eq '225000';
      0        
5720 0 0       0 if (defined $self->{REUSE_BITMAP}) {
5721             _croak "More 225255 encountered than current bit map allows"
5722 0 0       0 unless @{ $self->{REUSE_BITMAP}->[$isub] };
  0         0  
5723 0         0 $bm_idesc = undef;
5724             } else {
5725             _croak "More 225255 encountered than current bit map allows"
5726 0 0       0 unless @{$self->{CURRENT_BITMAP}};
  0         0  
5727 0         0 $bm_idesc = shift @{$self->{CURRENT_BITMAP}};
  0         0  
5728             }
5729             # Must remember to change data width and reference value
5730 0 0       0 $self->{NUM_CHANGE_OPERATORS}++ if !$self->{DIFFERENCE_STATISTICAL_VALUE};
5731 0         0 $self->{DIFFERENCE_STATISTICAL_VALUE} = 1;
5732 0         0 $flow = 'redo_bitmap';
5733             } elsif ($x == 32 && $y == 0) {
5734             # 232000 Replaced/retained values follow, each one following a
5735             # descriptor 232255. Which value they are a replacement for is
5736             # defined by a bit map, which already may have been defined
5737             # (if descriptor 23700 is encountered), or will shortly be
5738             # defined by data present indicators (031031)
5739 2         5 push @{ $self->{BITMAP_OPERATORS} }, '232000';
  2         11  
5740 2         7 $self->{NUM_BITMAPS}++;
5741             # Mark that a bit map probably needs to be built
5742 2         5 $self->{BUILD_BITMAP} = 1;
5743 2         5 $self->{BITMAP_INDEX} = 0;
5744 2         5 $flow = 'no_value';
5745             } elsif ($x == 32 && $y == 255) {
5746             # 232255 Replaced/retained values marker operator
5747             _croak "$id No bit map defined"
5748             unless (defined $self->{CURRENT_BITMAP} || defined $self->{REUSE_BITMAP})
5749 4 50 33     44 && $self->{BITMAP_OPERATORS}[-1] eq '232000';
      33        
5750 4 50       10 if (defined $self->{REUSE_BITMAP}) {
5751             _croak "More 232255 encountered than current bit map allows"
5752 0 0       0 unless @{ $self->{REUSE_BITMAP}->[$isub] };
  0         0  
5753 0         0 $bm_idesc = undef;
5754             } else {
5755             _croak "More 232255 encountered than current bit map allows"
5756 4 50       6 unless @{$self->{CURRENT_BITMAP}};
  4         11  
5757 4         19 $bm_idesc = shift @{$self->{CURRENT_BITMAP}};
  4         11  
5758             }
5759 4         7 $flow = 'redo_bitmap';
5760             } elsif ($x == 35 && $y == 0) {
5761             # 235000 Cancel backward data reference
5762 2         6 undef $self->{REUSE_BITMAP};
5763 2         5 $self->{BACKWARD_DATA_REFERENCE} = $self->{NUM_BITMAPS} + 1;
5764 2         4 $flow = 'no_value';
5765             } elsif ($x == 36 && $y == 0) {
5766             # 236000 Define data present bit map
5767 18         67 undef $self->{CURRENT_BITMAP};
5768 18         37 $self->{BUILD_BITMAP} = 1;
5769 18         36 $self->{BITMAP_INDEX} = 0;
5770 18         37 $flow = 'no_value';
5771             } elsif ($x == 37 && $y == 0) {
5772             # 237000 Use defined data present bit map
5773             _croak "$id No previous bit map defined"
5774 24 50       68 unless defined $self->{BITMAPS};
5775 24         50 my %hash = @{ $self->{BITMAPS}->[$self->{NUM_BITMAPS}-1]->[$isub] };
  24         260  
5776 24         253 $self->{REUSE_BITMAP}->[$isub] = [sort {$a <=> $b} keys %hash];
  559         818  
5777 24         112 $flow = 'no_value';
5778             } elsif ($x == 37 && $y == 255) {
5779             # 237255 Cancel 'use defined data present bit map'
5780             _complain("$id No data present bit map to cancel")
5781 0 0       0 unless defined $self->{REUSE_BITMAP};
5782 0         0 undef $self->{REUSE_BITMAP};
5783 0         0 $flow = 'next';
5784             } elsif ($x == 41 && $y == 0) {
5785             # 241000 Define event
5786 0         0 _croak "$id Define event (not implemented)";
5787             } elsif ($x == 41 && $y == 255) {
5788             # 241255 Cancel define event
5789 0         0 _croak "$id Cancel define event (not implemented)";
5790             } elsif ($x == 42 && $y == 0) {
5791             # 242000 Define conditioning event
5792 0         0 _croak "$id Define conditioning event (not implemented)";
5793             } elsif ($x == 42 && $y == 255) {
5794             # 242255 Cancel define conditioning event
5795 0         0 _croak "$id Cancel define conditioning event (not implemented)";
5796             } elsif ($x == 43 && $y == 0) {
5797             # 243000 Categorial forecast values follow
5798 0         0 _croak "$id Categorial forecast values follow (not implemented)";
5799             } elsif ($x == 43 && $y == 255) {
5800             # 243255 Cancel categorial forecast values follow
5801 0         0 _croak "$id Cancel categorial forecast values follow (not implemented)";
5802             } else {
5803 0         0 _croak "$id Unknown data description operator";
5804             }
5805              
5806 387         1443 return ($pos, $flow, $bm_idesc, @operators);
5807             }
5808              
5809             ## Extract data from selected subsets in selected bufr objects, joined
5810             ## into a single ($data_refs, $desc_refs), to later be able to make a
5811             ## single BUFR message by calling encode_message. Also returns number
5812             ## of subsets extracted.
5813             sub join_subsets {
5814 1     1 0 17 my $self = shift;
5815 1         4 my (@bufr, @subset_list);
5816 1         0 my $last_arg_was_bufr;
5817 1         2 my $num_objects = 0;
5818 1         3 while (@_) {
5819 3         4 my $arg = shift;
5820 3 100       8 if (ref($arg) eq 'Geo::BUFR') {
    50          
5821 2         3 $bufr[$num_objects++] = $arg;
5822 2         3 $last_arg_was_bufr = 1;
5823             } elsif (ref($arg) eq 'ARRAY') {
5824 1 50       3 _croak "Wrong input (multiple array refs) to join_subsets"
5825             unless $last_arg_was_bufr;
5826 1         4 $subset_list[$num_objects-1] = $arg;
5827 1         1 $last_arg_was_bufr = 0;
5828             } else {
5829 0         0 _croak "Input is not Geo::BUFR object or array ref in join_subsets";
5830             }
5831             }
5832              
5833 1         11 my ($data_refs, $desc_refs);
5834 1         2 my $n = 1; # Number of subsets included
5835             # Ought to check for common section 3 also?
5836 1         4 for (my $i=0; $i < $num_objects; $i++) {
5837 2         6 $bufr[$i]->rewind();
5838 2         2 my $isub = 1;
5839 2 100       5 if (!exists $subset_list[$i]) { # grab all subsets from this object
5840 1         7 while (not $bufr[$i]->eof()) {
5841 2         5 my ($data, $descriptors) = $bufr[$i]->next_observation();
5842 2 100       6 last if !$data;
5843 1 50       5 $self->_spew(2, "Joining subset %d from bufr object %d", $isub, $i) if $Spew;
5844 1         2 $data_refs->[$n] = $data;
5845 1         2 $desc_refs->[$n++] = $descriptors;
5846 1         14 $isub++;
5847             }
5848             } else { # grab the subsets specified, also inserting them in the specified order
5849 1         2 my $num_found = 0;
5850 1         4 while (not $bufr[$i]->eof()) {
5851 4         8 my ($data, $descriptors) = $bufr[$i]->next_observation();
5852 4 100       9 last if !$data;
5853 3         12 my $index = _get_index_in_list($subset_list[$i], $isub);
5854 3 100       4 if (defined $index) {
5855 2 50       9 $self->_spew(2, "Joining subset %d from subset %d"
5856             . " in bufr object %d", $isub, $index, $i) if $Spew;
5857 2         5 $data_refs->[$n + $index] = $data;
5858 2         3 $desc_refs->[$n + $index] = $descriptors;
5859 2         4 $num_found++;
5860             }
5861 3         7 $isub++;
5862             }
5863             _croak "Mismatch between number of subsets found ($num_found) and "
5864 0         0 . "expected from argument [@{$subset_list[$i]}] to join_subsets"
5865 1 50       2 if $num_found != @{$subset_list[$i]};
  1         3  
5866 1         59 $n += $num_found;
5867             }
5868 2         7 $bufr[$i]->rewind();
5869             }
5870 1         2 $n--;
5871 1         5 return ($data_refs, $desc_refs, $n)
5872             }
5873              
5874             1; # Make sure require or use succeeds.
5875              
5876              
5877             __END__