
/**************************************************************************
 *                                                                        *
 * Copyright (c) 2001 Michael Richmond and Richard Treffers               *
 *                                                                        *
 *                    This software may be copied and distributed for     *
 *                    educational, research and not for profit services   *
 *                    provided that this copyright and statement are      *
 *                    included in all such copies.                        *
 *                                                                        *
 **************************************************************************/


/**************************************************************************
 * Converts image files from the SBIG (Santa Barbara Instruments Corporation)
 * "uncompressed" format into FITS.  This is a very simple procedure,
 * as the SBIG files store a single 2-D, 16-bit integer image.
 *
 * The SBIG format has an ASCII header, with "keyword = value" pairs;
 * these header lines are not fixed at 80 characters, but end as soon
 * as possible.  They use a combination of \n and \r characters to
 * mark the end of each header line.  The "values" in these lines
 * are not quoted with apostrophes, as in FITS.  Thus,
 *
 *     SBIG          Time = 00:45:00 \n\r
 *     FITS          TIME-OBS= '00:45:00'              (to 80 chars)
 *
 * The header of each SBIG image is exactly 2048 characters long.
 * After that, the data portion begins.  Each pixel is represented
 * by two bytes:  the first byte contains the low-order numerical
 * information (i.e. is the low-order byte), and the second byte
 * the high-order informatio (i.e. is the high-order byte).
 * SBIG data values are unsigned.  I choose to convert these to
 * valid 16-bit signed integer values (as required for FITS)
 * by subtracting 32,768.  Thus, a pixel which SBIG software
 * claims is "10" counts will be converted to "-36758" counts
 * in the output file.
 *
 * This program is not designed for general purpose: it exists 
 * to convert images take at the RIT Observatory with our SBIG ST-8
 * camera to a FITS equivalent.
 *
 * Usage:
 *             sbig2fits   sbigfile  fitsfile
 * where
 *        sbigfile        is the name of the SBIG image
 *        fitsfile        is the name of the output FITS image
 *
 *
 * MWR 7/28/2001
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "pcvista.h"
#include "fits.h"


/* Starting location of pixel data in SBIG file (bytes from start of file) */
#define SBIG_DATA_START  2048

/*
 * This structure contains all the information we need to create an
 * equivalent FITS file.  We don't transfer all the header info,
 * just the parts I deem important.
 */
#define VAL_LEN  80
typedef struct header_info {
	int nrow;                 /* number of rows in image */
	int ncol;                 /* number of cols in image */
	char dateobs[VAL_LEN];    /* date of observation (presumably UT) */
	char timeobs[VAL_LEN];    /* time of observation (presumably UT) */
	char filter[VAL_LEN];     /* filter  */
	double exptime;           /* exposure time (seconds) */
	                          /*   I believe SBIG records this in hundredths */
	                          /*    of a second, so we must convert */
	double ccd_temp;          /* temperature of CCD (degrees C) */
	double ccd_gain;          /* gain of CCD (electrons per DN) */
} HEADER_INFO;


int main(int, char **);
static int read_header_info(FILE *fin, HEADER_INFO *info);
static int fill_fits_header(FITS_HANDLE handle, HEADER_INFO *info);
static int copy_pixel_data(FILE *fin, FITS_HANDLE handle, HEADER_INFO *info);




#define USAGE "usage: sbig2fits sbigfile fitsfile  [help]"

int
main(argc, argv)
int argc;
char *argv[];
{
	FILE *fin;
	FITS_HANDLE handle;
	char outname[NBUF];
	HEADER_INFO header_info;

	/* step 1: deal with command-line argument */
	if ((argc < 2) || (argc > 3))  {
		error(-1, USAGE);
	}
	if (strcmp(argv[1], "help") == 0) {
		printf("%s\n", USAGE);
		exit(0);
	}
	if ((fin = fopen(argv[1], "rb")) == NULL) {
		error(-1, "can't open input file");	
	}
	strcpy(outname, argv[2]);

	/* step 2: look in the input file, read a bunch of information */
	if (read_header_info(fin, &header_info) != 0) {
		error(-1, "invalid SBIG header information");
	}

	/* step 3: create the FITS file, write the appropriate header info */
	handle = fits_open(outname, "w", &(header_info.nrow), &(header_info.ncol));
	if (fill_fits_header(handle, &header_info) != 0) {
		error(-1, "cannot fill FITS header");
	}

	/* step 4: copy the pixel data from SBIG to FITS */
	if (copy_pixel_data(fin, handle, &header_info) != 0) {
		error(-1, "error copying pixel data");
	}

	/* step 5: all done */
	fits_close(handle);

	exit(0);
}
		


	

	/***********************************************************************
	 * PROCEDURE: read_header_info
	 *
	 * DESCRIPTION: Read a bunch of information from the header of 
	 *              an SBIG image file.  Fill in the appropriate
	 *              fields of the given "header_info" structure.
	 *
	 *              We assume that a header line with the single word
	 *              "End" (spelled exactly that way) indicates the 
	 *              end of the header.
	 *
	 * RETURNS: 
	 *     0        if all goes well
	 *     1        if an error occurs
	 */

#define LINELEN  256

static int 
read_header_info
	(
	FILE *fin,              /* I: FILE pointer to SBIG file */
	HEADER_INFO *info       /* O: structure to hold header information */
	)
{
	int i, len;
	char keyword[LINELEN+1];
	char value[LINELEN+1];
	char line[LINELEN+1];

	rewind(fin);

	while (1) {
		if (fgets(line, LINELEN, fin) == NULL) {
			error(1, "read_header_info: unexpected end of file");
		}
		/* 
		 * if the first character of the line is carriage return \r, 
		 * remove it and shift all other characters backwards.
		 */
		if (line[0] == '\r') {
			len = strlen(line);
			for (i = 1; i < len; i++) {
				line[i - 1] = line[i];
			}
		}
		if (strncmp(line, "End", 3) == 0) {
			break;
		}
		if (sscanf(line, "%s = %s", keyword, value) == 2) {
#ifdef DEBUG
			printf("keyword ..%s.. value ..%s..\n", keyword, value);
#endif
		}

		/* 
		 * check to see if this keyword is important.  If so,
		 * save its value in the structure
		 */
		if (strncmp(keyword, "Width", 5) == 0) {
			info->nrow = atoi(value);
#ifdef DEBUG
			printf ("nrow is %d\n", info->nrow);
#endif
		}
		if (strncmp(keyword, "Height", 6) == 0) {
			info->ncol = atoi(value);
#ifdef DEBUG
			printf ("ncol is %d\n", info->ncol);
#endif
		}
		if (strncmp(keyword, "Date", 4) == 0) {
			sprintf(info->dateobs, "'%s'", value);
#ifdef DEBUG
			printf ("dateobs is ..%s..\n", info->dateobs);
#endif
		}
		if (strncmp(keyword, "Time", 4) == 0) {
			sprintf(info->timeobs, "'%s'", value);
#ifdef DEBUG
			printf ("timeobs is ..%s..\n", info->timeobs);
#endif
		}
		if (strncmp(keyword, "Filter", 6) == 0) {
			sprintf(info->filter, "'%s'", value);
#ifdef DEBUG
			printf ("filter is ..%s..\n", info->filter);
#endif
		}
		if (strcmp(keyword, "Exposure") == 0) {
			info->exptime = atof(value);
			/* convert from hundredths of a second to seconds */
			info->exptime /= 100.0;
#ifdef DEBUG
			printf ("exptime is %f\n", info->exptime);
#endif
		}
		if (strncmp(keyword, "Temperature", 10) == 0) {
			info->ccd_temp = atof(value);
#ifdef DEBUG
			printf ("ccd_temp is %f\n", info->ccd_temp);
#endif
		}
		if (strncmp(keyword, "E_gain", 6) == 0) {
			info->ccd_gain = atof(value);
#ifdef DEBUG
			printf ("ccd_gain is %f\n", info->ccd_gain);
#endif
		}
	}

	return(0);

}


/************************************************************************
 * PROCEDURE: fill_fits_header
 *
 * DESCRIPTION: We have already created a FITS file, and have a handle
 *              to it.  Use the handle to fill the FITS header with
 *              information from the given header_info structure.
 *              
 *              This is a little awkward because we must convert 
 *              numerical values into string format before calling
 *              the "fits_put_symbol" value.  
 *
 * RETURNS:
 *     0          if all goes well
 *     1          if an error occurs
 */


static int
fill_fits_header
	(
	FITS_HANDLE handle,     /* I: handle to output FITS file */
	HEADER_INFO *info       /* I: pointer to struct full of information */
	)
{
	char value[VAL_LEN];

	fits_put_symbol(handle, "DATE-OBS", info->dateobs);
	fits_put_symbol(handle, "TIME-OBS", info->timeobs);
	fits_put_symbol(handle, "FILTER", info->filter);

	sprintf(value, "%7.2f", info->exptime);
	fits_put_symbol(handle, "EXPTIME", value);

	sprintf(value, "%7.1f", info->ccd_temp);
	fits_put_symbol(handle, "CAM-TEMP", value);

	sprintf(value, "%7.2f", info->ccd_gain);
	fits_put_symbol(handle, "GAIN", value);


	return(0);
}



/************************************************************************
 * PROCEDURE: copy_pixel_data
 *
 * DESCRIPTION: Copy all the pixel values from the SBIG file to
 *              the new FITS file.  We translate from the SBIG
 *              convention of storing pixel values to the FITS
 *              convention here.  
 *
 * RETURNS:
 *    0            if all goes well
 *    1            if there's a problem
 */

static int
copy_pixel_data
	(
	FILE *fin,              /* I: FILE pointer to input SBIG file */
	FITS_HANDLE handle,     /* I: handle to output FITS file */
	HEADER_INFO *info       /* I: structure with information about image */
	)
{
	char pixel_bytes[2];
	int high, low;
	int nrow, ncol, row, col;
	int value;
	int16 fits_value;
	static int16 data[NMAX];


	nrow = info->nrow;
	ncol = info->ncol;

	/* seek to the start of the data portion of the SBIG file */
	fseek(fin, (long) SBIG_DATA_START, SEEK_SET);

	for (row = 0; row < nrow; row++) {

		for (col = 0; col < ncol; col++) {
			/* read in one pixel's data */
			if (fread(pixel_bytes, 1, 2, fin) != 2) {
				error(-1, "reached end of file while reading pixel data");
			}

			/* calculate the appropriate pixel value, in unsigned 16-bit */
			high = (unsigned char) pixel_bytes[1];
			low  = (unsigned char) pixel_bytes[0];
			value = -32768 + 256*high + low;
			if (value < MIN_DATA_VAL) {
				fits_value = MIN_DATA_VAL;
			} else if (value > MAX_DATA_VAL) {
				fits_value = MAX_DATA_VAL;
			} else {
				fits_value = value;
			}

			/* set the appropriate element of the 'data' array */
			data[col] = fits_value;
		}
		fits_put_data_fast(handle, data, ncol);
	
	}

	return(0);
}
