Double Precision Array Files (DAF)







Purpose



This document describes the Double Precision Array File, DAF, an architecture for files that store arrays of double precision numbers. The SPICE SP-kernel and C-kernel files use the DAF architecture and associated software. The Required Reading files for these two SPICE kernels refer to this document for details.



Revisions



July 12, 1994



Intended Audience



This document is intended for users that require detailed knowledge of DAF-based file formats, such as the SP- and C-kernel formats already mentioned. It is also intended for users of the SPICELIB library who wish to create their own DAF-based file formats.

Most users of DAF-based formats will not generally need to understand the material presented in this document. For example, users of NAIF SP- and C-kernel files who wish to read state vectors and pointing angles from those files will normally do so using only subroutines and programs designed specifically for those formats. These subroutines are documented in the NAIF SPK and CK Required Reading files.



Related Documents



The following NAIF documents contain material that is closely related to the subject of this document.



Introduction



DAF---which stands for `Double precision Array File'---is a file architecture that provides the advantages of arrays and direct access files without incurring the disadvantages of either one.

This architecture is supported by a set of Fortran-77 subroutines, part of the NAIF Toolkit software library, SPICELIB.

DAF is called an architecture instead of a format because it includes an extensible family of file formats, each of which is characterized by a pair of parameters. A file that conforms to any one of these formats is called an `array file'.

An array file can contain any number of double precision arrays. Each of these arrays can contain an arbitrary number of elements. Because DAF files are intended to be portable, the DAF design requires that the array elements must be `pure' double precision numbers. That is, they may not contain equivalenced or encoded integer or character values.

The DAF subroutines in SPICELIB support the following operations:

The last two functions make array files portable to any environment that supports ANSI Standard Fortran-77.



DAF-based Formats



The DAF architevture has been designed with the intention that each array contained in a particular DAF possess a `descriptive summary' of itself. The information making up the summary and the organization of the summary should be the same for each array in the DAF. The descriptive summary is composed of double precision and integer components. The number of double precision components, ND, and the number of integer components, NI, making up the the array summaries, determine a particular format within the DAF architecture.

Values for ND and NI are fixed at the time an array file is created. Any two array files that have the same values for ND and NI can be thought of as having the same `format'. (This does not guarantee that the arrays in the files contain the same kinds of information, only that they could be stored in the same file.) The values selected for ND and NI must satisfy the following inequalities:

        (NI + 1)                     (Note that this is
   ND + -------- <= 125               integer division.
            2                         That is, (NI + 1)/2
                                      is rounded down to
                                      the nearest integer.)
 
   0 <= ND <= 124
 
   2 <= NI <= 250
Each array stored in an array file is `described', in part, by ND double precision numbers and NI integer numbers, which are stored separately from the array. Most of the details of this `description'---how many numbers are needed, and what they contain---are left to the designer of a specific DAF format.

The double precision numbers could include limits (the smallest and largest values in an array), a range of epochs throughout which the elements may be used, or statistics (the mean, median, and standard deviation of the elements).

The integer numbers could include contextual information (case number, identification codes for related objects or arrays) and conditional information (flags to indicate whether the array is unsorted, sorted by increasing or decreasing magnitude, or marked for deletion). Some integer numbers are used to keep track of the location of the array within the file.

Each array in an array file is is further described by NC characters of alphanumeric information. Examples of this alphanumeric information are producer names, archive codes, historical information, or anything else that is not easily encoded as double precision or integer numbers.

NC is a function of ND and NI. The relationship between NC and user-specified ND and NI was chosen to allowing a reasonable amount of space for storing the alphanumeric information for an array. NC is defined below:

                      (NI + 1)
   NC  =   8 * ( ND + -------- )     (Note that this is
                          2           integer division.)
The double precision and integer numbers that describe each array are `packed', or equivalenced, into an auxiliary double precision array before they are stored in the file. This auxiliary array is called the `summary' of the associated array. The individual (unpacked) numbers are called the `components' of the summary.

(The first ND elements of the summary contain the double precision components of the summary. Each of the remaining elements contains a pair of integer components. If NI is odd, the final element of the summary contains a single integer component.)

The NC alphanumeric characters that further describe each array are stored in a single character string, called the `name' of the array.



Array Addresses



The location of each array in an array file is defined by a pair of numbers, called the `initial address' and `final address' of the array.

The term `address' refers to a particular way of looking at an array file. Every array file is actually a standard Fortran-77 direct access file, with a particular record length. (Every array file has the same record length.) Each record is capable of storing up to 128 double precision numbers.

It is convenient, however, to think of an array file as a numbered collection of slots named `words'. Each word is large enough to hold one double precision number. Words 1 through 128 are located in the first record of the file; words 129 through 256 are located in the second record; and so on. The number of each word is called the `address' of the word within the file.

Any pair of addresses defines a contiguous set of words, which may fall within a single physical record or span a number of records. The elements of each array in an array file are stored in just such a set. The address of the first array element is the `initial address' of the array. The address of the final array element is the `final address' of the array.

The initial and final addresses of an array are always the values of the final two integer components of the summary for the array.



Array Files and Linked Lists



It is a simplification, but a useful one, to say that the arrays in an array file form a doubly-linked list. Each new array added to a file is placed at the tail of this list.

Because the list is doubly-linked, the head and tail of the list can be located immediately. The arrays can be located by moving a pointer through the list, in either direction, one array at a time.

At any time, the summary and name of the array at which the pointer is currently pointing can be retrieved and examined to determine whether the array is of interest. If it is, the initial and final addresses (the final two integer components of the summary) may be used to access---retrieve or update---the entire array, or any contiguous set of elements therein.

For example, if BEGIN and END are the initial and final addresses of an array, the first ten elements of the array can be retrieved by asking for the elements stored in addresses BEGIN through BEGIN+9. If the array contains an odd number of elements, the middle element can be retrieved by asking for the element stored in address (BEGIN+END)/2.



Read and Write Access



Array files may be opened for two kinds of access: read and write. A file opened for read access cannot be changed, either by adding a new array or by updating an existing one. Unless one of these operations must be performed, files should be opened for read access. The protection provided to files opened for read access is independent of any particular operating system.

A program may open only one array file at a time for write access. When a program attempts to open a file for write access, an error is signalled if another file is already open for write access, or if the file is already open for read access. An error is also signalled if a program attempts to open a file for read access if the file is already open for write access. (Errors are signalled through the standard SPICELIB error handling mechanism.)



File Handles



When a file is opened for either kind of access, it is assigned an integer `handle'. A mapping between handles and Fortran logical units is maintained internally by the DAF subroutines.

As a means of accessing files, handles have two advantages over logical unit numbers.



DAF Family of Subroutines



SPICELIB contains a family of subroutines that can be used to create, populate, and manipulate array files. The name of each routine begins with the letters `DAF', followed by a two- or three-character mnemonic. For example, the routine that begins a forward search of an array file is named DAFBFS, pronounced `DAF-B-F-S'. A complete list of mnemonics, translations, and calling sequences can be found at the end of this document.

Each subroutine is prefaced by a complete SPICELIB module header that describes inputs, outputs, restrictions, and exceptions, discusses the context in which the subroutine should be used, and shows typical examples of its use. Any discussion of the subroutines in this article is intended as an introduction: the final documentation for any subroutine is its module header.

In this document, whenever a subroutine appears in an example, the translation of the mnemonic part of its name will appear to the right of the reference, in braces. For example,

   CALL DAFBFS ( HANDLE )                 { Begin forward search }
Examples will make use of the structured DO ... END DO and DO WHILE ... END DO statements supported by the VAX/VMS Fortran compiler. These statements are easily converted to the standard equivalents

           DO label var = e1, e2, e3
              stmt
   label   CONTINUE
and

   label   IF       ( expr )
          .THEN
              stmt
              GO TO label
           END IF


Opening and Closing Array Files



An existing array file can be opened by supplying the name of the file to DAFOPR (for read access) or DAFOPW (for write access). Each routine returns a file handle, which must be used for all subsequent access to the file.

   CALL DAFOPR ( FNAME, HANDLE )          { Open for read }
   CALL DAFOPW ( FNAME, HANDLE )          { Open for write }
Once opened, an array file can be closed by supplying its handle to DAFCLS.

   CALL DAFCLS ( HANDLE )                 { Close }


Creating Array Files



A new array file can be created by supplying the name of the file, the type of data in the file, values for ND and NI, an internal file name, and the number of records to be reserved. NI must be greater than or equal to 2. (For more information about the bounds on NI and ND see the subsection `Summary records'.)

   CALL DAFONW ( FNAME,                   { Open new }
                 FTYPE,
                 ND,
                 NI,
                 IFNAME,
                 RESV,
                 HANDLE  )
The internal name of an array file is simply a string of up to 60 characters, which may be used to characterize the contents of the file. Its primary value is that, being internal to the file, it remains unchanged when the file is transferred between environments.

Any number of records may be reserved at the front of an array file. By definition, the contents of these records are invisible to DAF subroutines, and may contain any information that the user wishes to store in them.

Once created, a new array file remains open for write access until explicitly closed.



Modifying Reserved Records



When a DAF is created, the number of reserved records must be specified in the call to DAFONW. Because it is not always possible to know at the time the file is created exactly how many reserved records are needed, the reserved record area may need to be modified later. The reserved record area can be modified in two ways: reserved records can be added or they can be removed. DAFARR adds RESV number of reserved records to the end of the reserved record area. DAFRRR removes RESV number of reserved records from the end of the reserved record area.

   CALL DAFARR ( HANDLE, RESV )            { Add reserved records }
   CALL DAFRRR ( HANDLE, RESV )            { Remove reserved records }


Adding Arrays



A new array can be added to an existing array file by calling four routines: DAFPS, DAFBNA, DAFADA, and DAFENA.

First, the summary is packed by DAFPS, which requires two arrays containing the double precision and integer components of the summary. It also requires the values of ND and NI for the file.

   CALL DAFPS ( ND, NI, DC, IC, SUM )     { Pack summary }
The final two integer components of the summary are always used to store the initial and final addresses of the array imposing that NI be greater than or equal to two. These components are filled in after the array has been stored: any values for these components supplied by the user are ignored.

Next, the new array must be initialized by calling DAFBNA. DAFBNA requires the handle of the file (which must be open for write access), the array name, and the array summary.

   CALL DAFBNA ( HANDLE, SUM, NAME )      { Begin new array }
The elements of the array are added by DAFADA. The elements may be supplied in one shot,

   CALL DAFADA ( DATA, N )                { Add data to array }
or in any number of installments,

   DO WHILE ( MORE )
       ...
      CALL DAFADA ( DATA, N )             { Add data to array }
   END DO
Once the entire array has been supplied, DAFENA makes the addition permanent.

   CALL DAFENA                            { End new array }
If the process is aborted before DAFENA is called, the summary and name are not stored, and the new array does not become a permanent member of the file. Space allocated for elements of the array cannot be removed from the file; however, it will be overwritten by the elements of the next array added to the file.

One way to abort the addition of an array to a file is to call DAFBNA start a new array in the same file, without first ending the current array.



Adding Arrays to Multiple Array Files



It is possible add data to arrays in multiple files in an interleaved fashion: addition of data to an array in one file can be interrupted in order to add data to an array in another file. To accomplish this, it is necessary to tell DAFADA and DAFENA which file to act upon. This file is called the `current file'.

When DAFBNA is used to begin an array, the file specified by the handle passed to DAFBNA becomes the current file. Calls to DAFADA or DAFENA will add data to or end the last array begun in this file. If DAFBNA is called again, this time with a different handle, the file specified by that handle becomes current. Files that are not current are not affected in any way by beginning, adding data to, or ending arrays in the current file.

In any given file, an array that is in progress---that is, an array begun by DAFBNA but not yet ended by DAFENA---is called the `current array' for that file. No file can have more than one current array.

In order to continue or end an array in a file that is no longer current, the file in question is selected as the current file by a call to DAFCAD:

   CALL DAFCAD ( HANDLE )                { DAF, continue adding data }
After this call, the file identified by HANDLE will be the current file, and calls to DAFADA will add data to the current array in this file. The usual sequence of calls has the form:

   CALL DAFCAD ( HANDLE  )               { DAF, continue adding data }
   CALL DAFADA ( DATA, N )               { DAF, add data to array }
Since DAFENA can be used to end arrays only in the current file, DAFCAD is also used to select a file as current so that an array can be ended in that file:

   CALL DAFCAD ( HANDLE  )               { DAF, continue adding data }
   CALL DAFENA                           { DAF, end new array }
Only files that already have an array in progress may be selected as current by DAFCAD. An error will be signalled if DAFCAD is used to select an array file that does not have an array in progress.

The following example illustrates the use of DAFCAD:

We write data obtained from the routine GET_DATA (which is not a SPICELIB routine) into two separate array files. The first N/2 elements of the array DATA will be written to the first file; the rest of the array will be written to the second file.

Open the array files for write access, using either DAFOPW (if the files already exist) or DAFONW (if they do not).

   CALL DAFOPW ( FNAME1, HANDL1 )
   CALL DAFOPW ( FNAME2, HANDL2 )
Begin the new array files by calling DAFBNA.

   CALL DAFBNA ( HANDL1, SUM1, NAME1 )
   CALL DAFBNA ( HANDL2, SUM2, NAME2 )
Add data to the arrays, using DAFCAD to select the current file and DAFADA to add data to the current array in the current file.

   CALL GET_DATA ( DATA, N, FOUND )
 
   DO WHILE ( FOUND )
 
      CALL DAFCAD   ( HANDL1                          )
      CALL DAFADA   ( DATA,               N/2         )
 
      CALL DAFCAD   ( HANDL2                          )
      CALL DAFADA   ( DATA( N/2 + 1 ),    N - N/2     )
 
      CALL GET_DATA ( DATA, N, FOUND )
 
   END DO
End each array by calling DAFENA, selecting the file in which to end the array by calling DAFCAD:

   CALL DAFCAD ( HANDL1 )
   CALL DAFENA
 
   CALL DAFCAD ( HANDL2 )
   CALL DAFENA
The notions of `current array file' and `current array' apply to both adding data to arrays and to searching array files. However, the files and arrays regarded as current for the purpose of searching are unrelated to those regarded as current for the purpose of adding data.



Reordering Arrays



Once arrays are written to a DAF, it is conceivable that their order may need to be changed. Suppose that the arrays in a DAF are to be ordered according to the arithmetic mean of the data they contain. Also suppose that the arithmetic mean of data in an array is stored in the second double precision component of the summary. After reading each summary and creating a vector IORDER with dimension N that specifies the new order of the arrays, the subroutine DAFRA can be used to reorder the arrays.

   CALL DAFRA ( HANDLE, IORDER, N )       { Reorder arrays }


Searching



The process of locating an array of interest within an array file is known as `searching'. The organization of the arrays as a doubly-linked list makes it possible to conduct searches in forward or backward order.

Subroutines DAFBFS and DAFFNA are used to search an array file in forward order. DAFBFS places a pointer at the head of the doubly-linked list formed by the arrays in the file. Each call to DAFFNA moves the pointer to the next array in the list. (The first call to DAFFNA moves the pointer to the first array.) DAFFNA returns a logical flag which is true whenever another array has been found, and is false when the tail of the list has been reached. All forward searches are variations on the following template:

   CALL DAFBFS ( HANDLE )                 { Begin forward search }
   CALL DAFFNA ( FOUND  )                 { Find next array }
 
   DO WHILE ( FOUND )
       ...
      CALL DAFFNA ( FOUND )               { Find next array }
   END DO
Subroutines DAFBBS and DAFFPA are likewise used to search an array file in backward order. DAFBBS moves the pointer to the tail (instead of the head) of the list; DAFFPA moves the pointer to the previous (instead of the next) array in the list. The template shown above is modified to conduct backward searches by replacing calls to DAFBFS and DAFFNA with calls to DAFBBS and DAFFPA, respectively:

   CALL DAFBBS ( HANDLE )                 { Begin backward search }
   CALL DAFFPA ( FOUND  )                 { Find previous array }
 
   DO WHILE ( FOUND )
       ...
      CALL DAFFPA ( FOUND )               { Find previous array }
   END DO
Once a search has begun, the pointer may be moved in either direction.

After the pointer has been moved to a new array, the summary and name of the array can be retrieved by DAFGS and DAFGN:

   CALL DAFBBS ( HANDLE )                 { Begin backward search }
   CALL DAFFPA ( FOUND  )                 { Find previous array }
 
   DO WHILE ( FOUND )
      CALL DAFGS ( SUM  )                 { Get summary }
      CALL DAFGN ( NAME )                 { Get name }
       ...
      CALL DAFFNA ( FOUND )               { Find next array }
   END DO
Once returned, a name can be examined directly. However, a summary must first be unpacked into its components by subroutine DAFUS:

   CALL DAFUS ( SUM, ND, NI, DC, IC )     { Unpack summary }
The name and summary are used to determine whether the current array is of interest. For example, if the arithmetic mean of the elements in each array of an array file is stored in the second double precision component of the summary, then the following code fragment determines the initial and final addresses (IA and FA) of the array with the greatest average. (Assume function DPMIN returns the smallest double precision number supported in the host environment.)

   MAXAVG = DPMIN()
 
   CALL DAFBFS ( HANDLE )                 { Begin forward search }
   CALL DAFFNA ( FOUND  )                 { Find next array }
 
   DO WHILE ( FOUND )
      CALL DAFGS ( SUM )                  { Get summary }
      CALL DAFUS ( SUM, ND, NI, DC, IC )  { Unpack summary }
 
      IF ( DC(2) .GT. MAXAVG ) THEN
         MAXAVG  = DC(2)
         IA      = IC(NI-1)
         FA      = IC(NI  )
      END IF
 
      CALL DAFFNA ( FOUND )               { Find next array }
   END DO
Recall that the final two integer components of any array summary---IC(NI-1) and IC(NI)---contain the initial and final addresses of the array.



Searching Multiple Array Files



Searching multiple array files simultaneously is a little like adding data to multiple files simultaneously: in each case, it becomes necessary to identify the file to act upon, when calling routines that don't accept an input handle argument.

As with adding data, the notions of `current array file' and `current array' apply to searching. Starting a search in an array file by calling either DAFBFS or DAFBBS makes that file the `current file'. Subsequent calls to DAFFNA or DAFFPA advance or back up the array pointer in the current file. The last array found by DAFFNA or DAFFPA in the `current file' is the `current array' for that file. As mentioned above, there is no relation between the files or arrays that are considered current for searching and those considered current for adding data.

If, after a search is started in one array file, DAFBFS or DAFBBS are called to start a search in a second array file, the second file becomes current: DAFFNA, DAFFPA, DAFGN, and DAFGS will all operate on the second file.

The complete set of DAF routines that act on the current file (for searching) is:

   DAFFNA                       { DAF, find next array }
   DAFFPA                       { DAF, find previous array }
   DAFGS                        { DAF, get summary }
   DAFGN                        { DAF, get name }
   DAFGH                        { DAF, get handle }
   DAFRS                        { DAF, replace summary }
   DAFRN                        { DAF, replace name }
   DAFWS                        { DAF, write summary }
The routine DAFCS is used to continue a search in an array file that is no longer current. Calling DAFCS makes the file specified by the input handle argument the current file for searching:

   CALL DAFCS  ( HANDLE )                 { DAF, continue search }
After this call, the routines in the above list will act upon the file designated by HANDLE. For example, to continue a forward search in that file,

   CALL DAFCS  ( HANDLE )                 { DAF, continue search }
   CALL DAFFNA ( FOUND  )                 { DAF, find next array }
and to continue a backward search,

   CALL DAFCS  ( HANDLE )                 { DAF, continue search     }
   CALL DAFFPA ( FOUND  )                 { DAF, find previous array }
while to get the name and summary of the current array in the file,

   CALL DAFCS ( HANDLE )                  { DAF, continue search }
   CALL DAFGN ( NAME )                    { DAF, get name }
   CALL DAFGS ( SUM  )                    { DAF, get summary }
A search must have been started by DAFBFS or DAFBBS before it can be continued. An error will be signalled if DAFCS is used to continue a search in an array file in which no search has been started.



Accessing Array Elements



After an array of interest has been located, the entire array or any contiguous set of elements can be accessed---read or updated---by supplying a pair of addresses. Elements are read by DAFRDA and written by DAFWDA.

The following code fragment continues the example above by subtracting the average from each of the elements in the array. (Recall that IA and FA contain the initial and final addresses of the array.)

   CALL DAFRDA ( HANDLE, IA, FA, DATA )   { Read data
                                            from address }
   DO I = 1, FA - IA + 1
      DATA(I) = DATA(I) - MAXAVG
   END DO
 
   CALL DAFWDA ( HANDLE, IA, FA, DATA )   { Write data
                                            to address }
Note that it is not necessary to retrieve the entire array at once. The following code fragment illustrates how to process an array of unknown size using a fixed amount of local storage. The local array DATA is declared to be size CHUNK. DAFRDA reads a maximum of CHUNK elements from the double precision array and DAFWDA writes them. This technique is useful when the arrays stored in an array file may be arbitrarily large.

   FIRST = IA
 
   DO WHILE ( FIRST .LE. FA )
      LAST  = MIN ( FA,     FIRST + CHUNK - 1 )
      CALL DAFRDA ( HANDLE, FIRST, LAST, DATA )   { Read data
                                                    from address }
      NUMELE = LAST - FIRST + 1
 
      DO I = 1, NUMELE
         DATA(I) = DATA(I) - MAXAVG
      END DO
 
      CALL DAFWDA ( HANDLE, FIRST, LAST, DATA )   { Write data
      FIRST = FIRST + CHUNK                         to address }
   END DO


Updating Summaries and Names



In the previous example, once the average value of the array has been subtracted from each element of the array, the value for the average stored in the summary is no longer valid (the average is now zero) and should be changed.

Subroutines DAFRS and DAFRN are analogous to subroutines DAFGS and DAFGN. DAFGS `gets' the summary for the array to which the pointer currently points; DAFRS replaces it. DAFGN `gets' the name of the array to which the pointer currently points; DAFRN replaces it.

If the index, K, of the updated array is known, then the new average for the array (zero) is stored by the following code fragment.

   CALL DAFBFS ( HANDLE )                 { Begin forward search }
 
   DO I = 1, K
      CALL DAFFNA ( FOUND  )              { Find next array }
   END DO
 
   CALL DAFGS ( SUM )                     { Get summary }
   CALL DAFUS ( SUM, ND, NI, DC, IC )     { Unpack summary }
 
   DC(2) = 0.D0
 
   CALL DAFPS ( ND, NI, DC, IC, SUM )     { Pack summary }
   CALL DAFRS ( SUM )                     { Replace summary }


Buffering



Unless the value of CHUNK is 128 (the number of double precision words in a record) and the initial address of the array happens to correspond to the first word of a physical record (neither of which is very likely), each call to DAFRDA or DAFWDA will involve reading partial records---data that spans across records. In general, successive calls will refer to different parts of at least one record.

In fact, as records are read from array files they are saved in an internal buffer maintained by the DAF subroutines. If any part of a record is needed, it can frequently be returned directly from the buffer, without accessing the file again. In particular, when an entire array is accessed sequentially, as in the example above, each of the necessary records is read exactly one time. When the elements of an array are accessed more randomly, the number of file accesses may increase somewhat.

It is possible, at any point in a program, to determine the number of file accesses prevented by the buffering scheme. The subroutine DAFNRR returns the number of physical records actually read, and the number of records or partial records that have been requested, as illustrated below:

   CALL DAFNRR ( READS, REQS )            { Number of reads,
                                            requests         }
   RATIO  = DBLE(READS) / DBLE(REQS)
   PERCNT = INT ( RATIO * 100.D0 )
   WRITE (*,*) 'Reads/requests (%) = ', PERCNT
Ideally, the ratio of reads to requests should approach zero. In the worst case, where it approaches one, the size of the buffer should probably be adjusted. (The module headers for DAFRDR and DAFWDR provide details on adjusting the buffer size.)



Conversion and Transfer of DAF's



In order to be transferred to a new environment, a binary file is converted to an equivalent SPICE transfer file---a formatted, sequential file that contains only printable ASCII characters and blanks (ASCII 32-126). In order to be used in the new environment, it is converted back to a binary file. This conversion process must occur because binary representations of numbers vary from machine to machine, the SPICE transfer representations do not.

There are two routines for converting DAFs from binary to transfer and transfer to binary formats: DAFBT and DAFTB.

DAFTB creates a new binary file. It then converts and writes the information from a previously opened SPICE transfer file to the binary file. Before returning to the calling program it closes the binary file. DAFTB leaves the SPICE transfer file open.

DAFBT opens an existing binary file. It then converts and writes information from it to a previously opened SPICE transfer file. Before returning to the calling program it closes the binary file. DAFBT leaves the SPICE transfer file open.

Note that the routines DAFBT and DAFTB make no use of the DAF reserved record area. They only convert the data portion of the DAF file.

When converting a binary file for transfer (or archiving), it may be necessary to add additional information---catalog or history information, for example---to the resulting text file. The following code fragment creates a text file containing an ASCII array file preceded and followed by markers. The SPICELIB routine TXTOPN opens a new text file; TXTOPR opens an existing text file for read access.

   BEGMRK = '*** BEGIN ARRAY FILE ***'
   ENDMRK = '*** END ARRAY FILE ***'
 
   CALL TXTOPN ( FILENM, UNIT )
   WRITE (UNIT,*) BEGMRK
 
   CALL DAFBT ( BINARY, UNIT )           { Binary to transfer }
 
   WRITE ( UNIT,* ) ENDMRK
   CLOSE ( UNIT   )
The following code fragment converts the resulting text file into a binary array file.

   CALL TXTOPR ( FILENM, UNIT )
   READ ( UNIT,FMT='(A)' ) BEGMRK
 
   CALL DAFTB ( UNIT, BINARY )          { Transfer to binary }
 
   READ  ( UNIT,FMT='(A)' ) ENDMRK
   CLOSE ( UNIT )


Structure



Every array file is a Fortran-77 direct access file, created by the following statement (or an equivalent statement producing the same results):

   OPEN ( UNIT   = unit,
          FILE   = file name,
          ACCESS = 'DIRECT',
          RECL   = record length,
          STATUS = 'NEW'           )
The record length is processor dependent. The smallest possible value should be selected by the user such that each record in the file is large enough to contain 128 double precision numbers or 1000 characters, whichever is larger. Some suitable values for several compilers are shown below.

   Compiler                          Record length
   --------------                   -------------
   HP Workstation
     /HP-UX
     /HP Fortran                         1024
 
   Macintosh
     /Language Systems Fortran           1024
 
   NeXT
     /Absoft Fortran                     1024
 
   PC
     /Lahey                              1024
 
   PC
     /Microsoft Fortran PowerStation     1024
 
   Silicon Graphics
     /IRIX
     /SGI Fortran                         256
 
   Sun
     /SunOS and Solaris
     /Sun FORTRAN                        1024
 
   VAX
     /OpenVMS
     /VAX Fortran                         256
 
   VAX
     /OSF/1
     /DEC Fortran                         256
 
   VAX
     /VMS
     /VAX Fortran                         256


Organization



An array file contains five types of physical records:



The File Record



The file record is always the first physical record in an array file. It contains seven items.



Reserved Records



By definition, reserved records are invisible to DAF subroutines. The contents and formats of reserved records are left entirely to the user. The initial reserved record is located in the second record of the file; the final reserved record immediately precedes the initial summary record of the file.

Reserved records may be used to store information about the data such as its source, names and sites of programs that processed it, names and sites of programs to interpret it, people to contact for support, its peculiarities or omissions, just to name a few.



Summary Records



A summary record contains a maximum of 128 double precisions words. The first three words of each summary record are reserved for the following control information:

The record pointers form the basis of the array list. Each summary record is linked to two other summary records, allowing the summaries to be retrieved in forward or backward order. (The links between adjacent summaries in a summary record are implicit.) The names can be retrieved from the corresponding name records. And the locations (initial and final addresses) of the arrays themselves are stored in the summaries.

Although the control items are integer values, they are stored as double precision numbers. This allows summary records and element records, which contain only double precision numbers, to be buffered using the same mechanism.

The control items are followed immediately by the summaries themselves. The number of summaries (NS) that can fit in a single summary record depends on the size of a single summary (SS), a function of NI and ND:

                    (NI + 1)
   SS       =  ND + --------         (Note that this is
                        2             integer division.)
 
   SS * NS <=  125
 
   NS      <=  125/SS                (Note that NS must be an
                                      integer greater than or
                                      equal to one.)
A summary record can be depicted as written below, where the numbers correspond to the number of the double precision word in the record.

   -------------------------------------------------------------
   | 1 | 2 | 3 | 4 | 5 | 6 |       ...       | 126 | 127 | 128 |
   -------------------------------------------------------------
     ^   ^   ^
   NEXT  |   |
        PREV |
            NSUM
If SS is the size (in double precision words) of each summary array, then the first summary is stored in words 4 through SS+3; the second summary is stored in words SS+4 through 2(SS)+3; and so on. For example, if SS is equal to 3, 41 (125/3) summaries can fit in the summary record, leaving two words empty. In this case the record can be pictorially represented as written below, where the label below the record indicates the summary number.

   -------------------------------------------------------------
   | 1 | 2 | 3 | 4 | 5 | 6 | ... | 124 | 125 | 126 | 127 | 128 |
   -------------------------------------------------------------
     ^   ^   ^ { Summary 1 } ... {    Summary 41   }  ^     ^
   NEXT  |   |                                        |     |
        PREV |                                       These words
            NSUM                                     are unused.
Unlike arrays, summaries are never split across physical record boundaries, so the end of each summary record may remain unused. Whenever the number of summaries stored in the current summary record reaches the maximum number that will fit, a new (empty) summary record is added to the end of the file.

It is now clear that the bounds on the values for ND and NI are determined by their relation to the number of double precision words used for summary information in the summary record. ND and NI must satisfy the following inequalities:

        (NI + 1)                     (Note that this is
   ND + -------- <= 125               integer division.)
            2
 
   0 <= ND <= 124
 
   2 <= NI <= 250


Name Records



Each name record contains nothing but array names. A new name record is added to the file each time a new summary record is added. The new name record is located in the record immediately following the new summary record. Because a DAF is written in this manner, the number of summary records is equal to the number of name records.

Each time a new summary is added to a summary record, a new name is added to the corresponding name record. Therefore, the number of summaries in a summary record is equal to the number of names in the corresponding name record.

The values for ND and NI determine the maximum length for a name in the name record, NC:

                      (NI + 1)
   NC  =   8 * ( ND + -------- )     (Note that this is
                          2           integer division.)
If the numbers in the summary record represent double precision words, and the numbers in the name record represent characters, the two records can be depicted as written below for a DAF whose format is specified by ND = 2 and NI = 2.

   -------------------------------------------------------------
   | 1 | 2 | 3 | 4 | 5 | 6 | ... | 124 | 125 | 126 | 127 | 128 |
   -------------------------------------------------------------
     ^   ^   ^ { Summary 1 } ... {    Summary 41   }  ^     ^
   NEXT  |   |                                        |     |
        PREV |                                     Words 127 and
            NSUM                                   128 are unused.
 
   -------------------------------------------------------------
   | 1 |...| 24 |.......| 961 | ... | 984 | 985 |.......| 1000 |
   -------------------------------------------------------------
   {   Name 1   }.......{    Name 41      }  ^              ^
                                             |              |
                                           Characters 985 through
                                           1000 are unused.
The first name is stored in characters 1 through NC of the record; the second name is stored in characters NC+1 through 2(NC); and so on.



Element Records



Most of the records in any array file are element records. Element records hold the elements of the arrays stored in the file. (The other records are used for accounting purposes only.)

Each element record contains up to 128 double precision numbers. An element record is always full (contains 128 numbers) unless it immediately precedes a summary record, in which case it may be partially filled.

The elements stored in a particular element record may belong to more than one array. However, elements belonging to the same array are stored contiguously within the record.

For example, suppose three arrays exist: A, B, and C. Array A has 10 elements, array B has 100 elements, and array C has 15 elements. If all of the elements are stored in the same element record, it could be pictorially represented as written below:

   A[1]
   A[2]
   A[3]
    .
    .
    .
   A[10]
   B[1]
   B[2]
   B[3]
    .
    .
    .
   B[100]
   C[1]
   C[2]
   C[3]
    .
    .
    .
   C[15]
A particular element record always lies between two summary/name record pairs, or between a summary/name record pair and the end of the file.



Designing a DAF



During the Voyager-2 encounter with Neptune, NAIF processed pictures using an image center finding technique in order to determine instrument pointing. For each body in each picture, a set of limb points---pixel and line pairs---was selected and an ellipse was fitted to the set, producing a center for the body.

Suppose that the pixel and line pairs for the points are to be stored so that existing and future models can be used to interpret the data. While it's true that the pixel and line numbers are integers, they must be converted to double precision numbers for the ellipse fitting processing. As double precision numbers, they could be stored in a DAF.

Data stored in a DAF is characterized by a set of double precision and integer values. In this case, each picture has several values associated with it that could be used to describe it: the picture number, the spacecraft identification code, the time at which the picture was taken, the identifier for the command load, the identifier for the camera, the identification code for the body whose center is described by the data. Examples of each of these items is given below:

 
   Picture number        :        9230.4
   Spacecraft event time :  -332927103.9324339
   Spacecraft ID         :         -32
   Load                  :         901
   Camera ID             :           1
   Body ID               :         899
 
To specify the `format' of this DAF, the values of ND and NI must be determined. The number of double precision values used to describe the picture is two (picture number and spacecraft event time) so ND = 2. The number of integer values used to describe the picture is 4 (spacecraft ID, load, camera ID, and body ID). Because two of the integer components in the summary are used for storing locations of the arrays themselves, NI must always be two more than the number of integer values used to describe the data. In this example, NI = 4 + 2 = 6. Thus, the format of the DAF used to store the limb points is specified by ND = 2 and NI = 6.

Having determined the values for ND and NI, the value of NC can be computed. NC is defined as :

                   (NI + 1)
   NC = 8 * ( ND + -------- )
                       2
So, for this example:

                   (6 + 1)
   NC = 8 * ( 2 + -------- ) = 8 * ( 2 + 3 ) = 40
                      2
In this example, each array name may contain a maximum of forty characters. The array names could describe the science activity of the scan platform. They would then give a clue as to what the picture might contain. For example, `Slew to center of Neptune' could be used to describe picture number 9230.4.

After the format has been determined, the user needs to write software to transfer the limb point data to the DAF, and to read and interpret limb point data that has been stored in a DAF. Higher level DAF-based software, much like the SPK or CK software, can be written to achieve this functionality.



Creating an Array File



The following example illustrates the use of addresses and lists within an array file by showing how a simple array file might be created, and how arrays might be added to that file.

Throughout the example, the following notations will be used:

The first step in creating a file is to determine the name to be given to the type of data stored in the DAF file. For this example, the data type will be 'Xmpl'. For more information about the restrictions on the character string describing the type of data in the DAF file, see the header of the routine DAFONW. The IDWORD written to the new file is the concatenation of the string 'DAF/' with the data type string. So, for this example, the IDWORD written to the new file is 'DAF/Xmpl'.

The next step is to select values for ND and NI. Normally, these are relatively small, allowing several summaries to fit in each summary record and thus increasing the speed with which the file can be searched. A new file is opened by calling DAFONW, specifying the selected values for ND and NI. Recall that the final two integer components of any array summary---IC(NI-1) and IC(NI)---contain the initial and final addresses of the array, so NI must be at least 2.

The example will be easier to follow, however, if the number of summaries that can fit in a summary record is minimized. Therefore, in this example ND and NI will take on unusually large values:

   ND = 25
   NI = 27
Each array summary requires 39 double precision words of storage:

        (NI + 1)
   ND + -------- =  25 + 14 = 39
           2
Therefore, each summary record can hold 3 summaries:

   125 (words per record)
   -----------------------  =  3 (summaries per record)
    39 (words per summary)
If `Summary(i)[j]' represents the j'th element of the i'th summary array, then the layout of a typical summary record is shown below.

   Word     Value
   ----     ----------------
      1     NEXT
      2     PREV
      3     NSUM
      4     Summary( 1)[ 1]
      5     Summary( 1)[ 2]
             ...
     42     Summary( 1)[39]
     43     Summary( 2)[ 1]
             ...
     81     Summary( 2)[39]
     82     Summary( 3)[ 1]
             ...
    120     Summary( 3)[39]
    121     Unused
             ...
    128     Unused
The number of names that an array record can hold is equivalent to the number of summaries that the summary record can hold. In this example it's three.

Recall that NC, the maximum number of characters in an array name, is determined by the values of ND and NI. For this example, where ND = 25 and NI = 27, the value of NC is computed below:

                      (NI + 1)
   NC  =   8 * ( ND + -------- )  = 8 * ( 25 + 14 ) = 8 * 39 = 312
                          2
Each array name may use up to 312 characters of storage. An array name does not have to be exactly NC characters long. NC is simply the limit on the length of the array name.

If `Name(i)[j]' represents the j'th character of the i'th name, then the layout of a typical name record is shown below.

   Character    Value
   ---------    --------------
           1     Name( 1)[  1]
           2     Name( 1)[  2]
                  ...
         312     Name( 1)[312]
         313     Name( 2)[  1]
                  ...
         624     Name( 2)[312]
         625     Name( 3)[  1]
                  ...
         936     Name( 3)[312]
         937     Unused
                  ...
        1000     Unused
Assume that RESV, the number of reserved records, is 10. When DAFONW opens the new file, it stores the file record information in record 1, the reserved records in records 2 through 11, and the initial summary record in record 12. Because the file is empty, the initial summary record, RI, is also the final summary record, RF.

   RI = 12
   RF = 12
DAFONW stores the lone name record for the file immediately after the summary record, in record 13. Therefore the first free address, FFA, in the file is the first word in record 14:

   FFA = word + (record - 1) * 128
 
       =    1 + (14     - 1) * 128
 
       =    1 + 1664
 
       =    1665
DAFONW also writes the internal file name to the file record. For this example the internal file name will be 'TESTFILE'. For the rest of the example, the file record will be depicted as a collection of values enclosed by braces and preceded by a record number:

   r { IDWORD=x, ND=a, NI=b, IFNAME=c, RI=d, RF=e, FFA=f }
So, the file record for this example file is initially:

   1 { IDWORD='DAF/Xmpl', ND=25, NI=27, IFNAME='TESTFILE',
                          RI=12, RF=12, FFA=1665           }
Because there is only one summary record, the values of NEXT and PREV in that record are both zero. Because the file contains no arrays, the value of NSUM is also zero. The information needed to create the summary record is complete. For the rest of the example, each summary record will be depicted as a collection of values enclosed by angle brackets and preceded by a record number:

   r < NEXT=a, PREV=b, NSUM=c, (d,e),(f,g),(h,i) >
The ordered pairs enclosed in parentheses are the initial and final addresses of the arrays whose summaries are contained in the record. The remaining components of each summary are ignored in order to make the example easier to follow. Thus, the lone summary record for this example file is initially:

   12 < NEXT=0, PREV=0, NSUM=0, (0,0),(0,0),(0,0) >
Name records will always be depicted as

   r < " " >
Element records will always be depicted as

   r < N >
where N is the number of elements stored in the record.

Once the initial summary and name records have been written, the file is complete, if uninteresting:

    1 { IDWORD='DAF/Xmpl', ND=25, NI=27, IFNAME='TESTFILE',
                           RI=12, RF=12, FFA=1665           }
    2
    .
    . Records 2 through 11 are reserved records.
    .
   11
   12 < NEXT=0, PREV=0, NSUM=0, (0,0),(0,0),(0,0) >
   13 < " " >
Assume that an array A1, containing 100 elements, is to be added to the file. The array will be stored contiguously, beginning at the first free address. Thus, its initial and final addresses will be 1665 and 1764, respectively. The entire array fits into a single record, so one element record will be added to the file. The value of NSUM in the summary record is incremented by one. The new value of FFA is the address following the final address of the new array: 1765. This is stored in the file record.

    1 { IDWORD='DAF/Xmpl', ND=25, NI=27, IFNAME='TESTFILE',
                           RI=12, RF=12, FFA=1765           }
    2
    .
    . Records 2 through 11 are reserved records.
    .
   11
   12 < NEXT=0, PREV=0, NSUM=1, (1665,1764),(0,0),(0,0) >
   13 < " " >
   14 < 100 > 100 words for A1
Assume that a second array A2, containing 200 elements, is to be added to the file. The elements will be stored between addresses 1765 and 1964. The array will fill the remainder of the first element record, all of a second record, and part of a third, so two element records will be added to the file. The value of NSUM in the summary record is incremented again. And the new value of FFA (1965) is stored in the file record.

    1 { IDWORD='DAF/Xmpl', ND=25, NI=27, IFNAME='TESTFILE',
                           RI=12, RF=12, FFA=1965           }
    2
    .
    . Records 2 through 11 are reserved records.
    .
   11
   12 < NEXT=0, PREV=0, NSUM=2, (1665,1764),(1765,1964),(0,0) >
   13 < " " >
   14 < 128 > 100 words for A1, 28 words for A2
   15 < 128 > 128 words for A2
   16 <  44 >  44 words for A2
To add a third array A3, containing 150 elements, the process is repeated. The elements will be stored between addresses 1965 and 2114. The array will fill the remainder of the third element record, and part of a fourth, so one new element record is added. The value of NSUM is in the summary record is incremented again. And the new value of FFA (2115) is in the file record.

    1 { IDWORD='DAF/Xmpl', ND=25, NI=27, IFNAME='TESTFILE',
                           RI=12, RF=12, FFA=2115           }
    2
    .
    . Records 2 through 11 are reserved records.
    .
   11
   12 < NEXT=0, PREV=0, NSUM=3, (1665,1764),(1765,1964),(1965,2114) >
   13 < " " >
   14 < 128 > 100 words for A1, 28 words for A2
   15 < 128 > 128 words for A2
   16 < 128 >  44 words for A2, 84 words for A3
   17 <  66 >  66 words for A3
Note that the final summary record is full, so new summary and name records will added to the file. (Record 17 will remain only partially filled.) The values of NEXT and PREV in the summary records are adjusted so that the records point to each other:

    1 { IDWORD='DAF/Xmpl', ND=25, NI=27, IFNAME='TESTFILE',
                           RI=12, RF=12, FFA=2115           }
    2
    .
    . Records 2 through 11 are reserved records.
    .
   11
   12 < NEXT=18, PREV=0, NSUM=3, (1665,1764),(1765,1964),(1965,2114) >
   13 < " " >
   14 < 128 > 100 words for A1, 28 words for A2
   15 < 128 > 128 words for A2
   16 < 128 >  44 words for A2, 84 words for A3
   17 <  66 >  66 words for A3
   18 < NEXT=0, PREV=12, NSUM=0, (0,0),(0,0),(0,0) >
   19 < " " >
The file record is updated so that the value of RF points to the new summary record, and the value of FFA in the file record will point to the first word in the first record following the new name record (address 2433):

    1 { IDWORD='DAF/Xmpl', ND=25, NI=27, IFNAME='TESTFILE',
                           RI=12, RF=18, FFA=2433           }
    2
    .
    . Records 2 through 11 are reserved records.
    .
   11
   12 < NEXT=18, PREV=0, NSUM=3, (1665,1764),(1765,1964),(1965,2114) >
   13 < " " >
   14 < 128 > 100 words for A1, 28 words for A2
   15 < 128 > 128 words for A2
   16 < 128 >  44 words for A2, 84 words for A3
   17 <  66 >  66 words for A3
   18 < NEXT=0, PREV=12, NSUM=0, (0,0),(0,0),(0,0) >
   19 < " " >
Adding more arrays is identical to the previous example: the necessary element records are added; the summary and name records are updated; and the value of FFA is updated. However, every third array also adds new summary and name records, and the values of RF and FFA are updated as well.



Transfer Format for Porting



In order to transfer a DAF file from one computer platform to another, it should be converted to a SPICE transfer file. Previously, other SPICELIB routines such as DAFA2B, DAFB2A, DAFT2B, and DAFB2T were recommended for converting DAF files. These routines are now obsolete; however, they will remain in SPICELIB for backwards compatibility. Two routines replace them, DAFTB and DAFBT. Please use these routines in new software for converting DAF files. Older software does not need to changed to use the new routines. Software that calls the obsolete routines will continue to work properly, and the files they produce can be read using newer versions of the NAIF Toolkit.



Examples



The next several sections present example programs and subroutines to show how the DAF subroutines can be used to manipulate array files.

All subroutines and functions used in the examples are from SPICELIB or they have been provided as examples themselves.



Example 1: Converting Between Transfer and Binary Format



This example is a complete program to convert a binary double precision array file (DAF) to an equivalent SPICE transfer file, suitable for porting to a different environment. The program queries the user for the names of the binary file to be converted and the transfer file to be created.

   PROGRAM B2T
 
   CHARACTER*(128)       BINARY
   CHARACTER*(128)       XFER
   INTEGER               XFLUN
 
   WRITE (*,*)         'Name of binary file?'
   READ  (*,FMT='(A)')  BINARY
 
   WRITE (*,*)         'Name of transfer file?'
   READ  (*,FMT='(A)')  XFER
 
   CALL TXTOPN ( XFER,   XFLUN )
   CALL DAFBT  ( BINARY, CFLUN )          { Binary to transfer }
 
   CLOSE ( XFLUN )
 
   END
Now convert from SPICE transfer format back to binary:

   PROGRAM T2B
 
   CHARACTER*(128)       BINARY
   CHARACTER*(128)       TXFER
   INTEGER               TXTLUN
 
 
   WRITE (*,*)         'Name of text file?'
   READ  (*,FMT='(A)')  XFER
 
   WRITE (*,*)         'Name of binary file?'
   READ  (*,FMT='(A)')  BINARY
 
   CALL TXTOPR ( XFER,   XFLUN )
   CALL DAFTB  ( XFLUN, BINARY )          { Transfer to binary }
 
   CLOSE ( XFLUN )
 
   END
The subroutines DAFBT and DAFTB can be embedded in programs with more sophisticated interfaces as well, such as X windows.



Example 2: Summarizing the Contents of an Array



The next example is a subroutine, SUMARR, that summarizes the contents of an array in a DAF. SUMARR assumes that a DAF file is open, a search (forward or backward) is in progress, and that an array has been found.

The subroutine takes two character string arrays as inputs. Each character array contains labels describing what the double precision and integer components of the summary represent. A program calling SUMARR might print out each label followed by its corresponding value in the array summary.

The function LASTNB returns the index of the last non-blank character in a string.

         SUBROUTINE SUMARR ( DNAMES, INAMES )
 
         CHARACTER*(*)       DNAMES ( * )
         CHARACTER*(*)       INAMES ( * )
 
   C
   C     SPICELIB functions
   C
         INTEGER             LASTNB
 
   C
   C     Local variables
   C
         CHARACTER*(80)      PNAME
 
         DOUBLE PRECISION    DC     ( 125 )
         DOUBLE PRECISION    SUM    ( 125 )
 
         INTEGER             HANDLE
         INTEGER             I
         INTEGER             IC     ( 250 )
         INTEGER             ND
         INTEGER             NI
         INTEGER             LONG
 
   C
   C     Look up the handle of the file, and the summary of the
   C     array most recently found.
   C
         CALL DAFGH  ( HANDLE         )
         CALL DAFHSF ( HANDLE, ND, NI )
 
   C
   C     Get, and unpack, the summary of the array. Note that SUM,
   C     DC, and IC are dimensioned large enough to hold the
   C     biggest possible summary.
   C
         CALL DAFGS  ( SUM )
         CALL DAFUS  ( SUM, ND, NI, DC, IC )
 
   C
   C     Find the length of the longest print name. All names will
   C     be transferred into a temporary string for printing.
   C     Shorter names will be followed by enough blanks to make
   C     the components line up.
   C
         LONG = 1
 
         DO I = 1, ND
            LONG = MAX ( LONG, LASTNB ( DNAMES(I) ) )
         END DO
 
         DO I = 1, NI
            LONG = MAX ( LONG, LASTNB ( INAMES(I) ) )
         END DO
 
   C
   C     Write the summary: an aligned list of components, each
   C     preceded by a descriptive name.
   C
         WRITE (*,*)
         WRITE (*,*) 'Summary'
         WRITE (*,*) '-------'
         WRITE (*,*)
 
         DO I = 1, ND
            PNAME = DNAMES(I)
            WRITE (*,*) PNAME(1:LONG), ' :', DC(I)
         END DO
 
         DO I = 1, NI
            PNAME = INAMES(I)
            WRITE (*,*) PNAME(1:LONG), ' :', IC(I)
         END DO
 
         RETURN
         END
The following program uses SUMARR to summarize all of the arrays in a specific kind of array file. Each summary in the file contains four double precision components (minimum, maximum, average, standard deviation) and three integer components (sort flag, initial address, final address).

Note that the program opens a DAF and begins a forward search. After an array is found by DAFFNA, it is summarized by SUMARR. This process of searching and summarizing continues until all of the arrays in the file have been summarized.

         PROGRAM SUMDAF
 
         INTEGER           ND
         PARAMETER       ( ND = 4 )
 
         INTEGER           NI
         PARAMETER       ( NI = 3 )
 
         CHARACTER*(40)    DNAMES  ( ND )
         CHARACTER*(40)    INAMES  ( NI )
         CHARACTER*(128)   FILE
 
         INTEGER           HANDLE
 
         LOGICAL           FOUND
 
         DATA              DNAMES     / 'Largest value',
        .                               'Smallest value',
        .                               'Average value',
        .                               'Standard deviation'   /
 
         DATA              INAMES     / 'Sorted (1=yes, 0=no)',
        .                               'Begins at address',
        .                               'Ends at address'      /
 
 
 
         WRITE (*,*)
         WRITE (*,*)         'Name of file?'
         READ  (*,FMT='(A)')  FILE
 
         CALL DAFOPR ( FILE, HANDLE )
 
         CALL DAFBFS ( HANDLE )
         CALL DAFFNA ( FOUND  )
 
         DO WHILE ( FOUND )
            CALL SUMARR ( DNAMES, INAMES )
            CALL DAFFNA ( FOUND )
         END DO
 
         END
The summary of a typical array is shown below:

   Summary
   -------
 
   Largest value        : 221.42123212345
   Smallest value       : 17.332467369560
   Average value        : 148.37378239493
   Standard deviation   : 21.263546586965
   Sorted (1=yes, 0=no) : 0
   Begins at address    : 21463
   Ends at address      : 29271


Example 3: Copying Arrays from One File to Another



The next example is a subroutine to copy an entire array from one array file to another. It assumes that the array file that is to be updated already exists, a search (forward or backward) is in progress, and that an array has been found.

It takes a single input: the name of the file to which the array is to be copied.

         SUBROUTINE COPYA ( FILE )
 
         CHARACTER*(*)      FILE
 
   C
   C     Local variables
   C
         CHARACTER*(1000)   NAME
 
         INTEGER            FA
         INTEGER            FIRST
         INTEGER            FROM
         INTEGER            HANDLE
         INTEGER            IA
         INTEGER            IC      ( 250 )
         INTEGER            LAST
         INTEGER            ND
         INTEGER            NI
         INTEGER            TO
 
         DOUBLE PRECISION   DATA    ( 100 )
         DOUBLE PRECISION   DC      ( 125 )
         DOUBLE PRECISION   SUM     ( 250 )
 
   C
   C     Get the handle of the source file, and the values
   C     of ND and NI for that file.
   C
         CALL DAFGH  ( FROM         )
         CALL DAFHSF ( FROM, ND, NI )
 
   C
   C     Open the target file for write access.
   C
         CALL DAFOPW ( FILE, TO )
 
   C
   C     Get the summary and name for the array to be copied.
   C     Start the new array.
   C
         CALL DAFGS ( SUM  )
         CALL DAFGN ( NAME )
 
         CALL DAFBNA ( TO, SUM, NAME )
 
   C
   C     Unpack the summary to get the initial and final addresses
   C     of the original array.
   C
         CALL DAFUS ( SUM, ND, NI, DC, IC )
         IA = IC(NI-1)
         FA = IC(NI  )
 
   C
   C     Copy the elements in groups of 100.
   C
         FIRST = IA
 
         DO WHILE ( FIRST .LE. FA )
 
            LAST  = MIN ( FA, FIRST + 100 - 1 )
            CALL DAFRDA ( FROM, FIRST, LAST, DATA )
            CALL DAFADA ( DATA, LAST - FIRST + 1 )
            FIRST = FIRST + 100
 
         END DO
 
   C
   C     Make the addition permanent, then close the target file.
   C
         CALL DAFENA
         CALL DAFCLS ( TO )
 
         RETURN
         END


Example 4: Copying Arrays to a New File in Sorted Order



The final example is a complete program to copy the arrays in one file to a second file, so that the arrays in the new file are sorted according to the value of the first double precision component of each summary.

The program assumes that the file contains fewer than 1000 arrays.

Subroutine ORDERD creates an order vector for a double precision array. Subroutine COPYA, defined in the previous example, is used to copy the arrays.

         PROGRAM DAFSRT
 
         CHARACTER*(128)       SOURCE
         CHARACTER*(128)       TARGET
         CHARACTER*(80)        ARCH
         CHARACTER*(80)        TYPE
 
         DOUBLE PRECISION      DC      (  125 )
         DOUBLE PRECISION      SUM     (  125 )
         DOUBLE PRECISION      VALUES  ( 1000 )
 
         INTEGER               HANDLE
         INTEGER               IC      (  250 )
         INTEGER               NA
         INTEGER               ND
         INTEGER               NI
         INTEGER               ORDER   ( 1000 )
         INTEGER               TO
         INTEGER               I
         INTEGER               J
         INTEGER               TARHAN
 
         LOGICAL               FOUND
 
 
   C
   C     Prompt for the names of the source and target files.
   C
         WRITE (*,*)
         WRITE (*,*)         'Name of source (unsorted) file?'
         READ  (*,FMT='(A)')  SOURCE
 
         WRITE (*,*)
         WRITE (*,*)         'Name of target (sorted) file?'
         READ  (*,FMT='(A)')  TARGET
 
   C
   C     Determine the file architecture and the type of data
   C     in the file.
   C
         CALL GETFAT ( SOURCE, ARCH,   TYPE     )
 
   C
   C     Open the source file, and look up the values of ND and NI
   C     for the file. That's needed for the call to DAFONW for
   C     opening the new DAF file later on.
   C
         CALL DAFOPR ( SOURCE, HANDLE           )
         CALL DAFHSF (         HANDLE, ND,   NI )
 
 
   C
   C     Collect the values of the first double precision
   C     component of each summary.
   C
         CALL DAFBFS ( HANDLE )
         CALL DAFFNA ( FOUND  )
 
         NA = 0
         DO WHILE ( FOUND )
            CALL DAFGS ( SUM )
            CALL DAFUS ( SUM, ND, NI, DC, IC )
 
            NA         = NA + 1
            VALUES(NA) = DC(1)
 
            CALL DAFFNA ( FOUND )
         END DO
 
   C
   C     Create an order vector for the values, such that ORDER(1)
   C     is the index of the array with the smallest value,
   C     ORDER(2) is the index of the array with the next smallest
   C     value, and so on.
   C
         CALL ORDERD ( VALUES, NA, ORDER )
 
   C
   C     Open the new file to initialize it, then close it
   C     immediately. We'll set the type of data in the file
   C     to be the same as the type of data in the original
   C     file. COPYA will reopen the file and copy an array
   C     to it.
   C
         CALL DAFONW ( TARGET, TYPE, ND, NI, 'Sorted file', 0,
        .              TARHAN                                  )
         CALL DAFCLS ( TARHAN )
 
   C
   C     Look up the arrays in the specified order (starting from
   C     the beginning of the file each time). Copy each one as
   C     it is found to the target file.
   C
         DO I = 1, NA
            CALL DAFBFS ( HANDLE )
 
            DO J = 1, ORDER(I)
               CALL DAFFNA ( FOUND )
            END DO
 
            CALL COPYA ( TARHAN )
 
         END DO
 
         END
 


Summary of Mnemonics



SPICELIB contains a family of subroutines that can be used to create, populate, and manipulate double precision array files. The name of each routine begins with the letters `DAF', followed by a two- or three-character mnemonic. For example, the routine that begins a forward search of an array file is named DAFBFS, pronounced `DAF-B-F-S'. The following is a complete list of mnemonics and translations, in alphabetical order.

   ADA      Add data to array
   ARR      Add reserved records
   ARW      Address to record/word
   BBS      Begin backward search
   BFS      Begin forward search
   BNA      Begin new array
   CAD      Continue adding data
   CLS      Close
   CS       Continue search
   ENA      End new array
   FNH      File name to handle
   FNA      Find next array
   FPA      Find previous array
   GH       Get handle
   GN       Get name
   GS       Get summary
   HFN      Handle to file name
   HSF      Handle to summary format
   HLU      Handle to logical unit
   HOF      Handles of open files
   LUH      Logical unit to handle
   NRR      Number of reads, requests
   ONW      Open new
   OPR      Open for read
   OPW      Open for write
   PS       Pack summary
   RA       Re-order arrays
   RCR      Read character record
   RDA      Read data from address
   RDR      Read double precision record
   RFR      Read file record
   RN       Replace name
   RRR      Remove reserved records
   RS       Replace summary
   RWA      Record/word to address
   SIH      Signal invalid handles
   US       Unpack summary
   WCR      Write character record
   WDA      Write data to address
   WDR      Write double precision record
   WFR      Write file record
Many of the subroutines listed here are not normally used except to support other subroutines. For example, because the subroutines that read and write records (RCR, RDR, RFR, WCR, WDR, WFR) are low level routines, they are not usually called by a typical user, but instead by higher level DAF routines.



Summary of Calling Sequences



The calling sequences for the DAF subroutines are summarized below. Subroutines are grouped by function.

Opening and closing files:

 
   ONW ( FNAME, FTYPE, ND, NI, IFNAME, RESV, HANDLE )
   OPR ( FNAME,                              HANDLE )
   OPW ( FNAME,                              HANDLE )
   CLS (                                     HANDLE )
Modifying reserved records:

   ARR ( HANDLE, RESV )
   RRR ( HANDLE, RESV )
Adding an array to a file:

   BNA ( HANDLE, SUM, NAME )
   ADA ( DATA,   N         )
   CAD ( HANDLE            )
   ENA
Finding an array within a file:

   BFS ( HANDLE )
   FNA ( FOUND  )
 
   BBS ( HANDLE )
   FPA ( FOUND  )
 
   CS  ( HANDLE )
 
   GH ( HANDLE )
   GN ( NAME   )
   GS ( SUM    )
   RN ( NAME   )
   RS ( SUM    )
 
   US  ( SUM, ND, NI, DC, IC      )
   PS  (      ND, NI, DC, IC, SUM )
Reading and writing arrays:

   RDA ( HANDLE, BEGIN, END, DATA )
   WDA ( HANDLE, BEGIN, END, DATA )
Reordering arrays:

   RA ( HANDLE, IORDER, N )
Converting array files:

   BT ( BINARY, TXTLUN )
   TB ( TXTLUN, BINARY )
Reading, writing physical records:

   RFR ( HANDLE, ND, NI, IFNAME, FWARD, BWARD, FREE )
   WFR ( HANDLE, ND, NI, IFNAME, FWARD, BWARD, FREE )
 
   RCR ( HANDLE, RECNO, CREC )
   WCR ( HANDLE, RECNO, CREC )
 
   RDR ( HANDLE, RECNO, BEGIN, END, DATA, FOUND )
   WDR ( HANDLE, RECNO,             DREC        )
   NRR ( READS,  REQS                           )
Internal conversions:

   HSF ( HANDLE, ND, NI )
 
   FNH ( FNAME,  HANDLE )
   HFN ( HANDLE, FNAME  )
   HLU ( HANDLE, UNIT   )
   LUH ( UNIT,   HANDLE )
 
   ARW ( ADDR, RECNO, WORDNO       )
   RWA (       RECNO, WORDNO, ADDR )
Error handling utilities:

   HOF ( FHSET          )
   SIH ( HANDLE, ACCESS )
Related routine--Determining file architecture and type:

   GETFAT ( FILE, ARCH, TYPE )


Obsolete Routines



The following is a list of routines and their replacements. NAIF's policy is to maintain the old routines to assure that existing software continues to function. Bugs will be fixed; however, no new functionality will be added to them. The replacement routines should be used in any new software that is being developed. Existing software does not need to be updated.

As always, for more information about a given routine, see its header documentation.

   Routine   Replacement   Description
   --------  ------------  ----------------------------------
   DAFOPN    DAFONW        Open new DAF file
   DAFA2B    DAFTB         Convert transfer to binary format
   DAFB2A    DAFBT         Convert binary to transfer format
   DAFB2T    DAFBT         Convert binary to transfer format
   DAFT2B    DAFTB         Convert transfer to binary format