A User's Guide for The PDS to FITS Translator with Programming notes August 28, 1995 Version 1.0 PDS / Small Bodies Node University of Maryland College Park, Maryland Contents 1 What is PDS2FITS? 2 2 The Command Line 2 2.1 Explanation of required parameters 3 2.2 Explanation of switches and optional parameters 3 2.3 Command Line Example 3 3 The Reserved FITS Keyword List 3 4 The Translation Table 3 4.1 Syntax of first line 4 4.2 Syntax of translation string 4 4.3 Description of PDS key string 4 4.4 Description of FITS key string 4 4.5 Mapping Types 5 5 The Error Log File 6 6 Calculated Fields 6 7 Using PDS2FITS - Examples 7 A Error and Warning Messages 12 B The Reserved FITS Keyword List 13 C The Standard Tables 14 C.1 SIMPLE_IMAGE.std 15 C.2 ASCII_TABLE.std 15 C.3 BINARY_TABLE.std 16 D Programming Notes 17 D.1 Data Structures 17 D.2 Program Modules 18 1 What is PDS2FITS? The PDS to FITS Translator is a utility for generating FITS Header files from PDS labels. It was developed at the University of Maryland, by personnel associated with the Small Bodies Node of the PDS. It is written in ANSI standard C, compiled using a GNU C++ (gcc) compiler and has been tested on a Sun/SPARC running SunOS version 5.0 . The program has three required parameters and three optional ones. The first required parameter is the name of the input PDS file. The second is the name of the input translation table, a user-created file containing a list of keyword mappings of the form "PDS_keyword = FITS_keyword". The third require parameter is the name of the output FITS header file. For a discussion of the optional parameters see the Command Line description section of this document. The utility works by reading through the input PDS label file line by line and building a data structure that is then used to create the FITS header It does this by searching the translation table for each PDS keyword that is read in and then storing the corresponding FITS keyword in the data structure; along with the it's value string. The syntax of the FITS keyword string indicates the type of extension object with which it is associated (e.g. "IMAGE", "TABLE", or "BINTABLE"). The program then traverses through the data structure and outputs the keywords and values to the FITS header file. At present, the Translator can accommodate three different types of PDS files. These include: -Simple Image: A PDS label pointing to one image. -ASCII Table: A PDS label a two dimensional data matrix representing the rows and columns of a table. Each entry of the table is an ASCII value. -Binary Table: A two dimensional matrix similar to an ASCII table except that each table entry is stored in a binary format rather than coded into ASCII. Each of these file types has an associated Standard Translation Table. This file contains all the required PDS keywords and their FITS equivalents. The first line of the user created translation table (the User Translation Table) indicates which type of Standard Translation Table to include. The User Translation Table then contains keyword mappings that are particular to the PDS label being translated. See the appendix for examples of Standard and User Translation Tables. A log file of error messages and unmatched PDS keywords is also generated. Please note that the translator assumes that the input PDS label adher to correct PDS format. No verification of the label is done before processing 2 The Command Line The executable file, called p2f, has three required arguments and thre optional ones. The syntax of the command line is as follows: p2f {-h} {-p } {-t } {-r } < PDS label > < Translation Table > < FITS header > Note: - "{ } " indicate optional parameters. - The required arguments must appear in the order shown. The optional arguments must immediately follow the switches; otherwise they can appear in any order. 2.1 Explanation of required parameters < PDS label > The name of the input PDS label < Translation Table > The name of the user created translation table < FITS header > The name of the FITS header file. 2.2 Explanation of switches and optional parameters -h Prints this message -p Indicates the path to the PDS Data Dictionary . must immediately follow the switch. If this switch is not included in the command line the PDSDD is assumed to be in the directory specified by the header file defaults.h. -t Indicates the path to the Standard Translation table. must immediately follow the switch. If this switch is not included in the command line the Standard Translation table is assumed to be by the directory specified by the header file defaults.h. -r is a file containing reserved FITS keywords. If the switch is not included in the command line a default filename of RES_KEYS.LST is assumed. 2.3 Command Line Example p2f -p pdsdd -t tbl pmag0001.lbl tbl/pmag.user_table pmag0001.hdr Here, "pmag0001.lbl" is the PDS label for an ASCII table and "pmag0001.hdr" the corresponding output FITS header. "pmag.user_table" is the User Translation Table for the pmag data series; notice that the full path must be given since the table is not in the current directory. The -t switch is used to indicate the path to the Standard Translation Table. For a discussion of the Standard Translation Table see "The Translation Tables" section of this document. The -p switch is used to indicate the path to the PDS Data Dictionary. Because the -r switch is not set a default filename of RES_KEYS.LST is assumed for the reserved FITS keyword list. 3 Reserved FITS Keyword List A file containing reserved FITS keywords is required by this program. A default filename of RES_KEYS.LST is assumed if the "-r" switch is not used in the command line. Each keyword in the User Translation Table (see below) is compared against this list and if a match occurs a warning is generated. Most reserved keywords should have only one PDS equivalent and therefore should appear in the Standard Translation Table. Redefining a reserved keywor is acceptable but the user should take care not to give it a meaning at odds with the FITS Users Guide. Each keyword should begin on the first character on each line. Anything following a pound symbol ( "#" ) is regarded as a comment. The default keyword list is included in the appendix. 4 The Translation Tables The key to the flexibility of the Translator is the Translation Table A translation table is a series of keyword pairs of the form , < PDS_keyword > = < FITS_keyword > or "LITERAL STRING" = < FITS_keyword > Each translation string appears on a separate line in the table file. A series of pre-defined tables, called Standard Translation Tables, contains the mappings of required PDS keywords for each file type. For example "ASCII_TABLE.std" contains translations for ASCII tables, "SIMPLE_IMAGE.std" for simple images. The user creates a translation table, called the User Translation Table, which is particular to the PDS label(s) being translated. The first line of the User Translation Table is of the form, " < PDS_form > = < FITS_form > " and describes the general types of the PDS and FITS files. The < PDS form > string indicates which type of Standard Table is to be used by the Translator. The rest of the User Table consists of PDS keyword mappings that are peculiar to the PDS label(s) being translated. The Standard and User Translation Tables are parsed by the program to form two internal translation tables, called the Main table and the Literal table. The Literal table contains all direct literal string releations. The Main table contains all other relations. As the PDS label is read in, the Main table is used to build the FITS data structure. The Literal table then is used to add literal strings. The translation table syntax is described next. ( Note: braces, "{}", indicate optional elements ) . 4. 1 Syntax of first line < PDS_form > = < FITS_form > Examples: ASCII_TABLE = TABLE SIMPLE_IMAGE = IMAGE SERIES_IMAGE = IMAGE 4.2 Syntax of translation string < PDS key string > {+} = < FITS key string > # comments notes: - The optional "+" operator indicates that this mapping will be in addition to any previous definitions of this PDS keyword. Its absence indicates that the definition will replace all previous definitions. - Anything following a pound symbol ( "#" ) is regarded as a comment. - < PDS key string > may also be a literal value. If so, it must be surrounded by double quotation marks. 4.3 Description of PDS key string {} {[ | *].} ^^^^^^^^^^^^^^^^^^^^^^^^ may be repeated as needed to specify the path Notes: - If no object path is given, the main object is assumed. - refers to the value of the NAME keyword, included as the first keyword in every object (whether or not it is required or filled). - The asterisk symbol ( * ) indicates the same sequential number as found at the end of the FITS key string 4.4 Description of FITS key string {.} {*} Notes: - Only one extension is allowed. If none is given, the primary data header is assumed. - The * replaces the index number for indexed extensions or keywords being written into repetitive data structures ( i.e. COLUMNs in a TABLE An explicit number may also be used for indexed FITS keywords. 4.5 Mapping Types There are four different categories of PDS to FITS keyword mappings. 1) Direct mappings: This is a direct assignment of a FITS keyword to a PDS keyword. The value(s) associated with any and all occurrences of the FITS keyword are mapped to the PDS keyword. A literal string enclosed in quotes may also be mapped to a PDS keyword. Syntax: { . } PDS_keyword = { . } FITS_keyword Example A: RECORD_BYTES = TABLE.NAXIS1 Explanation of example A: The value of RECORD_BYTES is assigned to NAXIS1 from the "TABLE" extension header. 2) one-to-* mappings: This is a one-to-many mapping. The set of PDS values is mapped to a series of FITS keywords. If there are N values in the set then N FITS keywords are created with numeric suffix running from 1 to N. Syntax: { < object > .}PDS_keyword = { < extension > .} FITS_keyword* Example A: IMAGE.SAMPLING_PARAMETER_NAME = CTYPE* Explanation of example A: If SAMPLING_PARAMETER_NAME = {value1,....,valueN} then CTYPE1 = value1 . . . CTYPEN = valueN 3) *-to-* mappings: This is a one-to-one mapping of each indexed FITS keyword to the correspondingly indexed PDS keyword. Syntax: {< object > { [*] }}. PDS_keyword = {< extension > [*].}FITS_keyword{* ^^^^^^^^^^^^ ^^may be repeated as needed to specify the path Example A: TABLE.COLUMN[*].FORMAT = TFORM* Explanation of example A: COLUMN[n].FORMAT receives the value of TFORMn. Example B: IMAGE[*].LINES = IMAGE[*].NAXIS2 Explanation of example B: IMAGE[n].LINES is assigned the value of NAXIS2 from the nth data header in the FITS file. Example C: IMAGE[*].SAMPLING_PARAMETER_INTERVAL = IMAGE[*].CDELT* Explanation of example C: This is a combination of a *-to-* relation and a one-to-* relation. Each PDS image SAMPLING_PARAMETER_INTERVAL keyword will contain a list of all CDELT values from the corresponding FITS image header. 4.6 Note about Time strings A special translation format is required for time strings. PDS keywor ( e.g. 1981-12-02T13:36:28.797Z) and must correspond to two FITS keywords. The following convention is used: PDS_KEYWORD = FITS_DATE : FITS_TIME for example: OBSERVATION_TIME = DATE-OBS : TIME-OBS The date and time values are converted to conform to FITS standard formats. 5 The Error Log File In addition to the FITS header file a message log file is generated, named .log, where < filename > is the name of the input PDS label. It contains all general messages, warnings and error messages. Error messages can be caused by running out of memory or by an unexpected end-of-file marker. All unmatched PDS keywords (i.e. those that do not appear in either the standard or user defined Translation Tables) are also stored in the log file. For a list of error and warning messages see appendix A. 6 Calculated Fields There are certain required FITS keywords for which there are no PDS equivalents. These values must be calculated from several PDS values. Following is a list of these keywords for each file type. BINARY_TABLE: TFORMn : Derived from PDS COLUMN object keywords DATA_TYPE and BYTES, it's value specifies the width and data type of field n and is of the form rT. T is the field type obtained from DATA_TYPE and r is the repeat count, telling how many values of type T the field contains. It is calculated as BYTES/byte_cnt, where byte_cnt is the byte count of data type T. 7 Using PDS2FITS - Example This section provides one complete example of a PDS2FITS run for an ascii table. It includes the User Translation table, the input PDS label and the output FITS header and log file. Assuming that the PDSDD and Standard Translation Table are in the current directory, the command line would look like this: p2f pmag0001.lbl pmag.user_table pmag0001.hdr User Table: # # User translation table for the PMAG (ASCII table) datasets # #======================= ASCII_TABLE = TABLE OBSERVATION_ID = FILE-NUM(INTEGER) TARGET_NAME = OBJECT OBSERVATION_TIME = DATE-OBS:TIME-OBS DATA_SET_RELEASE_DATE = DATE-REL TELESCOPE_LONGITUDE = LONG-OBS TELESCOPE_LATITUDE = LAT--OBS DESCRIPTION = COMMENT NOTE = HISTORY # # PDS to FITS literal strings # "'FELENBOK,P/ET AL.'" = OBSERVER "'SICARDY,B/ET AL.'" = SUBMITTR "'STANDARD'" = DAT-FORM "'MAUNA-KEA,CFHT'" = OBSVTORY ------------------------------------------------------------------------------ PDS Label : CCSD3ZF0000100000001NJPL3IF0PDS200000001 = SFDU_LABEL /* File Format and Length */ RECORD_TYPE = FIXED_LENGTH RECORD_BYTES = 80 FILE_RECORDS = 2 DATA_SET_ID = "IHW-C-PPMAG-3-RDR-HALLEY-V1.0" OBSERVATION_ID = "520010" TARGET_NAME = "HALLEY" OBSERVATION_TIME = 1981-12-02T13:36:28.797Z /* Record Pointer to Object */ ^TABLE = "PMAG0001.TAB" /* Description of Object in File */ OBJECT = TABLE ROWS = 2 ROW_BYTES = 80 INTERCHANGE_FORMAT = ASCII NOTE = "PHOTOMETRY, BROAD BAND MAGNITUDE TABLE" COLUMNS = 13 OBJECT = COLUMN NAME = DAY_FRACTION DATA_TYPE = FLOAT BYTES = 8 START_BYTE = 1 FORMAT = "E8.5" END_OBJECT OBJECT = COLUMN NAME = ENTRY_NUMBER DATA_TYPE = INTEGER BYTES = 2 START_BYTE = 10 FORMAT = I2 END_OBJECT OBJECT = COLUMN NAME = FILTER_NAME DATA_TYPE = CHARACTER BYTES = 4 START_BYTE = 13 FORMAT = A4 END_OBJECT OBJECT = COLUMN NAME = WAVELENGTH DATA_TYPE = INTEGER BYTES = 4 UNIT = "ANGSTROM" START_BYTE = 18 FORMAT = I4 END_OBJECT OBJECT = COLUMN NAME = BANDPASS DATA_TYPE = INTEGER BYTES = 4 UNIT = "ANGSTROM" START_BYTE = 23 FORMAT = I4 END_OBJECT OBJECT = COLUMN NAME = LIMIT_FLAG DATA_TYPE = CHARACTER BYTES = 1 START_BYTE = 28 FORMAT = A1 END_OBJECT OBJECT = COLUMN NAME = MAGNITUDE DATA_TYPE = FLOAT BYTES = 7 UNIT = "MAGNITUDE" START_BYTE = 30 FORMAT = "E7.3" END_OBJECT OBJECT = COLUMN NAME = "ERROR" DATA_TYPE = FLOAT BYTES = 5 UNIT = "MAGNITUDE" START_BYTE = 38 FORMAT = "E5.3" END_OBJECT OBJECT = COLUMN NAME = DIAPHRAGM_DIAMETER DATA_TYPE = FLOAT BYTES = 5 UNIT = "ARCSEC" START_BYTE = 44 FORMAT = "E5.1" END_OBJECT OBJECT = COLUMN NAME = "RHO" DATA_TYPE = INTEGER BYTES = 4 UNIT = "ARCSEC" START_BYTE = 50 FORMAT = I4 END_OBJECT OBJECT = COLUMN NAME = "THETA" DATA_TYPE = INTEGER BYTES = 3 UNIT = "DEGREE" START_BYTE = 55 FORMAT = I3 END_OBJECT OBJECT = COLUMN NAME = DURATION DATA_TYPE = INTEGER BYTES = 4 UNIT = "SECOND" START_BYTE = 59 FORMAT = I4 END_OBJECT OBJECT = COLUMN NAME = MIDPOINT_AIRMASS DATA_TYPE = FLOAT BYTES = 5 START_BYTE = 64 FORMAT = "E5.3" END_OBJECT END_OBJECT = TABLE END FITS header: SIMPLE = T BITPIX = 8 NAXIS = 0 EXTEND = T FILE-NUM= 520010 OBJECT = 'HALLEY ' DATE-OBS= '02-12-81' DAT-FORM= 'STANDARD' OBSERVER= 'FELENBOK,P/ET AL.' OBSVTORY= 'MAUNA-KEA,CFHT' SUBMITTR= 'SICARDY,B/ET AL.' TIME-OBS= 0.567000 END XTENSION= 'TABLE ' BITPIX = 8 NAXIS = 2 NAXIS1 = 80 NAXIS2 = 2 PCOUNT = 0 GCOUNT = 1 TFIELDS = 13 TTYPE1 = 'DAY_FRACTION' TBCOL1 = 1 TFORM1 = 'E8.5 ' TTYPE2 = 'ENTRY_NUMBER' TBCOL2 = 10 TFORM2 = 'I2 ' TTYPE3 = 'FILTER_NAME' TBCOL3 = 13 TFORM3 = 'A4 ' TTYPE4 = 'WAVELENGTH' TUNIT4 = 'ANGSTROM' TBCOL4 = 18 TFORM4 = 'I4 ' TTYPE5 = 'BANDPASS' TUNIT5 = 'ANGSTROM' TBCOL5 = 23 TFORM5 = 'I4 ' TTYPE6 = 'LIMIT_FLAG' TBCOL6 = 28 TFORM6 = 'A1 ' TTYPE7 = 'MAGNITUDE' TUNIT7 = 'MAGNITUDE' TBCOL7 = 30 TFORM7 = 'E7.3 ' TTYPE8 = 'ERROR ' TUNIT8 = 'MAGNITUDE' TBCOL8 = 38 TFORM8 = 'E5.3 ' TTYPE9 = 'DIAPHRAGM_DIAMETER' TUNIT9 = 'ARCSEC ' TBCOL9 = 44 TFORM9 = 'E5.1 ' TTYPE10 = 'RHO ' TUNIT10 = 'ARCSEC ' TBCOL10 = 50 TFORM10 = 'I4 ' TTYPE11 = 'THETA ' TUNIT11 = 'DEGREE ' TBCOL11 = 55 TFORM11 = 'I3 ' TTYPE12 = 'DURATION' TUNIT12 = 'SECOND ' TBCOL12 = 59 TFORM12 = 'I4 ' TTYPE13 = 'MIDPOINT_AIRMASS' TBCOL13 = 64 TFORM13 = 'E5.3 ' END APPENDIX A Error and Warning Messages: ERROR: Unable to allocate memory for data structure. Explanation: Out of system memory. ERROR: bogus FITS keyword found in User Tranlation Table! Explanation: keyword longer than eight characters. APPENDIX B The Reserved FITS Keyword List # RES_KEYS.LST contains required and reserved FITS keywords as defined in "A U # Flexible Image Transport System (FITS), version 3.1 " # AUTHOR BITPIX BLANK BLOCKED BUNIT BSCALE BZERO CDELT* COMMENT CROTA* CRPIX* CRVAL* CTYPE* DATAMAX DATAMIN DATE DATE-OBS END EQUINOX EXTLEVEL EXTNAME EXTVER EXTEND GCOUNT HISTORY INSTRUME NAXIS NAXIS1 NAXIS2 OBJECT OBSERVER ORIGIN PCOUNT REFERNC SIMPLE TBCOL* TELESCOP TFIELDS TFORM* TNULL* TUNIT* TSCAL* TTYPE* TZERO* XTENSION APPENDIX C The Standard Translation Tables C.1 SIMPLE_IMAGE.std # # SIMPLE_IMAGE Standard translation table. # #========================================================================== RECORD_TYPE = "FIXED_LENGTH" FILE_RECORDS = NAXIS2 IMAGE.LINES = NAXIS2 IMAGE.LINE_SAMPLES = NAXIS1 IMAGE.SAMPLE_BITS = BITPIX IMAGE.SAMPLING_PARAMETER_INTERVAL = CDELT* IMAGE.SAMPLING_PARAMETER_UNIT = CTYPE* IMAGE.OFFSET = BZERO IMAGE.SCALING_FACTOR = BSCALE IMAGE.UNIT = BUNIT # END TABLE comments: - Note that the first two lines refer to the main PDS object; the rest refer to the IMAGE object. - The first line assigns a literal value to the PDS keyword "RECORD_TYPE". - The sixth and seventh lines are one-to-* relations. The PDS keyword "SAMPLING_PARAMETER_INTERVAL" will be assigned the set of values associated with the FITS keyword "CDELTn". C.2 ASCII_TABLE.std # # ASCII_TABLE Standard translation table. # #========================================================================== RECORD_TYPE = "FIXED_LENGTH" RECORD_BYTES = TABLE.NAXIS1 FILE_RECORDS = TABLE.NAXIS2 # # Table values: # TABLE.INTERCHANGE_FORMAT = "ASCII" TABLE.ROWS = TABLE.NAXIS2 TABLE.ROW_BYTES = TABLE.NAXIS1 TABLE.COLUMNS = TABLE.TFIELDS TABLE.NOTE = TABLE.HISTORY TABLE.DESCRIPTION = TABLE.COMMENT # # Column values: # TABLE.COLUMN[*].NAME = TABLE.TTYPE* TABLE.COLUMN[*].START_BYTE = TABLE.TBCOL* TABLE.COLUMN[*].FORMAT = TABLE.TFORM* TABLE.COLUMN[*].UNIT = TABLE.TUNIT* TABLE.COLUMN[*].SCALING_FACTOR = TABLE.TSCAL* TABLE.COLUMN[*].OFFSET = TABLE.TZERO* TABLE.COLUMN[*].INVALID = TABLE.TNULL* # END TABLE comments: - Note the *-to-* relation between the PDS COLUMN objects and the FITS keywords. C.3 BINARY_TABLE.std # # BINARY_TABLE Standard translation table. # #==================================================== RECORD_TYPE = "FIXED_LENGTH" DATA_SET_RELEASE_DATE = DATE PRODUCER_INSTITUTION_NAME = ORIGIN TARGET_NAME = OBJECT RECORD_BYTES = BINTABLE.NAXIS1 FILE_RECORDS = BINTABLE.NAXIS2 # # Table values: # TABLE.INTERCHANGE_FORMAT = "BINARY" TABLE.ROWS = BINTABLE.NAXIS2 TABLE.ROW_BYTES = BINTABLE.NAXIS1 TABLE.COLUMNS = BINTABLE.TFIELDS TABLE.NOTE = BINTABLE.HISTORY TABLE.DESCRIPTION = BINTABLE.COMMENT # # Column values: # TABLE.COLUMN[*].NAME = BINTABLE.TTYPE* TABLE.COLUMN[*].FORMAT = BINTABLE.TFORM* TABLE.COLUMN[*].UNIT = BINTABLE.TUNIT* TABLE.COLUMN[*].SCALING_FACTOR = BINTABLE.TSCAL* TABLE.COLUMN[*].OFFSET = BINTABLE.TZERO* TABLE.COLUMN[*].INVALID = BINTABLE.TNULL* APPENDIX D Programming Notes The main data structures and modules of the Translator are explained in this section. Pseudo-code is given for the important functions within each module. Note that FILE pointers to the PDSDD, FITS header, PDS label and Translation Tables are global. D.1 Data Structures table_element is the data structure for corresponding FITS and PDS keywords typedef struct te { char FITS_string[100]; /* FITS keyword */ char PDS_string[100]; /* PDS keyword */ char FITS_type[20]; /* optional FITS datatype */ struct te *next; /* pointer to next node */ }table_element; ______________________________________________________________________________ table is the data structure for the translation table typedef struct { char FITS_form[50]; /* input FITS file type */ char PDS_form[50]; /* output PDS file type */ table_element *elements /* element list */ }table; ______________________________________________________________________________ FITS_keyword is the data structure for PDS keyword and its' parameters typedef struct keyword { char name[100]; /* name of keyword */ char type[20]; /* general data type of keyword */ char *value; /* pointer to the output string */ int required; /* true if required */ struct keyword *next; /* pointer to next keyword in list */ struct keyword *prev; } FITS_keyword; FITS_object is the data structure for a PDS object and its associated paramete typedef struct object { char name[100]; /* name of object */ int extension; /* TRUE if object is extension */ int ext_cnt; /* number of extension objects */ int keyword_cnt; /* number of keywords */ FITS_keyword *keyword_list; /* pointer to keyword list */ struct object *next; /* pointer to list of objects at /* same level of hierachy*/ struct object *prev; } FITS_object; D.2 Program Modules The source code consists of six ANSI C based modules and their header files: 1) PDS2FITS.C The main shell routine 2) PDS_TBL.C These functions are used to build up an internal mapping table by combining the User Translation table and the Standard Translation table indicated by the first line of the User table. 3) FITS_OBJ.C Functions used to create FITS objects and FITS keywords 4) PDS.C Functions for reading in the PDS label and building the FITS object. 5) FITS_HDR.C Functions used to write the PDS label from the PDS object 6) PDS_UTIL.C Various support routines used by the other modules 1) PDS2FITS.C This module contains the main shell routine. BEGIN CALL parse_arguments() to get the file names OPEN all the input files, exiting if there are any errors CALL read_table(table_file, ttable) to create the translation table CALL setup_FITS_object( main_object) to initialize the FITS structure CALL read_PDS_file(main_object) to fill the FITS structure CALL write_FITS_header(main_object, hdr_file) to write the FITS header CLOSE(files) END 2) PDS_TBL.C These functions are used to build up the internal mapping table by combining the User Translation table and the appropriate Standard Translation Table. int read_table(table_file,ltable,table, main_object) FILE *table_file; table *ttable; table *ltable; PDS_object *main_object; /* needed for possible literal values */ { /* Routine to read the input translation file and set up the internal table* READ past blanks and comments (begin with "#") CALL split_line(line_buffer,ttable.FITS_form,ttable.PDS_form) to get input and output formats IF PDS_form is SERIES_IMAGE THEN check for list of image names. CALL setup_predefined(ltable,ttable,PDSDD, main_object) to add pre-defined translations and literal values from Standard Translation table to the internal tables. CALL load_table(table_file,ltable, ttable, PDSDD, main_object) to load User Translation table. RETURN status } ========================================================================== int setup_predefined(ltable,ttable, PDSDD, main_object) table *ltable; table *ttable; { /* Routine to add the pre-defined translations for this PDS type. These definitions are read out of the *Stanrard Translation Table. The filenam is created from the PDS form. */ FORMULATE the predefine file name from ttable.PDS_form and the appropriate directory path. IF this file exists THEN open it ELSE signal a warning and return CALL load_table(std_table,ltable,ttable) to load the predefined translations and values RETURN status } =========================================================================== int load_table(table_table, ttable, ltable) FILE *table_file; table *ttable; table *ltable; { /* Routine to loop through the lines of an already opened table file and add translation lines to the program table. */ READ next line WHILE (not EOF) DO IF this is a comment or blank NEXT line ALLOCATE space for new table element CALL split_line(line_buffer, element.FITS_string, element.PDS_string) IF (element.FITS_string is a literal OR *-to-one relation) THEN CALL link_element(ltable,element) to add element to literal table ELSE CALL link_element(ttable,element) to add element to main table END WHILE RETURN status } =========================================================================== int link_element(table, newone,add) table *table; table_element *newone; /* element to be added */ int add /* if false; search table and delete any previous definitions for this PDS keyword. */ { /* Routine to link the new element into the table. The elements are sorted alphabetically (i.e., by ASCII code). */ table_elements *next_one, *last_one; /* if add is false; search and destroy duplicate nodes */ IF (ADD = FALSE) THEN WHILE (table->element is not NULL) IF (table_element = newone) THEN delete table->element; ENDIF get next table element; END WHILE; END IF; /* check for first element: */ IF (ttable->elements = NULL) THEN ttable->elements = newone RETURN status ENDIF /* Find insertion point (beginning of list is a special case): */ IF (newone->FITS_string < ttable->elements->FITS_string) THEN newone->next = ttable->elements ttable->elements = newone RETURN status ENDIF last_one = next_one next_one = next_one->next WHILE (next_one <> NULL) DO IF (newone->FITS_string < next_one->FITS_string) THEN newone->next = next_one last_one->next = newone RETURN status ENDIF last_one = next_one next_one = next_one->next END WHILE /* If you got here it gets added on to the end of the chain: */ last_one->next = newone RETURN status } =========================================================================== int split_line(line, Pstring, Fstring) char *line; /* input line */ char *Pstring; /* PDS keyword string */ char *Fstring; /* FITS keyword string */ =========================================================================== int literal_value(string) char *string; { /* Returns 1 if the string is a literal value (a number or a string in matching quotes); 0 otherwise. */ } 3) PDS.C /* Routines for reading in the PDS label and building the FITS data structure */ int read_PDS_file( main_object) PDS_object *main_object; { /* Routine to step through the PDS label records and assign the appropriate value strings the the FITS structure elements. As each line is read, it is first broken into PDS keyword string and value string. The PDS object type is prepended onto the keyword, except for the implicit FILE object. A search is made of the translation table to match this name exactly. A warning is issued if the keyword is not matched; otherwise the value string is then assigned to all appropriate FITS elements. Certain values are stored for calculated FITS fields such as NAXIS. */ stack *object_stack; /* used to build pds keyword string */ link_list *object_list; /* keeps track of PDS objects */ READ first line SKIP (blank lines or comments) CALL split_pds_line(line,key,value) to get the key and value fields WHILE (key != "END") IF (key = "OBJECT") { push(object_stack, value); add(object_list, value); } ELSE IF (key = "END_OBJECT") pop(object_stack) ELSE IF ( object_stack is not empty ) build object_string from object_stack key = object_string || key ENDIF CALL entry = find_pds_keyword(ttable,key) IF (entry = NULL) signal unmatched keyword ELSE CALL set_value(value,entry,object_list,main_object) to assign the value to the appropriate keyword(s) ENDIF READ next line SKIP (blank lines or comments) CALL split_pds_line(line,key,value) to get the key and value fields ENDIF END WHILE /* now add calculated fields for specific object type */ post_process(main_object,ttable); RETURN status } =========================================================================== table_element *find_pds_keyword(ttable,key,start_here) table *ttable; char *key; table_element *start_here; /* staring point - may be null */ { /* This routine attempts to find a matching translation table entry for the given key. The table is assumed to be sorted in alphabetic order. First attempt is to match exactly. Failing that the search is repeated with "[*]" after every object in the pds key string. If the no match is found then return NULL */ /* Exact match search: */ IF (start_here is NULL) entry = ttable->elements ELSE entry = start_here ENDIF WHILE (entry != NULL) DO IF (entry->PDS_string = key) RETURN entry ELSE entry = entry->next ENDIF END WHILE /* If you made it this far, no success. Try again with wildcard "*" */ /* parse PDS string into individual objects and store in linllist*/ make_link_list(entry->PDS_string, object_list); object_ptr = object_list; WHILE ( object_ptr != NULL ) { modified_obj = *object_ptr || "[*]"; modified_key = build_new_string(object_list,modified_obj); WHILE (entry != NULL) { IF (entry->PDS_string = modified_key) RETURN entry ELSE entry = entry->next ENDIF } object_ptr = object_ptr->next; } /* Still no success: */ RETURN status {not found} } =========================================================================== int set_value(value,entry,object_list,main_object) char *value; table_element *entry; link_list *object_list; FITS_object *main_object; { /* Routine to assign the value string to the appropriate FITS keyword. Objects and keywords are created on the fly as they are needed. */ /* Determine if this is a 1->* relation */ IF ( 1->*) { N = 1; PARSE value set into list of tokens deleimeted by comas (,); WHILE (token_pointer != NULL) { modified_string = entry->FITS_string || N FITSkey = find_FITS_keyword(modified_string,main_object); FITSkey->value = *token_ptr; N++; } } ELSE /* Determine if this is a *->* relation */ IF (*->*) { /* first find PDS object containing "[*]" from PDS key string*/ object = object in entry->PDS_string containing "[*]"; /* now count how many objects of that type are in object_list */ object_cnt = how_many_objects(object_list, object); modified_string = entry->FITS_string with "*" replaced by object_cnt; FITSkey = find_FITS_keyword(modified_string,main_object); FITSkey->value = value; } ELSE /* must be a 1->1 relation */ { CALL FITSkey = find_FITS_keyword(entry->FITS_string, main_object) IF (FITSkey->value is NULL) FITSkey->value = value } RETURN status } =========================================================================== FITS_keyword *find_FITS_keyword(key,object) FITS_keyword *key; FITS_object *object; { /* Workhorse routine to locate a specific keyword and extension, if one exists. */ SPLIT key into extension and keyword IF (extension = "") CALL keyword_pointer = find_keyword_in_object(keyword,object) ELSE CALL extension_pointer = find_extension_in_object(extension,object) CALL keyword_pointer = find_keyword_in_object(keyword, extension_pointer) ENDIF RETURN pointer } ========================================================================== FITS_object *find_extension_in_object(extension,object) char *extension; FITS_object *object; { /* Routine to find a given extension object given the main header object. If it is not found, it is created. */ extension_object = object->object_list WHILE (extension_object != NULL) DO IF (extension_object->name = extension) RETURN extension_object ENDIF extension_object = extension_object->next END WHILE /* Not found. Create it: */ CALL extension_object = create_FITS_object(extension, object) return extension_object; } =========================================================================== FITS_keyword *find_keyword_in_object(keyword,object) char *keyword; FITS_object *object; { /* Routine to find a given keyword in the given object. If the keyword is not found, it is created. */ key = object->keyword_list WHILE (key != NULL) DO IF (key->name = keyword) RETURN key ENDIF key = key->next END WHILE /* Not found. Create it: */ CALL key = create_FITS_keyword(keyword, object) RETURN key } =========================================================================== int post_process(main,ttable) FITS_object *main; table *ttable; { /* determine object type */ IF (ttable->PDS_form = "ASCII_TABLE") { process_ascii_table(main); } else if (ttable->PDS_form = "BINARY_TABLE") { process_binary_table(main); } else if (ttable->PDS_form = "SIMPLE_IMAGE") { process_simple_image(main); } else if (ttable->PDS_form = "SET_IMAGE") { process_set_image(main); } } /* end post_process */ 4) FITS_OBJ.C FITS_OBJ.TXT /* Routines relating to the FITS data structure */ int setup_FITS_object(table *ttable, FITS_object **main_object) { /* get object type from the translation table */ get_subobj(ttable->PDS_form,obj_type); /* create main object */ *main_object = create_FITS_object(obj_type,NULL); /* if it's NULL, then return error */ IF (main_object == NULL) return MALLOC_ERROR; /* if object is a table, create the TABLE extension object */ IF (obj_type = "ACII_TABLE") ext_object = create_FITS_object("TABLE",main_object); /* if object is a binary table, create the BINTABLE extension object */ IF (obj_type = "BINARY_TABLE") ext_object = create_FITS_object("BINTABLE",main_object); IF (ext_object == NULL) return MALLOC_ERROR; return OK; } =========================================================================== FITS_object *create_FITS_object(type,parent) char *type; /* name of object */ FITS_object *parent; /* Previous object in list This may be NULL * { /* Routine to create a new FITS object of the named type, and add it to the end of the list of objects attached to parent */ /* Create object and initialize fields: */ ALLOCATE space for the new object /* Check if this is the PRIMARY HEADER OBJECT */ IF (parent = NULL) { newobject.extension = FALSE; IF (type = "IMAGE") newobject.name = "IMAGE[1]"; ELSE newobject.name = type; ENDIF newobject.ext_cnt = 0; newobject.prev = newobject.next = NULL; } ELSE newobject.extension = TRUE; IF (type = "IMAGE") { IMG_NUM = extension count from main object + 1; newobject.name = "IMAGE[ IMG_NUM ]"; } ELSE newobject.name = type; ENDIF ENDIF /* add the required keywords for the given object type */ add_required_keys(newobject); /* Link the new object onto the end of the object list */ IF (parent != NULL) { object_ptr = parent; WHILE (object_ptr.next != NULL) object_ptr = object_ptr.next; END WHILE object_ptr.next = newobject; newobject.prev = object_ptr; new_object.next = NULL; } RETURN newobject } =========================================================================== int add_required_keys(object) { /* Routine to add the required FITS keywords of the given FITS object */ IF (object.extension = FALSE) { key = create_FITS_keyword("SIMPLE",object); key.type = "LOGICAL"; key.value = "T"; IF (object.name = "IMAGE[1]") { key = create_FITS_keyword("BITPIX",object); key.type = "INTEGER"; key = create_FITS_keyword("NAXIS",object); key.type = "INTEGER"; key = create_FITS_keyword("NAXIS1",object); key.type = "INTEGER"; key = create_FITS_keyword("NAXIS2",object); key.type = "INTEGER"; } IF (object.name = "TABLE" OR "BINTABLE") { key = create_FITS_keyword("BITPIX",object); key.type = "INTEGER"; key.value = "8"; key = create_FITS_keyword("NAXIS",object); key.type = "INTEGER"; key.value = 0; } } IF (object.extension = TRUE) { primary_object = pointer back to primary header object; IF (primary_object.ext_num = 0) { key = create_FITS_keyword("EXTENSION", main_object); key.type = "LOGICAL" key.value = "T"; } IF (object.name = "IMAGE[*]") { key = create_FITS_keyword("XTENSION",object); key.type = "CHARACTER"; key.value = "IMAGE"; key = create_FITS_keyword("BITPIX",object); key.type = "INTEGER"; key = create_FITS_keyword("NAXIS",object); key.type = "INTEGER"; key.value = 2; key = create_FITS_keyword("NAXIS1",object); key.type = "INTEGER"; key = create_FITS_keyword("NAXIS2",object); key.type = "INTEGER"; key = create_FITS_keyword("PCOUNT",object); key.type = "INTEGER"; key = create_FITS_keyword("GCOUNT",object); key.type = "INTEGER"; } IF (object.name = "TABLE") { key = create_FITS_keyword("XTENSION",object); key.type = "CHARACTER"; key.value = "TABLE"; key = create_FITS_keyword("BITPIX",object); key.type = "INTEGER"; key.value = 8; key = create_FITS_keyword("NAXIS",object); key.type = "INTEGER"; key.value = 2; key = create_FITS_keyword("NAXIS1",object); key.type = "INTEGER"; key = create_FITS_keyword("NAXIS2",object); key.type = "INTEGER"; key = create_FITS_keyword("PCOUNT",object); key.type = "INTEGER"; key.value = "0"; key = create_FITS_keyword("GCOUNT",object); key.type = "INTEGER"; key.value = "1"; key = create_FITS_keyword("TFIELDS",object); key.type = "INTEGER"; } IF (object.name = "BINTABLE"){ key = create_FITS_keyword("XTENSION",object); key.type = "CHARACTER"; key.value = "BINTABLE"; key = create_FITS_keyword("BITPIX",object); key.type = "INTEGER"; key.value = 8; key = create_FITS_keyword("NAXIS",object); key.type = "INTEGER"; key.value = 2; key = create_FITS_keyword("NAXIS1",object); key.type = "INTEGER"; key = create_FITS_keyword("NAXIS2",object); key.type = "INTEGER"; key = create_FITS_keyword("PCOUNT",object); key.type = "INTEGER"; key = create_FITS_keyword("GCOUNT",object); key.type = "INTEGER"; key.value = "1"; key = create_FITS_keyword("TFIELDS",object); key.type = "INTEGER"; } } return success; } =========================================================================== PDS_keyword *create_FITS_keyword(keyword, parent) char *keyword; FITS_object *parent; { /* Routine to create a new FITS keyword and link it onto the end of the current list of the parent object */ ALLOCATE space for the new keyword SET newkeyword.name = keyword SET newkeyword.value = NULL SET newkeyword.next = NULL /* link keyword onto end of parent keyword list: */ IF (parent->keyword_list = NULL) THEN parent->keyword_list = newkeyword newkeyword.previous = NULL ELSE searchkey = parent->keyword_list WHILE (searchkey->next <> NULL) searchkey = searchkey->next searchkey->next = newkeyword newkeyword.previous = searchkey ENDIF INCREMENT parent->keyword_count RETURN newkeyword } 5) FITS_HDR.C HEADER.TXT /* Routines to OUTPUT the FITS data structure to the FITS HEADER FILE*/ int write_FITS_header(main_object,header_file) FITS_object *main_object; FILE *header_file; { /* Routine to translate and output the object definitions and keyword values. */ FITS_object *object; object = main; /* traverse FITS object list */ WHILE (object != NULL) { key = keyword_list WHILE (key != NULL) { CALL write_keyword(key,header_file) key = key->next; } WRITE "END" line object = object->next; } RETURN status } =========================================================================== int write_keyword(key,header_file) FITS_keyword *key; FILE *header_file; { /* Routine to write one keyword and its value to the output file. The Value of the keyword is first converted from the string to the output type, then verified. */ /* Check for null required keyword: */ IF (key->required and key->value = NULL) THEN signal missing keyword RETURN status ENDIF /* Check for null keywords (not required): */ IF (key->value = NULL) THEN RETURN status CALL convert_value(key) to convert value string to output format WRITE keyword = value line RETURN status } =========================================================================== int convert_value(key) FITS_keyword *key; { /* Routine to take the string currently containing the value, reformat it according to the type, and write it back into a value, still as a string. */ /* Call the appropriate reformatting routine: */ IF (key->type = "ALPHABET" | "ALPHANUMERIC" | "CHARACTER") THEN call format_string(key) ELSE IF (key->type = "DATE") THEN call format_date(key) ELSE IF (key->type = "TIME") THEN call format_time(key) ELSE IF (key->type = "REAL" | "EXPONENTIAL") THEN call format_real(key) ELSE IF (key->type = "INTEGER") THEN call format_integer(key) ELSE SIGNAL unrecognized type END IF RETURN status }