It will be evident from the rest of this document that the standard components of an NDF (title, data, quality, etc.) have rather well-defined meanings and rules associated with them which govern their interpretation and processing. This is necessary in order to allow items of general-purpose software to be written (often by independent authors) in such a way that they can be used together co-operatively and without the different authors having interpreted the meanings of the various components in different ways.
In contrast, extensions are the means by which the information stored in an NDF may be extended to suit local or personal requirements. For instance, an extension may be used to hold information specific to a particular form of processing, a particular software package, a particular author, or a particular astronomical instrument. In this case, universal agreement about how to process the information is not necessary, so no restrictions are imposed on the information which may be stored in extensions. As a consequence, a clear distinction has to be maintained between the processing of standard NDF components and of extensions, since truly general-purpose software can only be written to process the standard components of an NDF, whose interpretation is universally defined. If an NDF contains extension information, then this can only be successfully processed by more specialised software.
An NDF may contain any number of extensions which are distinguished by unique names. An extension name may consist of up to 15 characters. It must start with an alphabetic character, and may contain only alphanumeric characters (including underscore ‘_’).
Extensions are normally associated with particular software packages which may use a particular extension name to identify additional information stored in an NDF in a form which only they understand.9 For instance, a software package for analysing IRAS data might “own” the extension name ‘IRAS’ and use it to store additional information in NDF data structures relating to the processing of IRAS data. All the applications in that package would then be expected to recognise this extension in all NDF structures and correctly process the information it contains so that it remains valid throughout. However, there is no requirement for other software to be aware of this extension, other than to avoid using the same name.
In general, software which does not recognise an extension need do no more than propagate it (i.e. copy it), if appropriate, to any output data structure. Normally, this will be performed automatically by the NDF_ system and is considered in more detail in §14.
An NDF extension is an HDS object, normally a structure, whose contents are entirely at the discretion of the extension’s designer. Although some simple NDF_ routines are described below for identifying and accessing extensions, the contents of extensions are generally unknown and their interpretation lies outside the scope of the NDF_ system. This information must therefore normally be handled by means of HDS routines, so anyone planning to use NDF extensions in their software will need to be familiar with the concepts used by HDS (see SUN/92).
In addition, some general guidelines aimed at minimising the risk of poor design and eliminating name clashes between extensions are to be found in SGP/38. These should be consulted by all potential designers of NDF extensions. The following additional recommendation should perhaps also be added:
Do not design over-elaborate extensions.
The freedom allowed by HDS is a great temptation to do so, but the cost of writing software to support the extension must always be kept in mind.
The existence or non-existence of a specified extension in an NDF can be determined using the routine NDF_XSTAT. For instance:
will return a .TRUE. value via the logical argument THERE if an extension called ‘IRAS’ is present in an NDF. If it is, then it can be accessed using the routine NDF_XLOC. For instance:
will return an HDS locator to the extension (which is an HDS object) via the LOC argument.10
Note that an access mode of ‘READ’ was specified in this call to NDF_XLOC, indicating that the extension’s contents would be read but not modified. An access mode of ‘UPDATE’ would be used if the contents were to be modified, while ‘WRITE’ access would only be used in order to re-write the extension’s contents; in this case the NDF_ system will erase any previous contents before returning a locator to the extension.
The contents of an extension can be accessed by passing the resulting locator to suitable HDS routines. For instance, the value of an integer component called OFFSET within the ‘IRAS’ extension could be obtained as follows:
Note that the extension locator LOC must be annulled using the routine DAT_ANNUL when it is no longer required, since the NDF_ system will not perform this task itself.
This process of reading a scalar value from a component within an NDF extension is sufficiently common that a set of routines is provided to do it directly. These routines have names of the form NDF_XGT0x, where the (lower-case) “x” represents one of the standard Fortran 77 types and should be replaced by I, R, D, L or C, according to the type of value required. For instance, the above operation of reading an integer value from an ‘IRAS’ extension could also be performed with a single call to NDF_XGT0I, as follows:
These routines also have the advantage that they will accept a compound HDS component name as their third argument, making it possible to access components nested within sub-structures inside an extension. Array subscripts may also be specified, so that individual elements of non-scalar components and extensions may be accessed. For example:
Would read the 13th element from a ‘FITS’ extension which consists of a 1-dimensional array of character strings.
New extensions are created by calling the routine NDF_XNEW and specifying the extension name together with its HDS type and shape. For instance:
would create a new scalar ‘IRAS’ extension with an HDS type of ‘IRAS_EXTENSION’ and return a locator for it via the LOC argument. In practice, extensions will almost always be scalar HDS structures, but this routine allows for other possibilities if required. An error will result if the named extension already exists.
Once an extension structure has been created, components may be created within it and values assigned to them using HDS routines, as before. For instance, to create and assign a value of 30.5 to a real component called ANGLE in a newly-created ‘IRAS’ extension, the following calls might be used:
Here, the call to CMP_MOD ensures that the required ANGLE component exists, creating it if necessary. CMP_PUT0R then assigns a value to it. The extension locator LOC must be annulled when it is no longer required, since the NDF_ system will not perform this task.
Again, this process of creating a scalar component within an NDF extension and writing a value to it is sufficiently common that a set of routines is provided to do it directly. In this case, the routines have names of the form NDF_XPT0x, where the (lower-case) “x” should be replaced by I, R, D, L or C, according to the type of value being written. The above operation of writing a real value into a component of an ‘IRAS’ extension can therefore be performed with a single call to NDF_XPT0R, as follows:
As with the NDF_XGT0x routines (see §11.4), the NDF_XPT0x routines will also accept a compound HDS component name, possibly including array subscripts, as a third argument. This allows the creation of components which are nested more deeply inside the extension, but remember that all HDS structures lying above the new component must already exist. When writing to an array element, the array itself must also previously have been created.
It is relatively common to store quite large amounts of information in NDF extensions and this may include arrays whose size and shape matches that of the main NDF data array itself. In this case, the close association between the NDF_ routines and the ARY_ library (SUN/11) provides a number of convenient features for accessing this information.
For example, suppose that an astronomical instrument produces data in NDF format and also produces a measure of (say) temperature for each pixel of the NDF. This temperature information might conveniently be stored as an array within an NDF extension so that it is available during data reduction. The ARRAY data structure, which is handled by the ARY_ routines (a primitive HDS array is an example), would be convenient for this purpose since it also allows arbitrary pixel-index bounds which may be chosen to match those of the associated NDF.
To access the temperature information, we could use the techniques discussed above to locate the array within its extension and then to import it into the ARY_ system. This system provides analogous facilities to the NDF_ routines except that it applies to ARRAY data structures. In particular, the ARY_MAP routine may be used to obtain mapped access to an array, so that the process of accessing the temperature data might be as follows:
Here, the integer value IARY is an ARY_ system identifier, analogous to an NDF identifier.
A more convenient method of achieving the same result would be to call the routine NDF_XIARY, which combines the process of locating the extension and finding the array into a single call, as follows:
An additional advantage of this latter method is that NDF_XIARY will check whether the NDF identifier refers to an NDF section and will, if necessary, select a matching section from the temperature array (for a discussion of NDF sections, see §15). NDF_XIARY is also able to accept a compound HDS component name as its third argument, and can therefore access arrays nested more deeply within an extension.
Specific extensions may be deleted from an NDF using the routine NDF_XDEL. For instance:
would delete the ‘IRAS’ extension, if present, together with its contents. No error would result if the extension did not exist.
The routine NDF_STATE may be used to determine whether an NDF contains any extensions by specifying a component name of ‘Extension’. Thus:
will return a .TRUE. value via the STATE argument if one or more extensions are present in the NDF. The actual number of extensions present can be determined using the routine NDF_XNUMB. For instance:
will return the number of extensions via the NEXTN argument. The names of any extensions present can also be obtained, in this case using the routine NDF_XNAME. For instance:
will return the name of the N’th extension in upper-case via the XNAME argument. A blank name will be returned if no such extension exists. Note the use of the NDF__SZXNM symbolic constant (defined in the include file NDF_PAR) to declare the size of the character string which is to receive the extension name.
As an example, the following loop will list the names of all the extensions in an NDF:
9While the sharing of extensions between software packages and/or authors is not excluded, this is entirely the responsibility of those concerned. Since the requirements are so diverse, no specific recommendations can be made except to note that some documentary provision is normally necessary to ensure that separate authors interpret extension information in a consistent way.
10Note that an extension called ‘IRAS’ will actually be stored in an HDS object called .MORE.IRAS but the ‘.MORE.’ should not be included when using NDF_ routines.