/* fitsio.c Library of FITS file read/write and header encode/decode routines */ /* Modifications: 30-Mar-92 GCE Copied from libfitsrw.c 02-Apr-92 GCE Added header line decoding routines 25-Aug-1992 GCE Improved error handling/error messages 04-Sep-1992 GCE Improvements in all routines 05-Sep-1992 GCE swap_bytes no longer mallocs 07-Sep-1992 GCE Added fits_head_bbso as a standard function 30-Oct-1997 JRV Modifications for year 2000 compliance 12-Nov-1997 JRV Modify to activate new DATE-OBS in 1999. 08-Jul-1998 JRV Rewrite for UNIX, remove VMS calls. 11-May-2000 JRV Add some functions to support SOHO-style keywords for H-alpha full disk observations. */ #include #include #include #include #include #include #include #include #include #include #include "fitsionew.h" /* Realloc fix */ #define xalloc(Loc, Bytes) \ ((Loc) == NULL ? malloc (Bytes) : realloc ((Loc), (Bytes))) /* Routines to construct 81-character (NUL terminated) FITS header lines. */ /* Arguments: headline - the header line string, keywd - the header line keyword (8 chars max), xval - the value to be inserted, comstr - a (47 char max) comment */ /* Enter a T/F logical value into a FITS header line */ void flinl0 (char headline[81], char *keywd, int xval, char *comstr) { sprintf (headline, "%-8.8s= %20c / %-47.47s", keywd, (xval) ? 'T' : 'F', comstr); } /* Enter a character string (18 chars max) into a FITS header line */ void flins0 (char headline[81], char *keywd, char *xval, char *comstr) { char tmpstr[21]; char quot[2] = "\'"; strcpy(tmpstr,quot); strcat(tmpstr,xval); strcat(tmpstr,quot); sprintf (headline, "%-8.8s= %-20.20s / %-47.47s", keywd, tmpstr, comstr); } /* Enter a longer character string (24 chars max) into a FITS header line */ void flinsl0 (char headline[81], char *keywd, char *xval, char *comstr) { char tmpstr[27]; char quot[2] = "\'"; strcpy(tmpstr,quot); strcat(tmpstr,xval); strcat(tmpstr,quot); sprintf (headline, "%-8.8s= %-26.26s / %-41.41s", keywd, tmpstr, comstr); } /* Enter an integer value into a FITS header line */ void flini0 (char headline[81], char *keywd, int xval, char *comstr) { sprintf (headline, "%-8.8s= %20d / %-47.47s", keywd, xval, comstr); } /* Enter a real (float) value into a FITS header */ void flinr0 (char headline[81], char *keywd, float xval, char *comstr) { sprintf (headline, "%-8.8s= %20.5f / %-47.47s", keywd, xval, comstr); } /* Encodes key info into FITS header */ /* Arguments: header - buffer of fits header lines ltype - logical type (SIMPLE), usually true bitpix - number of bits per pixel, usually 8 or 16 nax - number of axes, usually 2 naxes - int array for holding axis values bzero - zero point for scaling bscale - slope for scaling : rval = bz + bs * pix */ /* Returns line number of first blank line in header (which is also number of lines in header) */ int fits_head_encode (char (*header)[81], int ltype, int bitpix, int nax, int naxes[], float bzero, float bscale, char *bunit) { int line = 0, axis; char keywd[9]; flinl0 (header[line++], "SIMPLE", ltype, " "); flini0 (header[line++], "BITPIX", bitpix, " "); flini0 (header[line++], "NAXIS", nax, " "); for (axis = 0; axis < nax; axis++) { sprintf (keywd, "NAXIS%1d", axis + 1); flini0 (header[line++], keywd, naxes[axis], " "); } flinr0 (header[line++], "BZERO", bzero, " "); flinr0 (header[line++], "BSCALE", bscale, bunit); return line; } /* Convert a character obs type to a string for FITS header */ char *obsstr (char obstyp) { static char tempbuf[9]; switch (obstyp) { case 'V': strcpy (tempbuf, "VMG"); break; case 'Q': strcpy (tempbuf, "VMG-Q"); break; case 'U': strcpy (tempbuf, "VMG-U"); break; case 'D': strcpy (tempbuf, "DOPPL"); break; case 'I': strcpy (tempbuf, "VMG W/L"); break; case 'J': strcpy (tempbuf, "DIRECT"); break; case 'X': strcpy (tempbuf, "LINE MAP"); break; case 'O': strcpy (tempbuf, "OSL CCD"); break; case 'H': strcpy (tempbuf, "FULLDISK"); break; case 'K': strcpy (tempbuf, "FULLDISK"); break; case 'W': strcpy (tempbuf, "FULLDISK"); break; default: strcpy (tempbuf, "REGION"); break; } return tempbuf; } /* Encode BBSO-specific info into FITS header */ int fits_head_bbso (char (*header)[81], int iline, char *object, char *objcom, char obs, char *obscom, int seeing, int telpos[2], struct tm btim, char *timecom, char *tel, char *waveln, char *wavecom, char *obsrvr) { static char *seestr[] = { "VERY POOR", "POOR", "FAIR", "GOOD", "VERY GOOD" }; char tempbuf[9]; int tempyr; flins0 (header[iline++], "OBJECT", object, objcom); flins0 (header[iline++], "TYPE-OBS", obsstr (obs), obscom); flini0 (header[iline++], "SEEING", seeing, seestr[seeing]); flins0 (header[iline++], "CTYPE1", "E-W ARCS", "WEST POSITIVE"); flins0 (header[iline++], "CTYPE2", "N-S ARCS", "NORTH POSITIVE"); flini0 (header[iline++], "CRVAL1", telpos[0], " "); flini0 (header[iline++], "CRVAL2", telpos[1], " "); sprintf (tempbuf, "%02d:%02d:%02d", btim.tm_hour, btim.tm_min, btim.tm_sec); flins0 (header[iline++], "TIME-OBS", tempbuf, timecom); tempyr = 1900 + btim.tm_year; if (tempyr >= 1999) { sprintf (tempbuf, "%04d-%02d-%02d", tempyr, btim.tm_mon + 1, btim.tm_mday); } else { sprintf (tempbuf, "%02d/%02d/%02d", btim.tm_mday, btim.tm_mon + 1, btim.tm_year); } flins0 (header[iline++], "DATE-OBS", tempbuf, " "); flins0 (header[iline++], "ORIGIN", "BBSO", "BIG BEAR LAKE, CA"); flins0 (header[iline++], "TELESCOP", tel, " "); flins0 (header[iline++], "WAVELNTH", waveln, wavecom); flins0 (header[iline++], "OBSERVER", obsrvr, " "); return iline; } /* Encode Singer-specific info into FITS header */ int fits_head_singer (char (*header)[81], int iline, char *object, char *objcom, char obs, char *obscom, int seeing, int telpos[2], struct tm btim, char *timecom, char *tel, char *waveln, char *wavecom, char *obsrvr) { static char *seestr[] = { "VERY POOR", "POOR", "FAIR", "GOOD", "VERY GOOD" }; char tempbuf[9], dobsbuf[25]; int tempyr; flins0 (header[iline++], "OBJECT", object, objcom); flins0 (header[iline++], "TYPE-OBS", obsstr (obs), obscom); flini0 (header[iline++], "SEEING", seeing, seestr[seeing]); flinr0 (header[iline++], "CRPIX1", 1016.5, " "); flinr0 (header[iline++], "CRPIX2", 1016.5, " "); flinr0 (header[iline++], "CDELT1", 1.0544, " "); flinr0 (header[iline++], "CDELT2", 1.0544, " "); flins0 (header[iline++], "CTYPE1", "ARCSEC", "WEST POSITIVE"); flins0 (header[iline++], "CTYPE2", "ARCSEC", "NORTH POSITIVE"); flini0 (header[iline++], "CRVAL1", telpos[0], " "); flini0 (header[iline++], "CRVAL2", telpos[1], " "); sprintf (tempbuf, "%02d:%02d:%02d", btim.tm_hour, btim.tm_min, btim.tm_sec); flins0 (header[iline++], "TIME-OBS", tempbuf, timecom); tempyr = 1900 + btim.tm_year; sprintf (tempbuf, "%04d-%02d-%02d", tempyr, btim.tm_mon + 1, btim.tm_mday); sprintf (dobsbuf, "%04d-%02d-%02dT%02d:%02d:%02d.000Z", tempyr, btim.tm_mon + 1, btim.tm_mday, btim.tm_hour, btim.tm_min, btim.tm_sec); flins0 (header[iline++], "DATE-OBS", tempbuf, " "); flinsl0 (header[iline++], "DATE_OBS", dobsbuf, " "); flins0 (header[iline++], "ORIGIN", "BBSO", "BIG BEAR LAKE, CA"); flins0 (header[iline++], "TELESCOP", tel, " "); flins0 (header[iline++], "WAVELNTH", waveln, wavecom); flins0 (header[iline++], "OBSERVER", obsrvr, " "); return iline; } /* Encode Kanzelhoehe-specific info into FITS header */ int fits_head_kanz (char (*header)[81], int iline, char *object, char *objcom, char obs, char *obscom, int seeing, int telpos[2], struct tm btim, char *timecom, char *tel, char *waveln, char *wavecom, char *obsrvr) { static char *seestr[] = { "VERY POOR", "POOR", "FAIR", "GOOD", "VERY GOOD" }; char tempbuf[9], dobsbuf[25]; int tempyr; flins0 (header[iline++], "OBJECT", object, objcom); flins0 (header[iline++], "TYPE-OBS", obsstr (obs), obscom); flini0 (header[iline++], "SEEING", seeing, seestr[seeing]); flinr0 (header[iline++], "CRPIX1", 1016.5, " "); flinr0 (header[iline++], "CRPIX2", 1016.5, " "); /* note that this value (1.0544) will need to be changed to the correct one for the Kanzelhoehe optical configuration */ flinr0 (header[iline++], "CDELT1", 1.0544, " "); flinr0 (header[iline++], "CDELT2", 1.0544, " "); flins0 (header[iline++], "CTYPE1", "ARCSEC", "WEST POSITIVE"); flins0 (header[iline++], "CTYPE2", "ARCSEC", "NORTH POSITIVE"); flini0 (header[iline++], "CRVAL1", telpos[0], " "); flini0 (header[iline++], "CRVAL2", telpos[1], " "); sprintf (tempbuf, "%02d:%02d:%02d", btim.tm_hour, btim.tm_min, btim.tm_sec); flins0 (header[iline++], "TIME-OBS", tempbuf, timecom); tempyr = 1900 + btim.tm_year; sprintf (tempbuf, "%04d-%02d-%02d", tempyr, btim.tm_mon + 1, btim.tm_mday); sprintf (dobsbuf, "%04d-%02d-%02dT%02d:%02d:%02d.000Z", tempyr, btim.tm_mon + 1, btim.tm_mday, btim.tm_hour, btim.tm_min, btim.tm_sec); flins0 (header[iline++], "DATE-OBS", tempbuf, " "); flinsl0 (header[iline++], "DATE_OBS", dobsbuf, " "); flins0 (header[iline++], "ORIGIN", "KANZELHOEHE", "TREFFEN, OESTERREICH"); flins0 (header[iline++], "TELESCOP", tel, " "); flins0 (header[iline++], "WAVELNTH", waveln, wavecom); flins0 (header[iline++], "OBSERVER", obsrvr, " "); return iline; } /* Decode information from FITS header lines */ /* Delete leading and trailing spaces from a string; used in extracting strings from header lines */ char *delspace (char *buffer) { short length; char white[3]; char *bj; short i; white[0] = ' '; white[1] = (char) 9; white[2] = '\0'; bj = buffer; bj += strspn(buffer, white); length = strlen(bj); for (i = length - 1; i > 0; i--) { if (*(bj + i) != ' ' && *(bj + i) != (char) 9) break; } *(bj + i + 1) = '\0'; return bj; } /* Searches through a FITS header to find the line with a given keyword; the line is parsed for the strings containing the value and comment */ /* Returns line number on success, -1 if line not found. */ int fhedr_parse (char header[][81], int maxln, char *keywd, char **value, char **comment) { static char tmpstr[81]; char *keystr, *valstr, *comstr; int line = 0; /* Loop until end of header reached */ while (line < maxln && strncmp (header[line], "END ", 8) != 0) { /* Copy header line to temporary string */ strcpy (tmpstr, header[line]); /* Parse off keyword with equal sign */ keystr = strtok (tmpstr, "="); /* If equal sign found, compare keyword found with search keyword */ if (keystr && strcmp (keywd, delspace (keystr)) == 0) { /* If keywords match, rest of line contains value */ valstr = strtok (NULL, "\0"); /* Parse off comment by finding LAST slash */ comstr = strrchr (valstr, '/'); if (comstr != NULL) { /* Replace slash with NULL and trim comment */ *comstr = '\0'; *comment = delspace (comstr + 1); } else *comment = NULL; /* Trim value and return line number */ *value = delspace (valstr); return line; } line++; } /* Header line not found */ return -1; } /* Extract values from FITS header */ /* Arguments: header - buffer of header lines maxln - number of lines in FITS header keywd - keyword to search for xval - pointer to value in header line comstr - storage for comment string from header line */ /* Note that strings must be valid pointers (or NULL, for comstr) */ /* These routines return line number if OK, -1 if keyword not found */ /* Extract a logical value */ int freadl (char header[][81], int maxln, char *keywd, int *xval, char *comstr) { char *valstr, *tmpstr, tf = '\0'; int line; line = fhedr_parse (header, maxln, keywd, &valstr, &tmpstr); if (line < 0) return -1; /* Parse value string for T/F character */ sscanf (valstr, " %c", &tf); *xval = (tf == 'T'); /* Copy comment string to permanent storage */ if (tmpstr && comstr) strcpy (comstr, tmpstr); return line; } /* Extract an integer value */ int freadi (char header[][81], int maxln, char *keywd, int *xval, char *comstr) { char *valstr, *tmpstr; int line; line = fhedr_parse (header, maxln, keywd, &valstr, &tmpstr); if (line < 0) return -1; *xval = atoi (valstr); if (tmpstr && comstr) strcpy (comstr, tmpstr); return line; } /* Extract a real (float) value */ int freadr (char header[][81], int maxln, char *keywd,float *xval,char *comstr) { char *valstr, *tmpstr; int line; line = fhedr_parse (header, maxln, keywd, &valstr, &tmpstr); if (line < 0) return -1; *xval = atof (valstr); if (tmpstr && comstr) strcpy (comstr, tmpstr); return line; } /* Extract a string value */ int freads (char header[][81], int maxln, char *keywd, char *xval,char *comstr) { char *valstr, *tmpstr; int line; line = fhedr_parse (header, maxln, keywd, &valstr, &tmpstr); if (line < 0) return -1; /* Take care of comment first */ if (tmpstr && comstr) strcpy (comstr, tmpstr); /* Find the string between the single quotes */ /* Skip past first single quote, if any, and NUL the last single quote */ tmpstr = strchr (valstr, '\''); if (tmpstr) valstr = tmpstr + 1; tmpstr = strrchr (valstr, '\''); if (tmpstr) *tmpstr = '\0'; /* Copy the string into the return buffer, trimming spaces */ strcpy (xval, delspace (valstr)); return line; } /* Decodes key information from FITS header; assumes two axes */ /* Arguments: header - block of header lines maxln - number of lines in header bitpix - bits per pixel in image (currently 8 or 16) naxes - axes of image */ /* Returns 0 if OK, negative number on error indicating keyword */ int fits_head_decode (char (*header)[81], int maxln, int *bitpix, int naxes[2]) { int ltype, nax; if (freadl (header, maxln, "SIMPLE", <ype, NULL) < 0) return -11; if (ltype != 1) return -11; if (freadi (header, maxln, "BITPIX", bitpix, NULL) < 0) return -12; if (*bitpix != 8 && *bitpix != 16) return -12; if (freadi (header, maxln, "NAXIS", &nax, NULL) < 0) return -13; if (nax != 2) return -13; if (freadi (header, maxln, "NAXIS1", &naxes[0], NULL) < 0) return -14; if (naxes[0] <= 0) return -14; if (freadi (header, maxln, "NAXIS2", &naxes[1], NULL) < 0) return -15; if (naxes[1] <= 0) return -15; return 0; } /* Decode some useful information from header and filename */ void fits_bbso_decode (char header[][81], int maxln, struct tm *ptime, int pcoords[2], char *filename, char *obs, int *n_integr, char *icode) { char tmpstr[48]; char itmpcode[48]; char *cindex; char ictchar; unsigned short length; int lendateobs, tempyr; int i, j; /* Extract off date and time */ if (freads (header, maxln, "TIME-OBS", tmpstr, NULL) >= 0) sscanf (tmpstr, "%d:%d:%d", &ptime->tm_hour, &ptime->tm_min, &ptime->tm_sec); if (freads (header, maxln, "DATE-OBS", tmpstr, NULL) >= 0) { lendateobs = strlen(tmpstr); if (lendateobs == 8) { sscanf (tmpstr, "%d/%d/%d",&ptime->tm_mday,&ptime->tm_mon, &ptime->tm_year); } else { sscanf (tmpstr,"%d-%d-%d",&tempyr,&ptime->tm_mon,&ptime->tm_mday); ptime->tm_year = tempyr - 1900; } ptime->tm_mon--; /* Convert to 0-11 */ } /* Extract off coordinates */ freadi (header, maxln, "CRVAL1", &pcoords[0], NULL); freadi (header, maxln, "CRVAL2", &pcoords[1], NULL); /* parse observation type off filename */ length = strlen (filename); cindex = strrchr(filename, '/'); if (cindex) { *obs = *(cindex + 1); } else { *obs = filename[0]; } /* Get number of frames (or exposure time) and set code */ if (*obs == 'O' || *obs == 'L') { /* OSL image; get exposure time, summing mode, and wavelength */ freadi (header, maxln, "EXPOSURE", n_integr, NULL); freads (header, maxln, "SUMMING", icode, NULL); tmpstr[0] = '\0'; freads (header, maxln, "WAVELNTH", tmpstr, NULL); /* Trim the spaces out of the summing string and concat with wavelength */ length = strlen (icode); j = 0; for (i = 0; i < length; i++) { ictchar = icode[i]; if (!(isspace(ictchar))) { itmpcode[j] = toupper(ictchar); j++; } } strcpy(icode,itmpcode); strcat(icode," "); strcat(icode,tmpstr); } else if (*obs == 'C' || *obs == 'F') { /* Region image; get telescope into "icode" */ freads (header, maxln, "TELESCOP", icode, NULL); } else { /* Get the VMG-type stuff */ freadi (header, maxln, "FRAMES", n_integr, NULL); /* Strictly a temporary code */ strcpy (icode, "08"); } } /* Swap bytes of 16-bit FITS buffer for read/write */ void swap_bytes (unsigned char *buffer, int size) { int i; unsigned char temp; for (i = 0; i < size; i+=2) { temp = buffer[i]; buffer[i] = buffer[i + 1]; buffer[i + 1] = temp; } } /* Read in 2-D FITS image - Assumes picture has two axes */ /* Returns 0 on success, -1 on error */ int fits_read (char *filename, char (**header)[81], int *maxln, void **picture, int naxes[2], int *bitpix) { char blkbuf[2880], (*headbuf)[81] = NULL; int fldesc, bytesleft, bytesread; unsigned char *picpos; fldesc = open (filename, O_RDONLY, 0); if (fldesc < 0) return -1; /* Read in records of header. Allocate and copy header lines. */ *maxln = 0; do { if (*maxln % 36 == 0) { /* Get a fresh 2880 byte block of data */ bytesleft = 2880; while (bytesleft) { bytesread = read (fldesc, blkbuf + 2880 - bytesleft, bytesleft); if (bytesread <= 0) { return - 1; } bytesleft -= bytesread; } /* allocate 36 blank header lines (one block) to hold data */ headbuf = (char (*)[81]) xalloc (headbuf, (*maxln + 36) * 81); if (headbuf == NULL) return -1; } /* Copy header line from read buffer to header buffer */ memcpy (headbuf[*maxln], blkbuf + (*maxln % 36) * 80, 80); headbuf[*maxln][80] = '\0'; } /* Check for end, and increment maxln to next line */ while (strncmp (headbuf[(*maxln)++], "END ", 8) != 0); *header = headbuf; /* Decode header to find picture size */ bytesread = fits_head_decode (headbuf, *maxln, bitpix, naxes); if (bytesread < 0) { return -1; } /* Allocate picture memory */ bytesleft = naxes[0] * naxes[1] * (*bitpix / 8); *picture = (unsigned char *) malloc (bytesleft); if (*picture == NULL) return -1; /* Read in picture */ picpos = *picture; while (bytesleft) { bytesread = read (fldesc, picpos, bytesleft); if (bytesread <= 0) { return - 1; } picpos += bytesread; bytesleft -= bytesread; } /* Swap bytes if 16 bit picture */ if (*bitpix == 16) swap_bytes ((void *) (*picture), naxes[0] * naxes[1] * 2); if (close (fldesc) != 0) return -1; return 0; } /* Write out 2-D FITS image - Assumes picture has two axes */ /* Return 0 on success, -1 on error */ int fits_write (char *filename, char (*header)[81], int maxln, void *picture, int naxes[2], int bitpix) { char blkbuf[2880], allocstr[15]; int fldesc, line = 0, numbytes, hdr_fullbytes, hdr_bytesleft, pic_fullbytes, pic_bytesleft; /* Calculate total bytes for file to allocate blocks */ numbytes = naxes[0] * naxes[1] * (bitpix/8); pic_fullbytes = (numbytes / 2880) * 2880; pic_bytesleft = numbytes % 2880; numbytes = maxln * 80; hdr_fullbytes = (numbytes / 2880) * 2880; hdr_bytesleft = numbytes % 2880; numbytes = hdr_fullbytes + pic_fullbytes + ((pic_bytesleft > 0) + (hdr_bytesleft > 0)) * 2880; /* Create string containing allocated block quantity */ sprintf (allocstr, "alq=%d", numbytes / 512 + (numbytes % 512 > 0)); /* Create file */ fldesc = creat(filename,S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); if (fldesc < 0) return -1; /* Write out header */ while (line < maxln) { /* Copy header lines into buffer, and write out buffer when full */ memcpy (blkbuf + (line % 36) * 80, header[line], 80); if (++line % 36 == 0 && write (fldesc, blkbuf, 2880) != 2880) return -1; } if (hdr_bytesleft) { /* Fill rest of buffer with spaces and write */ memset (blkbuf + hdr_bytesleft, ' ', 2880 - hdr_bytesleft); if (write (fldesc, blkbuf, 2880) != 2880) return -1; } /* Write out complete picture records */ line = 0; if (bitpix == 16) while (line < pic_fullbytes) { /* Write out full records one at a time, swapping each record */ memcpy (blkbuf, (unsigned char *) picture + line, 2880); swap_bytes (blkbuf, 2880); if (write (fldesc, blkbuf, 2880) != 2880) return -1; line += 2880; } else if (pic_fullbytes) { /* Write out all full records at once */ if (write (fldesc, picture, pic_fullbytes) != pic_fullbytes) return -1; } if (pic_bytesleft) { /* If there is an incomplete picture record, fill and write it */ memcpy (blkbuf, (unsigned char *) picture + pic_fullbytes, pic_bytesleft); if (bitpix == 16) swap_bytes (blkbuf, pic_bytesleft); memset (blkbuf + pic_bytesleft, 0, 2880 - pic_bytesleft); if (write (fldesc, blkbuf, 2880) != 2880) return -1; } if (close (fldesc) != 0) return -1; return 0; }