/*-------------------------------------------------------------------------*/ /** @file tfits.c @author Y. Jung @date July 1999 @version $Revision: 1.13 $ @brief FITS table handling */ /*--------------------------------------------------------------------------*/ /* $Id: tfits.c,v 1.13 2002/01/10 08:53:08 ndevilla Exp $ $Author: ndevilla $ $Date: 2002/01/10 08:53:08 $ $Revision: 1.13 $ */ /*--------------------------------------------------------------------------- Includes ---------------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "tfits.h" #include "fits_std.h" #include "byteswap.h" #include "simple.h" #include "t_iso8601.h" #include "fits_rw.h" #include "fits_md5.h" #include "xmemory.h" #include "qerror.h" /*--------------------------------------------------------------------------- Function prototypes ---------------------------------------------------------------------------*/ static int qfits_table_append_xtension(FILE *, qfits_table *, void **) ; static int qfits_table_interpret_type(char *, int *, char *, int) ; static char * qfits_strstrip(char *); /*--------------------------------------------------------------------------- Function codes ---------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /** @brief Remove blanks at the beginning and the end of a string. @param s String to parse. @return ptr to statically allocated string. This function returns a pointer to a statically allocated string, which is identical to the input string, except that all blank characters at the end and the beg. of the string have been removed. Do not free or modify the returned string! Since the returned string is statically allocated, it will be modified at each function call (not re-entrant). */ /*--------------------------------------------------------------------------*/ static char * qfits_strstrip(char * s) { static char l[ASCIILINESZ+1]; char * last ; if (s==NULL) return NULL ; while (isspace((int)*s) && *s) s++; memset(l, 0, ASCIILINESZ+1); strcpy(l, s); last = l + strlen(l); while (last > l) { if (!isspace((int)*(last-1))) break ; last -- ; } *last = (char)0; return (char*)l ; } /*-------------------------------------------------------------------------*/ /** @brief Identify a file as containing a FITS table in extension. @param filename Name of the FITS file to examine. @param xtnum Extension number to check (starting from 1). @return int 1 if the extension contains a table, 0 else. Examines the requested extension and identifies the presence of a FITS table. */ /*--------------------------------------------------------------------------*/ int qfits_is_table(char * filename, int xtnum) { char * value ; int ttype ; ttype = QFITS_INVALIDTABLE ; value = qfits_query_ext(filename, "XTENSION", xtnum); if (value==NULL) return ttype ; value = qfits_pretty_string(value); if (!strcmp(value, "TABLE")) { ttype = QFITS_ASCIITABLE; } else if (!strcmp(value, "BINTABLE")) { ttype = QFITS_BINTABLE; } return ttype; } /*-------------------------------------------------------------------------*/ /** @brief Read a FITS extension. @param filename Name of the FITS file to examine. @param xtnum Extension number to read (starting from 1). @return Pointer to newly allocated qfits_table structure. Read a FITS table from a given file name and extension, and return a newly allocated qfits_table structure. This structure contains the following informations: - Name of the FITS file associated to table - Table type - Number of columns in table - Array of qfits_col objects The table type is an int taking the following values: - QFITS_BINTABLE for binary tables - QFITS_ASCIITABLE for ascii tables */ /*--------------------------------------------------------------------------*/ qfits_table * qfits_table_open(char * filename, int xtnum) { qfits_table * tload ; qfits_col * curr_col ; int nb_raws ; char * str_value ; char keyword[FITSVALSZ] ; int offset_jump ; int offset_beg ; int col_pos ; /* For ASCII tables */ int next_col_pos ; /* For ASCII tables */ int i ; /* See if 'filename' is a fits file */ if (is_fits_file(filename) != 1) { qfits_error("[%s] is not FITS", filename) ; return NULL ; } /* Allocate output structure */ tload = malloc(sizeof(qfits_table)) ; strcpy(tload->filename, filename) ; tload->col = NULL ; /* Identify a table and get the table type : ASCII or BIN */ if ((tload->tab_t = qfits_is_table(filename, xtnum))==0) { qfits_error("[%s] extension %d is not a table", filename, xtnum) ; qfits_table_close(tload); return NULL ; } /* Get number of columns and allocate them: nc <-> TFIELDS */ if ((str_value = qfits_query_ext(filename, "TFIELDS", xtnum)) == NULL) { qfits_error("cannot read TFIELDS in [%s]:[%d]", filename, xtnum) ; return NULL ; } tload->nc = atoi(str_value) ; tload->col = malloc(tload->nc * sizeof(qfits_col)) ; /* Get the number of raws */ if ((str_value = qfits_query_ext(filename, "NAXIS2", xtnum)) == NULL) { qfits_error("cannot read NAXIS2 in [%s]:[%d]", filename, xtnum) ; qfits_table_close(tload); return NULL ; } nb_raws = atoi(str_value) ; /* Loop on all columns and get column descriptions */ offset_jump = 0 ; for (i=0 ; inc ; i++) { curr_col = &(tload->col[i]) ; /* A column is a priori readable */ curr_col->readable = 1 ; /* Initialize */ curr_col->zero_present = 0 ; curr_col->scale_present = 0 ; /* nelem <-> NAXIS2 */ curr_col->nelem = nb_raws ; /* tlabel <-> TTYPE */ sprintf(keyword, "TTYPE%d", i+1) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum)) == NULL) { qfits_error("cannot read [%s] in [%s]:[%d]", keyword, filename, xtnum); qfits_table_close(tload); return NULL ; } strcpy(curr_col->tlabel, qfits_pretty_string(str_value)) ; /* tunit <-> TUNIT */ sprintf(keyword, "TUNIT%d", i+1) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum)) == NULL) { strcpy(curr_col->tunit, "") ; } else { strcpy(curr_col->tunit, qfits_pretty_string(str_value)) ; } /* disp <-> TDISP */ sprintf(keyword, "TDISP%d", i+1) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum)) == NULL) { strcpy(curr_col->disp, "") ; } else { strcpy(curr_col->disp, qfits_pretty_string(str_value)) ; } /* nullval <-> TNULL */ sprintf(keyword, "TNULL%d", i+1) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum)) != NULL) { strcpy(curr_col->nullval, str_value) ; } else (curr_col->nullval)[0] = (char)0 ; /* zero <-> TZERO */ sprintf(keyword, "TZERO%d", i+1) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum)) != NULL) { curr_col->zero = (float)atof(str_value) ; curr_col->zero_present = 1 ; } else { curr_col->zero = (float)0.0 ; } /* scale <-> TSCAL */ sprintf(keyword, "TSCAL%d", i+1) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum)) != NULL) { curr_col->scale = (float)atof(str_value) ; curr_col->scale_present = 1 ; } else { curr_col->scale = (float)1.0 ; } /* atom_size, natoms, atom_type <-> TFORM */ sprintf(keyword, "TFORM%d", i+1) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum))==NULL) { qfits_error("cannot read [%s] in [%s]:[%d]", keyword, filename, xtnum); qfits_table_close(tload); return NULL ; } /* Interpret the type in header */ if (qfits_table_interpret_type(qfits_pretty_string(str_value), &(curr_col->atom_nb), &(curr_col->atom_type), tload->tab_t) == -1) { qfits_error("cannot interpret the type: %s", str_value) ; qfits_table_close(tload) ; return NULL ; } if (tload->tab_t == QFITS_BINTABLE) { curr_col->ascii_size = 0 ; curr_col->natoms = curr_col->atom_nb ; switch (curr_col->atom_type) { case 'A': curr_col->atom_size = curr_col->ascii_size = curr_col->atom_nb ; curr_col->natoms = 1; break ; case 'L': curr_col->atom_size = 1 ; break ; case 'I': curr_col->atom_size = 2 ; break ; case 'J': case 'E': curr_col->atom_size = 4 ; break ; case 'C': curr_col->atom_size = 4 ; curr_col->natoms *= 2 ; break ; case 'D': curr_col->atom_size = 8 ; break ; case 'M': curr_col->atom_size = 8 ; curr_col->natoms *= 2 ; break ; case 'X': curr_col->atom_size = 1 ; curr_col->natoms = (int)((curr_col->natoms - 1)/ 8) + 1 ; break ; case 'B': curr_col->atom_size = 1 ; break ; case 'P': curr_col->atom_size = 4 ; curr_col->natoms *= 2 ; break ; default: qfits_error("unrecognized type : [%c]", curr_col->atom_type) ; curr_col->readable = 0 ; break ; } } else if (tload->tab_t == QFITS_ASCIITABLE) { /* natoms is always 1 */ curr_col->natoms = 1 ; curr_col->atom_nb = 1 ; /* ascii_size <-> TBCOL and NAXIS1 */ sprintf(keyword, "TBCOL%d", i+1) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum))==NULL) { qfits_error("cannot read [%s] in [%s]", keyword, filename); qfits_table_close(tload); return NULL ; } col_pos = atoi(qfits_pretty_string(str_value)) ; if (i < tload->nc - 1) { sprintf(keyword, "TBCOL%d", i+2) ; if ((str_value=qfits_query_ext(filename, keyword, xtnum))==NULL) { qfits_error("cannot read [%s] in [%s]", keyword, filename) ; qfits_table_close(tload) ; return NULL ; } next_col_pos = atoi(qfits_pretty_string(str_value)) ; curr_col->ascii_size = (int)(next_col_pos - col_pos) ; } else if (i == tload->nc - 1) { /* Last column size in bytes is given through NAXIS1 */ strcpy(keyword, "NAXIS1"); if ((str_value=qfits_query_ext(filename, keyword, xtnum))==NULL) { qfits_error("cannot read [%s] in [%s]", keyword, filename) ; qfits_table_close(tload) ; return NULL ; } next_col_pos = atoi(qfits_pretty_string(str_value)) ; curr_col->ascii_size = (int)(next_col_pos - col_pos + 1) ; } /* atom_size */ switch (curr_col->atom_type) { case 'A': curr_col->atom_size = curr_col->ascii_size ; break ; case 'I': curr_col->atom_size = 4 ; break ; case 'F': case 'E': curr_col->atom_size = 4 ; break ; case 'D': curr_col->atom_size = 8 ; break ; default: qfits_error("unrecognized type : [%c]", curr_col->atom_type) ; curr_col->readable = 0 ; break ; } } else { qfits_error("unrecognized table type: [%d]", tload->tab_t) ; return NULL ; } /* calcul of the size of a row */ if (tload->tab_t == QFITS_BINTABLE) { offset_jump += curr_col->natoms * curr_col->atom_size ; } else if (tload->tab_t == QFITS_ASCIITABLE) { offset_jump += curr_col->ascii_size ; } } if (qfits_get_datinfo(filename, xtnum, &offset_beg, NULL)!=0) { qfits_error("cannot find data start in [%s]:[%d]", filename, xtnum); qfits_table_close(tload); return NULL ; } /* off_jmp and off_beg */ curr_col = tload->col ; for (i=1 ; i<=tload->nc ; i++) { curr_col->off_beg = offset_beg ; curr_col->off_jmp = offset_jump ; if (tload->tab_t == QFITS_BINTABLE){ offset_beg += curr_col->natoms * curr_col->atom_size ; curr_col++ ; } else if (tload->tab_t == QFITS_ASCIITABLE) { offset_beg += curr_col->ascii_size ; curr_col++ ; } } return tload ; } /*-------------------------------------------------------------------------*/ /** @brief Parse a FITS type @param str string read in the FITS header (e.g. TFORM value) @param nb pointer to the number @param type pointer to the type @param table_type Table type (BIN, ASCII, ...) @return 0 if ok, -1 otherwise This functions reads the input string and uses it to update nb and type */ /*--------------------------------------------------------------------------*/ static int qfits_table_interpret_type( char * str, int * nb, char * type, int table_type) { if (table_type == QFITS_BINTABLE) { if (sscanf(str, "%d%c", nb, type) == 0) { /* nb is 1 by default */ if (sscanf(str, "%c", type) == 0) { qfits_error("cannot interpret this type: %s", str) ; return -1 ; } *nb = 1 ; } } else if (table_type == QFITS_ASCIITABLE) { if (sscanf(str, "%c%d", type, nb) == 0) { qfits_error("cannot interpret this type: %s", str) ; return -1 ; } } else { qfits_error("unrecognized table type") ; return -1 ; } return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Free a FITS table and associated pointers @param t qfits_table to free @return void Frees all memory associated to a qfits_table structure. */ /*--------------------------------------------------------------------------*/ void qfits_table_close(qfits_table * t) { if (t==NULL) return ; if (t->nc>0) { if (t->col!=NULL) { free(t->col); } } free(t); return ; } /*-------------------------------------------------------------------------*/ /** @brief Extract data from a column in a FITS table @param th Allocated qfits_table @param colnum Number of the column to extract (from 0 to colnum-1) @return unsigned char array Extract a column from a FITS table and return the data as a bytes array. The returned array type and size are determined by the column object in the qfits_table. Returned array size in bytes is: col->nelem * col->natoms * col->atom_size Numeric types are correctly understood and byte-swapped if needed, to be converted to the local machine type. NULL values have to be handled by the caller */ /*--------------------------------------------------------------------------*/ unsigned char * qfits_query_column( qfits_table * th, int colnum) { char * start ; qfits_col * col ; int field_size ; unsigned char * array ; unsigned char * r ; unsigned char * inbuf ; int i ; /* Pointer to requested column */ col = th->col+colnum ; /* Test if column is readable */ if (col->readable == 0) { qfits_warning("column is not readable: %d", colnum+1) ; return NULL ; } /* Test if column is empty */ if (col->nelem * col->atom_size * col->natoms == 0) { qfits_warning("empty column: %d", colnum+1) ; return NULL ; } /* Compute the field size in bytes */ switch (th->tab_t) { case QFITS_BINTABLE: field_size = col->atom_size * col->natoms ; break ; case QFITS_ASCIITABLE: field_size = col->ascii_size ; break ; default: qfits_warning("unrecognized table type") ; return NULL ; } /* Load input file */ if ((start=falloc(th->filename, 0, NULL))==NULL) { qfits_error("cannot open table for query [%s]", th->filename); return NULL ; } /* Allocate data array */ array = malloc(col->nelem * field_size * sizeof(char)) ; /* Position the input pointer at the begining of the column data */ r = array ; inbuf = (unsigned char*)start + col->off_beg ; for (i=0 ; inelem ; i++) { /* Copy all atoms on this field into array */ memcpy(r, inbuf, field_size); r += field_size ; /* Jump to next line */ inbuf += col->off_jmp ; } free(start); /* SWAP the bytes if necessary */ #ifndef WORDS_BIGENDIAN if (th->tab_t == QFITS_BINTABLE) { /* Perform conversion according to type */ switch (col->atom_type) { /* 1 byte/elem: no conversion needed */ case 'L': case 'X': case 'A': case 'B': break ; /* 2 bytes/elem: conversion needed for endian-ness 16-bit integer */ case 'I': r = array ; for (i=0 ; inelem * col->natoms ; i++) { swap_bytes(r, 2); r += 2 ; } break ; /* 4 bytes/elem: conversion needed for endian-ness 32-bit integer */ case 'J': case 'C': case 'E': case 'P': r = array ; for (i=0 ; inelem * col->natoms ; i++) { swap_bytes(r, 4); r+=4 ; } break ; /* 8 bytes/elem: conversion needed for endian-ness Double */ /* precision floating point */ case 'D': case 'M': r = array ; for (i=0 ; inelem * col->natoms ; i++) { swap_bytes(r, 8) ; r+=8 ; } break ; default: qfits_error("unrecognized data type in table: [%c]", col->atom_type); free(array); return NULL ; } } #endif /* Return allocated and converted array */ return array ; } /*-------------------------------------------------------------------------*/ /** @brief Extract binary data from a column in a FITS table @param th Allocated qfits_table @param colnum Number of the column to extract (from 0 to colnum-1) @param null_value Value to return when a NULL value comes @return Pointer to void * Extract a column from a FITS table and return the data as a generic void* array. The returned array type and size are determined by the column object in the qfits_table. Returned array size in bytes is: col->nelem * col->atom_nb * col->atom_size NULL values are recognized and replaced by the specified value. */ /*--------------------------------------------------------------------------*/ void * qfits_query_column_data( qfits_table * th, int colnum, void * null_value) { void * out_array ; qfits_col * col ; unsigned char * in_array ; char * field ; unsigned char ucnull ; short snull ; int inull ; double dnull ; float fnull ; int i ; /* Initialize */ if (null_value == NULL) { inull = (int)0 ; snull = (short)0 ; ucnull = (unsigned char)0 ; fnull = (float)0.0 ; dnull = (double)0.0 ; } else { inull = *(int*)null_value ; snull = *(short*)null_value ; ucnull = *(unsigned char*)null_value ; fnull = *(float*)null_value ; dnull = *(double*)null_value ; } /* Pointer to requested column */ col = th->col+colnum ; /* Handle each type separately */ switch (th->tab_t) { case QFITS_ASCIITABLE: switch(col->atom_type) { case 'A': out_array = (char*)qfits_query_column(th, colnum) ; break ; case 'I': in_array = (unsigned char*)qfits_query_column(th, colnum) ; out_array = malloc(col->nelem*col->atom_size); field = malloc((col->ascii_size+1)*sizeof(char)) ; for (i=0 ; inelem ; i++) { /* Copy all atoms of the field into 'field' */ memcpy(field, &in_array[i*col->ascii_size], col->ascii_size); field[col->ascii_size]=(char)0 ; /* Write the data in out_array */ /* Test if a NULL val is encoutered */ if (!strcmp(col->nullval, qfits_strstrip(field))) { ((int*)out_array)[i*col->atom_nb] = inull ; } else { ((int*)out_array)[i*col->atom_nb] = (int)atoi(field) ; } } free(field) ; free(in_array) ; break ; case 'E': case 'F': in_array = (unsigned char*)qfits_query_column(th, colnum) ; out_array = malloc(col->nelem*col->atom_size); field = malloc((col->ascii_size+1)*sizeof(char)) ; for (i=0 ; inelem ; i++) { /* Copy all atoms of the field into 'field' */ memcpy(field, &in_array[i*col->ascii_size], col->ascii_size); field[col->ascii_size]=(char)0 ; /* Write the data in out_array */ /* Test if a NULL val is encoutered */ if (!strcmp(col->nullval, qfits_strstrip(field))) { ((float*)out_array)[i*col->atom_nb] = fnull ; } else { ((float*)out_array)[i*col->atom_nb] = (float)atof(field) ; } } free(field) ; free(in_array) ; break ; case 'D': in_array = (unsigned char*)qfits_query_column(th, colnum) ; out_array = malloc(col->nelem*col->atom_size); field = malloc((col->ascii_size+1)*sizeof(char)) ; for (i=0 ; inelem ; i++) { /* Copy all atoms of the field into 'field' */ memcpy(field, &in_array[i*col->ascii_size], col->ascii_size); field[col->ascii_size]=(char)0 ; /* Write the data in out_array */ /* Test if a NULL val is encoutered */ if (!strcmp(col->nullval, qfits_strstrip(field))) { ((double*)out_array)[i*col->atom_nb] = dnull ; } else { ((double*)out_array)[i*col->atom_nb]=(double)atof(field) ; } } free(field) ; free(in_array) ; break ; default: qfits_error("unrecognized data type: %c", col->atom_type) ; return NULL ; } break ; case QFITS_BINTABLE: switch(col->atom_type) { case 'A': case 'L': out_array = (char*)qfits_query_column(th, colnum) ; break ; case 'D': case 'M': out_array = (double*)qfits_query_column(th, colnum) ; break ; case 'E': case 'C': out_array = (float*)qfits_query_column(th, colnum) ; break ; case 'X': out_array = (unsigned char*)qfits_query_column(th, colnum) ; break ; case 'B': out_array = (unsigned char*)qfits_query_column(th, colnum) ; for (i=0 ; inelem * col->atom_nb ; i++) { if (atoi(col->nullval)== (int)((unsigned char*)out_array)[i]) { ((unsigned char*)out_array)[i] = ucnull ; } } break ; case 'I': out_array = (short*)qfits_query_column(th, colnum) ; for (i=0 ; inelem * col->atom_nb ; i++) { if (atoi(col->nullval)==(int)((short*)out_array)[i]) { ((short*)out_array)[i] = snull ; } } break ; case 'J': out_array = (int*)qfits_query_column(th, colnum) ; for (i=0 ; inelem * col->atom_nb ; i++) { if (atoi(col->nullval)==((int*)out_array)[i]) { ((int*)out_array)[i] = inull ; } } break ; case 'P': out_array = (int*)qfits_query_column(th, colnum) ; break ; default: qfits_error("unrecognized data type: %c", col->atom_type) ; return NULL ; } break ; default: qfits_warning("Table type not recognized") ; return NULL ; } return out_array ; } /*-------------------------------------------------------------------------*/ /** @brief Appends an extension header + data to a FITS file. @param outfile Pointer to (opened) file ready for writing. @param t Pointer to qfits_table @param data Table data to write @return int 0 if Ok, -1 otherwise Dumps a FITS table to a file. The whole table described by qfits_table, and the data arrays contained in 'data' are dumped to the file. An extension header is produced with all keywords needed to describe the table, then the data is dumped to the file. The output is then padded to reach a multiple of 2880 bytes in size. Notice that no main header is produced, only the extension part. */ /*--------------------------------------------------------------------------*/ static int qfits_table_append_xtension( FILE * outfile, qfits_table * t, void ** data) { qfits_header * fh ; qfits_col * curr_col ; char line[FITS_LINESZ+1] ; char str_val[FITS_LINESZ] ; char str_val2[FITS_LINESZ] ; char * date ; unsigned char ** array ; unsigned char * r ; unsigned char * inbuf ; int writt_char ; int nb_blanks ; int i, j ; /* Create fits header */ if ((fh=qfits_header_new()) == NULL) { qfits_error("cannot create new fits header") ; return -1 ; } /* Write extension header */ qfits_header_append(fh, "XTENSION", "BINTABLE", "FITS Binary Table Extension", NULL) ; qfits_header_append(fh, "BITPIX", "8", "8-bits character format", NULL) ; qfits_header_append(fh, "NAXIS", "2", "Tables are 2-D char. array", NULL) ; sprintf(str_val, "%d", (int)(t->col->off_jmp)) ; qfits_header_append(fh, "NAXIS1", str_val, "Characters in a row", NULL) ; sprintf(str_val, "%d", (int)(t->col->nelem)) ; qfits_header_append(fh, "NAXIS2", str_val, "No. of rows in table", NULL) ; qfits_header_append(fh, "PCOUNT", "0", "Parameter count always 0", NULL) ; qfits_header_append(fh, "GCOUNT", "1", "Group count always 1", NULL); sprintf(str_val, "%d", (int)(t->nc)) ; qfits_header_append(fh, "TFIELDS", str_val, "No. of col in table", NULL) ; /* Columns descriptors */ curr_col = t->col ; for (i=0 ; inc ; i++) { sprintf(str_val, "TFORM%d", i+1) ; sprintf(str_val2, "'%d%c'", curr_col->natoms, curr_col->atom_type) ; qfits_header_append(fh, str_val, str_val2, "Format of field", NULL) ; sprintf(str_val, "TTYPE%d", i+1) ; sprintf(str_val2, "%s", curr_col->tlabel) ; qfits_header_append(fh, str_val, str_val2, "Field label", NULL) ; sprintf(str_val, "TUNIT%d", i+1) ; sprintf(str_val2, "%s", curr_col->tunit) ; qfits_header_append(fh, str_val, str_val2, "Physical unit of field", NULL) ; curr_col++ ; } qfits_header_append(fh,"ORIGIN","ESO-ECLIPSE","Written by ECLIPSE", NULL); date = get_datetime_iso8601() ; sprintf(str_val, "'%s'", date) ; qfits_header_append(fh, "DATE", str_val, "[UTC] Date of writing", NULL) ; qfits_header_append(fh, "END", NULL, NULL, NULL); /* Write the fits header in the file */ if (qfits_header_dump(fh, outfile) == -1) { qfits_error("cannot dump header in file") ; qfits_header_destroy(fh) ; fclose(outfile) ; return -1 ; } qfits_header_destroy(fh) ; /* Write DATA */ array = malloc(t->nc*sizeof(unsigned char *)) ; curr_col = t->col ; for (i=0 ; inc ; i++) { /* Copy data from data to array (unsigned char) */ array[i] = malloc(curr_col->nelem * curr_col->atom_size * curr_col->natoms * sizeof(unsigned char)) ; r = (unsigned char *)array[i] ; inbuf = (unsigned char *)(data[i]) ; for (j=0 ; jnelem ; j++) { memcpy(r, inbuf, curr_col->natoms*curr_col->atom_size) ; inbuf += (curr_col->natoms * curr_col->atom_size) ; r += (curr_col->natoms * curr_col->atom_size) ; } /* Byte swapping needed if on a little-endian machine */ #ifndef WORDS_BIGENDIAN switch(curr_col->atom_type) { /* 1 byte/elem: no conversion needed */ case 'A': case 'X': case 'B': case 'L': break ; /* 2 bytes/elem: conversion needed for endian-ness 16-bit integer */ case 'I': r = array[i] ; for (j=0 ; jnelem * curr_col->natoms ; j++) { swap_bytes(r, 2); r += 2 ; } break ; /* 4 bytes/elem: conversion needed for endian-ness 32-bit integer */ case 'J': case 'P': case 'C': case 'E': r = array[i] ; for (j=0 ; jnelem * curr_col->natoms ; j++) { swap_bytes(r, 4); r+=4 ; } break ; /* 8 bytes/elem: conversion needed for endian-ness Double */ /* precision floating point */ case 'D': case 'M': r = array[i] ; for (j=0 ; jnelem * curr_col->natoms ; j++) { swap_bytes(r, 8) ; r+=8 ; } break ; default: qfits_error("unrecognized data type in table: [%c]",curr_col->atom_type); for (j=0 ; j<=i ; j++) { free(array[j]) ; } free(array); return -1 ; } #endif curr_col++ ; } /* Write to the outfile */ writt_char = 0 ; for (i=0 ; icol->nelem ; i++) { curr_col = t->col ; for (j=0 ; jnc ; j++) { r = array[j] + curr_col->atom_size*curr_col->natoms*i ; memcpy(line, r, curr_col->atom_size*curr_col->natoms) ; line[curr_col->atom_size*curr_col->natoms] = (char)0 ; fwrite(line, 1, curr_col->atom_size*curr_col->natoms, outfile) ; writt_char += curr_col->atom_size*curr_col->natoms ; curr_col++ ; } } /* Complete with blanks to FITS_BLOCK_SIZE characters */ nb_blanks = FITS_BLOCK_SIZE - ((writt_char)%FITS_BLOCK_SIZE) ; for (i=1 ; i<=nb_blanks ; i++) { fwrite(" ", 1, 1, outfile) ; } /* Free and return */ for(i=0 ; inc ; i++) { free(array[i]) ; } free(array) ; return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Save a table to a FITS file with a given FITS header. @param array Data array. @param table table @param filename Name of the image on disk. @param fh FITS header to insert in the output file. @return -1 in error case, 0 otherwise */ /*--------------------------------------------------------------------------*/ int qfits_save_table_hdrdump( void ** array, qfits_table * table, char * filename, qfits_header * fh) { FILE * outfile ; char * md5hash ; char md5card[81]; /* Open the destination file */ if ((outfile = fopen(filename, "w")) == NULL) { qfits_error("cannot open file [%s]", filename) ; return -1 ; } /* Write the fits header in the file 'outname' */ if (qfits_header_dump(fh, outfile) == -1) { qfits_error("cannot dump header in file") ; fclose(outfile) ; return -1 ; } /* Append the extension */ if (qfits_table_append_xtension(outfile, table, array) == -1) { qfits_error("in writing fits table") ; fclose(outfile) ; return -1 ; } fclose(outfile) ; /* Update MD5 keyword */ if (strcmp(filename, "STDOUT")) { md5hash = qfits_datamd5(filename); if (md5hash==NULL) { qfits_error("computing MD5 signature for output file %s", filename); return -1 ; } sprintf(md5card, "DATAMD5 = '%s' / MD5 checksum", md5hash); qfits_replace_card(filename, "DATAMD5", md5card); } return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief given a col and a row, find out the string to write for display @param table table structure @param col_id col id (0 -> nbcol-1) @param row_id row id (0 -> nrow-1) @param column array that contains the column data @param use_zero_scale Flag to use or not zero and scale @return the string to write */ /*--------------------------------------------------------------------------*/ char * qfits_table_field_to_string( qfits_table * table, int col_id, int row_id, void * column, int use_zero_scale) { char * str ; /* Test inputs */ if (column == NULL) return NULL ; switch (table->tab_t) { case QFITS_BINTABLE: str = qfits_bintable_field_to_string(table, col_id, row_id, column, use_zero_scale) ; break ; case QFITS_ASCIITABLE: str = qfits_asciitable_field_to_string(table, col_id, row_id, column, use_zero_scale) ; break ; default: qfits_error("Table type not recognized") ; return NULL ; break ; } return str ; } /*-------------------------------------------------------------------------*/ /** @brief given a col and a row, find out the string to write for display @param table table structure @param col_id col id (0 -> nbcol-1) @param row_id row id (0 -> nrow-1) @param column array that contains the column data @param use_zero_scale Flag to use or not zero and scale @return the string to write ASCII tables specific */ /*--------------------------------------------------------------------------*/ char * qfits_asciitable_field_to_string( qfits_table * table, int col_id, int row_id, void * column, int use_zero_scale) { qfits_col * col ; char * ccol ; int * icol ; float * fcol ; double * dcol ; char ctmp[512]; static char stmp[512]; /* Test inputs */ if (table->tab_t != QFITS_ASCIITABLE) return NULL ; if (column == NULL) return NULL ; /* Initialize */ ctmp[0] = (char)0 ; stmp[0] = (char)0 ; /* Set reference to the column */ col = table->col + col_id ; /* Get the string to write according to the type */ switch(col->atom_type) { case 'A': ccol = (char*)column ; strncpy(ctmp, ccol+col->atom_size * col->natoms * row_id, col->atom_size*col->natoms); ctmp[col->atom_size*col->natoms] = (char)0; strcpy(stmp, ctmp); break ; case 'I': icol = (int*)column ; /* Two cases: use col->zero and col->scale or not */ if (col->zero_present && col->scale_present && use_zero_scale) { sprintf(stmp, "%f", (float)(col->zero + (float)icol[row_id] * col->scale)) ; } else { sprintf(stmp, "%d", icol[row_id]); } break ; case 'E': case 'F': fcol = (float*)column ; /* Two cases: use col->zero and col->scale or not */ if (col->zero_present && col->scale_present && use_zero_scale) { sprintf(stmp, "%f", (float)(col->zero + fcol[row_id] * col->scale)) ; } else { sprintf(stmp, "%f", fcol[row_id]); } break ; case 'D': dcol = (double*)column ; /* Two cases: use col->zero and col->scale or not */ if (col->zero_present && col->scale_present && use_zero_scale) { sprintf(stmp, "%f", (float)(col->zero + (float)dcol[row_id] * col->scale)) ; } else { sprintf(stmp, "%g", dcol[row_id]) ; } break ; default: qfits_warning("Type not recognized") ; break ; } return stmp ; } /*-------------------------------------------------------------------------*/ /** @brief given a col and a row, find out the string to write for display @param table table structure @param col_id col id (0 -> nbcol-1) @param row_id row id (0 -> nrow-1) @param column array that contains the column data @param use_zero_scale Flag to use or not zero and scale @return the string to write BIN tables specific */ /*--------------------------------------------------------------------------*/ char * qfits_bintable_field_to_string( qfits_table * table, int col_id, int row_id, void * column, int use_zero_scale) { qfits_col * col ; unsigned char * uccol ; char * ccol ; int * icol ; short * scol ; float * fcol ; double * dcol ; char ctmp[512]; static char stmp[512]; int i ; /* Test inputs */ if (table->tab_t != QFITS_BINTABLE) return NULL ; if (column == NULL) return NULL ; /* Initialize */ ctmp[0] = (char)0 ; stmp[0] = (char)0 ; /* Set reference to the column */ col = table->col + col_id ; /* Get the string to write according to the type */ switch(col->atom_type) { case 'A': ccol = (char*)column ; strncpy(ctmp, ccol + col->atom_size * col->natoms * row_id, col->atom_size * col->natoms) ; ctmp[col->atom_size*col->natoms] = (char)0 ; strcpy(stmp, ctmp) ; break ; case 'B': uccol = (unsigned char*)column ; /* Two cases: use col->zero and col->scale or not */ if (col->zero_present && col->scale_present && use_zero_scale) { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%f, ", (float)(col->zero + (float)uccol[col->natoms*row_id+i] * col->scale)) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%f", (float)(col->zero + (float)uccol[col->natoms*row_id+col->natoms-1] * col->scale)) ; strcat(stmp, ctmp) ; } else { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%d, ", (int)uccol[col->natoms*row_id+i]) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp,"%d",(int)uccol[col->natoms*row_id+col->natoms-1]); strcat(stmp, ctmp) ; } break ; case 'D': case 'M': dcol = (double*)column ; /* Two cases: use col->zero and col->scale or not */ if (col->zero_present && col->scale_present && use_zero_scale) { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%g, ", (double)((double)col->zero + dcol[col->natoms*row_id+i] * (double)col->scale)) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%g", (double)((double)col->zero + dcol[col->natoms*row_id+col->natoms-1] * (double)col->scale)) ; strcat(stmp, ctmp) ; } else { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%g, ", dcol[col->natoms*row_id+i]) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%g", dcol[col->natoms*row_id+col->natoms-1]) ; strcat(stmp, ctmp) ; } break ; case 'E': case 'C': fcol = (float*)column ; /* Two cases: use col->zero and col->scale or not */ if (col->zero_present && col->scale_present && use_zero_scale) { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%f, ", (float)(col->zero + (float)fcol[col->natoms*row_id+i] * col->scale)) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%f", (float)(col->zero + (float)fcol[col->natoms*row_id+col->natoms-1] * col->scale)) ; strcat(stmp, ctmp) ; } else { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%f, ", fcol[col->natoms*row_id+i]) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%f", fcol[col->natoms*row_id+col->natoms-1]) ; strcat(stmp, ctmp) ; } break ; case 'I': scol = (short*)column ; /* Two cases: use col->zero and col->scale or not */ if (col->zero_present && col->scale_present && use_zero_scale) { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%f, ", (float)(col->zero + (float)scol[col->natoms*row_id+i] * col->scale)) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%f", (float)(col->zero + (float)scol[col->natoms*row_id+col->natoms-1] * col->scale)) ; strcat(stmp, ctmp) ; } else { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%d, ", (int)scol[col->natoms*row_id+i]) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%d",(int)scol[col->natoms*row_id+col->natoms-1]); strcat(stmp, ctmp) ; } break ; case 'J': icol = (int*)column ; /* Two cases: use col->zero and col->scale or not */ if (col->zero_present && col->scale_present && use_zero_scale) { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%f, ", (float)(col->zero + (float)icol[col->natoms*row_id+i] * col->scale)) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%f", (float)(col->zero + (float)icol[col->natoms*row_id+col->natoms-1] * col->scale)) ; strcat(stmp, ctmp) ; } else { /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%d, ", (int)icol[col->natoms*row_id+i]) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%d",(int)icol[col->natoms*row_id+col->natoms-1]); strcat(stmp, ctmp) ; } break ; case 'L': ccol = (char*)column ; /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%c, ", ccol[col->natoms*row_id+i]) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%c", ccol[col->natoms*row_id+col->natoms-1]) ; strcat(stmp, ctmp) ; break ; case 'X': uccol = (unsigned char*)column ; /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%d, ", uccol[col->natoms*row_id+i]) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%d", uccol[col->natoms*row_id+col->natoms-1]) ; strcat(stmp, ctmp) ; break ; case 'P': icol = (int*)column ; /* For each atom of the column */ for (i=0 ; inatoms-1 ; i++) { sprintf(ctmp, "%d, ", (int)icol[col->natoms*row_id+i]) ; strcat(stmp, ctmp) ; } /* Handle the last atom differently: no ',' */ sprintf(ctmp, "%d",(int)icol[col->natoms*row_id+col->natoms-1]); strcat(stmp, ctmp) ; break ; default: qfits_warning("Type not recognized") ; break ; } return stmp ; } /* vim: set ts=4 et sw=4 tw=75 */