NCEP Home > NCO Home > Systems Integration Branch > Decoders > BUFRLIB > BUFRLIB Table of Contents > General-purpose utility for decoding BUFR files
Printer Friendly Version
Example Program: General purpose utility for decoding BUFR files
In this example, we show how various BUFRLIB subroutines and functions can be
integrated within an application program to read and dump the contents
of a file containing any types of valid BUFR
messages, without the need to pre-define any DX BUFR tables.
As discussed earlier within the documentation, this can be done via the use
of appropriate Master BUFR tables, and by specifying
IO='SEC3' when making the appropriate call
to subroutine OPENBF for the file in question.
Unlike in some of our other examples, the below program can be directly compiled
and run "as is", without any additional code supplied by the user. The
program consists of a main C-language program "debufr.c" which in turn calls a
FORTRAN subroutine "fdebufr.f" as shown below.
--------------------------- main program "debufr.c" --------------------------
/*
** MAIN PROGRAM DOCUMENTATION BLOCK
**
** MAIN PROGRAM: debufr
** PRGMMR: J. Ator ORG: NP12 DATE: 2009-07-01
**
** ABSTRACT: This program decodes a specified BUFR file and generates a
** verbose listing of the contents.
**
** PROGRAM HISTORY LOG:
** 2009-07-01 J. Ator Original author
**
** USAGE:
** ./debufr [-b] [-o outfile] [-t tabledir] bufrfile
**
** WHERE:
** -b specifies the "basic" option, whereby only information
** from Sections 0-3 of each BUFR message in the bufrfile
** is decoded, and no attempt is made to decode the data
** in Section 4
** outfile [path/]name of file to contain verbose output listing;
** the default is "debufr.out" in the local run directory
** tabledir directory containing BUFR master tables to be used for
** decoding; the default is "/nwprod/fix"
** bufrfile [path/]name of BUFR file to be decoded
**
** REMARKS:
** SUBPROGRAMS CALLED:
** LOCAL - fdebufr
** BUFRLIB - ccbfl cobfl crbmg datelen dxdump
** ireadsb iupbs01 iupbs3 mtinfo openbf
** readerme ufdump upds3
**
** FORTRAN logical unit numbers 51, 60, 90 and 91 are reserved for
** use within the fdebufr subroutine.
**
** ATTRIBUTES:
** LANGUAGE: C
** MACHINE: Portable to all platforms
*/
#include
#include
#ifdef UNDERSCORE
#define cobfl cobfl_
#define ccbfl ccbfl_
#define fdebufr fdebufr_
#endif
int main( int argc, char *argv[ ] ) {
int ch;
int errflg;
char basic = 'F';
char io = 'r';
char outfile[120] = "debufr.out";
char tbldir[120] = "/nwprod/fix";
/*
** Get the valid options from the command line:
** -b "basic" option - only decodes information from Sections 0-3
** of each input message, with no decoding of Section 4 data
** -o defines the output filename (default is "debufr.out")
** -t defines the master table directory (default is "/nwprod/fix")
*/
errflg = 0;
while ( ( ch = getopt ( argc, argv, "bo:t:" ) ) != EOF ) {
switch ( ch ) {
case 'b':
basic = 'T';
break;
case 'o':
strcpy ( outfile, optarg );
break;
case 't':
strcpy ( tbldir, optarg );
break;
}
}
/*
** There should be one remaining command line argument specifying the
** input BUFR file.
*/
if ( (optind+1) != argc ) {
printf( "\nUsage: %s [options] BUFRfile\n\n", argv[0] );
printf( " where possible options are:\n" );
printf( " -b\n" );
printf( " -o [path/]filename of output file\n" );
printf( " -t [path/]filename of master table directory\n\n" );
printf( " and BUFRfile is [path/]filename of BUFR input file\n\n" );
return -1;
}
/*
** Open the input BUFR file.
*/
cobfl( argv[optind], &io );
/*
** Read and decode each message from the input BUFR file.
*/
fdebufr( outfile, tbldir, &basic,
strlen(outfile), strlen(tbldir) );
/*
** Close the input BUFR file.
*/
ccbfl( );
return 0;
}
------------------------------------------------------------------------------
---------------------- FORTRAN subroutine "fdebufr.f" ------------------------
SUBROUTINE FDEBUFR ( ofile, tbldir, basic )
C$$$ SUBPROGRAM DOCUMENTATION BLOCK
C
C SUBPROGRAM: fdebufr
C PRGMMR: J. Ator ORG: NP12 DATE: 2009-07-01
C
C ABSTRACT: This subroutine reads every BUFR message from within the
C input file that was specified on the command line. Each such
C message is decoded and the results are written as output to ofile.
C No DX dictionary files or messages are necessary; rather, only
C BUFR master tables are required and must exist in the directory
C specified by tbldir.
C
C PROGRAM HISTORY LOG:
C 2009-07-01 J. Ator Original author
C
C USAGE: call fdebufr ( ofile, tbldir, basic )
C INPUT ARGUMENT LIST:
C ofile - character*(*): file to contain verbose output
C listing for each decoded BUFR message
C tbldir - character*(*): directory containing BUFR master
C tables to be used for decoding
C basic - character: indicator as to whether the "basic"
C option was specified on the command line:
C 'Y' = yes
C 'N' = no
C
C REMARKS:
C FORTRAN logical unit numbers 51, 60, 90 and 91 are reserved for
C use within this subroutine.
C
C ATTRIBUTES:
C LANGUAGE: FORTRAN 77
C MACHINE: Portable to all platforms
C
C$$$
PARAMETER ( MXBF = 950000 )
PARAMETER ( MXBFD4 = MXBF/4 )
PARAMETER ( MXDS3 = 500 )
CHARACTER*(*) ofile, tbldir
CHARACTER basic
CHARACTER*8 cmgtag
CHARACTER*6 cds3 ( MXDS3 )
CHARACTER*1 bfmg ( MXBF )
INTEGER ibfmg ( MXBFD4 )
EQUIVALENCE ( bfmg (1), ibfmg (1) )
C----------- End of declarations --------------
C Open the output file.
OPEN ( UNIT = 51, FILE = ofile )
C Note that in the below OPEN statement we just need to specify
C a dummy placeholder file.
lunit = 60
OPEN ( UNIT = lunit, FILE = '/dev/null' )
CALL OPENBF ( lunit, 'SEC3', lunit )
CALL MTINFO ( tbldir, 90, 91 )
CALL DATELEN ( 10 )
nmsg = 0
nsubt = 0
DO WHILE ( .true. )
C Get the next message from the input BUFR file.
CALL CRBMG ( bfmg, MXBF, nbyt, ierr )
IF ( ierr .ne. 0 ) THEN
IF ( ierr .eq. -1 ) THEN
WRITE ( UNIT = 51, FMT = '( /, 2A, I7, A, I9, A )')
+ 'Reached end of BUFR file; it contained a total ',
+ 'of', nmsg, ' messages and', nsubt, ' subsets'
ELSE
WRITE ( UNIT = 51, FMT = '( /, 2A, I4 )' )
+ 'Error while reading BUFR file; the return code ',
+ 'from CRBMG = ', IERR
ENDIF
IF ( basic .eq. 'F' ) THEN
WRITE (51, FMT = '( /, A, / )' )
+ 'Here is the DX table that was generated:'
CALL DXDUMP ( lunit, 51 )
ENDIF
C Close the output file.
CLOSE ( 51 )
RETURN
ENDIF
nmsg = nmsg + 1
WRITE ( UNIT = 51, FMT = '( /, A, I7 )' )
+ 'Found BUFR message #', nmsg
WRITE (51,*) ' '
WRITE (51,*) ' Message length:', IUPBS01(ibfmg,'LENM')
WRITE (51,*) ' Section 0 length:', IUPBS01(ibfmg,'LEN0')
WRITE (51,*) ' BUFR edition:', IUPBS01(ibfmg,'BEN')
WRITE (51,*) ' '
WRITE (51,*) ' Section 1 length:', IUPBS01(ibfmg,'LEN1')
WRITE (51,*) ' Master table:', IUPBS01(ibfmg,'BMT')
WRITE (51,*) ' Originating center:', IUPBS01(ibfmg,'OGCE')
WRITE (51,*) 'Originating subcenter:', IUPBS01(ibfmg,'GSES')
WRITE (51,*) 'Update sequence numbr:', IUPBS01(ibfmg,'USN')
IF ( IUPBS01(ibfmg,'ISC2') .eq. 1 ) THEN
WRITE (51,*) ' Section 2 present?: Yes'
ELSE
WRITE (51,*) ' Section 2 present?: No'
ENDIF
WRITE (51,*) ' Data category:', IUPBS01(ibfmg,'MTYP')
WRITE (51,*) ' Local subcategory:', IUPBS01(ibfmg,'MSBT')
WRITE (51,*) 'Internatl subcategory:',
+ IUPBS01(ibfmg,'MSBTI')
WRITE (51,*) ' Master table version:', IUPBS01(ibfmg,'MTV')
WRITE (51,*) ' Local table version:', IUPBS01(ibfmg,'MTVL')
WRITE (51,*) ' Year:', IUPBS01(ibfmg,'YEAR')
WRITE (51,*) ' Month:', IUPBS01(ibfmg,'MNTH')
WRITE (51,*) ' Day:', IUPBS01(ibfmg,'DAYS')
WRITE (51,*) ' Hour:', IUPBS01(ibfmg,'HOUR')
WRITE (51,*) ' Minute:', IUPBS01(ibfmg,'MINU')
WRITE (51,*) ' Second:', IUPBS01(ibfmg,'SECO')
WRITE (51,*) ' '
nsub = IUPBS3(ibfmg,'NSUB')
WRITE (51,*) 'Number of data subsets:', nsub
nsubt = nsubt + nsub
IF ( IUPBS3(ibfmg,'IOBS') .eq. 1 ) THEN
WRITE (51,*) ' Data are observed?: Yes'
ELSE
WRITE (51,*) ' Data are observed?: No'
ENDIF
IF ( IUPBS3(ibfmg,'ICMP') .eq. 1 ) THEN
WRITE (51,*) ' Data are compressed?: Yes'
ELSE
WRITE (51,*) ' Data are compressed?: No'
ENDIF
CALL UPDS3 ( ibfmg, MXDS3, cds3, nds3 )
WRITE (51,*) ' Number of descriptors:', nds3
DO jj = 1, nds3
WRITE ( 51, FMT = '( 5X, I4, A, A6)' )
+ jj, ": ", cds3 ( jj )
END DO
IF ( basic .eq. 'F' ) THEN
C Decode and output the data from Section 4.
CALL READERME ( ibfmg, lunit, cmgtag, imgdt, ierme )
IF ( ierme .ge. 0 ) THEN
WRITE ( UNIT = 51,
+ FMT = '( /, A, I7, 3A, I10, A, I6, A )' )
+ 'BUFR message #', nmsg, ' of type ', cmgtag,
+ ' and date ', imgdt, ' contains ', nsub,
+ ' subsets:'
DO WHILE ( IREADSB ( lunit ) .eq. 0 )
CALL UFDUMP ( lunit, 51 )
ENDDO
ENDIF
ENDIF
WRITE ( UNIT = 51, FMT = '( /, A, I7 )' )
+ 'End of BUFR message #', nmsg
WRITE ( UNIT = 51, FMT = '( /, 120("-"))' )
ENDDO
RETURN
END
------------------------------------------------------------------------------
As shown above, this particular application program is designed to be run
as a UNIX shell command, with the BUFR file to be decoded as the lone argument.
The output file where decoded information is to be printed can be specified via
the option -o; otherwise the file "debufr.out" is
created in the same directory. Similarly, and since the program will need
access to master BUFR tables, an option -t is also
provided where the directory containing these master tables is located. Note
that this same directory name is eventually passed in as an argument to subroutine
MTINFO within the FORTRAN code, in order to indicate the
location of these master tables to the BUFRLIB software. Finally, note that an
additional -b option is also provided in case the user
just wants a basic listing of the contents of Sections 0 through 3 of each BUFR
message without a full-blown listing of all of the data values in Section 4.
For further details see the "USAGE:" section within the documentation block
of the above C code. Otherwise, before moving into the FORTRAN code, note that
this C code also does a call to COBFL, since we will
be demonstrating the use of BUFRLIB's C language interface to read each BUFR
message from the specified input file.
Now, within the FORTRAN code, note that we make an initial call to
OPENBF,
despite the fact that we will be using CRBMG to actually
read the BUFR messages from the specified input file. This call to
OPENBF is nonetheless needed in order to associate a
FORTRAN logical unit number that can later be passed into subroutine
READERME, and also to let the BUFRLIB software know
(via the use of IO='SEC3') that we want messages
decoded according to their Section 3 data description section once we pass
them in via READERME later on in the code.
At this point, and after the aforementioned required call to subroutine
MTINFO is made, the main loop is entered to read
each BUFR message in turn from the specified file using
CRBMG,
print the basic information from Sections 0 through 3 using multiple
successive calls to IUPBS01, IUPBS3 and
UPDS3, and then (unless the -b option
was specified on the command line), pass the message into subroutine
READERME, where each
data subset can then be read using IREADSB and subsequently
printed to the specified output file using subroutine UFDUMP.
Finally, after all such BUFR messages have been read and decoded from the file, the
program makes a call to subroutine DXDUMP to print a copy
of the internal DX table that was automatically generated by the BUFRLIB during
processing of the file.
Control is then passed back to the above C code, where the final call to
CCBFL is made and then the program exits.
|