16 Dealing with Extensions – using HDS routines

As described in Section 2, extensions can be used to store non-standard items in an NDF. A typical use of an extension would be to store information associated with a particular instrument; this information would be processed by the data-reduction package specific to that instrument. Programmers who design extensions should register the extension names with Starlink to avoid duplication. Obvious generic names such as ‘ASTROMETRY’ should be avoided as these are likely to be defined by Starlink in the future.

It is, of course, possible to perform all access to HDS structures using HDS routines (see SUN/92). The NDF routines simply provide a convenient method of accessing standard items in NDFs, but the programmer must resort to HDS routines to deal with items in extensions. However, the NDF routines do include facilities for propagating extensions, checking for the presence of extensions, creating and deleting extensions and finding HDS locators to specific extensions.

This last statement requires some explanation. HDS refers to items in a structure via locators; each locator is a CHARACTER*15 variable which points to a HDS object. Strictly, a locator is a string of length DAT__SZLOC – this latter item being a symbolic constant which is defined in the SAE_PAR include file. Locators must be declared as CHARACTER*(DAT__SZLOC) as there is no guarantee that the length of 15 will be used for future HDS implementations on machines other than VAXs. The use of locators is illustrated in the example which follows.

Recalling the program ADAM_EXAMPLES:ADD7.FOR discussed in Section 6, a constant value was added to each element in an NDF main data array. NDF_ASSOC was used to associate the input file with an NDF identifier and NDF_MAP was used to map the main data array. The program below performs a similar task, but uses HDS DAT_ routines.

        SUBROUTINE ADD8 (STATUS)
        IMPLICIT NONE
        INCLUDE ’SAE_PAR’
        CHARACTER*(DAT__SZLOC) ILOC, DLOC
        INTEGER NELM, STATUS, PTR
  
  *   Associate locator with input file.
        CALL DAT_ASSOC (’INPUT’, ’UPDATE’, ILOC, STATUS)
  
  *   Get locator for input.DATA_ARRAY.
        CALL DAT_FIND (ILOC, ’DATA_ARRAY’, DLOC, STATUS)
  
  *   Map input.DATA_ARRAY.
        CALL DAT_MAPV (DLOC, ’_REAL’, ’UPDATE’, PTR, NELM, STATUS)
  
  *   Call subroutine to add 8.0 to each array element.
        CALL ADDIT (NELM, %VAL(PTR), 8.0, STATUS)
  
  * Tidy up
        CALL DAT_ANNUL (DLOC, STATUS)
        CALL DAT_ANNUL (ILOC, STATUS)
        END

Two locators ILOC and DLOC are used in this program; ILOC is associated with the top-level of the input NDF, i.e. with INPUT, and DLOC with INPUT.DATA_ARRAY. Both are declared as CHARACTER*DAT__SZLOC and both are annulled with a call to DAT_ANNUL at the end of the program. Annulling a locator cancels the association between the locator and the object and unmaps any arrays mapped via the locator. The first call to DAT_ASSOC associates a locator ILOC with the input file. This locator effectively points to the top-level of the file. To find a locator to a first-level object, DAT_FIND is used. For example, the call above finds a locator to INPUT.DATA_ARRAY. DAT_MAPV maps the object INPUT.DATA_ARRAY as a vectorised array, just as NDF_MAP did in ADD7.

However, this program will only work if the main data array is to be found in INPUT.DATA_ARRAYi.e. the NDF is primitive. If INPUT.DATA_ARRAY is a structure with the actual data array contained in INPUT.DATA_ARRAY.DATA as is also consistent with the NDF definition, then ADD8 will crash. The program could perform checks to discern the format for itself, but it is obviously easier to use the NDF routines to access standard objects.

However this option is not available when wishing to process information in extensions. The next example considered here uses information from the FIGARO extension. This extension may contain the exposure time for an observation. If this exists it will be held in .OBS.TIME in the FIGARO extension. This extract from a TRACE on ADAM_EXAMPLES:FIGDATA.SDF shows the actual location is FIGDATA.MORE.FIGARO.OBS.TIME.

  FIGDATA  <NDF>
    ...
    MORE           <EXT>           {structure}
       FIGARO         <EXT>           {structure}
          OBS            <STRUCT>        {structure}
             TIME           <_REAL>         7.5

The example program ADAM_EXAMPLES:DIVTIM.FOR reads the value of the exposure time and divides the main data array by this value. The steps involved in accessing the exposure time value are described below.

The input file is associated with an NDF identifier in the usual way. The program checks for the existence of a FIGARO extension and if such an extension does exist, a locator to it is found.

  *   Check that FIGARO extension is there.
        CALL NDF_XSTAT (NDF, ’FIGARO’, EXIST, STATUS)
        IF (EXIST) THEN
  *      Get locator to FIGARO extension.
           CALL NDF_XLOC (NDF, ’FIGARO’, ’UPDATE’, FLOC, STATUS)

It is now necessary to use HDS routines. The program checks to see if there is an .OBS structure in the FIGARO extension; if there is, a locator to it is found.

  *      See if FIGARO .OBS  structure is there.
           CALL DAT_THERE (FLOC, ’OBS’, EXIST, STATUS)
           IF (EXIST) THEN
  *         Get locator for FIGARO .OBS structure.
              CALL DAT_FIND (FLOC, ’OBS’, OLOC, STATUS)

Now the program checks for the existence of a .TIME object in the FIGARO .OBS structure. If this exists, a locator to it is found.

  *         See if .OBS.TIME is there.
              CALL DAT_THERE (OLOC, ’TIME’, EXIST, STATUS)
              IF (EXIST) THEN
  *            Get locator for FIGARO .OBS.TIME item.
                 CALL DAT_FIND (OLOC, ’TIME’, TLOC, STATUS)

The value of the .OBS.TIME object is now retrieved. The locator to it can be annulled.

  *            Get value of .OBS.TIME
                 CALL DAT_GET(TLOC, ’_REAL’, 0, 0, TIME, STATUS)
                 CALL DAT_ANNUL (TLOC, STATUS)