File Coverage

blib/lib/Geo/BUFR.pm
Criterion Covered Total %
statement 926 2877 32.1
branch 401 1924 20.8
condition 89 685 12.9
subroutine 57 146 39.0
pod 0 93 0.0
total 1473 5725 25.7


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