Printer image icon. Send To Printer
NCEP Home > NCO Home > Systems Integration Branch > Decoders > BUFRLIB > BUFRLIB Table of Contents > Decoding wind profiler BUFR messages
Printer image icon. Printer Friendly Version

Example Program: Decoding wind profiler BUFR messages

In this example, we will be decoding (i.e. reading) BUFR messages that were encoded according to the "WIND PROFILER" format listed within our sample DX BUFR tables file. In effect, this means that we will be reading data subsets that are described by the Table A mnemonic "NC002007". In doing so, we will pass each BUFR message directly from the application program to the BUFRLIB software via an array in memory, since this will allow us to demonstrate the use of subroutine READERME in lieu of subroutine READMG:

        PARAMETER       ( MXMN = 7 )
        PARAMETER       ( MXLV = 86 )

        REAL*8          r8arr ( MXMN, MXLV )

        PARAMETER       ( MXBF = 16000 )

	CHARACTER	cbfmsg*(MXBF),
     +			csubset*8

	INTEGER		ibfmsg ( MXBF / 4 )

	EQUIVALENCE	( cbfmsg (1), ibfmsg (1) )
C*-----------------------------------------------------------------------

C*	Open the BUFR messages file.

	OPEN  ( UNIT = 11, FILE = 'dummy' )

C*	Open the BUFR tables file.

	OPEN  ( UNIT = 12, FILE = 'bufrtab.example' )

C*	Associate the tables file with the messages file, and identify
C*	the latter to the BUFRLIB software.

	CALL OPENBF  ( 11, 'IN', 12 )

C*	Specify that we would like IDATE values returned using 10 digits
C*	(i.e. YYYYMMDDHH ).

	CALL DATELEN  ( 10 )

	DO WHILE  ( .true. )

C*	    Read the next BUFR message.

            ( assume that there is code here to read the next BUFR message
	    from the local system into the character array cbfmsg )

	    IF  ( there are no more messages )  THEN
		CALL CLOSBF  ( 11 )
		( perform any other necessary local shutdown tasks )
	        STOP
	    END IF

	    msgok = .true.

C*	    Pass the BUFR message into the BUFRLIB software.

	    CALL READERME  ( ibfmsg, 11, csubset, idate, ierrme )

	    IF  ( ierrme .ne. 0 )  THEN
		PRINT *, 'Error reading BUFR message within READERME'
		msgok = .false.
	    ELSE IF  ( csubset .ne. 'NC002007' )  THEN
		PRINT *, 'BUFR message is not of type WIND PROFILER'
		msgok = .false.
	    END IF

	    DO WHILE  ( msgok )

C*		Read the next data subset from the BUFR message.

		IF  ( IREADSB ( 11 ) .ne. 0 )  THEN

		    msgok = .false.

		ELSE

C*		    At this point, we have a data subset within the
C*		    internal arrays of BUFRLIB, and we can now begin
C*		    reading actual data values:

C*		    Report identification.

		    CALL UFBINT  ( 11, r8arr, MXMN, MXLV, nlv,
     +				   'WMOB WMOS CLAT CLON SELV' )

		    PRINT *, '      WMO Block # = ', r8arr (1,1)
		    PRINT *, '    WMO Station # = ', r8arr (2,1)
		    PRINT *, '         Latitude = ', r8arr (3,1)
		    PRINT *, '        Longitude = ', r8arr (4,1)
		    PRINT *, 'Station Elevation = ', r8arr (5,1)

C*		    Report valid time.

		    CALL UFBINT  ( 11, r8arr, MXMN, MXLV, nlv,
     +			           'YEAR MNTH DAYS HOUR MINU' )

		    PRINT *, '             Year = ', r8arr (1,1)
		    PRINT *, '            Month = ', r8arr (2,1)
		    PRINT *, '              Day = ', r8arr (3,1)
		    PRINT *, '             Hour = ', r8arr (4,1)
		    PRINT *, '           Minute = ', r8arr (5,1)

C*		    Surface wind, temperature, humidity, etc.

		    CALL UFBINT  ( 11, r8arr, MXMN, MXLV, nlv,
     +		  		   'WDIR WSPD TMDBST REHU' )

		    PRINT *, '   Wind direction = ', r8arr (1,1)
		    PRINT *, '       Wind speed = ', r8arr (2,1)
		    PRINT *, '  Temperature (K) = ', r8arr (3,1)
		    PRINT *, 'Relative Humidity = ', r8arr (4,1)

C*		    Height increments.

		    CALL UFBREP  ( 11, r8arr, MXMN, MXLV, nlv,
     +			           'HINC' )
		    DO ii = 1, 3
		        PRINT *, ' Height increment #', ii, ' = ',
     +				     r8arr ( 1, ii )
		    END DO

C*		    Observation sequence (i.e. "OBSEQ" data).

		    CALL UFBREP  ( 11, r8arr, MXMN, MXLV, nlv,
     +				  'NPHL NPQC UCMP VCMP SDHS WCMP SDVS' )
		    DO ii = 1, 43 
		        PRINT *, '    Profiler mode #', ii, ' = ',
     +				     r8arr ( 1, ii )
		        PRINT *, '      Profiler QC #', ii, ' = ',
     +				     r8arr ( 2, ii )
		        PRINT *, '      U Component #', ii, ' = ',
     +				     r8arr ( 3, ii )
		        PRINT *, '      V Component #', ii, ' = ',
     +		  		     r8arr ( 4, ii )
			PRINT *, 'Stddev of Horiz wind #', ii, ' = ',
     +				     r8arr ( 5, ii )
			PRINT *, '      W Component #', ii, ' = ',
     +				     r8arr ( 6, ii )
			PRINT *, ' Stddev of Vert wind #', ii, ' = ',
     +				     r8arr ( 7, ii )
		    END DO

		    CALL UFBREP  ( 11, r8arr, MXMN, MXLV, nlv,
     +				       'ACAV' )

C*		    Total number of values
C*		    (with respect to accumulation or average)

		    DO ii = 1, 86
		        PRINT *, '          ACAV value #', ii, ' = ',
     +				     r8arr ( 1, ii )
		    END DO

		END IF

	    END DO

	END DO

	END

In the above program example, the first thing that is done is to associate logical unit numbers with our BUFR messages and tables files via the FORTRAN OPEN statement. Note that the messages file is an empty "dummy" file for this program, since we will be using subroutine READERME (instead of READMG), and, therefore, the BUFRLIB software will never actually try to read BUFR messages (or, for that matter, anything else) from logical unit #11. However, the call to OPENBF is still necessary in order to associate our sample DX BUFR tables file (as local file "bufrtab.example") with this logical unit #11, which will itself then be passed as the second (i.e. LUBFR) argument to READERME. Then, following the call to OPENBF, a looping mechanism is set up to continuously read each new BUFR message (in some unspecified manner from the local system) into the character array cbfmsg, until there are no more messages to read, at which time subroutine CLOSBF is called and the program stops. Otherwise, the message is passed into subroutine READERME via the integer array ibfmsg, which is EQUIVALENCED to cbfmsg. Upon return from each call to READERME, we check that the read was successful and also that the message contains the type of data subset (i.e. "NC002007") that we are interested in. In fact, this latter check could actually be accomplished prior to even calling READERME, as in:

	IF  ( ( IUPBS01 ( ibfmsg, 'MTYP' ) .eq. '2' ) .and.
     +	      ( IUPBS01 ( ibfmsg, 'MSBT' ) .eq. '7' ) )  THEN
	    msgok = .true.
	ELSE
	    msgok = .false.
	END IF

Or, alternatively, we could even call UPDS3 in order to confirm that the sequence of FXY numbers in Section 3 of the BUFR message matches what was defined for "NC002007" within our "bufrtab.example" tables file! At any rate, once we have an actual "NC002007" message internally within BUFRLIB, a second (i.e. inner) loop reads each data subset from that message via successive calls to function IREADSB, until a non-zero return code indicates that there are no more data subsets and that, therefore, we need to go back to the first (i.e. outer) loop to read and pass the next BUFR message into READERME. Otherwise, successive calls to UFBINT, UFBREP, etc. return the desired actual data values, in the units listed for the respective mnemonics within the associated BUFR tables file. In particular, note how the order in which the mnemonics are listed within the final (i.e. CMNSTR) call argument defines the order in which their respective values are stored within the output array r8arr, and note that any such values which are "missing" for a particular data subset will be returned as 10.0E10. Note also that the use of UFBREP is required for reading the levels of observation sequence data, since that data was replicated via regular (i.e. non-delayed) replication with a fixed replication factor of ( 36 + 7 = 43 ). In fact, the use of UFBREP is also required for the repeated mnemonics "HINC" and "ACAV", the latter of which occurs twice within each observation sequence level and which therefore is replicated ( 43 * 2 = 86 ) total times within each data subset. This, it is worth noting, is why we set the parameter MXLV = 86, recalling that is the responsibility of the application program to ensure that the r8arr array is dimensioned at least as large enough to hold all of the possible data values that could be returned by any call of UFBINT, UFBREP , etc. in which it is used! Further, and for that very same reason, if the observation sequence data had been, say, 8-bit delayed replicated instead of regularly replicated with a fixed (i.e. previously known) replication factor of 43, then we would have been wise to set MXLV = 510 ( = 255 * 2 ), since 255 is the maximum number of levels that could possibly be returned by UFBINT when using 8-bit delayed replication, and, in such a case, we would have to inspect the output value nlv in order to know how many levels were actually returned for each data subset!