Printer image icon. Send To Printer
NCEP Home > NCO Home > Systems Integration Branch > Decoders > BUFRLIB > BUFRLIB Table of Contents > Other Useful Subroutines
Printer image icon. Printer Friendly Version

Other Useful BUFRLIB Subroutines

First, we will discuss how to interface an application program with the BUFRLIB software in a whole new way! Specifically, the reader may recall, from our initial discussion, that the BUFRLIB software normally reads/writes BUFR messages directly from/to a BUFR file that is physically stored on the local system and interfaced to the software via logical unit number LUBFR. However, it is also possible to read/write these same BUFR messages directly from/to a memory array within the application program itself, in order to provide users with greater flexibility from an input/output perspective:

	CALL READERME  ( IBFMSG, LUBFR, CSUBSET, IDATE, IRET )

	Input argument:
	    IBFMSG	INTEGER(*)	BUFR message
	    LUBFR	INTEGER		Logical unit for BUFR file

	Output arguments:
	    CSUBSET	CHAR*(*)	Table A mnemonic for IBFMSG
	    IDATE	INTEGER		Section 1 date-time for IBFMSG
	    IRET	INTEGER		Return code:
					  0 = normal return
					 -1 = could not read IBFMSG

This subroutine looks and behaves a lot like the already-familiar READMG, except that here we have one additional input argument IBFMSG which contains the BUFR message to be read by the software. As such, READERME can be used in any context in which READMG might otherwise be used, and, further, from that point on, the application program can proceed with the usual calls to READSB (and, subsequently UFBINT, UFBREP, UFBSEQ, etc.), just as though READMG had previously been used, so that there is no further modification required within the application program in order to use this input mechanism.

However, do note that, when using READERME, it is still necessary for the application program to have previously called subroutine OPENBF in order to associate a DX BUFR tables file with the messages that are being input via IBFMSG, and it is still also necessary to pass in the relevant LUBFR value as a call argument to READERME even though, in this case, the software will not actually try to read from the associated logical unit. Note also that, for READERME, the meaning of the output argument IRET is subtly different than was the case for READMG, in that a return value of -1 now simply indicates that the particular input BUFR message in question could not be successfully read for some reason but that the interface to the BUFRLIB software remains open and, therefore, subsequent calls to READERME with new input BUFR messages may still be attempted.

Now, for the opposite case where we wish to output encoded messages from the BUFRLIB software back into the application program, we have:

	CALL WRITSA  ( LUBFR, MXIBFMSG, IBFMSG, LIBFMSG )

	Input argument:
	    LUBFR	INTEGER		Logical unit for BUFR file
            MXIBFMSG    INTEGER         Dimensioned size of IBFMSG array

	Output arguments:
	    IBFMSG	INTEGER(*)	BUFR message
	    LIBFMSG	INTEGER		Length of IBFMSG
This subroutine is designed to be called in the same context as the already-familiar WRITSB, in that it indicates to the BUFRLIB software that all of the necessary data values for the current data subset have been stored via appropriate preceding calls to UFBINT, UFBREP, etc., and therefore the subset is ready to be encoded and packed into the current message for the BUFR file associated with logical unit LUBFR. However, in the case of WRITSA, each completed BUFR message is passed back to the application program within the array IBFMSG, and the second output argument LIBFMSG indicates the number of array elements of IBFMSG that are occupied by the message. Note that the dimensioned size of IBFMSG (in INTEGER words!) must also be passed in as input, in order to prevent the subroutine from possibly overflowing this array.

When using WRITSA, it becomes important to realize that, since the BUFRLIB software is designed to pack as many data subsets as possible into each message, not every call to WRITSA will result in a message being returned. In such cases, the second output argument LIBFMSG will contain the value 0, indicating that no message (or, more literally, a message of length 0!) was returned. In other words, only when LIBFMSG contains a value greater than 0 is there a BUFR message within array IBFMSG; otherwise, the message into which the data subset was packed remains internally within BUFRLIB so that future data subsets can be packed into it as well, and the message will eventually be returned during some other future call to WRITSA. This, however, leads to the question of what to do if we have no more data subsets to store and thus were not planning to make any more future calls to WRITSA from the application program - how do we retrieve that last message that still remains internally within BUFRLIB before we exit our program? The answer is to call WRITSA one last time, but in a special way. Specifically, if we call WRITSA with the input argument LUBFR set to the additive inverse of its usual value (i.e. (-1) * LUBFR), then this is a signal to WRITSA that we are not storing a new data subset but, rather, wish to forcibly retrieve the current internal BUFR message associated with logical unit LUBFR. By doing this, we are assured that all BUFR message output has been returned to the application program.

Note, however, that a similar special final call is never required for WRITSB (i.e. which is used when directing output BUFR messages to a file on the local system), because, as was discussed earlier, the subsequent required call to CLOSBF for logical unit LUBFR will ensure that all available BUFR output is properly flushed to that logical unit before closing the associated file. This brings up another important point, which is to say that, even when we are using WRITSA instead of WRITSB, and are therefore receiving the output BUFR messages via a memory array within the application program, these messages are nonetheless still also being written out to the file associated with LUBFR, just like when we are using WRITSB! This rather surprising fact is due solely to the overall design of the BUFRLIB software; however, there is a way around this problem for the user who does not want to allocate an actual file to hold this extra copy of the BUFR output. Specifically, the solution is to use the special input string of 'NUL' (rather than 'OUT') for the CIO call argument to OPENBF when identifying this LUBFR to the BUFRLIB software. That way, even though LUBFR must still contain a unique integer value, the BUFRLIB software will never actually try to write anything out to that logical unit number, and thus there is no need for an actual file to be associated with that LUBFR value on the local system!

As a final note, before we leave this overall discussion on interfacing BUFRLIB with a memory array in an application program, the reader may have noticed that, for both of the above subroutines, the IBFMSG call argument, whereby the BUFR message is actually passed into or out of the BUFRLIB software (depending on the case), is an INTEGER array rather than a CHARACTER array as might be expected. This is due solely to the way that the BUFRLIB software functions internally, but a user can easily overcome this issue, if necessary, via an appropriate EQUIVALENCE between an array of each type within the application program itself.


Now, here are some additional subroutines that might be of interest:

	CALL DATELEN  ( LEN )

	Input argument:
	    LEN		INTEGER		Length of Section 1 date-time values to
					be output by message-reading subroutines
					such as READMG, READERME, etc.
					  8 =   YYMMDDHH (i.e. 2-digit year)
					 10 = YYYYMMDDHH (i.e. 4-digit year)

This subroutine allows the user to specify the format for the IDATE output argument that is returned by each subsequent call to any of the subroutines, such as READMG or READERME, which read BUFR messages from a BUFR file that is open for input. This subroutine may be called at any time from the user's application program in order to set or reset this date-time format accordingly, and in which case the new format will then apply for all future calls to any of the message-reading subroutines (or until it is changed again via a new call to DATELEN!). Alternatively, if DATELEN is never called, then a default value of LEN = 8 (i.e. YYMMDDHH) applies.

Note that, in the case of a BUFR file that is open for output, there is no need to ever call this subroutine, because the corresponding message-writing subroutines such as OPENMB and OPENMG will automatically accept the IDATE argument in either format (i.e. YYMMDDHH or YYYYMMDDHH) without any special effort on the part of the application program.


	CALL READNS  ( LUBFR, CSUBSET, IDATE, IRET )

	IRET = IREADNS  ( LUBFR, CSUBSET, IDATE )

	Input argument:
	    LUBFR	INTEGER		Logical unit for BUFR file

	Output arguments:
	    CSUBSET	CHAR*(*)	Table A mnemonic for data subset
	    IDATE	INTEGER		Section 1 date-time for data subset
	    IRET	INTEGER		Return code:
					  0 = normal return
					 -1 = no more BUFR data subsets in LUBFR

Subroutine READNS (or its corresponding functional equivalent IREADNS) provides a handy way to combine the functionalities of READMG/IREADMG and READSB/IREADSB into one simple call. The application program needs only to make the initial call to OPENBF in order to define the BUFR file that is to be read, and then each subsequent call to READNS (or IREADNS) will read the next new BUFR data subset from that file, all the while automatically opening and closing each new BUFR message as needed, so that subsequent calls may be immediately made to the data values routines such as UFBINT, UFBSEQ, etc.

At first, the concept of READNS/IREADNS may seem somewhat puzzling, since the call sequence is identical to that of READMG/IREADMG, which, as we've already seen, functions at the BUFR message level rather than the BUFR data subset level. However, the reader may also recall, from the discussion of subroutines OPENMG and OPENMB, that every BUFR data subset within a particular BUFR message must always have identical CSUBSET and IDATE values, and, in that context, it does make sense.


	CALL UFBTAB  ( LUBFR, R8ARR, MXMN, MXLV, NSUB, CMNSTR )

	Input arguments:
	    LUBFR	INTEGER		Logical unit for BUFR file
	    CMNSTR	CHAR*(*)	String of blank-separated mnemonics
					associated with R8ARR
	    MXMN	INTEGER		Size of first dimension of R8ARR
	    MXLV	INTEGER		Size of second dimension of R8ARR

	Output arguments:
	    R8ARR(*,*)	REAL*8		Data values read from BUFR file
	    NSUB	INTEGER		Number of data subsets in BUFR file

The call sequence for this subroutine looks similar to that of UFBINT, UFBREP and UFBSEQ. However, its usage is quite different, in that UFBTAB provides a mechanism whereby a user can do a quick scan of the range of values corresponding to one or more mnemnonics amongst all data subsets for an entire BUFR file, whereas, as we have seen, UFBINT, UFBREP and UFBSEQ only operate on one data subset at a time. More specifically, UFBTAB opens the input BUFR file connected to logical unit LUBFR (i.e. the application does not itself even have to call subroutine OPENBF!), reads through every BUFR message and data subset in the file, and, for each data subset, decodes (into the next-available row of R8ARR) the values corresponding to the mnemonics listed within CMNSTR, so that, in the end, the number of rows of R8ARR that are filled with data values is exactly the same as the number of data subsets (i.e. NSUB) that were contained within the BUFR file! The file itself is then automatically disconnected from the BUFRLIB software via an internal call to subroutine CLOSBF; therefore, UFBTAB serves as a convenient "all-in-one" subroutine call for reading specified data values from every data subset within a BUFR file!

There are some caveats, however, so beware! First and foremost, when using UFBTAB, the input value MXLV (and corresponding second dimension of R8ARR!) must now be larger than is normally the case for use with the UFBINT, UFBREP and UFBSEQ subroutines, since it now must be greater than or equal to the number of data subsets within the overall BUFR file, rather than just large enough to hold the maximum number of replications of any particular mnemonic within a single data subset. In addition, UFBTAB is normally not very useful whenever CMNSTR contains any mnemonic which is repeated or multiply-replicated within each data subset, since, in such cases, only the value corresponding to the first occurrence of that mnemonic will be returned. Finally, since the application program does not itself directly call subroutine OPENBF, then UFBTAB can only be used to read BUFR files which contain embedded DX BUFR tables information, since there is no way to specify a separate LUNDX value containing the equivalent information in an ASCII text file!


	CALL UFDUMP  ( LUBFR, LUPRT )

	Input arguments:
	    LUBFR	INTEGER		Logical unit for BUFR file
	    LUPRT	INTEGER		Logical unit for print output

This subroutine can be used to dump a verbose print listing of the contents of a data subset from an input BUFR file. The file must already be opened for input via a previous call to subroutine OPENBF, and a data subset must have subsequently been read into the internal BUFRLIB arrays via calls to READMG/IREADMG and READSB/IREADSB (or a single call to READNS/IREADNS!), at which point a call to UFDUMP will then dump a listing of the contents of that data subset to the file pointed to by logical unit LUPRT. This listing contains each mnemonic in the data subset, accompanied by its corresponding FXY number, definition, encoded data value and other potentially useful information such the scale factor, reference value and bit width that were used to encode the value. Therefore, and especially for large BUFR files containing many data subsets, there is the potential for a lot of print output to be generated, so be forewarned! Nevertheless, UFDUMP can be a useful way to quickly look at all of the data values within a subset, especially since, for mnemonics which are replicated, it will list all of the occurrences of that mnemonic, irrespective of the type of replication that was used. Furthermore, for mnemonics whose units are "CCITT IA5", the subroutine will automatically convert the corresponding values from REAL*8 to character and print them as such, and it will even print the descriptive string "MISSING" for all values which were encoded as missing within the corresponding subset.


	CALL DXDUMP  ( LUBFR, LDXOT )

	Input arguments:
	    LUBFR	INTEGER		Logical unit for BUFR file
	    LDXOT	INTEGER		Logical unit for output BUFR tables file

This subroutine provides a handy way to view the DX BUFR tables information that is embedded in the first few messages of a BUFR file. The user needs only to have identified the file to the BUFRLIB software via a prior call to subroutine OPENBF, and then a subsequent call to subroutine DXDUMP will unpack the embedded tables information and write it out to the file pointed to by logical unit LDXOT. The output file is written using the same ASCII-text table format described within the document Description and Format of DX BUFR Tables, so it is suitable for use as subsequent input to subroutine OPENBF via the call argument LUNDX. Subroutine DXDUMP can be most useful for learning the contents of archive BUFR files.



	IRET = IBFMS  ( R8VAL )

	Input argument:
	    R8VAL	REAL*8		Data value read from previous call to UFBINT,
                                        UFBREP, UFBSEQ, UFBTAB, etc.

	Output arguments:
	    IRET	INTEGER		Return code:
					  0 = R8VAL is not "missing"
					  1 = R8VAL is "missing"

This function provides a handy way to test whether a data value returned from a previous call to any of the subroutines UFBINT, UFBREP, UFBSEQ, UFBTAB, etc. contains the value of 10.0E10, which denotes "missing" in the context of the BUFRLIB software. As noted in a prior discussion, this means that the corresponding value was encoded as "missing" (i.e. all bits set to 1) within the actual BUFR data subset.

Although this function may seem fairly trivial, it does test using a fuzziness threshold to ensure that real numbers aren't tested for actual equality to each other. This in turn resolves most floating point representation discrepancies between different hardware platforms.


From our earlier discussions, it was noted that data values are normally read from or written to BUFR subsets using REAL*8 arrays, such as when the BUFRLIB subroutines UFBINT, UFBREP or UFBSEQ are used. In other words, individual data values are normally expected to fit within an 8-byte (i.e. 32-bit) variable. It was also noted that character (i.e. CCITT IA5) values were read and written in the same way using a REAL*8 variable, with the conversion of such values to or from actual character values being handled by the application program using a FORTRAN EQUIVALENCE or similar translation mechanism. However, there are often character values longer than 8 bytes which need to be read from or written into BUFR subsets, so the question then is how to handle such cases within the BUFRLIB software. The following two subroutines provide the solution.

First we'll show how to write "long" (i.e. greater than 8 byte) character strings into a BUFR data subset:

	CALL WRITLC  ( LUBFR, CHRSTR, CMNEM )

	Input arguments:
	    LUBFR	INTEGER		Logical unit for BUFR file
            CHRSTR	CHAR*(*)	Character value to be written into data subset
            CMNEM	CHAR*(*)	Mnemonic corresponding to CHRSTR

This subroutine will store CHRSTR into the current data subset as the value corresponding to mnemonic CMNEM. The actual length of CHRSTR, and therefore the actual number of characters to be stored, is determined automatically by the subroutine using the bit width for CMNEM as defined within the DX BUFR tables file corresponding to LUBFR.

When using this subroutine, there are a couple of caveats to be aware of. First of all, unlike when using subroutines UFBINT, UFBREP or UFBSEQ, any calls to subroutine WRITLC must be made after the call to subroutine WRITSB or WRITSA for the data subset in question. While this may seem counterintuitive, it is merely due to the way in which the BUFRLIB internally packs up subsets in 8-byte chunks by default. In either case, a BUFR message that is output by either of these subroutines never contains the data subset that was just packed during the call in question, so there is no danger that a subset will ever be written out before we've had a chance to modify its contents with a subsequent call to WRITLC!

Note also that the call sequence for WRITLC contains no inherent way of storing multiple occurrences of long character strings corresponding to the same mnemonic. So if the mnemonic CMNEM is replicated or otherwise repeated within the overall data subset definition, how can we store each successive CHRSTR into the proper location within the subset? The answer is to do a separate call for each such string, but modify CMNEM in each case to append the ordinal number of the occurrence in question. For example, if there are five occurrences of mnemonic LSTID within a given subset definition, then each successive occurrence can be stored by calling WRITLC a total of five times, with each respective CMNEM set to 'LSTID#1', 'LSTID#2', 'LSTID#3', 'LSTID#4' and 'LSTID#5'. Note that the first case is superfluous, since omitting the ordinal number always defaults to the first occurrence of a particular string, so we could just specify 'LSTID' instead of 'LSTID#1'.


Now that we know how to use WRITLC to write "long" (i.e. greater than 8 byte) character strings into a BUFR data subset, we'll show how to use its counterpart READLC for reading such strings from a subset:

	CALL READLC  ( LUBFR, CHRSTR, CMNEM )

	Input arguments:
	    LUBFR	INTEGER		Logical unit for BUFR file
            CMNEM	CHAR*(*)	Mnemonic indicating value to be read from
                                        data subset

        Output argument:
            CHRSTR	CHAR*(*)	Character value corresponding to CMNEM

The call sequence here is identical to that for WRITLC; however, in this case CHRSTR is an output value, so the user must allocate enough space for the resulting string within the application program. Subroutine READLC can be called at any time after the call to subroutine READSB, READNS or equivalent for the data subset in question, and note that the same ordinal notation used for WRITLC can also be used for mnemonics when calling READLC, in order to handle any cases where a particular mnemonic occurs multiple times within the overall subset definition. For example, calling READLC with CMNEM set to 'LSTID#4' will return the string corresponding to the 4th occurrence of mnemonic 'LSTID' within the overall subset definition.


	CALL UFBEVN  ( LUBFR, R8ARR, MXMN, MXLV, MXEV, NLV, CMNSTR )

	Input arguments:
	    LUBFR	INTEGER		Logical unit for BUFR file
	    CMNSTR	CHAR*(*)	String of blank-separated mnemonics
					associated with R8ARR
	    MXMN	INTEGER		Size of first dimension of R8ARR
	    MXLV	INTEGER		Size of second dimension of R8ARR
	    MXEV	INTEGER		Size of third dimension of R8ARR


	Output argument:
	    R8ARR(*,*,*)REAL*8		Data values read from data subset
	    NLV		INTEGER		Number of levels of data values
					read from data subset

This subroutine can be used to mimic the effect of a multiple-replication sequence within another multiple-replication sequence, because here our R8ARR array contains a third dimension, which allows for multiple replications of each data value in the otherwise two-dimensional R8ARR array that we looked at earlier during our previous discussion of UFBINT, UFBREP, and UFBSEQ. As was the case with those subroutines, the R8ARR array must be initially declared and dimensioned within the application program, and the input string CMNSTR corresponds to the values in the first dimension of R8ARR. However, there is also an important difference, in that subroutine UFBEVN can only be used when logical unit LUBFR points to a BUFR file that is open for input (i.e. reading). Therefore, the call arguments MXMN, MXLV and MXEV are always input arguments describing the actual dimensions of R8ARR. Furthermore, and despite the addition of a third dimension to the R8ARR array, note that there is no additional corresponding output argument which definitively indicates the number of third-dimension values that were read from the data subset; rather, this information must be indirectly inferred by the application program via direct inspection of the actual values returned within R8ARR.

In a DX BUFR tables file, the fact that a certain sequence of data values contains three dimensions (and, thus, that UFBEVN may be used to read them!) is indicated by enclosing the sequence within the special indicator characters "[ ]". An example of this is included within the sample program for decoding NCEP PREPBUFR files.


	(integer value) = IUPBS01  ( IBFMSG, S01MNEM )

	(integer value) = IUPVS01  ( LUBFR, S01MNEM )

	Input argument:
	    IBFMSG	INTEGER(*)	BUFR message
	    LUBFR	INTEGER		Logical unit for BUFR file
	    S01MNEM	CHAR*(*)	Mnemonic describing value to be decoded from
                                        within Section 0 or Section 1 of BUFR message:
                                         'LENM'  = Length (in bytes) of BUFR message
                                         'LEN0'  = Length (in bytes) of Section 0
                                         'BEN'   = BUFR edition number
                                         'LEN1'  = Length (in bytes) of Section 1
                                         'BMT'   = BUFR master table
                                         'OGCE'  = Originating center
                                         'GSES'  = Originating subcenter
                                         'USN'   = Update sequence number
                                         'ISC2'  = Flag indicating absence/presence of
                                                   (optional) Section 2 in BUFR message
                                                   0 = Section 2 absent
                                                   1 = Section 2 present
                                         'MTYP'  = Data category
                                         'MSBT'  = Data subcategory (local)
                                         'MSBTI' = Data subcategory (international)
                                         'MTV'   = Version number of master table
                                         'MTVL'  = Version number of local tables
                                         'YEAR'  = Year (4-digit)
                                         'MNTH'  = Month
                                         'DAYS'  = Day
                                         'HOUR'  = Hour
                                         'MINU'  = Minute
                                         'SECO'  = Second

	Output argument:
	    IUPBS1	INTEGER		Decoded value
                                              -1 = invalid S01MNEM input value

Either of these functions can be used to decode and return a specified value from Section 0 or Section 1 of an input BUFR message; the difference is in how the input BUFR message is provided to the function. In the case of IUPBS01, the BUFR message is directly input to the function as an integer array, with the start of the message aligned on the first 4 bytes of the array. Alternatively, IUPVS01 always operates directly on the BUFR message that was most recently read into the internal BUFRLIB arrays from logical unit LUBFR via a call to one of the routines READMG, IREADMG or READERME. In either case, the value to be decoded is specified via the descriptive mnemonic S01MNEM, whose possible values are listed above. Furthermore, either function will work correctly on any BUFR message encoded according to BUFR edition 2, 3 or 4.


	CALL PKVS01 ( S01MNEM, IVAL )

	CALL PKBS1  ( IVAL, IBFMSG, S01MNEM )

	Input argument:
	    IBFMSG	INTEGER(*)	BUFR message
	    IVAL	INTEGER		Value to be encoded within Section 0 or
                                        Section 1 of BUFR message
	    S01MNEM	CHAR*(*)	Mnemonic describing value to be encoded
                                        within Section 0 or Section 1 of BUFR message:
                                         'BEN'   = BUFR edition number
                                         'BMT'   = BUFR master table
                                         'OGCE'  = Originating center
                                         'GSES'  = Originating subcenter
                                         'USN'   = Update sequence number
                                         'MTYP'  = Data category
                                         'MSBT'  = Data subcategory (local)
                                         'MSBTI' = Data subcategory (international)
                                         'MTV'   = Version number of master table
                                         'MTVL'  = Version number of local tables
                                         'YEAR'  = Year (4-digit)
                                         'MNTH'  = Month
                                         'DAYS'  = Day
                                         'HOUR'  = Hour
                                         'MINU'  = Minute
                                         'SECO'  = Second

These subroutines in essence function as the logical inverses of, respectively, IUPVS01 and IUPBS01, in that they allow a user to directly specify a value to be encoded into Section 0 or Section 1 of a BUFR message for output, overriding the default value stored in that same location. As was the case with IUPVS01 and IUPBS01, a descriptive mnemonic S01MNEM is used to specify the value in question, and the choice of which subroutine to use depends solely on whether the BUFR message to be written is passed directly as a memory array (PKBS1) or stored internally within the BUFRLIB software (PKVS01). Either way, both subroutines will work correctly on any BUFR message encoded using BUFR edition 2, 3 or 4. However, note that the list of possible values that may be overwritten using these routines (as shown above) is somewhat shorter than the list of possible values that can be decoded using the inverse routines IUPVS01 and IUPBS01. Also, when using subroutine PKVS01, the new value IVAL actually remains in effect for all future BUFR messages output by any of the BUFRLIB subroutines such as WRITSB or WRITSA that are subsequently called from within the same application program! This means that a user can issue one call to PKVS01 at the beginning of his or her application program and have it remain in effect throughout the life of the application program (or until it is overridden by a subsequent call to PKVS01 with the same S01MNEM value!). Subroutine PKVS01 can even be called prior to the initial call to subroutine OPENBF if it is desired for the new value to also be included in any DX BUFR table information messages that are to be written to the beginning of the output file!


	(integer value) = IUPBS3  ( IBFMSG, S3MNEM )

	Input argument:
	    IBFMSG	INTEGER(*)	BUFR message
	    S3MNEM	CHAR*(*)	Mnemonic describing value to be decoded from
                                        within Section 3 of BUFR message:
                                         'NSUB'  = Number of data subsets in message 
                                         'IOBS'  = Flag indicating whether message
                                                   contains observed data:
                                                   0 = no
                                                   1 = yes
                                         'ICMP'  = Flag indicating whether message
                                                   contains compressed data:
                                                   0 = no
                                                   1 = yes

	Output argument:
	    IUPBS3	INTEGER		Decoded value
                                              -1 = invalid S3MNEM input value

This function can be used to decode and return a specified value from Section 3 of an input BUFR message. The message is directly input to the function as an integer array, with the start of the message aligned on the first 4 bytes of the array. The value to be decoded is specified via the descriptive mnemonic S3MNEM, whose possible values are listed above. The function will work correctly on any BUFR message encoded according to BUFR edition 2, 3 or 4, but note that it cannot be used to determine the list of data descriptors encoded within Section 3. Instead, for that we have the following separate subroutine:


	CALL UPDS3  ( IBFMSG, MXCDS3, CDS3, NDS3 )

	Input argument:
	    IBFMSG	INTEGER(*)	BUFR message
            MXCDS3      INTEGER         Dimensioned size of CDS3 array

	Output arguments:
	    CDS3	CHARACTER*6(*)	Decoded sequence of data descriptors
					from within Section 3 of IBFMSG
	    NDS3	INTEGER		Number of data descriptors within CDS3

This subroutine decodes and returns the complete sequence of data descriptors (i.e. FXY numbers) from Section 3 of the BUFR message contained within IBFMSG, and as such is especially useful in analyzing new or unfamiliar BUFR messages from unknown or unfamiliar sources. Specifically, the information returned by a call to UPDS3 can subsequently be used to generate a DX BUFR tables file in the ASCII text format required by the BUFRLIB software, and then the software itself can subsequently be used to read/decode these messages. However, it should be noted that UPDS3 does not recursively resolve FXY numbers that are themselves Table D (i.e. sequence) FXY numbers; rather, what is returned is the exact sequence of FXY numbers as it appears within Section 3. Note also that, as was the case with IUPBS01, the beginning of the BUFR message must be aligned on the first 4 bytes of the input array IBFMSG. Finally, note that the dimensioned size of the CDS3 array must be passed in as input, in order to prevent the subroutine from possibly overflowing this array.


	CALL GETLENS  ( IBFMSG, LL, LEN0, LEN1, LEN2, LEN3, LEN4, LEN5 )

	Input argument:
	    IBFMSG	INTEGER(*)	BUFR message
            LL          INTEGER         Number of last section whose length is to
                                        be decoded

	Output arguments:
	    LEN0	INTEGER		Length of Section 0
	    LEN1	INTEGER		Length of Section 1
	    LEN2	INTEGER		Length of Section 2
	    LEN3	INTEGER		Length of Section 3
	    LEN4	INTEGER		Length of Section 4
	    LEN5	INTEGER		Length of Section 5

This subroutine takes as input a BUFR message (the start of which, as before, must be aligned on the first 4 bytes of the integer array IBFMSG) and returns the lengths of each of the individual sections of that message, up to a specified point as indicated via the additional input variable LL. For example, setting LL to a value of 3 means that all of the lengths up to and including Section 3 (i.e. Sections 0, 1, 2 and 3) will be decoded. The subroutine will work correctly on any BUFR message encoded using BUFR edition 2, 3 or 4, and any section lengths that are not requested to be decoded are returned with a default value of -1 for the corresponding output argument.


	CALL CNVED4  ( IBFMSG, LOBFMSG, OBFMSG )

	Input argument:
	    IBFMSG	INTEGER(*)	BUFR message encoded using BUFR edition 3
	    LOBFMSG	INTEGER 	Dimensioned size of OBFMSG array

	Output arguments:
	    OBFMSG	INTEGER(*)	BUFR message encoded using BUFR edition 4

This subroutine reads an input BUFR message encoded using BUFR edition 3 and then generates and outputs an equivalent BUFR message encoded using BUFR edition 4. When using this subroutine, IBFMSG and OBFMSG must be separate arrays, and the dimensioned size of OBFMSG (in INTEGER words!) must also be passed in as input, in order to prevent the subroutine from possibly overflowing this array.

Note that there's an easy way to internally activate this subroutine on BUFR messages that are being generated for writing to an output file on the local system. Specifically, if subroutine PKVS01 is called with an input S01MNEM value of 'BEN' (i.e. BUFR edition number) and a corresponding input IVAL value of 4, then subroutine CNVED4 will be called internally for all future output BUFR messages written via calls to subroutine WRITSB or WRITSA within the same application program, meaning that all such output BUFR messages will be automatically encoded using BUFR edition 4. This is often an easier approach than directly calling CNVED4 from within the application program.


As we touched upon briefly earlier, the BUFRLIB software has a default limit of 10000 bytes for the maximum size of any message that will be created and written to a BUFR output file; however, it is very easy for a user to override this limit:

	CALL MAXOUT  ( MXMSIZ )

	Input argument:
	    MXMSIZ	INTEGER(*)	New maximum message length (in bytes)
                                        for all future output BUFR messages
                                          0 = set to maximum value possible

This subroutine can be called at any time after the initial call to subroutine OPENBF (which itself sets the default limit of 10000), and it remains in effect for all future output BUFR messages associated with all output BUFR files (i.e. logical unit LUBFR values) written by the BUFRLIB software within the same application program. A user may repeatedly call MAXOUT in order to repeatedly adjust the maximum message length as desired, or a value of 0 may be passed in which is a signal to the software to set the limit at whatever is the current maximum limit allowed by the software. But either way, it is important to note that this length is only ever just a maximum length and that some BUFR messages that are generated may, in fact, end up being much shorter than this value. For example, as we mentioned earlier during the discussion for subroutines OPENMG and OPENMB, the BUFRLIB software will in certain situations automatically close and flush (to logical unit LUBFR) any existing BUFR message within the internal arrays and initialize a new one. This behavior remains true for any value of MXMSIZ, even if the length of the previous internal message was still well below the maximum limit.


	CALL BVERS  ( CVERSTR )

	Output argument:
	    CVERSTR	CHAR*(*)	String containing BUFRLIB version

This subroutine can be called by an application program to determine the version of the BUFRLIB software currently in use. The output string CVERSTR must be dimensioned at least 8 bytes long in order to hold the resulting string.


	CALL ERRWRT  ( CSTR )

	Input argument:
	    CSTR	CHAR*(*)	Error string or other diagnostic to be written
                                        to user-specified destination

By default, when the BUFRLIB encounters an error or generates any type of diagnostic during processing, a message to that effect is written to standard output. However, if a user would like to direct such messages elsewhere, this can be done by providing an inline version of subroutine ERRWRT, using the calling sequence shown above, in order to override the default version of this subroutine contained within the BUFRLIB source code distribution. The user does not need to explicitly call this subroutine from within the application code, since the BUFRLIB automatically issues a call to ERRWRT when needed. Instead, the user simply needs to define his or her own subroutine with this same name and include it within the compilation, and then the input string CSTR can be logged or handled in any way of the user's choosing. In any case, note that the amount and types of messages generated can also be controlled by the user via a separate call to subroutine OPENBF with the argument CIO='QUIET' and an appropriate verbosity level indicator, as described earlier within the documentation.


	CALL CMPMSG  ( CF )

	Input argument:
	    CF		CHARACTER	Flag indicating whether BUFR messages output
                                        by future calls to subroutines WRITSB/WRITSA
                                        are to be compressed:
                                         'N' = no (the default)
                                         'Y' = yes

This subroutine call provides an easy way to specify whether future BUFR messages output via calls to subroutine WRITSB or WRITSA are to be compressed using the algorithm for data subset compression prescribed within the official WMO BUFR regulations. The default is 'N' (i.e. no) if left unspecified, but this value can easily be toggled on ('Y') and off ('N') for different calls to WRITSB or WRITSA for, e.g. different output BUFR files connected to different LUBFR logical unit numbers via separate prior calls to subroutine OPENBF. Either way, once a value for CF is set via a call to subroutine CMPMSG, it remains in effect for all messages written to all logical units within the same application program, unless or until it is overridden by a subsequent call to this same subroutine with the opposite value for CF.

Note that, from a high-level standpoint, it may seem reasonable to ask why one wouldn't always want to compress BUFR output messages (and, therefore, why the default value for CF isn't 'Y')? Without going into too much detail here about the specifics of the WMO BUFR compression algorithm, suffice it to say that compression only provides a real space-saving benefit when the subsets to be compressed are devoid of any delayed replication and when corresponding data values between different subsets within the same message contain minimal variation. In other words, the use of compression is often times not the best approach, and that's why it is built into the BUFRLIB software as a user-specified option rather than as a default.


Now, before we cover the next two subroutines, a bit of historical background is needed. Specifically, when the BUFRLIB software was originally written, it was intended to be used to exchange BUFR data internally within NCEP. Because of this, and owing also to the slower computer processing speeds that were available at the time, certain non-standard "enhancements" were written into the software to provide for faster encoding and decoding. For instance, the software will normally, when writing/encoding a BUFR message, insert byte counters in front of each Section 4 data subset in order to allow subsequent reads of that same message to be faster and more efficient. Also, liberal use is made of bit padding, and Section 3 is kept as short as possible by representing the entire data subset via the use of the FXY number that is associated with the top-level Table A mnemonic and, therefore, which is itself almost invariably a local (i.e. non WMO-standard) FXY number. It should be pointed out that all of these "enhancements" are entirely legal, since local FXY numbers are permitted to be used in a BUFR message and, in fact, are actually used by BUFRLIB to denote the existence of the aforementioned byte counters and bit pads. However, and quite obviously, any alternative BUFR decoding algorithm other than the NCEP BUFRLIB software would not be able to cleanly decode such messages without prior knowledge of the meaning of all such local FXY numbers and "enhancements", and, therefore, it is strongly recommended to use one of the following additional subroutines when writing/encoding BUFR messages that are intended to or could possibly be read by persons using software other than the NCEP BUFRLIB software:

	CALL STNDRD  ( LUBFR, IBFMSG, LOBFMSG, OBFMSG )

	CALL STDMSG  ( CF )

	Input arguments:
	    LUBFR	INTEGER		Logical unit for BUFR file
	    IBFMSG	INTEGER(*)	BUFR message
	    LOBFMSG	INTEGER 	Dimensioned size of OBFMSG array
	    CF		CHARACTER	Flag indicating whether BUFR messages output
                                        by future calls to subroutines WRITSB/WRITSA
                                        are to be standardized:
                                         'N' = no (the default)
                                         'Y' = yes

	Output arguments:
	    OBFMSG	INTEGER(*)	Standardized copy of IBFMSG

Subroutine STNDRD takes as input a BUFR message, such as would have previously been output by, e.g. subroutine WRITSA, and using the DX BUFR tables information associated with logical unit LUBFR, outputs a standardized version of this same message within array OBFMSG, which must itself be a separate array from IBFMSG. This "standardization" involves removing all references to the aforementioned byte counters and bit pads from Section 4 as well as replacing the Table A FXY number from Section 3 of IBFMSG with an equivalent sequence of lower-level Table B, Table C, Table D and/or replication FXY numbers which directly constitute that Table A FXY number and which are themselves all WMO-standard. The result is that the new message within OBFMSG is now entirely and strictly standard according to the WMO BUFR regulations.

The alternative subroutine STDMSG provides an easy way to standardize messages in an inline fashion, similar to how the aforementioned subroutine CNVED4 can be called in an inline fashion via a special call to subroutine PKVS01 (see above). Specifically, if STDMSG is called with a value of 'Y', then subroutine STNDRD will be called internally for all future output BUFR messages written via calls to subroutine WRITSB or WRITSA within the same application program, meaning that all such output BUFR messages will already be automatically standardized without the user having to directly call STNDRD on his/her own from within the application program! Note however that, as was the case similarly with the aforementioned subroutine CMPMSG for compression, any call to STDMSG will likewise remain in effect for all future calls to WRITSB or WRITSA within the same application program, unless or until it is superseded by a subsequent call to STDMSG with the opposite value for CF.

Note that the above subroutines STNDRD, STDMSG, CNVED4 and CMPMSG are only applicable when writing/encoding BUFR messages for output. Alternatively, when reading/decoding BUFR messages that may or may not contain standardization, compression and/or different editions of BUFR, the BUFRLIB software, via the message-reading subroutines such as READMG, READERME, etc. will automatically handle any such variations or combinations thereof, all without any special additional effort on the part of the user's application program!


During the earlier discussion of subroutine OPENBF, we noted that the BUFRLIB software, by default, handles file input and output via the use of this subroutine, and where the actual opening of the underlying system file is handled at the application level where the file is also associated with a FORTRAN logical unit number before issuing the call to OPENBF. Having said that, it is possible to read and write BUFR messages to or from system files using an alternative C language interface. Each of the following functions can be called from application programs written in either FORTRAN or C, but for demonstration purposes we will use the FORTRAN syntax during the remainder of the discussion.

In order to use this interface, we must first open the system file in question:

        CALL COBFL  ( BFL, IO )

        Input arguments:
            BFL         CHAR*(*)        System file to be opened
            IO          CHARACTER       Flag indicating how BFL is to be opened:
                                         'r' = reading (input)
                                         'w' = writing (output)

This function opens the specified system file as either an input file (i.e. file of existing BUFR messages to be decoded) or an output file (i.e. location where BUFR messages will be written using the interface). The file name can be up to 120 characters and may include directory prefixes or any other notation allowed by the underlying filesystem. Any errors will be automatically logged to standard output or to an alternate location previously specified via the use of subroutine ERRWRT. Note that, when using this interface, it is only possible to have two system files open simultaneously (i.e. at most one for input and at most one for output), whereas it was previously noted that the FORTRAN OPENBF interface allows up to 32 simultaneous open files with no limit on how many of these may be used for input or output.

Now, if we have opened a system file for input, we can use the following function to read each successive BUFR message from the file:

        CALL CRBMG  ( MXMB, BMG, NMB, IRET )

        Input arguments:
            MXMB        INTEGER         Dimensioned size (in bytes) of BMG array 

        Output arguments:
            BMG         CHARACTER*1 (*) BUFR message
            NMB         INTEGER         Size (in bytes) of BUFR message in BMG
            IRET        INTEGER         Return code:
                                          0 = normal return
                                          1 = overflow of BMG array
                                          2 = "7777" indicator not found in
                                              expected location
                                         -1 = end-of-file encountered while reading
                                         -2 = I/O error encountered while reading

Likewise, if we have opened a system file for output, we can use the following function to write each successive BUFR message to this file:

        CALL CWBMG  ( BMG, NMB, IRET )

        Input arguments:
            BMG         CHARACTER*1 (*) BUFR message
            NMB         INTEGER         Size (in bytes) of BUFR message in BMG

        Output arguments:
            IRET        INTEGER         Return code:
                                          0 = normal return
                                         -1 = I/O error encountered while writing

In either case, note that there is no need to explicitly specify which system file we want to read to or write from, since this was already specified during the previous call to COBFL and, as we noted, there is at most one input file and one output file permitted to be open simultaneously when using this interface; therefore, there is no danger of ambiguity. Note that when reading BUFR messages using CRBMG, the dimensioned size (in bytes) of the BMG array must be passed in as input, in order to prevent the function from possibly overflowing this array.

Once we're done reading and/or writing BUFR messages, the system files themselves can be closed and the interface disconnected via a final call to the following function. Even if there is a file open for input and a separate one open for output, only a single call to the following function is needed to close both of them. There is no call argument required.

        CALL CCBFL
Note that the above set of C language interface functions create a number of new possibilities for overall application program design. For example, the above interface could be used to easily read each complete BUFR message from a system file using CRBMG, and then each such message could be passed as input to subroutine READERME for subsequent processing via READSB, UFBINT, UFBREP, etc. as discussed previously. This is demonstrated in one of the example programs included with the distribution. Conversely, an application program could also be developed which uses the BUFRLIB to generate BUFR messages for output and which passes them back to the application program using subroutine WRITSA, so that each such message could then be written out to a local system file using CWBMG as shown above.


In version 10.1.0 and earlier versions of BUFRLIB, the writing out of BUFR messages using subroutine WRITSB often meant that the messages were written to disk with FORTRAN-blocking information surrounding them. This occurred because the subroutine was using a FORTRAN "WRITE" command, and standard FORTRAN, unlike certain other programming languages such as C and C++, does not have the capability to read or write data to or from a system file as a pure binary stream of bits. Some compilers did provide their own non-standard extensions to allow this, but this was by no means standard across the many available UNIX platforms in existence at the time. So for most users, the best available option was to use the standard FORM='UNFORMATTED' access mode when opening an output file within their application program, and then later use the separate NCEP program cwordsh to remove the extraneous FORTRAN-blocking information and thereby obtain a "pure" BUFR file.

Now however, with versions 10.2.x and later of BUFRLIB, the logic within WRITSB has been internally rewritten to do a C-language write of each BUFR message, so the resulting output file is now pure BUFR by default, without the need to subsequently run cwordsh or use any special non-standard extensions when opening the file within the application program! Likewise, the subroutines READMG, IREADMG, READNS etc. have also been updated to be able to read any type of BUFR file (whether FORTRAN-blocked or not!) without any additional effort on the part of the user! So there is no longer a need for the cwordsh program; however, any users who wish to continue writing FORTRAN-blocked BUFR output files (e.g. for consistency with historical archives) can continue to do so via the following new subroutine:

        CALL SETBLOCK  ( IBLK )

        Input arguments:
            IBLK        INTEGER         Blocking indicator
                                         -1 = little-endian blocking
                                          0 = no blocking (i.e. "pure" BUFR)
                                          1 = big-endian blocking 

This new subroutine should be called immediately prior to the first call to subroutine OPENBF, and then all writes to output files will be done in accordance with the selected indicator, so that users now have full control over the content of their resulting BUFR files and can even specify output that is blocked with reverse-endianness to the native platform. Note however that the default style is now "pure" BUFR for all output files, so users who wish to retain any sort of FORTRAN-blocking must now call subroutine SETBLOCK to specify it.


With versions 10.2.x and later of BUFRLIB, it is now also possible to specify a custom "missing" value for writing and reading from BUFR files, rather than using the BUFRLIB default value of 10.0E10:

        CALL SETBMISS  ( XMISS )

        Input arguments:
            XMISS       REAL*8          Value to be used for "missing"

This new subroutine should be called immediately prior to the first call to subroutine OPENBF, and the supplied value will then be treated as "missing" for all future calls to subroutines IBFMS, UFBINT, UFBREP, UFBSEQ, UFBTAB etc. Correspondingly, a user can always check the value of "missing" that is currently in use via a call to the following new function:

        XMISS = GETBMISS()

        Output arguments:
            XMISS       REAL*8          BUFRLIB "missing" value