1 Introduction

 1.1 Some Example ARD Descriptions
 1.2 An Example ARD Application
 1.3 Supplying ARD Descriptions to an Application

Astronomical applications often require the user to identify regions of interest within a data array. For instance a statistics application may need to be told the region within the input image in which it is to evaluate the pixel statistics. Another example is a data calibration application which needs to be told the regions in which the detector was unreliable so that it can flag the corresponding output pixels as bad.

One way of identifying such regions is through the use of a pixel mask. In a pixel mask, different pixel values are used to differentiate between those pixels which are to be included by the application and those which are to be excluded. Such pixel masks are usually the same size and shape as the data array being processed by the application (so that a one-for-one correspondence exists between mask pixels and data pixels). This results in such masks occupying large amounts of disk space. More importantly, it means that different pixel masks are required for data arrays with different sizes or shapes.

ARD circumvents these problems by using textual expressions (known as “ARD descriptions”) to describe the pixels to be included by the application. Multi-dimensional data arrays can be handled. A simple 2-dimensional ARD description such as:

        CIRCLE( 20, 20, 5 ) .OR. RECT( 20, 20, 30, 30 )

tells the application to process all pixels which are either within a circle centred on pixel coordinates (20,20) with a radius of 5 pixels, or are within the rectangle with opposite corners at pixels coordinates (20,20) and (30,30). The ARD_WORK subroutine will convert an ARD description such as this into a pixel mask, with a shape and size specified by the application. The application will usually know the shape and size of the data array and so can ask ARD_WORK to create a pixel mask of the correct shape and size. Once the pixel mask is no longer needed, the storage space used to hold the mask can be released; there is no need to keep permanent copies of the pixel mask on disk.

As well as creating pixel masks, the ARD library also provides facilities to plot an ARD description on a graphics device. The ARD_PLOT routine draws the borders of the regions described in the ARD description, using a supplied AST Plot (see SUN/210) to perform the graphics.

In the above example, positions and distances in the ARD description were given in pixel coordinates. The ARD language includes two systems which allow positions and distances to be given in other coordinate systems:

As of ARD version 2.0, the calling application can define an arbitrary collection of coordinate systems (which need not be linearly related to pixel coordinates) by supplying an AST FrameSet (see SUN/210). The FrameSet contains information which allows positions to be mapped from any of these coordinate systems into pixel coordinates. Positions within the ARD description can then be given in any of these coordinates systems (a statement in the ARD description indicating which system is being used). Thus, for instance, if the pixel array has an RA/DEC calibration, the application could supply a FrameSet indicating how to convert from RA/DEC to pixel coordinates. This would allow the ARD description to include positions in RA/DEC.

In addition, the ARD description itself can include a FrameSet defining a collection of inter-related coordinate systems (positions in the ARD description should be given in the "current" Frame of this FrameSet). In this case, an attempt is made to find a coordinate system which is contained both within the FrameSet supplied by the calling application, and within the FrameSet supplied in the ARD description. For instance, extending the previous example, if an ARD description contains position given in pixel coordinates in some other specific image and also contains a FrameSet which relates pixel positions within that image to RA/DEC, then positions will be mapped from pixel coordinates within the original image, into RA/DEC, and then into pixel coordinates within the required mask image. This effectively allows pixel positions to be given within one image and then transformed so that they can be used within another image.

The calling application can also define an “application coordinate system” which is linearly related to pixel coordinates. Positions within the ARD description can then either be given directly in application coordinates, or in any coordinate system linearly related to application coordinates (in which case the ARD description must include statements describing the linear transformation). This system was present in version 1 of ARD and is still present in the current version, but is now deprecated in favour of the above more general system.

1.1 Some Example ARD Descriptions

ARD descriptions are made up by using logical operators (.AND., .OR., .NOT., etc.) to combine together keywords which represent the basic shapes known to ARD. statements can also be included which modify the way the ARD description is interpreted (for instance, by setting up an alternative coordinate system).

The ARD library includes two subroutines (ARD_GROUP and ARD_GRPEX) which simplify the task of obtaining ARD descriptions from the user or environment. Using these routines, an ARD description can be supplied to an application either directly, or by storing it in a text file and supplying the name of the text file to the application. The following examples represent lines stored in a text file. Such lines are effectively concatenated together into a single string before being processed by ARD:

        ROTBOX( 0, 0, 20, 10, 30 )

This example is simply a single keyword representing one of the basic shapes known to ARD. It selects all pixels which have centres on or within a 2-dimensional rotated box. The box is centred on coordinates (0,0) and has sides of length 20 and 10. The first side of the box (i.e. the one with length 20) is at an angle of 30 to the array X axis (measured anti-clockwise).

        CIR( 0, 0, 10 ) .AND. .NOT. ( COLUMN( 10 ) .OR. ROW( 5 ) )

This example uses logical operators and parentheses to combine several basic shapes together into a more complex shape. It also shows the use of abbreviated keywords. All pixels within the circle of radius 10 centred on (0,0) are selected, except for those which are on column 10 or row 5.

        COFRAME( SKY, System=FK5, Equinox=2003.5 )
        BOX( 12:23:41, -89:14, 1h40m, 20m )

This example shows the use of statements to specify the coordinate system in which positions are supplied. In this case, the COFRAME statement indicates that positions are supplied in FK5 equatorial (RA/DEC) coordinates, referred to the equinox of 2003.5. The BOX keyword then selects a box centred at RA 12h23m41s and Dec. 89deg14 . The box covers an RA range of 1h40m and a Dec range of 20 . The edges of a BOX region are always lines of constant axis value. Since this region is very close to the south equatorial pole, the pixel region containing this "box" will have quite strongly curved sides.

        PIXEL(  1,  1 )
        PIXEL( 20, 13 )
        PIXEL(-55,122 )
        PIXEL(112, 87 )
        PIXEL( 33, 12 )

This example demonstrates the ability of ARD to recognise implicit .OR. operators. The list of PIXEL keywords specifies a set of individual pixels. Since these keywords have no intervening operators, ARD assumes that a .OR. operator is to be inserted between each pair, i.e. the union of all the individual pixels is assumed.

1.2 An Example ARD Application

This section presents Fortran code for an ADAM application which obtains a data array (in the form of an NDF structure, see SUN/33) and an ARD description from the environment, converts the ARD description into a pixel mask, and then finds and displays the data sum within the region specified by the ARD description. The application will deal with NDFs of any dimensionality up to the limit imposed by the NDF_ system.

        SUBROUTINE ARD_TEST( STATUS )                             [1]
  *  Include definitions of global constants.
        INCLUDE ’SAE_PAR’                                         [2]
  *  Declare local variables.
       :        LBND( NDF__MXDIM ), UBND( NDF__MXDIM ),           [3]
       :        LBNDI( NDF__MXDIM ), UBNDI( NDF__MXDIM ),
       :        LBNDE( NDF__MXDIM ), UBNDE( NDF__MXDIM ),
       :        REGVAL
        REAL SUM, TRCOEF( 1 )
  *  Check inherited global status.
        IF ( STATUS .NE. SAI__OK ) RETURN                         [4]
  *  Obtain an identifier for the input NDF.
        CALL NDF_ASSOC( ’NDF’, ’READ’, INDF, STATUS )             [5]
  *  Obtain the bounds of the NDF.
       :                STATUS )
  *  Map the DATA component of the NDF.
        CALL NDF_MAP( INDF, ’DATA’, ’_REAL’, ’READ’, IPDATA, EL,  [7]
       :              STATUS )
  *  Obtain an ARD description specifying the region in which
  *  the pixel values are to be summed.
  *  Obtain workspace to hold the pixel mask corresponding to
  *  the supplied ARD description.
        CALL PSX_CALLOC( EL, ’_INTEGER’, IPMASK, STATUS )         [9]
  *  Get an AST FrameSet describing the WCS coordinate Frames stored
  *  in the NDF.
        CALL NDF_GTWCS( INDF, IWCS, STATUS )                      [10]
  *  Indicate that positions within the ARD description can be given in
  *  any coordinate Frame included in the above WCS FrameSet.
        CALL ARD_WCS( IWCS, ’ ’, STATUS )                              [11]
  *  Indicate that the value 2 should be used to represent
  *  pixels specified by the first keyword in the ARD
  *  description.
        REGVAL = 2
  *  Call ARD_WORK to store positive values at all mask pixels
  *  specified by the ARD description, and zero at all other
  *  pixels.
       :               REGVAL, %VAL( IPMASK ), LBNDI, UBNDI,
       :               LBNDE,  UBNDE, STATUS )
  *  Call a subroutine to sum the data in the specified regions.
       :            STATUS )                                      [13]
  *  Display the data sum.
        CALL MSG_SETR( ’SUM’, SUM )
        CALL MSG_OUT( ’ARD_TEST_MSG1’, ’  Data sum: ^SUM’,        [14]
       :               STATUS )
  *  Release the work space used to hold the pixel mask.
        CALL PSX_FREE( IPMASK, STATUS )                           [15]
  *  Delete the group used to hold the ARD description.
        CALL GRP_DELET( IGRP, STATUS )                            [16]
  *  Annul the AST FrameSet identifier.
        CALL AST_ANNUL( IWCS, STATUS )                            [17]
  *  Annul the NDF identifier.
        CALL NDF_ANNUL( INDF, STATUS )                            [18]

Programming notes:

The example is actually an ADAM A-task, and so consists of a subroutine with a single argument giving the inherited status value. See SUN/101 for further details about writing ADAM A-tasks. A “stand-alone” equivalent to the ARD_GROUP routine is available which can be used with non-ADAM applications.
The INCLUDE statements are used to define the various “symbolic constants”, which are used in this routine. Starlink software makes widespread use of such constants, which should always be defined in this way rather than by using actual numerical values. They are recognisable by the double underscore “__” (e.g. “SAI__OK”) which distinguishes them from subroutine names. SAE_PAR defines constants starting with “SAI__”, NDF_PAR defines constants starting with “NDF__” (see SUN/33), PRM_PAR defines constants starting with “VAL__” (see SUN/39), GRP_PAR defines constants starting with “GRP__” (see SUN/150), and AST_PAR defines constants starting with “AST__” (see SUN/210).
This application is designed to be able to handle data arrays of any dimensionality, up to the limit set by the NDF library. This limit is given by the symbolic constant NDF__MXDIM, which is used in the declaration of various arrays used to hold information describing each axis.
The value of the STATUS argument is checked. This is because the application uses the Starlink error handling strategy (see 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.
The input NDF is now obtained using the ADAM parameter ’NDF’. This may involve prompting the user, or the NDF may be identified using some other means (for instance, the NDF may have been specified on the command line which invoked the application). An integer value is returned to the application in variable INDF. This is an NDF identifier and is used to refer to the NDF throughout the rest of the application.
The shape and size of the NDF is now obtained. This returns the number of dimensions, and the upper and lower bounds on each axis. These bounds are needed to be able to correctly locate positions supplied within the ARD description.
The DATA array in the NDF is then accessed by calling NDF_MAP. Rather than returning actual data values, this routine returns a pointer to the data values in IPDATA. The total number of pixels in the array is returned in EL.
Next, the ARD description is obtained using ADAM parameter ’REGION’. The returned ARD description is stored in a “GRP group” (rather like a Fortran character array). The GRP package is described in SUN/150 and programmers using ARD should be aware of its contents. An integer value is returned in IGRP which is used to identify the group containing the ARD description throughout the rest of the application. The second argument is a “null group identifier” (a symbolic constant defined within the include file GRP_PAR). It is used to indicate that there is no existing ARD description on which to base the new ARD description; a completely new ARD description must be supplied.
We now obtain a pointer to a temporary array in which we can store the pixel mask corresponding to the ARD description. The mask has the same number of pixels as the data array, and each pixel stores an integer value. The PSX library is a Fortran interface to the POSIX library and is described in SUN/121.
An NDF structure can have a range of “World Coordinate Systems” associated with it. Information describing these coordinate systems, and how to transform positions between them, is stored in the WCS component of the NDF. The NDF_GTWCS routine returns an identifier for an AST FrameSet which is a representation of the WCS component. The facilities of the AST library can then be used to manipulate the WCS information in many different ways.
ARD_WCS stores the supplied FrameSet pointer for later use by the ARD_WORK routine. Making this call to ARD_WCS is optional. If it were not made, then positions within the ARD description would have to be supplied in pixel coordinates. Since we are in fact calling ARD_WCS, positions within the ARD description can be supplied in any coordinate system which can be related to any of the coordinate systems in the NDF’s WCS FrameSet.
The subroutine ARD_WORK is now called to create the pixel mask identifying the pixels specified by the ARD description. Positive values are stored in the mask for such pixels, and zero is stored for all other pixels. Note, no value need be assigned to the TRCOEF argument since it will be ignored anyway. This is because of the earlier call to ARD_WCS which specifies the WCS information, and makes the TRCOEF argument redundant.
A subroutine is now called to add up the pixel values in the regions specified by the ARD description. The pointer values returned by NDF_MAP and PSX_CALLOC are turned into actual Fortran arrays at this point, which SUMIT can access. This is done using the %VAL function in the call to SUMIT. SUMIT is not part of the ARD package, but would be written by the application programmer. It may look like this:
  *  Include definitions of global constants.
  *  Arguments Given.
        INTEGER EL
        REAL DATA( EL )
        INTEGER MASK( EL )
  *  Arguments Returned.
        REAL SUM
  *  Arguments Given and Returned.
  *  Declare local variables.
        INTEGER I
  *  Check inherited global status.
  *  Initialise the sum of the valid data values.
        SUM = 0.0
  *  Loop round every element in the data array.
        DO I = 1, EL
  *  Check to see if this pixel was included in the ARD
  *  description. It will have a positive mask value if it was.
  *  Skip over the pixel if it was not included.
           IF( MASK( I ) .GT. 0 ) THEN
  *  The regions selected by the ARD description may contain
  *  pixels which are flagged as unusable in the input NDF.
  *  Such pixels have the value given by the symbolic constant
  *  VAL__BADR and should not be included in the returned data
  *  sum.
              IF( DATA( I ) .NE. VAL__BADR ) THEN
                 SUM = SUM + DATA( I )
              END IF
           END IF
        END DO

The two arrays can be treated as one dimensional vectors because they are the same size and shape. This makes it easy to process arrays of any dimensionality.

The data sum is displayed by assigning its value to an “MSG token” and then incorporating this token into a message to be displayed on the standard output device. See SUN/104 for a description of the MSG package.
The storage space used to hold the pixel mask is released so that it can be re-used.
The storage space used by the GRP group to hold the ARD description is released.
We now tell the AST library to release the resources used to store the FrameSet read from the NDF.
Finally, the NDF is closed.

1.3 Supplying ARD Descriptions to an Application

This section outlines some of the ways in which a user could supply an ARD description in response to a prompt for the ’REGION’ parameter in the example application described in the previous section.

The first thing to be said is that the dimensionality of the ARD description (i.e. the number of values used to represent a single position in the ARD description) need not match that of the supplied NDF. The ARD description may refer to some subset of the axes in the NDF, in which case the mask will be applied independently to every value on the unspecified axes. By default, ARD descriptions are always assumed to be 2-dimensional, but this default can be over-ridden by including a DIMENSION statement in the ARD description. Thus for instance, if the positions in the ARD description are 3-dimensional, the user may want to give an ARD description such as:

        DIMENSION( 3 )
        CIRCLE( 40, 50, 60, 10) .OR. CIRCLE( 45, 55, 65, 10 )

The DIMENSION statement tells ARD to expect three values per position. The rest of the ARD description specifies the union of two spheres (i.e. “3-dimensional circles”), centred on (40,50,60) and (45,55,65), each of radius 10. This ARD description uses the default coordinate system established by the application’s call to ARD_WORK. In the case of the application above, this default coordinate system is just the pixel coordinate system of the NDF.

ARD uses the versatile facilities of the GRP package to obtain the ARD description. The following examples outline some of the ways in which the above ARD description could be specified in response to a prompt for parameter ’REGION’. For more details on the facilities of GRP, see SUN/150. In each case the “ >” represents the final character of the prompt string issued by the ADAM parameter system and is not actually typed in by the user:

The entire ARD description could be given as a single literal string:
        > DIMENSION(3) CIRCLE( 40, 50, 60, 10) .OR. CIRCLE( 45, 55, 65, 10 )

All blanks and tabs are ignored.

The ARD description could be split up into several strings given in response to successive prompts. This is particularly useful for long ARD descriptions:
        > DIMENSION(3) -
        > CIRCLE( 40, 50, 60, 10) .OR. -
        > CIRCLE( 45, 55, 65, 10 )

If an ARD description ends with a minus sign (“”), the ARD_GROUP subroutine will issue another prompt and append any string supplied to the end of the previously supplied string. This continues until an ARD description is supplied which doesn’t end with a minus sign.

ARD descriptions can be split anywhere except in the middle of a numerical value. So, for instance, the following responses would be valid:
        > DIMENSION(3) CIRCLE( 40, 50, -
        > 60, 10) .OR. CIRCLE( 45, 55, -
        > 65, 10 )
If ARD finds adjacent keywords without any intervening operator, an implicit .OR. is inserted between them. So the following, in which the .OR. operator is not explicitly included, would give the same results as the previous examples:
        > DIMENSION(3) -
        > CIRCLE( 40, 50, 60, 10 ) -
        > CIRCLE( 45, 55, 65, 10 )
Instead of supplying the ARD description directly in response to the parameter prompt, it can be stored in a text file and the name of the text file given in response to the prompt. To do this, the file name must be preceded by an up-arrow symbol (“^”). If the file desc.ard contained the three lines:
        CIRCLE( 40, 50, 60, 10 )
        CIRCLE( 45, 55, 65, 10 )

then the user could give the following response:

        > ^desc.ard

The ARD description can be split between the lines of the file in any way the user chooses.

If only part of the ARD description is stored in a text file, then the two methods can be combined. For instance, if the DIMENSION statement is omitted from the file, so that desc.ard contains:
        CIRCLE( 40, 50, 60, 10 )
        CIRCLE( 45, 55, 65, 10 )

then the user could give the complete ARD description by giving the following response:

        > DIMENSION(3);^desc.ard

This would cause the contents of the file desc.ard to be concatenated with the string preceding the “;” character. Note, the semi-colon is not included in the ARD description returned by ARD_GROUP.

If the ARD description were to be split over several files, the contents of the files could be concatenated together in a similar way:
        > ^desc1.ard;^desc2.ard;^desc3.ard

This would cause the contents of files desc1.ard, desc2.ard and desc3.ard to be combined to form the final ARD description.

Indirection through text files can be nested. So if the string

were stored in a file total.ard, then the user could give the response:

        > ^total.ard