next up previous
Next: OBTAINING AND USING NDF IDENTIFIERS
Up: OVERVIEW
Previous: Error Handling

Overview of a Typical Application   

The following contains an example of a simple application which uses the NDF_ routines to add the data arrays of two NDF data structures to produce a new NDF. This is not quite the simplest ``add'' application which could be written, but is close to it. Nevertheless, it will do a good job, and will respond correctly to unforseen circumstances or conditions which it is not designed to handle by issuing sensible error messages.

The intention here is simply to give a flavour of how the NDF_ routines are used, so don't worry if you don't understand all the details. The example is followed by some brief programming notes which include references to other relevant sections of this document which can be consulted if necessary. If you are interested, a more sophisticated ``add'' application with extra commentary can also be found in §[*].

      SUBROUTINE ADD( STATUS )                                 [1]
      INCLUDE 'SAE_PAR'                                        [2]
      INTEGER STATUS, EL, NDF1, NDF2, NDF3, PNTR1( 1 ), PNTR2( 1 ), PNTR3( 1 )
                                                              
*  Check inherited global status and begin an NDF context.    
      IF ( STATUS .NE. SAI__OK ) RETURN                        [3]
      CALL NDF_BEGIN                                           [4]
                                                              
*  Obtain identifiers for the two input NDFs and trim their pixel-index
*  bounds to match.                                           
      CALL NDF_ASSOC( 'IN1', 'READ', NDF1, STATUS )            [5]
      CALL NDF_ASSOC( 'IN2', 'READ', NDF2, STATUS )           
      CALL NDF_MBND( 'TRIM', NDF1, NDF2, STATUS )              [6]
                                                              
*  Create a new output NDF based on the first input NDF.      
      CALL NDF_PROP( NDF1, 'Axis,Quality', 'OUT', NDF3, STATUS )  [7]
                                                              
*  Map the input and output data arrays.                      
      CALL NDF_MAP( NDF1, 'Data', '_REAL', 'READ', PNTR1, EL, STATUS )  [8]
      CALL NDF_MAP( NDF2, 'Data', '_REAL', 'READ', PNTR2, EL, STATUS )
      CALL NDF_MAP( NDF3, 'Data', '_REAL', 'WRITE', PNTR3, EL, STATUS )
                                                              
*  Check that the input arrays do not contain bad pixels.     
      CALL NDF_MBAD( .FALSE., NDF1, NDF2, 'Data', .TRUE., BAD, STATUS )  [9]
                                                              
*  Add the data arrays.                                       
      CALL ADDIT( EL, %VAL( PNTR1( 1 ) ), %VAL( PNTR2( 1 ) ),  [10]
     :            %VAL( PNTR3( 1 ) ), STATUS )                
                                                              
*  End the NDF context.                                       
      CALL NDF_END( STATUS )                                   [12]
      END                                                     
                                                              
                                                              
*  Subroutine to perform the addition.                        
      SUBROUTINE ADDIT( EL, A, B, C, STATUS )                  [11]
      INCLUDE 'SAE_PAR'                                       
      INTEGER EL, STATUS, I                                   
      REAL A( EL ), B( EL ), C( EL )                          
                                                              
      IF ( STATUS .NE. SAI__OK ) RETURN                       
                                                              
      DO 1 I = 1, EL                                          
         C( I ) = A( I ) + B( I )                             
 1    CONTINUE                                                
      END

Programming notes:

1.
Note that the application is actually a subroutine, called ADD, with a single integer argument called STATUS. This is the ADAM method of writing applications (see SUN/101).
2.
The INCLUDE statement is used to define standard ``symbolic constants'', such as the value SAI__OK which is used in this routine. Such constants should always be defined in this way rather than by using actual numerical values. The file SAE_PAR is almost always needed, and should be included as standard in every application.

3.
The value of the STATUS argument is checked. This is because the application uses the error handling strategy described in SUN/104, which requires that a subroutine should do nothing unless its STATUS argument is set to the value SAI__OK on entry. Here, we simply return without action if STATUS has the wrong value.

4.
An NDF context is now opened, by calling NDF_BEGIN. This call matches the corresponding NDF_END call at the end of the ADD routine. When the NDF_END call is reached, the NDF_ system will ``clean up'' by closing down everything which has been used since the matching call to NDF_BEGIN (see §[*]). Since we want to clean up everything in the application at this point, the initial call to NDF_BEGIN is put right at the start.

5.
The two input NDFs which we want to add are now obtained using the parameters `IN1' and `IN2'. Lots of things happen behind the scenes at this point, possibly involving prompting the user to supply the names of the data structures to be added, and a pair of integer values NDF1 and NDF2 are returned. These values are NDF identifiers and are used to refer to the NDFs throughout the rest of the application (see §[*]).

6.
The first thing we do with these identifiers is to pass them to NDF_MBND. This routine ensures that the two NDFs are the same shape and size, which is what we require. The details of how this is done are explained much later (in §[*]). For now, just accept that it works.

7.
An output NDF is created next by calling NDF_PROP which uses the parameter `OUT' to get the new data structure (probably prompting the user for its name) and returns another identifier for it in NDF3. NDF_PROP bases the new NDF on the first input NDF (see §[*]). This ensures that it's the right size, etc., and also that it contains any ancillary information which can legitimately be copied from the input.

8.
The data arrays in the input and output NDFs are then accessed by calling NDF_MAP. Rather than returning actual data values, this routine returns pointers for the data values in PNTR1, PNTR2 and PNTR3 (see §[*]). Note that we want to `READ' the input arrays and `WRITE' to the output array.

9.
Since this is a very simple application, it cannot handle the special bad-pixel values which may be present in some NDF data structures. The call to NDF_MBAD at this point checks that there are none present (see §[*]). If there are, then an appropriate error message will result and the application will abort.

10.
The subroutine ADDIT which performs the work is now called. The pointer values returned by NDF_MAP are turned into actual Fortran arrays at this point, which ADDIT can access. This is done by using the %VAL function in the call to ADDIT (see §[*]).

11.
ADDIT itself is a very simple subroutine. Since all the arrays it will be passed are the same size (we have ensured this), there is no need to worry about their dimensions. They are all handled as if they were 1-dimensional, and simply added. The application could easily be altered to perform a different function by changing this routine.

12.
Finally, NDF_END is called. As already explained, this shuts everything down, ensuring that all NDF data files are closed, etc. before the application finishes.



next up previous
Next: OBTAINING AND USING NDF IDENTIFIERS
Up: OVERVIEW
Previous: Error Handling


Starlink User Note 33
R.F. Warren-Smith
11th January 2000
E-mail:rfws@star.rl.ac.uk

Copyright © 2000 Council for the Central Laboratory of the Research Councils