mirror of https://github.com/XEphem/XEphem.git
1186 lines
26 KiB
C
1186 lines
26 KiB
C
/* FITS file handling utilities.
|
|
* in memory, we store them in 2-bytes, unsigned, native byte oder.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <float.h>
|
|
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "ip.h"
|
|
|
|
extern void pm_set (int percentage);
|
|
|
|
static int writeFITSHeader (FImage *fip, int fd, char *errmsg);
|
|
static int fmtFITSHeader (FImage *fip, char **mem, char *errmsg);
|
|
static int findFImageVar (FImage *fip, char *name, char **rpp);
|
|
static void fmtLogicalFITS (FITSRow line, char *name, int value, char *cmt);
|
|
static void fmtIntFITS (FITSRow line, char *name, int value, char *cmt);
|
|
static void fmtRealFITS (FITSRow line, char *name, double value, int sigdig,
|
|
char *cmt);
|
|
static void fmtStringFITS (FITSRow line, char *name, char *value, char *cmt);
|
|
static void fmtENDFITS (FITSRow line);
|
|
static void fmtInlineComment (FITSRow line, char *cmt);
|
|
static void enFITSPixels (FImage *fip);
|
|
static void unFITSPixels (FImage *fip);
|
|
static int lendian (void);
|
|
static void FITS32to16 (FImage *fip);
|
|
|
|
static char simple[] = "SIMPLE = T";
|
|
|
|
/* write out the given fip to file descriptor fd.
|
|
* we assume fip->var contains all the fields we will need; all we do is
|
|
* add END and pad with blanks to a multiple of FITS_HROWS*FITS_HCOLS.
|
|
* N.B. we don _not_ modify the original var list.
|
|
* we assume fip->image points to an array of fip->sw * fip->sh
|
|
* unsigned short pixels, with the first pixel in the upper left of the scene.
|
|
* we force them into the form required by FITS before writing them out. we do
|
|
* this IN PLACE. if restore is 0, we DO NOT put the pixels back the way we
|
|
* found them.
|
|
* return 0 if ok, else put a short message into errmsg and return -1.
|
|
*/
|
|
int
|
|
writeFITS (fd, fip, errmsg, restore)
|
|
int fd;
|
|
FImage *fip;
|
|
char *errmsg;
|
|
int restore;
|
|
{
|
|
int nbytes, n, nw;
|
|
|
|
if (!fip->image) {
|
|
sprintf (errmsg, "No pixels :-(");
|
|
return (-1);
|
|
}
|
|
|
|
if (writeFITSHeader (fip, fd, errmsg) < 0)
|
|
return (-1);
|
|
|
|
/* format the pixels */
|
|
if (fip->bitpix == 16)
|
|
enFITSPixels(fip);
|
|
|
|
/* write the pixels.
|
|
* might be a pipe so keep writing until eof or error
|
|
*/
|
|
nbytes = fip->sw * fip->sh * (fip->bitpix/8);
|
|
for (nw = 0; nw < nbytes; nw += n) {
|
|
n = write (fd, fip->image+nw, nbytes-nw);
|
|
if (n <= 0) {
|
|
if (n < 0)
|
|
strcpy (errmsg, strerror(errno));
|
|
else
|
|
sprintf (errmsg, "Short write of FITS pixels");
|
|
if (restore && fip->bitpix == 16)
|
|
unFITSPixels (fip);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
/* ok */
|
|
|
|
if (restore && fip->bitpix == 16)
|
|
unFITSPixels (fip);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* write the given FITS object *fts which we malloc.
|
|
* we assume fip->var contains all the fields we will need; all we do is
|
|
* add END and pad with blanks to a multiple of FITS_HROWS*FITS_HCOLS.
|
|
* N.B. we don _not_ modify the original var list.
|
|
* we assume fip->image points to an array of fip->sw * fip->sh
|
|
* unsigned short pixels, with the first pixel in the upper left of the scene.
|
|
* we force them into the form required by FITS before writing them out. we do
|
|
* this IN PLACE. if restore is 0, we DO NOT put the pixels back the way we
|
|
* found them.
|
|
* return length of malloced array or -1 with reason in errmsg[]
|
|
*/
|
|
int
|
|
writeFITSmem (FImage *fip, char **fts, char errmsg[], int restore)
|
|
{
|
|
int nhdrbytes, npixbytes;
|
|
char *hdrmem, *ftsmem;
|
|
|
|
/* start with the header */
|
|
nhdrbytes = fmtFITSHeader (fip, &hdrmem, errmsg);
|
|
if (nhdrbytes < 0)
|
|
return (-1);
|
|
|
|
/* format the pixels */
|
|
if (fip->bitpix == 16)
|
|
enFITSPixels(fip);
|
|
|
|
/* add the pixels */
|
|
npixbytes = fip->sw * fip->sh * (fip->bitpix/8);
|
|
ftsmem = realloc (hdrmem, nhdrbytes + npixbytes);
|
|
if (!ftsmem) {
|
|
free (hdrmem);
|
|
sprintf (errmsg, "No memory for FITS pixels");
|
|
return (-1);
|
|
}
|
|
memcpy (ftsmem+nhdrbytes, fip->image, npixbytes);
|
|
|
|
/* ok */
|
|
|
|
if (restore && fip->bitpix == 16)
|
|
unFITSPixels (fip);
|
|
|
|
*fts = ftsmem;
|
|
return (nhdrbytes + npixbytes);
|
|
}
|
|
|
|
/* read the given FITS file, filling in fields in fip and mallocing as needed.
|
|
* all header lines are copied to fip->var UP TO BUT NOT INCLUDING "END".
|
|
* we assume the pixels in the file are in standard FITS format and we convert
|
|
* them to 2-byte unsigned native byte order with the first pixel in the upper
|
|
* left.
|
|
* return 0 if ok, else put a short message into errmsg and return -1.
|
|
*/
|
|
int
|
|
readFITS (fd, fip, errmsg)
|
|
int fd;
|
|
FImage *fip;
|
|
char *errmsg;
|
|
{
|
|
int s;
|
|
|
|
if (readFITSHeader (fd, fip, errmsg) < 0)
|
|
return (-1);
|
|
|
|
/* get some memory for the pixels */
|
|
fip->totbytes = fip->sw * fip->sh * abs(fip->bitpix/8);
|
|
fip->image = malloc (fip->totbytes);
|
|
if (!fip->image) {
|
|
sprintf (errmsg, "Could not malloc %d for pixels", fip->totbytes);
|
|
resetFImage (fip);
|
|
return (-1);
|
|
}
|
|
|
|
/* now read the pixels.
|
|
* might be a pipe so keep reading until eof or error
|
|
*/
|
|
for (fip->nbytes = 0; fip->nbytes < fip->totbytes; fip->nbytes += s) {
|
|
pm_set (fip->nbytes*100/fip->totbytes);
|
|
|
|
s= read (fd, fip->image + fip->nbytes, fip->totbytes - fip->nbytes);
|
|
if (s <= 0) {
|
|
if (s < 0)
|
|
strcpy (errmsg, strerror(errno));
|
|
else
|
|
sprintf (errmsg, "data is short");
|
|
resetFImage (fip);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
/* get byte order correct if 16 bits or handle -32 */
|
|
if (fip->bitpix == 16)
|
|
unFITSPixels (fip);
|
|
else if (fip->bitpix == -32)
|
|
FITS32to16 (fip);
|
|
|
|
/* ok */
|
|
return (0);
|
|
}
|
|
|
|
/* read the FITS file from memory in fits[nfits], filling in fields in fip
|
|
* and mallocing as needed.
|
|
* return 0 if ok, else put a short message into errmsg and return -1.
|
|
*/
|
|
int
|
|
readFITSmem (char *fits, int nfits, FImage *fip, char errmsg[])
|
|
{
|
|
char *row;
|
|
int nrows;
|
|
int sawend;
|
|
int n1, n2;
|
|
int i;
|
|
|
|
/* init fip */
|
|
initFImage (fip);
|
|
|
|
/* read header lines until we see END
|
|
* and have digested a whole number of blocks.
|
|
*/
|
|
nrows = 0;
|
|
sawend = 0;
|
|
row = fits;
|
|
do {
|
|
if (row+FITS_HCOLS > fits+nfits) {
|
|
sprintf (errmsg, "FITS header image memory is short");
|
|
goto err;
|
|
}
|
|
|
|
if (nrows == 0 && strncmp (row, simple, sizeof(simple)-1) != 0) {
|
|
sprintf (errmsg, "Not a simple FITS file");
|
|
goto err;
|
|
}
|
|
|
|
/* add the row to fip unless we've seen or see END */
|
|
if (!sawend && !(sawend = strncmp (row, "END", 3) == 0))
|
|
addFImageVar (fip, row);
|
|
|
|
nrows++;
|
|
row += FITS_HCOLS;
|
|
|
|
} while (!sawend || (nrows%FITS_HROWS));
|
|
|
|
/* confirm SIMPLE */
|
|
if (getLogicalFITS (fip, "SIMPLE", &i) < 0 || !i) {
|
|
sprintf (errmsg, "File must claim to be a SIMPLE image.");
|
|
goto err;
|
|
}
|
|
|
|
/* get size */
|
|
if (getIntFITS (fip, "BITPIX", &i) < 0 || (i != 16 && i != 8 && i != -32)) {
|
|
sprintf (errmsg, "BITPIX must be 8, 16 or -32.");
|
|
goto err;
|
|
}
|
|
fip->bitpix = i;
|
|
if (getNAXIS (fip, &n1, &n2, errmsg) < 0)
|
|
goto err;
|
|
fip->sw = n1;
|
|
fip->sh = n2;
|
|
|
|
/* get memory for the pixels */
|
|
fip->totbytes = fip->sw * fip->sh * abs(fip->bitpix/8);
|
|
if (row + fip->totbytes > fits + nfits) {
|
|
sprintf (errmsg, "FITS pixel image memory is short");
|
|
goto err;
|
|
}
|
|
fip->image = malloc (fip->totbytes);
|
|
if (!fip->image) {
|
|
sprintf (errmsg, "Could not malloc %d for pixels", fip->totbytes);
|
|
goto err;
|
|
}
|
|
|
|
/* now copy in the pixels */
|
|
memcpy (fip->image, row, fip->totbytes);
|
|
|
|
/* get byte order correct if 16 bits or handle -32 */
|
|
if (fip->bitpix == 16)
|
|
unFITSPixels (fip);
|
|
else if (fip->bitpix == -32)
|
|
FITS32to16 (fip);
|
|
|
|
/* ok */
|
|
return (0);
|
|
|
|
err:
|
|
resetFImage (fip);
|
|
return (-1);
|
|
}
|
|
|
|
/* read another chunk of pixels for the given FITS file, mallocing as needed.
|
|
* we assume the pixels in the file are in standard FITS format and we convert
|
|
* them to 2-byte unsigned native byte order with the first pixel in the upper
|
|
* left.
|
|
* return 0 if ok, else put a short message into errmsg and return -1.
|
|
* N.B. read header portion first using readFITSHeader().
|
|
*/
|
|
int
|
|
readIncFITS (fd, fip, errmsg)
|
|
int fd;
|
|
FImage *fip;
|
|
char *errmsg;
|
|
{
|
|
#define READCHUNK 4096
|
|
|
|
int s;
|
|
int n;
|
|
|
|
/* if first time, malloc all the pixel memory */
|
|
if (fip->totbytes == 0) {
|
|
fip->totbytes = fip->sw * fip->sh * abs(fip->bitpix/8);
|
|
fip->image = malloc (fip->totbytes);
|
|
if (!fip->image) {
|
|
sprintf (errmsg,"Could not malloc %d for pixels",fip->totbytes);
|
|
resetFImage (fip);
|
|
return (-1);
|
|
}
|
|
fip->nbytes = 0;
|
|
}
|
|
|
|
/* now read up to a chunk more of pixels. */
|
|
n = fip->totbytes - fip->nbytes;
|
|
if (n > READCHUNK)
|
|
n = READCHUNK;
|
|
s = read (fd, fip->image + fip->nbytes, n);
|
|
if (s <= 0) {
|
|
if (s < 0)
|
|
strcpy (errmsg, strerror(errno));
|
|
else
|
|
sprintf (errmsg, "FITS file short after %d bytes", fip->nbytes);
|
|
resetFImage (fip);
|
|
return (-1);
|
|
}
|
|
fip->nbytes += s;
|
|
|
|
/* if complete: get byte order correct if 16 bits or handle -32 */
|
|
if (fip->nbytes == fip->totbytes) {
|
|
if (fip->bitpix == 16)
|
|
unFITSPixels (fip);
|
|
else if (fip->bitpix == -32)
|
|
FITS32to16 (fip);
|
|
}
|
|
|
|
/* ok */
|
|
return (0);
|
|
|
|
#undef READCHUNK
|
|
}
|
|
|
|
/* read the given FITS file header into fip; we don't read the pixels.
|
|
* N.B. we call initFImage(fip) and also resetFImage(fip) if there is an error.
|
|
* return 0 if ok, else put message into errmsg and return -1.
|
|
*/
|
|
int
|
|
readFITSHeader (fd, fip, errmsg)
|
|
int fd;
|
|
FImage *fip;
|
|
char errmsg[];
|
|
{
|
|
FITSRow row;
|
|
int nrows;
|
|
int sawend;
|
|
int n1, n2;
|
|
int s;
|
|
int i;
|
|
|
|
initFImage (fip);
|
|
|
|
/* read header lines until we see END
|
|
* and have digested a whole number of blocks.
|
|
*/
|
|
nrows = 0;
|
|
sawend = 0;
|
|
do {
|
|
for (i = 0; i < sizeof(row); i += s) {
|
|
s = read (fd, row+i, sizeof(row)-i);
|
|
if (s <= 0) {
|
|
if (s < 0)
|
|
strcpy (errmsg, strerror(errno));
|
|
else
|
|
sprintf (errmsg, "FITS file header is short");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (nrows == 0 && strncmp (row, simple, sizeof(simple)-1) != 0) {
|
|
sprintf (errmsg, "Not a simple FITS file");
|
|
goto err;
|
|
}
|
|
|
|
nrows++;
|
|
|
|
/* add the row to fip unless we've seen or see END */
|
|
if (!sawend && !(sawend = strncmp (row, "END", 3) == 0))
|
|
addFImageVar (fip, row);
|
|
|
|
} while (!sawend || (nrows%FITS_HROWS));
|
|
|
|
/* crack the required fields into fip
|
|
* and check for required conditions
|
|
*/
|
|
|
|
if (getLogicalFITS (fip, "SIMPLE", &i) < 0 || !i) {
|
|
sprintf (errmsg, "File must claim to be a SIMPLE image.");
|
|
goto err;
|
|
}
|
|
|
|
if (getIntFITS (fip, "BITPIX", &i) < 0 || (i != 16 && i != 8 && i != -32)) {
|
|
sprintf (errmsg, "BITPIX must be 8, 16 or -32.");
|
|
goto err;
|
|
}
|
|
fip->bitpix = i;
|
|
|
|
if (getNAXIS (fip, &n1, &n2, errmsg) < 0)
|
|
goto err;
|
|
|
|
fip->sw = n1;
|
|
fip->sh = n2;
|
|
|
|
return (0);
|
|
|
|
err:
|
|
resetFImage (fip);
|
|
return (-1);
|
|
}
|
|
|
|
/* write a nominal FITS-format file of pixels file to fd.
|
|
* we assume pix points to an array of w*h unsigned short pixels, with the
|
|
* first pixel in the upper left of the scene. thus, we force them into the
|
|
* form required by FITS before writing them out. we do this IN PLACE. if
|
|
* restore is 0, we DO NOT put the pixels back the way we found them.
|
|
* return 0 if ok else -1 if error.
|
|
*/
|
|
int
|
|
writeSimpleFITS (fd, pix, w, h, restore)
|
|
int fd;
|
|
char *pix;
|
|
int w, h;
|
|
int restore;
|
|
{
|
|
char errmsg[1024];
|
|
FImage fimage;
|
|
|
|
initFImage (&fimage);
|
|
|
|
fimage.sw = w;
|
|
fimage.sh = h;
|
|
fimage.bitpix = 16;
|
|
fimage.image = pix;
|
|
|
|
setSimpleFITSHeader (&fimage);
|
|
|
|
return (writeFITS (fd, &fimage, errmsg, restore));
|
|
}
|
|
|
|
/* get NAXIS1 and NAXIS2 from fip.
|
|
* return 0 if ok else fill errmsg and return -1.
|
|
* we also require that if NAXISi, with i > 2, exist they be 1.
|
|
*/
|
|
int
|
|
getNAXIS (fip, n1p, n2p, errmsg)
|
|
FImage *fip;
|
|
int *n1p, *n2p;
|
|
char errmsg[];
|
|
{
|
|
int n;
|
|
int i;
|
|
|
|
if (getIntFITS (fip, "NAXIS", &n) < 0) {
|
|
sprintf (errmsg, "No NAXIS");
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/* check for higher dimensions */
|
|
for (i = 3; i <= n; i++) {
|
|
char naxisi[16];
|
|
int ni;
|
|
|
|
sprintf (naxisi, "NAXIS%d", i);
|
|
if (getIntFITS (fip, naxisi, &ni) < 0) {
|
|
sprintf (errmsg, "NAXIS=%d but no %s", n, naxisi);
|
|
return (-1);
|
|
}
|
|
if (ni != 1) {
|
|
sprintf (errmsg, "Require %s to be 1", naxisi);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
if (getIntFITS (fip, "NAXIS1", n1p) < 0) {
|
|
sprintf (errmsg, "No NAXIS1");
|
|
return (-1);
|
|
}
|
|
|
|
if (getIntFITS (fip, "NAXIS2", n2p) < 0) {
|
|
sprintf (errmsg, "No NAXIS2");
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* make a copy of old into new.
|
|
* if pixtoo copy pixel values too, else just leave new's new pix mem garbage.
|
|
*/
|
|
void
|
|
cloneFImage (FImage *new, FImage *old, int pixtoo)
|
|
{
|
|
int nbytes;
|
|
|
|
/* copy the basics */
|
|
*new = *old;
|
|
|
|
/* copy the var list */
|
|
nbytes = old->nvar * sizeof(FITSRow);
|
|
new->var = malloc (nbytes);
|
|
memcpy (new->var, old->var, nbytes);
|
|
|
|
/* malloc room for pixels */
|
|
new->image = malloc (old->totbytes);
|
|
|
|
/* copy pixels too if want */
|
|
if (pixtoo)
|
|
memcpy (new->image, old->image, old->totbytes);
|
|
}
|
|
|
|
/* malloc room at *mem and fill with header from fip.
|
|
* return size of header memory or -1 with reason in errmsg.
|
|
*/
|
|
static int
|
|
fmtFITSHeader (FImage *fip, char **mem, char *errmsg)
|
|
{
|
|
int nvar = fip->nvar; /* handy */
|
|
char *hdr;
|
|
int npad;
|
|
int nbytes;
|
|
|
|
/* find number of extra rows we need to add to nvar to make it a
|
|
* multiple of FITS_NROWS, _NOT_ including the row we add for END.
|
|
*/
|
|
npad = (FITS_HROWS - ((nvar+1)%FITS_HROWS)) % FITS_HROWS;
|
|
|
|
/* get room for the var, END and pad rows */
|
|
nbytes = (nvar + 1 + npad) * sizeof(FITSRow);
|
|
hdr = malloc (nbytes);
|
|
if (!hdr) {
|
|
sprintf(errmsg, "Can not malloc %d for padded FITS header", nbytes);
|
|
return (-1);
|
|
}
|
|
|
|
/* copy the existing fields in var */
|
|
memcpy (hdr, (char *)fip->var, nvar*sizeof(FITSRow));
|
|
|
|
/* add the END keyword */
|
|
fmtENDFITS (&hdr[nvar*sizeof(FITSRow)]);
|
|
|
|
/* pad out the remaining lines with blanks */
|
|
memset (&hdr[(nvar+1)*sizeof(FITSRow)], ' ', npad*sizeof(FITSRow));
|
|
|
|
/* ok */
|
|
*mem = hdr;
|
|
return (nbytes);
|
|
}
|
|
|
|
/* write fip->var then add END and pad to FITS block size.
|
|
* if trouble put message in errmsg and return -1, else return 0.
|
|
*/
|
|
static int
|
|
writeFITSHeader (fip, fd, errmsg)
|
|
FImage *fip;
|
|
int fd;
|
|
char *errmsg;
|
|
{
|
|
char *hdr;
|
|
int nbytes, nw, n;
|
|
|
|
/* create the header */
|
|
nbytes = fmtFITSHeader (fip, &hdr, errmsg);
|
|
if (nbytes < 0)
|
|
return (-1);
|
|
|
|
/* write out the header: var + END + pad
|
|
* might be a pipe so keep writing until eof or error
|
|
*/
|
|
for (nw = 0; nw < nbytes; nw += n) {
|
|
n = write (fd, hdr+nw, nbytes-nw);
|
|
if (n <= 0) {
|
|
if (n < 0)
|
|
strcpy (errmsg, strerror(errno));
|
|
else
|
|
sprintf (errmsg, "Short write of FITS header");
|
|
free (hdr);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
free (hdr);
|
|
return (0);
|
|
}
|
|
|
|
/* initialize each field of a garbage fip to a default.
|
|
*/
|
|
void
|
|
initFImage (fip)
|
|
FImage *fip;
|
|
{
|
|
memset ((char *)fip, 0, sizeof(*fip));
|
|
}
|
|
|
|
/* prepare a fip for reuse.
|
|
* if it already has memory in use, free it also.
|
|
* N.B. don't pass one right off the stack because we free memory when we find
|
|
* pointer fields that are non-0; use initFImage for that.
|
|
*/
|
|
void
|
|
resetFImage (fip)
|
|
FImage *fip;
|
|
{
|
|
if (fip->var)
|
|
free ((char *)fip->var);
|
|
if (fip->image)
|
|
free (fip->image);
|
|
|
|
initFImage (fip);
|
|
}
|
|
|
|
/* add the basic FITS fields to fip.
|
|
* we assume fip->var is empty and we do not add END or pad.
|
|
*/
|
|
void
|
|
setSimpleFITSHeader (fip)
|
|
FImage *fip;
|
|
{
|
|
setLogicalFITS (fip, "SIMPLE", 1, "Standard FITS");
|
|
setIntFITS (fip, "BITPIX", fip->bitpix, "Bits per pixel");
|
|
setIntFITS (fip, "NAXIS", 2, "Number of dimensions");
|
|
setIntFITS (fip, "NAXIS1", fip->sw, "Number of columns");
|
|
setIntFITS (fip, "NAXIS2", fip->sh, "Number of rows");
|
|
setRealFITS (fip, "BZERO", 32768.0, 6, "Real = Pixel*BSCALE + BZERO");
|
|
setRealFITS (fip, "BSCALE", 1.0, 6, "Pixel scale factor");
|
|
}
|
|
|
|
/* add the Logical field v to fip->var */
|
|
void
|
|
setLogicalFITS (fip, name, v, comment)
|
|
FImage *fip;
|
|
char *name;
|
|
int v;
|
|
char *comment;
|
|
{
|
|
char *rp;
|
|
|
|
if (findFImageVar (fip, name, &rp) == 0)
|
|
fmtLogicalFITS (rp, name, v, comment);
|
|
else {
|
|
FITSRow row;
|
|
fmtLogicalFITS (row, name, v, comment);
|
|
addFImageVar (fip, row);
|
|
}
|
|
}
|
|
|
|
/* add (or replace) the Integer field v to fip->var */
|
|
void
|
|
setIntFITS (fip, name, v, comment)
|
|
FImage *fip;
|
|
char *name;
|
|
int v;
|
|
char *comment;
|
|
{
|
|
char *rp;
|
|
|
|
if (findFImageVar (fip, name, &rp) == 0)
|
|
fmtIntFITS (rp, name, v, comment);
|
|
else {
|
|
FITSRow row;
|
|
fmtIntFITS (row, name, v, comment);
|
|
addFImageVar (fip, row);
|
|
}
|
|
}
|
|
|
|
/* add (or replace) the Real field v with so many significant digits */
|
|
void
|
|
setRealFITS (fip, name, v, sigdig, comment)
|
|
FImage *fip;
|
|
char *name;
|
|
double v;
|
|
int sigdig;
|
|
char *comment;
|
|
{
|
|
char *rp;
|
|
|
|
if (findFImageVar (fip, name, &rp) == 0)
|
|
fmtRealFITS (rp, name, v, sigdig, comment);
|
|
else {
|
|
FITSRow row;
|
|
fmtRealFITS (row, name, v, sigdig, comment);
|
|
addFImageVar (fip, row);
|
|
}
|
|
}
|
|
|
|
/* add (or replace) a character string field to fip->var.
|
|
* strings are represented by a ' in column 11 and closed by a ' not before
|
|
* col 20, i.e. 8 characters minimum including blanks.
|
|
*/
|
|
void
|
|
setStringFITS (fip, name, string, comment)
|
|
FImage *fip;
|
|
char *name;
|
|
char *string;
|
|
char *comment;
|
|
{
|
|
char *rp;
|
|
|
|
if (findFImageVar (fip, name, &rp) == 0)
|
|
fmtStringFITS (rp, name, string, comment);
|
|
else {
|
|
FITSRow row;
|
|
fmtStringFITS (row, name, string, comment);
|
|
addFImageVar (fip, row);
|
|
}
|
|
}
|
|
|
|
/* add the comment field to fip->var left justified in columns 9-80.
|
|
* if too wide, break into several rows, all but first starting with "... ".
|
|
* this is generally only used for names of HISTORY and COMMENT
|
|
*/
|
|
void
|
|
setCommentFITS (fip, name, comment)
|
|
FImage *fip;
|
|
char *name;
|
|
char *comment;
|
|
{
|
|
char lline[FITS_HCOLS+1]; /* room for sprintf's trailing '\0' */
|
|
int l = strlen(comment);
|
|
int n;
|
|
|
|
for (n = 0; n < l; ) {
|
|
if (n == 0) {
|
|
sprintf (lline, "%-8.8s%-72.72s", name, comment);
|
|
n += 72;
|
|
} else {
|
|
sprintf (lline, "%-8.8s... %-68.68s", name, comment+n);
|
|
n += 68;
|
|
}
|
|
addFImageVar (fip, lline);
|
|
}
|
|
}
|
|
|
|
/* search fip->var for the given Logical field.
|
|
* return 0 and set *vp if we find it, else return -1.
|
|
*/
|
|
int
|
|
getLogicalFITS (fip, name, vp)
|
|
FImage *fip;
|
|
char *name;
|
|
int *vp;
|
|
{
|
|
char *rp;
|
|
|
|
if (findFImageVar (fip, name, &rp) < 0)
|
|
return (-1);
|
|
|
|
switch (rp[29]) {
|
|
case 'T': case 't': *vp = 1; return (0);
|
|
case 'F': case 'f': *vp = 0; return (0);
|
|
default: return (-1);
|
|
}
|
|
}
|
|
|
|
/* search fip->var for the given Integer field.
|
|
* return 0 and set *vp if we find it, else return -1.
|
|
*/
|
|
int
|
|
getIntFITS (fip, name, vp)
|
|
FImage *fip;
|
|
char *name;
|
|
int *vp;
|
|
{
|
|
char *rp;
|
|
|
|
if (findFImageVar (fip, name, &rp) < 0)
|
|
return (-1);
|
|
*vp = atoi (rp+10);
|
|
return (0);
|
|
}
|
|
|
|
/* search fip->var for the given Real field.
|
|
* return 0 and set *vp if we find it, else return -1.
|
|
*/
|
|
int
|
|
getRealFITS (fip, name, vp)
|
|
FImage *fip;
|
|
char *name;
|
|
double *vp;
|
|
{
|
|
char buf[32];
|
|
char *dp, *rp;
|
|
|
|
if (findFImageVar (fip, name, &rp) < 0)
|
|
return (-1);
|
|
memcpy (buf, rp+10, 30);
|
|
buf[30] = '\0';
|
|
if ((dp = strchr (buf,'D')) || (dp = strchr (buf,'d')))
|
|
*dp = 'e';
|
|
*vp = atof (buf);
|
|
return (0);
|
|
}
|
|
|
|
/* search fip->var for the given Comment field.
|
|
* return 0 and fill in buf (including a trailing 0) if we find it, else
|
|
* return -1.
|
|
* buf should be at least 73 chars long.
|
|
*/
|
|
int
|
|
getCommentFITS (fip, name, buf)
|
|
FImage *fip;
|
|
char *name;
|
|
char *buf;
|
|
{
|
|
char *rp;
|
|
|
|
if (findFImageVar (fip, name, &rp) < 0)
|
|
return (-1);
|
|
memcpy (buf, rp, 72);
|
|
rp[72] = '\0';
|
|
return (0);
|
|
}
|
|
|
|
/* search fip->var for the given Character string field.
|
|
* fill in buf and return 0 if we find name, else return -1.
|
|
* buf will include a trailing 0, no ' and no trailing blanks.
|
|
* string should be at least 69 chars long.
|
|
*/
|
|
int
|
|
getStringFITS (fip, name, string)
|
|
FImage *fip;
|
|
char *name;
|
|
char *string;
|
|
{
|
|
char *string0 = string;
|
|
char *rp;
|
|
char c;
|
|
int i;
|
|
|
|
if (findFImageVar (fip, name, &rp) < 0)
|
|
return (-1);
|
|
if (rp[10] != '\'') /* should have a ' in column 11 */
|
|
return (-1);
|
|
for (i = 11; i < FITS_HCOLS; i++) {
|
|
c = rp[i];
|
|
if (c == '\'') { /* and another someplace later */
|
|
while (string > string0 && string[-1] == ' ')
|
|
string--;
|
|
*string = '\0';
|
|
return (0);
|
|
} else
|
|
*string++ = c;
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
/* search through var for an entry with the given name.
|
|
* N.B. name should _not_ include trailing blanks.
|
|
* if find it set *rpp to its address and return 0, else -1.
|
|
*/
|
|
static int
|
|
findFImageVar (fip, name, rpp)
|
|
FImage *fip;
|
|
char *name;
|
|
char **rpp;
|
|
{
|
|
char field[9]; /* FITS field name */
|
|
int i;
|
|
|
|
sprintf (field, "%-8.8s", name);
|
|
|
|
for (i = 0; i < fip->nvar; i++)
|
|
if (strncmp (field, fip->var[i], 8) == 0) {
|
|
*rpp = fip->var[i];
|
|
return (0);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
/* add the row to the end of the fip->var array.
|
|
*/
|
|
void
|
|
addFImageVar (fip, row)
|
|
FImage *fip;
|
|
FITSRow row;
|
|
{
|
|
char *mem;
|
|
int newn;
|
|
|
|
newn = fip->nvar + 1;
|
|
|
|
/* get room for one more FITSrow */
|
|
if (fip->var)
|
|
mem = realloc ((char *)fip->var, newn*sizeof(FITSRow));
|
|
else
|
|
mem = malloc (newn*sizeof(FITSRow));
|
|
|
|
if (!mem) {
|
|
fprintf (stderr, "No memory for more FITS header lines\n");
|
|
return;
|
|
}
|
|
|
|
/* copy to the new (last) position */
|
|
fip->var = (FITSRow *) mem;
|
|
memcpy (fip->var[fip->nvar], row, FITS_HCOLS);
|
|
fip->nvar = newn;
|
|
}
|
|
|
|
/* delete the given field from the FImage.
|
|
* return 0 if ok, else -1 if field didn't exist.
|
|
*/
|
|
int
|
|
delFImageVar (fip, name)
|
|
FImage *fip;
|
|
char *name;
|
|
{
|
|
char *rp;
|
|
char *mem;
|
|
FITSRow *dst;
|
|
int newn;
|
|
int i;
|
|
|
|
if (!fip->var || findFImageVar (fip, name, &rp) < 0)
|
|
return (-1);
|
|
|
|
newn = fip->nvar - 1;
|
|
|
|
/* get room for one fewer FITSrow */
|
|
mem = malloc (newn*sizeof(FITSRow));
|
|
if (!mem) {
|
|
fprintf (stderr, "No memory for fewer FITS header lines (?!)\n");
|
|
return (-1);
|
|
}
|
|
|
|
/* copy all entries except the one at rp */
|
|
dst = (FITSRow *) mem;
|
|
for (i = 0; i < fip->nvar; i++) {
|
|
char *src = fip->var[i];
|
|
if (src != rp)
|
|
memcpy ((void *)(dst++), (void *)(src), FITS_HCOLS);
|
|
}
|
|
|
|
free ((char *)fip->var);
|
|
fip->var = (FITSRow *) mem;
|
|
fip->nvar = newn;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* copy the given field from fip2 to fip1.
|
|
* return 0 if ok, else -1 if trouble.
|
|
*/
|
|
int
|
|
cpyFImageVar (fip1, fip2, name)
|
|
FImage *fip1, *fip2;
|
|
char *name;
|
|
{
|
|
char *srcrow, *dstrow;
|
|
|
|
if (!fip2->var || findFImageVar (fip2, name, &srcrow) < 0)
|
|
return (-1);
|
|
if (fip1->var && findFImageVar (fip1, name, &dstrow) == 0)
|
|
memcpy (dstrow, srcrow, FITS_HCOLS);
|
|
else
|
|
addFImageVar (fip1, srcrow);
|
|
return (0);
|
|
}
|
|
|
|
/* given a name and a 0 or !0 write the logical FITS variable to line
|
|
* as a F or T in column 30.
|
|
*/
|
|
static void
|
|
fmtLogicalFITS (line, name, value, comment)
|
|
FITSRow line;
|
|
char *name;
|
|
int value;
|
|
char *comment;
|
|
{
|
|
sprintf (line, "%-8.8s=%20s%c", name, "", value ? 'T' : 'F');
|
|
fmtInlineComment (line, comment);
|
|
}
|
|
|
|
/* given a name and an int value write the int FITS variable to line
|
|
* right justified in columns 11-30.
|
|
*/
|
|
static void
|
|
fmtIntFITS (line, name, value, comment)
|
|
FITSRow line;
|
|
char *name;
|
|
int value;
|
|
char *comment;
|
|
{
|
|
char str[30];
|
|
int strl;
|
|
|
|
sprintf (str, "%d", value);
|
|
strl = strlen (str);
|
|
|
|
sprintf (line, "%-8.8s= %*s%s", name, 20-strl, "", str);
|
|
fmtInlineComment (line, comment);
|
|
}
|
|
|
|
/* given a name and a double write the floating point FITS variable to line
|
|
* in columns 11-30 with at most sigdig significant digits.
|
|
*/
|
|
static void
|
|
fmtRealFITS (line, name, value, sigdig, comment)
|
|
FITSRow line;
|
|
char *name;
|
|
double value;
|
|
int sigdig;
|
|
char *comment;
|
|
{
|
|
sprintf (line, "%-8.8s= %20.*G", name, sigdig, value);
|
|
fmtInlineComment (line, comment);
|
|
}
|
|
|
|
/* given a name and a character string write the string FITS variable to line
|
|
* represented by a ' in column 11 and closed by a ' not before col 20, i.e. 8
|
|
* characters minimum including blanks.
|
|
*/
|
|
static void
|
|
fmtStringFITS (line, name, value, comment)
|
|
FITSRow line;
|
|
char *name;
|
|
char *value;
|
|
char *comment;
|
|
{
|
|
char lline[FITS_HCOLS+1]; /* room for sprintf's trailing '\0' */
|
|
int l = strlen(value);
|
|
|
|
if (l < 8)
|
|
l = 8;
|
|
else if (l > 68)
|
|
l = 68;
|
|
|
|
sprintf (lline, "%-8.8s= '%-*.*s'%*s", name, l, l, value, 80-12-l, "");
|
|
if (comment && l < 80-3-12) {
|
|
int start = 12+l;
|
|
if (start < 30)
|
|
start = 30;
|
|
sprintf (&lline[start], " / %-*.*s", 80-3-start, 80-3-start,
|
|
comment);
|
|
}
|
|
|
|
memcpy (line, lline, FITS_HCOLS);
|
|
}
|
|
|
|
/* write the END marker to the FITS line */
|
|
static void
|
|
fmtENDFITS (line)
|
|
FITSRow line;
|
|
{
|
|
sprintf (line, "%-79s", "END");
|
|
line[79] = ' ';
|
|
}
|
|
|
|
/* attend to the final 50 chars of line */
|
|
static void
|
|
fmtInlineComment (line, comment)
|
|
FITSRow line;
|
|
char *comment;
|
|
{
|
|
char buf[100]; /* buffer to allow for sprintf's trailing '\0' */
|
|
|
|
if (comment)
|
|
sprintf (buf, " / %-47.47s", comment);
|
|
else
|
|
sprintf (buf, "%50s", "");
|
|
memcpy (&line[30], buf, FITS_HCOLS-30);
|
|
}
|
|
|
|
/* turn native unsigned shorts into FITS' big-endian signed.
|
|
*/
|
|
static void
|
|
enFITSPixels (fip)
|
|
FImage *fip;
|
|
{
|
|
unsigned short *pixp = (unsigned short *)fip->image;
|
|
int n = fip->sh * fip->sw;
|
|
unsigned short *r0;
|
|
int p0;
|
|
|
|
if (fip->bitpix == 8)
|
|
return; /* nothing to do if just bytes */
|
|
|
|
if (lendian()) {
|
|
while (--n >= 0) {
|
|
r0 = pixp++;
|
|
p0 = (int)(*r0) - 32768;
|
|
*r0 = ((p0 << 8) & 0xff00) | ((p0 >> 8) & 0xff);
|
|
}
|
|
} else {
|
|
while (--n >= 0) {
|
|
r0 = pixp++;
|
|
p0 = (int)(*r0) - 32768;
|
|
*r0 = p0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* turn FITS' big-endian signed shorts into native unsigned shorts.
|
|
*/
|
|
static void
|
|
unFITSPixels (fip)
|
|
FImage *fip;
|
|
{
|
|
unsigned short *pixp = (unsigned short *)fip->image;
|
|
int n = fip->sh * fip->sw;
|
|
unsigned short *r0;
|
|
int p0;
|
|
|
|
/* noop if not 16 */
|
|
if (fip->bitpix != 16)
|
|
return;
|
|
|
|
if (lendian()) {
|
|
while (--n >= 0) {
|
|
r0 = pixp++;
|
|
p0 = ((*r0 << 8) & 0xff00) | ((*r0 >> 8) & 0xff);
|
|
*r0 = (unsigned short)(p0 + 32768);
|
|
}
|
|
} else {
|
|
while (--n >= 0) {
|
|
r0 = pixp++;
|
|
p0 = (int)(*r0);
|
|
*r0 = (unsigned short)(p0 + 32768);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* return 1 if this machine is little-endian (low-byte first) else 0. */
|
|
static int
|
|
lendian()
|
|
{
|
|
union {
|
|
short s;
|
|
char c[2];
|
|
} U;
|
|
|
|
U.c[0] = 1;
|
|
U.c[1] = 0;
|
|
|
|
return (U.s == 1);
|
|
}
|
|
|
|
/* convert the given FImage with bitpix == -32 to 16.
|
|
*/
|
|
static void
|
|
FITS32to16 (FImage *fip)
|
|
{
|
|
float t, min, max, del;
|
|
char *fp, *tp; /* from ... to */
|
|
char s; /* swap temp */
|
|
int le; /* whether we are little-endian */
|
|
|
|
/* noop if not -32 */
|
|
if (fip->bitpix != -32)
|
|
return;
|
|
|
|
/* find range and convert to native byte order */
|
|
le = lendian();
|
|
min = FLT_MAX;
|
|
max = -FLT_MAX;
|
|
for (fp = fip->image; fp < &fip->image[fip->totbytes]; fp += 4) {
|
|
if (le) {
|
|
s = fp[0]; fp[0] = fp[3]; fp[3] = s;
|
|
s = fp[1]; fp[1] = fp[2]; fp[2] = s;
|
|
}
|
|
t = *(float *)fp;
|
|
if (t < min)
|
|
min = t;
|
|
if (t > max)
|
|
max = t;
|
|
}
|
|
|
|
/* map to native CamPix */
|
|
del = max - min;
|
|
for (tp = fp = fip->image; fp < &fip->image[fip->totbytes]; fp += 4, tp += 2)
|
|
*(CamPix *)tp = (CamPix) (65535 * (*(float *)fp - min)/del);
|
|
|
|
/* need only half as much pixel memory */
|
|
fip->image = realloc (fip->image, fip->totbytes /= 2);
|
|
|
|
/* update header */
|
|
fip->bitpix = 16;
|
|
setIntFITS (fip, "BITPIX", fip->bitpix, "Bits per pixel");
|
|
}
|
|
|