/* 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 NewPtrs 07-Sep-1992 GCE Added fits_head_bbso as a standard function 28-Jun-1994 JRV Modifications for Mac */ #include #include #include #include #include #include #include "fitsio.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 (8 chars max) into a FITS header line */ void flins0 (char headline[81], char *keywd, char *xval, char *comstr) { sprintf (headline, "%-8.8s= '%-8.8s' / %-47.47s", keywd, xval, 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 'H': case 'K': 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]; 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); sprintf (tempbuf, "%2d/%2d/%2d", 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; } /* 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; } /* 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; } } /* 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]; FILE * fldesc; int 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 = fopen (filename, "wb"); if (fldesc == NULL) 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 && fwrite (blkbuf, sizeof (unsigned char), 2880, fldesc) != 2880) return -1; } if (hdr_bytesleft) { /* Fill rest of buffer with spaces and write */ memset (blkbuf + hdr_bytesleft, ' ', 2880 - hdr_bytesleft); if (fwrite (blkbuf, sizeof (unsigned char), 2880, fldesc) != 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); if (fwrite (blkbuf, sizeof (unsigned char), 2880, fldesc) != 2880) return -1; line += 2880; } else if (pic_fullbytes) { /* Write out all full records at once */ if (fwrite (picture, sizeof (unsigned char), pic_fullbytes, fldesc) != 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); memset (blkbuf + pic_bytesleft, 0, 2880 - pic_bytesleft); if (fwrite (blkbuf, sizeof (unsigned char), 2880, fldesc) != 2880) return -1; } if (fclose (fldesc) != 0) return -1; return 0; }