File Coverage

blib/lib/Geo/BUFR.pm
Criterion Covered Total %
statement 921 2853 32.2
branch 398 1906 20.8
condition 89 679 13.1
subroutine 57 145 39.3
pod 0 92 0.0
total 1465 5675 25.8


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