/* ** MAIN PROGRAM DOCUMENTATION BLOCK ** ** MAIN PROGRAM: debufr ** PRGMMR: J. Ator ORG: NP12 DATE: 2009-07-01 ** ** ABSTRACT: This program decodes a BUFR file and generates a verbose ** listing of the contents. If an NCEP DX dictionary tables file is ** specified (using the -f option) or if the specified BUFR file ** contains an embedded NCEP DX dictionary message as the first ** message in the file, then this DX information is used to decode ** the data messages in the file. Otherwise, or whenever the -m option ** is specified, BUFR master tables are read and used to decode the ** data messages in the file. ** ** PROGRAM HISTORY LOG: ** 2009-07-01 J. Ator Original author ** 2012-06-18 J. Ator Modified to allow decoding of BUFR files ** based on NCEP DX dictionary table ** information. The program can now process ** any files that previously required the use ** of ckbufr. ** 2012-12-07 J. Ator Modified to add -m and -v options and inline ** version of OPENBT subroutine for mixed BUFR files ** 2013-10-07 J. Ator Print Section 1 tank receipt time information ** for NCEP/NCO BUFR messages if available ** 2013-11-15 J. Ator Add -h option and check for non-existent tablefil ** 2014-09-15 J. Ator Change default path for tabledir, change default ** name for outfile, and confirm outfile is writeable ** ** USAGE: ** debufr [-v] [-h] [-b] [-m] [-o outfile] [-t tabledir] [-f tablefil] bufrfile ** ** WHERE: ** -v prints version information and exits ** -h prints program help and usage information and exits ** -b specifies the "basic" option, meaning that only the ** information in Sections 0-3 will be decoded from each ** BUFR message in the bufrfile, and no attempt will be ** made to decode the data in Section 4 ** -m specifies that BUFR master tables will be used to ** decode the data messages in the file, regardless of ** whether they contain any embedded NCEP DX dictionary ** messages. This option can be used to view the actual ** contents of dictionary messages, which otherwise would ** not be printed in the output listing. ** outfile [path/]name of file to contain verbose output listing. ** The default is "bufrfilename.debufr.out" in the current ** working directory, where bufrfilename is the basename of ** the bufrfile (i.e. bufrfile with any preceding [path/] ** removed). ** tabledir [path/]name of directory containing tables to be used ** for decoding. This directory contains the NCEP DX ** dictionary tables file to be used (if one was specified ** via the -f option), or it may contain all of the BUFR ** master tables when these are being used to decode a ** file. The default is "/nwprod/decoders/decod_shared/fix" ** if unspecified. ** tablefil file within tabledir containing DX dictionary tables ** file to be used for decoding. ** bufrfile [path/]name of BUFR file to be decoded ** ** REMARKS: ** SUBPROGRAMS CALLED: ** LOCAL - fdebufr openbt prtusage ** BUFRLIB - ccbfl cobfl crbmg datelen dxdump ** idxmsg ireadsb iupbs01 iupbs3 mtinfo ** openbf readerme ufdump upds3 bvers ** rtrcptb ** ** FORTRAN logical unit numbers 51, 90, 91, 92 and 93 are reserved ** for use within the fdebufr subroutine. ** ** ATTRIBUTES: ** LANGUAGE: C ** MACHINE: Portable to all platforms */ #include #include #include #ifdef UNDERSCORE #define cobfl cobfl_ #define ccbfl ccbfl_ #define fdebufr fdebufr_ #define bvers bvers_ #define prtusage prtusage_ #endif #define MXFLEN 300 #ifdef F77_INTSIZE_8 typedef long f77int; #else typedef int f77int; #endif int main( int argc, char *argv[ ] ) { int ch; int errflg; char basic = 'N'; char forcemt = 'N'; char io = 'r'; char tbldir[MXFLEN] = "/nwprod/decoders/decod_shared/fix"; char tblfil[MXFLEN]; char outfile[MXFLEN]; char wkstr[MXFLEN]; char wkstr2[MXFLEN]; char bvstr[9] = " "; unsigned short ii; f77int lentd; /* ** Get and process the valid options from the command line: */ errflg = 0; wkstr[0] = '\0'; /* initialize to empty string */ outfile[0] = '\0'; /* initialize to empty string */ while ( ( ch = getopt ( argc, argv, "vhbmo:t:f:" ) ) != EOF ) { switch ( ch ) { case 'v': bvers ( bvstr, sizeof(bvstr) ); /* append a trailing NULL to bvstr for printf */ for ( ii = 0; ii < sizeof(bvstr); ii++ ) { if ( ( bvstr[ii] != '.' ) && ( !isdigit(bvstr[ii]) ) ) { bvstr[ii] = '\0'; break; } } printf( "This is debufr v2.2.0, built with BUFRLIB v%s\n", bvstr ); return 0; case 'h': printf( "\nPROGRAM %s\n", argv[0] ); printf( "\nABSTRACT: This program decodes a BUFR file and generates a verbose\n" ); printf( " listing of the contents. If an NCEP DX dictionary tables file is\n" ); printf( " specified (using the -f option) or if the specified BUFR file\n" ); printf( " contains an embedded NCEP DX dictionary message as the first\n" ); printf( " message in the file, then this DX information is used to decode\n" ); printf( " the data messages in the file. Otherwise, or whenever the -m option\n" ); printf( " is specified, BUFR master tables are read and used to decode the\n" ); printf( " data messages in the file.\n" ); prtusage( argv[0] ); return 0; break; case 'b': basic = 'Y'; break; case 'm': forcemt = 'Y'; break; case 'o': strcpy ( outfile, optarg ); break; case 't': strcpy ( tbldir, optarg ); break; case 'f': strcpy ( wkstr, optarg ); break; } } /* ** There should be one remaining command line argument specifying the ** input BUFR file. */ if ( (optind+1) != argc ) { printf( "\nERROR: You must specify an input BUFR file to be decoded!\n" ); prtusage( argv[0] ); return -1; } /* ** Open the input BUFR file. */ cobfl( argv[optind], &io ); /* ** Check whether a DX tables file was specified. */ if ( strlen( wkstr ) > 0 ) { sprintf( tblfil, "%s%c%s", tbldir, '/', wkstr ); } else { strcpy( tblfil, "NULLFILE" ); } /* ** Check whether an output file was specified. If not, make a default ** filename in the current working directory using the basename of the ** input BUFR file. */ if ( strlen( outfile ) == 0 ) { strcpy( wkstr2, argv[optind] ); strcpy( outfile, basename( wkstr2 ) ); strcat( outfile, ".debufr.out" ); } /* ** Confirm that the output directory is writeable. */ strcpy( wkstr2, outfile ); strcpy( wkstr, dirname( wkstr2 ) ); if ( access( wkstr, W_OK ) != 0 ) { printf( "\nERROR: Cannot write output file to directory %s\n", ( strcmp( wkstr, "." ) == 0 ? getcwd( wkstr2, MXFLEN ) : wkstr ) ); prtusage( argv[0] ); return -1; } /* ** Read and decode each message from the input BUFR file. */ lentd = (f77int) strlen(tbldir); fdebufr( outfile, tbldir, &lentd, tblfil, &basic, &forcemt, strlen(outfile), strlen(tbldir), strlen(tblfil) ); /* ** Close the input BUFR file. */ ccbfl( ); return 0; } void prtusage( char *prgnam ) { /* ** Prints program usage information to stdout. */ printf( "\nUSAGE:\n" ); printf( " %s [-v] [-h] [-b] [-m] [-o outfile] [-t tabledir] [-f tablefil] bufrfile\n\n", prgnam ); printf( "WHERE:\n" ); printf( " -v prints program version information and exits\n" ); printf( " -h prints program help and usage information and exits\n" ); printf( " -b specifies the \"basic\" option, meaning that only the\n" ); printf( " information in Sections 0-3 will be decoded from each\n" ); printf( " BUFR message in the bufrfile, and no attempt will be\n" ); printf( " made to decode the data in Section 4\n" ); printf( " -m specifies that BUFR master tables will be used to\n" ); printf( " decode the data messages in the file, regardless of\n" ); printf( " whether they contain any embedded NCEP DX dictionary\n" ); printf( " messages. This option can be used to view the actual\n" ); printf( " contents of dictionary messages, which otherwise would\n" ); printf( " not be printed in the output listing.\n" ); printf( " outfile [path/]name of file to contain verbose output listing.\n" ); printf( " The default is \"bufrfilename.debufr.out\" in the current\n" ); printf( " working directory, where bufrfilename is the basename of\n" ); printf( " the bufrfile (i.e. bufrfile with any preceding [path/]\n" ); printf( " removed).\n" ); printf( " tabledir [path/]name of directory containing tables to be used\n" ); printf( " for decoding. This directory contains the NCEP DX\n" ); printf( " dictionary tables file to be used (if one was specified\n" ); printf( " via the -f option), or it may contain all of the BUFR\n" ); printf( " master tables when these are being used to decode a\n" ); printf( " file. The default is \"/nwprod/decoders/decod_shared/fix\"\n" ); printf( " if unspecified.\n" ); printf( " tablefil file within tabledir containing DX dictionary tables\n" ); printf( " file to be used for decoding.\n" ); printf( " bufrfile [path/]name of BUFR file to be decoded\n" ); } ------------------------------------------------------------------------------ ---------------------- FORTRAN subroutine "fdebufr.f" ------------------------ SUBROUTINE FDEBUFR ( ofile, tbldir, lentd, tblfil, + basic, forcemt ) 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 C PROGRAM HISTORY LOG: C 2009-07-01 J. Ator Original author C 2012-06-18 J. Ator Added tblfil argument and options to decode C files according to DX dictionary information C 2012-12-07 J. Ator Added forcemt and lentd arguments C 2013-10-07 J. Ator Print Section 1 tank receipt time information C for NCEP/NCO BUFR messages if available C 2013-11-15 J. Ator Added check for missing or unreadable tblfil C 2014-09-15 J. Ator Confirm BUFR file was opened (i.e. at least C one good return from CRBMG) before calling C DXDUMP. C C USAGE: call fdebufr ( ofile, tbldir, lentd, tblfil, C basic, forcemt ) 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 tables C to be used for decoding C lentd - integer: length of tbldir string C tblfil - character*(*): file containing BUFR DX dictionary C information to be used for decoding. If set to C 'NULLFILE', then no such file will be used. C basic - character: indicator as to whether the "basic" C option was specified on the command line: C 'Y' = yes C 'N' = no C forcemt - character: indicator as to whether the forced use C of master tables was specified on the command line: C 'Y' = yes C 'N' = no C C REMARKS: C FORTRAN logical unit numbers 51, 90, 91, 92 and 93 are reserved C for use within this subroutine. C C ATTRIBUTES: C LANGUAGE: FORTRAN 77 C MACHINE: Portable to all platforms C C$$$ USE Share_Table_Info PARAMETER ( MXBF = 2500000 ) PARAMETER ( MXBFD4 = MXBF/4 ) PARAMETER ( MXDS3 = 500 ) CHARACTER*(*) ofile, tbldir, tblfil LOGICAL exists CHARACTER*8 cmgtag CHARACTER*6 cds3 ( MXDS3 ) CHARACTER*1 basic, forcemt, opened, usemt, + bfmg ( MXBF ) INTEGER ibfmg ( MXBFD4 ) EQUIVALENCE ( bfmg (1), ibfmg (1) ) C----------------------------------------------------------------------- C----------------------------------------------------------------------- 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 = 92 OPEN ( UNIT = lunit, FILE = '/dev/null' ) CALL DATELEN ( 10 ) C Initialize the values in the Share_Table_Info module. ludx = 93 ltbd = lentd ctbldir = tbldir(1:lentd) C Initialize some other values. nmsg = 0 nsubt = 0 opened = 'N' usemt = 'N' 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. 'N' ) .and. ( opened .eq. 'Y' ) ) THEN WRITE (51, FMT = '( /, A, / )' ) + 'Here is the DX table that was generated:' CALL DXDUMP ( lunit, 51 ) ENDIF C Close the output file and return. CLOSE ( 51 ) RETURN ENDIF IF ( opened .eq. 'N' ) THEN CALL ISETPRM ( 'MAXCD', MXDS3 ) CALL ISETPRM ( 'MXMSGL', MXBF ) CALL ISETPRM ( 'MAXSS', 160000 ) CALL ISETPRM ( 'NFILES', 2 ) C Decide how to process the file. IF ( ( IDXMSG ( ibfmg ) .eq. 1 ) .and. + ( forcemt .eq. 'N' ) ) THEN C The first message in the file is a DX dictionary C message, so assume there's an embedded table at the C front of the file and use this table to decode it. CALL OPENBF ( lunit, 'INUL', lunit ) ELSE IF ( ( tblfil(1:8) .ne. 'NULLFILE' ) .and. + ( forcemt .eq. 'N' ) ) THEN C A DX dictionary tables file was specified on the C command line, so use it to decode the BUFR file. INQUIRE ( FILE = tblfil, EXIST = exists ) IF ( .not. exists ) THEN PRINT *, 'ERROR: COULD NOT FIND FILE ', tblfil RETURN ENDIF OPEN ( UNIT = 91, FILE = tblfil, IOSTAT = ier ) IF ( ier .ne. 0 ) THEN PRINT *, 'ERROR: COULD NOT OPEN FILE ', tblfil RETURN ENDIF CALL OPENBF ( lunit, 'IN', 91 ) ELSE C Decode the file using the master tables in tbldir. usemt = 'Y' CALL OPENBF ( lunit, 'SEC3', lunit ) CALL MTINFO ( tbldir, 90, 91 ) ENDIF opened = 'Y' ENDIF IF ( basic .eq. 'N' ) THEN C Pass the message to the decoder. CALL READERME ( ibfmg, lunit, cmgtag, imgdt, ierme ) ENDIF C If this is a DX dictionary message, then don't generate any C output unless master tables are being used for decoding. IF ( ( IDXMSG ( ibfmg ) .ne. 1 ) .or. + ( usemt .eq. 'Y' ) ) THEN 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' ) IF ( ( IUPBS01 ( ibfmg, 'OGCE' ) .eq. 7 ) .and. + ( IUPBS01 ( ibfmg, 'GSES' ) .eq. 3 ) ) THEN CALL RTRCPTB ( ibfmg, iryr, irmo, irdy, irhr, + irmi, irtret ) IF ( irtret .eq. 0 ) THEN WRITE (51,*) ' NCEP tank rcpt year:', iryr WRITE (51,*) ' NCEP tank rcpt month:', irmo WRITE (51,*) ' NCEP tank rcpt day:', irdy WRITE (51,*) ' NCEP tank rcpt hour:', irhr WRITE (51,*) 'NCEP tank rcpt minute:', irmi END IF END IF 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. 'N' ) .and. + ( ierme .ge. 0 ) ) THEN C Decode and output the data from Section 4. 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 WRITE ( UNIT = 51, FMT = '( /, A, I7 )' ) + 'End of BUFR message #', nmsg WRITE ( UNIT = 51, FMT = '( /, 120("-"))' ) ENDIF ENDDO RETURN END SUBROUTINE OPENBT ( lundx, mtyp ) C$$$ SUBPROGRAM DOCUMENTATION BLOCK C C SUBPROGRAM: OPENBT C PRGMMR: J. ATOR ORG: NP12 DATE: 2012-12-07 C C ABSTRACT: Given a BUFR message type, this subroutine opens the C appropriate DX dictionary table in the specified table directory C as Fortran logical unit "lundx". This subroutine overrides the C default subroutine of the same name in the NCEP BUFRLIB. C C PROGRAM HISTORY LOG: C 2012-12-07 J. ATOR -- ORIGINAL AUTHOR C C USAGE: CALL OPENBT ( lundx, mtyp ) C INPUT ARGUMENT LIST: C mtyp - integer: message type of input BUFR file C C OUTPUT ARGUMENT LIST: C lundx - integer: unit number of BUFR mnemonic table C 0 = unable to open table C C ATTRIBUTES: C LANGUAGE: FORTRAN 90 C MACHINE: Portable to all platforms C C$$$ USE Share_Table_Info CHARACTER*11 bftab CHARACTER*240 bftabfil LOGICAL exists C----------------------------------------------------------------------- C----------------------------------------------------------------------- WRITE ( bftab, '("bufrtab.",i3.3)' ) mtyp bftabfil = ctbldir(1:ltbd) // '/' // bftab INQUIRE ( FILE = bftabfil, EXIST = exists ) IF ( exists ) THEN lundx = ludx CLOSE ( lundx ) OPEN ( UNIT = lundx, FILE = bftabfil ) ELSE lundx = 0 END IF RETURN END C The following module is used to share information between C subroutine FDEBUFR and subroutine OPENBT, since the latter C is not called by the former but rather is called directly C from within the NCEP BUFRLIB. MODULE Share_Table_Info CHARACTER*120 ctbldir INTEGER ltbd, ludx END MODULE