File Coverage

blib/lib/Geo/BUFR.pm
Criterion Covered Total %
statement 934 2884 32.3
branch 405 1926 21.0
condition 94 695 13.5
subroutine 57 146 39.0
pod 0 93 0.0
total 1490 5744 25.9


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