
/**************************************************************************
 *                                                                        *
 * Copyright (c) 1996 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.                        *
 *                                                                        *
 **************************************************************************/

/*
	find stars in a CCD frame and output their positions, together
	with some other information. similar to DAOPHOT's 'FIND' command.

		STARS file [BOX=] [SKY=] [SKYSIG=] [FWHM=] [MINFWHM=] [MAXFWHM=] 
	              [MINSIG=] [MINROUND=] [MAXROUND=] [MINSHARP=] [MAXSHARP=]
		          [OUTFILE=] [VERBOSE] [SHOW] [CONVOLVE] [IMAGE] [QUIET] [help]

    see the .hlp file for more information. 

	1/30/91 - added 'floor' to "sky" expression   - MWR

	5/30/92 - added 'CONVOLVE' option - does DAOPHOT-like convolution 
	          of image with gaussian before looking for stars.  Takes
	          longer, but works better in complication regions. MWR

	6/3/92 - modified is_peak() to avoid the edges -RRT
			- added show_options()
			- bug fix in sharpness()
			- modified the behavior of verbose
	6/8/92  - MAXFWHM raised from  9.5 to 12.0 -RRT

	10/4/92 - reduced memory allocation by using only one image-sized chunk
	             of memory, not two, if no convolution is desired.  MWR

	10/5/92 - deleted a loop in which star_axes was called additional times
	          until the light center stopped moving.  It was computationally
	          expensive, and threw out only ~1% of objects - some of which
	          were good!  MWR

	12/13/92- added a test when after star_axes() - if the star moves too
	          far (MOVEMAX) from its initial peak position, reject it. 
	          also, added some checks to prevent out-of-bounds array refs.
	          also, decreased TINYSIZE from 13 to 9.
	             ... MWR
   12/17/92 -add MAXSTARs keyword -rrt
	12/28/92 - remove the exten() 
			 - add MOVEMAX keyword
	10/19/1994 - add timeout option -rrt
	1/10/1996  - added 'help' option. MWR

	5/23/2000 - added "quiet" option.  MWR
*/


#define XWINDOWS		/* def this to get 'show' function */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "pcvista.h"
#include "fits.h"
#include "stars.h"
#include "usleep.h"
#ifdef XWINDOWS
#include <signal.h>
#include "pcvistax.h"
#endif


int main(int, char **);
static void do_stars(int, int, int, int);
static int is_peak(int r, int c, int sr,int  sc,int  nr,int  nc);
static double sharpness(int, int, int, int, int, int);
static void markpix(int, int, int, int, double, double, double);
static int valid_fwhm(double, double, double);
static void calc_box(int row, int col, int nrow, int ncol , 
	int *tsr, int *tsc , int rsize , int csize);
static void allo_map(char **, int, int);
static void allo_image(int16 **, int16 **, int, int, int);
static int read_image(int nrow, int ncol);
static void copy_image(void );
static void show_options(FILE *fp);
static void timeout_handler(int);
#ifdef XWINDOWS
static int do_crosshair(int row, int col);
static void sig_trap(int);
#endif /* XWINDOWS */

#undef DEBUG			/* to see lots of messages as program progresses */
#undef DEBUG2			/* more messages */
#undef STATS			/* define to see # of times routines entered at end */


#ifdef STATS
#ifdef PROTO
static void do_stats(void);
#else
static void do_stats();
#endif
#endif

#define YES       1
#define NO        0


#define TINYSIZE    9		/* size of box used to calculate stellar params */
#define MOVEMAX   1.0		/* only accepts stars w/centroid this close to peak */
#define STARFWHM  5.0		/* default FWHM of stellar features */
#define MINFWHM   2.0		/* default smallest fwhm of object which could be star */
#define MAXFWHM   12.0		/* default largest ditto */
#define MINSIG    3.0		/* default min sigma above sky to find stars */
#define MINROUND  -1.0		/* default min 'round' criterion */
#define MAXROUND  1.0		/* default max 'round' criterion */
#define MINSHARP  0.3		/* default min 'sharp' criterion */
#define MAXSHARP  0.9		/* default max 'sharp' criterion */
#define CMINSHARP  0.2		/* default min 'sharp' criterion when convolving */
#define CMAXSHARP  1.0		/* default max 'sharp' criterion when convolving */

#define BADVAL    1.0e6		/* use for a divide-by-zero situation */


	/* some definitions for the bytemap */
#define ISBITON(p, nr, nc)   (p[nr][nc] != 0)
#define TURNON(p, nr, nc)    (p[nr][nc] = 1)

	/* the various arrays we have to deal with */
static char *bytemap[NMAX];		/* map of CCD frame, 1 byte per pixel */
int16 *image[NMAX];		/* array for CCD data, 16 bits per pixel */
int16 *conv_image[NMAX];		/* convolved data */
int timeout_time;

	/* some other global variables */
FITS_HANDLE fh;			/* file handle for file being examined */
static char fname[NBUF];/* its name (with extension .fts added */
static int sky;			/* background sky value */
static double skysig;	/* std. dev. of sky noise */
static double minsig;	/* minimum # of std. dev. above which we look for stars */
static double starfwhm;	/* default fwhm of stellar features */
static double peakrad;	/* radius within which pixels checked for peak */
static double minfwhm;	/* min allowed stellar profile fwhm */
static double maxfwhm;	/* max ditto */
static double minround;	/* min allowed 'round' criterion for a valid star */
static double maxround; /* max ditto */
static double minsharp;	/* min allowed 'sharp' criterion for a valid star */
static double maxsharp; /* max ditto */
static double maxstars=10000;	/* maximum number of stars to print out */
static double cminsharp;/* min 'sharp' criterion, when convolving */ 
static double cmaxsharp; /* max ditto */
static double movemax=MOVEMAX;
static int verbose;		/* if verbose=1, print out info on star candidates */
static int quickflag;	/* if == 1, don't convolve image w/ gaussian */
static int showflag;	/* if showflag=1, draw cross near each star as found */
static char imagename[NBUF];   /* name of FITS file for convolved image */
FILE *outfp;			/* file into which output goes (or stdout, if none) */

static int nrow;		/* true number of rows in the entire image */
                		/*    not just the box subset */
static int ncol;		/* true number of cols in the entire image */
                		/*    not just the box subset */

	/* variables used in optimization */
#ifdef STATS
static long c_biton, c_abovesig, c_1fillbox, c_peak, c_1validfwhm,
            c_itcenter, c_2fillbox, c_2validfwhm, c_movedaway, c_round,
            c_sharp, c_star;
#endif
 
char *progname = "stars";

int
main(argc, argv)
int argc;
char *argv[];
{
	static int i;
	static int sc, sr, nr, nc, boxno;
	char *cp, *gotit;
	char buf[80];

#ifdef STATS
	c_biton = c_abovesig = c_1fillbox = c_peak = c_1validfwhm = 0;
	c_itcenter = c_2fillbox = c_movedaway = c_star = c_2validfwhm = 0;
	c_round = c_sharp = 0;
#endif

	/* assign default values */
	starfwhm = STARFWHM;
	minfwhm = MINFWHM;
	maxfwhm = MAXFWHM;
	minsig = MINSIG;
	minround = MINROUND;
	maxround = MAXROUND;
	minsharp = MINSHARP;
	maxsharp = MAXSHARP;
	cminsharp = CMINSHARP;
	cmaxsharp = CMAXSHARP;

	if (argc < 2) {
		show_options(stderr);
		exit(1);
	}
	if (strcmp(argv[1], "help") == 0) {
		show_options(stdout);
		exit(0);
	}

	strcpy(fname, argv[1]);
	fh = fits_open(fname, "r", &nrow, &ncol);
	nr = nrow;
	nc = ncol;
	sr = 0;
	sc = 0;

	boxno = 0;
	sky = -1;
	skysig = -1.0;
	outfp = stdout;
	verbose = 0;
	showflag = 0;
	quickflag = 1;			/* default is NOT to convolve */
	strcpy(imagename, "");

	/* take care of the command-line arguments */
	for (i = 2; i < argc; i++) {
		if (keyword("help", argv[i])) {
			show_options(stdout);
			exit(0);
		}
		if ((gotit = find("box",argv[i])) != NULL) {
			boxno = (int) (evaluate(gotit) + 0.5);
			if(getbox(boxno, &sr, &sc, &nr, &nc))
				error(-1, "box not found");
			check_box(boxno, nrow, ncol);
			continue;
		}
		if ((gotit = find("sky", argv[i])) != NULL) {
			sky = (int) floor(evaluate(gotit) + 0.5);
			continue;
		}
		if ((gotit = find("skysig", argv[i])) != NULL) {
			skysig = evaluate(gotit); 
			continue;
		}
		if ((gotit = find("minsig", argv[i])) != NULL) {
			minsig = evaluate(gotit);
			continue;
		}
		if ((gotit = find("fwhm", argv[i])) != NULL) {
			starfwhm = evaluate(gotit);
			continue;
		}
		if ((gotit = find("minfwhm", argv[i])) != NULL) {
			minfwhm = evaluate(gotit);
			continue;
		}
		if ((gotit = find("timeout", argv[i])) != NULL) {
			timeout_time = evaluate(gotit);
			signal(SIGALRM,timeout_handler);
			alarm(timeout_time);
			continue;
		}
		if ((gotit = find("maxfwhm", argv[i])) != NULL) {
			maxfwhm = evaluate(gotit);
			continue;
		}
		if ((gotit = find("minround", argv[i])) != NULL) {
			minround = evaluate(gotit);
			continue;
		}
		if ((gotit = find("maxround", argv[i])) != NULL) {
			maxround = evaluate(gotit);
			continue;
		}
		if ((gotit = find("minsharp", argv[i])) != NULL) {
			minsharp = evaluate(gotit);
			cminsharp = minsharp;
			continue;
		}
		if ((gotit = find("maxsharp", argv[i])) != NULL) {
			maxsharp = evaluate(gotit);
			cmaxsharp = maxsharp;
			continue;
		}
		if ((gotit = find("maxstars", argv[i])) != NULL) {
			maxstars = evaluate(gotit);
			continue;
		}
		if ((gotit = find("movemax", argv[i])) != NULL) {
			movemax = evaluate(gotit);
			continue;
		}
		if ((gotit = find("outfile", argv[i])) != NULL) {
			if ((outfp = fopen(gotit, "w")) == NULL)
				error(-1, "can't open output file");
			continue;
		}
		if (keyword("verbose", argv[i])) {
			verbose = 1;
			continue;
		}
		if (keyword("quiet", argv[i])) {
			disable_warnings();
			continue;
		}
		if (keyword("show", argv[i])) {
			showflag = 1;
			continue;
		}
		if (keyword("convolve", argv[i])) {
			quickflag = 0;
			continue;
		}
		if ((gotit = find("image", argv[i])) != NULL) {
			strcpy(imagename, gotit);
			continue;
		}
		sprintf(buf, "unknown keyword %s", argv[i]);
		error(-1, buf);
	}
	if (sky == -1)
		if ((cp = getsym("sky")) != NULL)
			sscanf(cp, "%d", &sky);
	if (skysig == -1.0)
		if ((cp = getsym("skysig")) != NULL)
			sscanf(cp, "%lf", &skysig);
	sky = (sky == -1 ? 0 : sky);
	skysig = (skysig == -1.0 ? 1.0 : skysig);
	peakrad = 0.5*starfwhm;

#ifdef DEBUG
	printf(" sky=%d skysig=%lf minsig=%lf fwhm=%lf minfwhm=%lf maxfwhm=%lf\n", 
		sky, skysig, minsig, starfwhm, minfwhm, maxfwhm);
#endif

	if (verbose)
		fprintf(outfp, "using SKY=%d  SKYSIG=%.2f\n", sky, skysig);

	/* and now go to the star-finding algorithm */
	do_stars(sr, sc, nr, nc);

#ifdef STATS
	do_stats();
#endif

	if (outfp != stdout)
		fclose(outfp);

	exit(0);
}

static void show_options(fp)
FILE *fp;
{
	fprintf(fp, "usage: stars file [box=] [sky=] [skysig=] \n");
	fprintf(fp, "  [minsig=%.1f] \n",minsig);
	fprintf(fp, "  [minfwhm=%.1f] [maxfwhm=%.1f]  \n",minfwhm,maxfwhm);
	fprintf(fp, "  [minround=%.1f] [maxround=%.1f] \n",minround,maxround);
	fprintf(fp, "  [minsharp=%.1f] [maxsharp=%.1f] \n",minsharp,maxsharp);
	fprintf(fp, "  [maxstars=%.0f]  \n",maxstars);
	fprintf(fp, "  [movemax=%.1f]  \n",movemax);
	fprintf(fp, "  [timeout=]  \n");
	fprintf(fp, "  [outfile=] [verbose] [quiet] [convolve] [show]\n");
	fprintf(fp, "  [image=] [fwhm=%.2f]\n",starfwhm);
	fprintf(fp, "  [help]\n");
}


#ifdef STATS
static void
do_stats()
{
	printf(" final stats:  c_biton %ld  c_abovesig %ld \n", c_biton, c_abovesig);
	printf(" c_1fillbox %ld   c_peak %ld  c_1validfwhm %ld\n", 
	           c_1fillbox, c_peak, c_1validfwhm);
	printf("c_itcenter %ld\n", c_itcenter);
	printf("c_2fillbox %ld   c_2validfwhm %ld\n", c_2fillbox, c_2validfwhm);
	printf("c_movedaway %ld   c_round %ld   c_sharp %ld  c_star %ld \n", 
				c_movedaway, c_round, c_sharp, c_star);
}
#endif


/* 
	go through the 'image' data array, finding stars by picking
	things out that are above the sky background.

	use a bytemap to mark whether each pixel has been chosen as part
	of a star or not.

	for now, print out info on each star as it is found.

	return the number of stars found.
*/

static void
do_stars(sr, sc, nr, nc)
int sr, sc, nr, nc;
{
	int er, ec;
	static int row, col, tsr, tsc, num_star;
	static int irow, icol, nrtiny, nctiny;
	static int16 signif;
	static double xc, yc, fwhm, orig_xc, orig_yc, conv_sig;
	static double sharp, round, xwidth, ywidth;

	nrtiny = TINYSIZE;
	nctiny = TINYSIZE;
	num_star = 0;
	er = sr + nr;
	ec = sc + nc;

	/* first, create and initialize to zero the bit map */
	allo_map(bytemap, nrow, ncol);
#ifdef DEBUG
	printf("got past allo_map\n");
#endif

	/* next, create the image arrays */
	allo_image(image, conv_image, nrow, ncol, quickflag);
#ifdef DEBUG
	printf("got past allo_image\n");
#endif
	read_image(nrow, ncol);
#ifdef DEBUG
	printf("got past read_image\n");
#endif

	/* if (quickflag == 0), convolve the image with a gaussian and put 
	   the convolved version into the 'conv_image[]' array.  
	   the stdev of counts in the convolved image is returned as 'conv_sig' */
	if (quickflag) {
		copy_image();
		signif = sky + (int) (minsig*skysig);
	}
	else {
		/* 
		 * we use "skysig" as a first estimate of the expected
		 * standard deviation of values in the convolved image;
		 * it allows us to calculate a clipped mean very quickly.
		 */
		conv_sig = skysig;
		signif = sky + (int) (minsig*skysig);
		star_convolve(sr, sc, nr, nc, starfwhm, signif, 	
					&conv_sig, verbose, imagename);
		signif = (int) (minsig*conv_sig);
		sky = 0;
		minsharp = cminsharp;
		maxsharp = cmaxsharp;
	}

	/* next, go through the image looking for peaks which haven't
	   been marked yet */
	for (row = sr; row < sr + nr; row++) {

#ifdef DEBUG2
		printf("row %d\n", row + sr);
#endif
		for (col = sc; col < sc + nc; col++) {

#ifdef STATS
			c_biton++;
#endif

			if (conv_image[row][col] < signif) {
				continue;
			}
#ifdef STATS
			c_abovesig++;
#endif

			/* first, let's see if this data value is a peak */
			if (!is_peak(row, col, sr, sc, nr, nc)) {
				continue;
			}
#ifdef STATS
			c_peak++;
#endif

			if(verbose)
			fprintf(outfp,"  !! testing pixel (%3d, %3d)  %d sigma \n", 
				row, col, (int)((conv_image[row][col] - sky)/skysig));

			calc_box(row, col, nrow, ncol, &tsr, &tsc, nrtiny, nctiny);

			star_axes(conv_image, 0, 0, tsr, tsc, nrtiny, nctiny, 
						sky, &xc, &yc, &fwhm, &xwidth, &ywidth);

			if (verbose)
					fprintf(outfp, "    candidate at (%.2f, %.2f)  fwhm %.2f \n", xc, yc, fwhm);

			if ((fabs(xc - row) > movemax) || (fabs(yc - col) > movemax)) {
				if (verbose)
					fprintf(outfp, "    rejecting star because peak moved by more than %4.1f pixel\n", movemax);
				continue;
			}
				

			/* depending on the results, call it a star or don't */
			if (valid_fwhm(fwhm, minfwhm, maxfwhm) == NO)
				continue;
#ifdef STATS
			c_1validfwhm++;
#endif
			orig_xc = xc;
			orig_yc = yc;


			/* there used to be a loop here, but I deleted it - it slowed
			   things down and threw out some good stars.  MWR */
			/* added checks on 'irow' and 'icol' to make sure they're
			   inside the image.  MWR 12/13/92 */
			irow = (int)(xc + 0.5);
			if (irow >= sr + nr)
				irow = (sr + nr) - 1;
			icol = (int)(yc + 0.5);
			if (icol >= sc + nc)
				icol = (sc + nc) - 1;



			if ISBITON(bytemap, irow, icol) {
#ifdef STATS
				c_biton++;
#endif
				if(verbose)
				fprintf(outfp,"    rejecting star at (%4d, %4d) because already marked\n", irow, icol);
				continue;
			}


			if (valid_fwhm(fwhm, minfwhm, maxfwhm) == NO) {
#ifdef STATS
				c_2validfwhm++;
#endif
				continue;
			}

			round = 2.0 * ((xwidth - ywidth) / (xwidth + ywidth));
			if ((round < minround) || (round > maxround)) {
#ifdef STATS
				c_round++;
#endif
				if(verbose)
					fprintf(outfp,"   rejected roundness=%.2f\n",round);
				continue;
			}

			sharp = sharpness(irow, icol, sr, sc, nr, nc);
			if ((sharp < minsharp) || (sharp > maxsharp)) {
#ifdef STATS
				c_sharp++;
#endif
				if(verbose)
					fprintf(outfp,"    rejected sharpness=%.2f\n",sharp);
				continue;
			}
					
			if (verbose)
				fprintf(outfp, "    candidate at (%.2f, %.2f) will be marked as star \n", xc, yc);
			if (showflag) {
#ifdef XWINDOWS
				do_crosshair(irow, icol);
#endif
			}
			fprintf(outfp, "%5d %7.2f   %7.2f   %5d   %6.3f    %6.3f   %6.3f \n",
					++num_star, xc, yc,	conv_image[irow][icol] - sky, 
					fwhm, round, sharp);
#ifdef STATS
			c_star++;
#endif

			/* if it WAS a star, mark out all the pixels around it */
			markpix(sr, sc, er, ec, xc, yc, fwhm);

		   if(num_star == maxstars)
			error(1,"stars: too many stars found");
		
		}
	}

}

/*
	return 1 if the given row and col within the image is a local
	maximum, or 0 if not.  

	modified 5/27/92 by MWR: now check that given pixel is highest 
	peak with one peakrad of itself, not just that it is higher than
	nearest four neighbors. 

	modified 6/3/92 by RRT to avoid the edges
*/


static int
is_peak(r, c, sr, sc, nr, nc)
int r, c, sr, sc, nr, nc;
{
	int val, ssr, ssc, er, ec, i, j;

#ifdef DO_EDGE
	if ((ssr = r - peakrad) < sr)
		ssr = sr;
	if ((ssc = c - peakrad) < sc)
		ssc = sc;
	if ((er = r + peakrad) > (sr+nr))
		er = sr+nr;
	if ((ec = c + peakrad) > (sc+nc))
		ec = sc+nc;
#else
	if ((ssr = r - peakrad) < sr)
		return 0;
	if ((ssc = c - peakrad) < sc)
		return 0;
	if ((er = r + peakrad) > (sr+nr))
		return 0;
	if ((ec = c + peakrad) > (sc+nc))
		return 0;
#endif
	
	val = conv_image[r][c];

	for (i = ssr; i < er; i++)
		for (j = ssc; j < ec; j++)
			if (val < conv_image[i][j])
				return(0);

	return(1);
}


	/* return the 'SHARPNESS' index of the star, given by

	(image value at star centroid) - (mean of image values around centroid)
	-----------------------------------------------------------------------
	                (value of convolved image H at star centroid)

	   if can't find any values around the centroid, return BADVAL
	   (which is very large). 

	6/3/92 - edge bug fix -rrt
	*******/

static double
sharpness(r, c, sr, sc, nr, nc)
int r, c, sr, sc, nr, nc;
{
	static long sum;
	int i, j, ssr, ssc, er, ec, num;
	double x;

	sum = 0;
	if ((ssr = r - peakrad) < sr)
		ssr = sr;
	if ((ssc = c - peakrad) < sc)
		ssc = sc;
	if ((er = r + peakrad) > (sr+nr))
		er = sr+nr;
	if ((ec = c + peakrad) > (sc+nc))
		ec = sc+nc;

	num = 0;
	for (i = ssr; i < er; i++) {
		for (j = ssc; j < ec; j++) {
			sum += conv_image[i][j] - sky;
			num++;
		}
	}
	sum -= (conv_image[r][c] - sky);
	num--;
	if (num > 0)
		sum /= num;
	x = conv_image[r][c] - sky;

	if (x > 0)
		return((x - sum)/x);
	else
		return(BADVAL);
}


/*
	mark all the pixels within a radius of peakrad around a star centroid 
	as "taken", so that if another star is found here, we won't report it.

	this routine used to mark all stars within one FWHM as taken, but I think
	that's not as good as this - in some cases, two very close stars would
	be missed by that approach.

	'xc' is the row coord of the star 
	'yc' is the col coord ditto 
*/

static void
markpix(sr, sc, er, ec, xc, yc, fwhm)
int sr, sc, er, ec;
double xc, yc, fwhm;
{
	static int i, j, msr, msc, mer, mec, skynoise;

	skynoise = sky + (int) (minsig*skysig);


	/* this is the old version */
/*  
 *	if ((msr = (int)(xc - fwhm)) < sr)
 *		msr = sr;
 *	if ((msc = (int)(yc - fwhm)) < sc)
 *		msc = sc;
 *	if ((mer = (int)(xc + fwhm)) > er)
 *		mer = er;
 *	if ((mec = (int)(yc + fwhm)) > ec)
 *		mec = ec;
 */
	/* here is the new version */
 	if ((msr = (int)(xc - peakrad)) < sr)
		msr = sr;
 	if ((msc = (int)(yc - peakrad)) < sc)
 		msc = sc;
 	if ((mer = (int)(xc + peakrad)) > er - 1)
 		mer = er - 1;
 	if ((mec = (int)(yc + peakrad)) > ec - 1)
 		mec = ec - 1;


#ifdef DEBUG2
	printf("          in markpix, sr=%d er=%d, sc=%d ec=%d\n", sr, er, sc, ec);
#endif
	for (i = msr; i <= mer; i++) {
		for (j = msc; j <= mec; j++) {
			TURNON(bytemap, i, j);
#ifdef DEBUG2
	printf("          marking pixel (%5d, %5d) = %d > %d in bytemap \n", i, j,
						image[i - sr][j - sc], skynoise);
#endif
		}
	}
}


	/* return YES if the given fwhm is in between the two limits, NO otherwise */

static int
valid_fwhm(fwhm, minfwhm, maxfwhm)
double fwhm, minfwhm, maxfwhm;
{
	if (fwhm < minfwhm) {
		if (verbose)
			fprintf(outfp, "    rejected because fwhm %.2f is too small \n", fwhm);
		return(NO);
	}
	else if (fwhm > maxfwhm) {
		if (verbose)
			fprintf(outfp, "    rejected because fwhm %.2f is too big \n", fwhm);
		return(NO);
	}
	else
		return(YES);
}


	/* calculate the starting row and col of a box which has 'rsize2' rows
	   and 'csize2' columns, which is centered on pixel (row, col) 
	   as closely as possible. place the starting
	   row and column of the tinybox (in image coords)
	   into '*tsr' and '*tsc'. 

	   the user is responsible for making sure that the 
	   box is smaller than the image. */

static void
calc_box(row, col, nrow, ncol, tsr, tsc, rsize2, csize2)
int row, col, nrow, ncol, *tsr, *tsc, rsize2, csize2;
{
	static unsigned int npix;

#ifdef DEBUG2
  printf("into calc_box: row=%d col=%d rs1=%d cs1=%d sr=%d sc=%d rs2=%d rc2=%d\n",
        row, col, nrow, ncol, *tsr, *tsc, rsize2, csize2);
#endif
	npix = csize2;

	if (row < (rsize2 / 2))
		*tsr = 0;
	else if ((row + (rsize2/2) + 1) > nrow)
		*tsr = nrow - rsize2;
	else
		*tsr = row - (rsize2/2);
	if (col < (csize2 / 2))
		*tsc = 0;
	else if ((col + (csize2/2) + 1) > ncol)
		*tsc = ncol - csize2;
	else
		*tsc = col - (csize2/2);

#ifdef DEBUG2
	printf("here's the data in the tinybox \n");
	for (i = *tsr; i < *tsr + rsize2; i++) {
		for (j = *tsc; j < *tsc + csize2; j++) {
			printf("%5d ", image[i][j]);
		}
		printf("\n");
	}
#endif
	
}


	/* try to allocate 'nr' rows of 'nc' bytes each.
	   the user must make sure that the array of pointers has 'nr' elements.
	   also, set the elements all to zero. */

static void
allo_map(pointer, nr, nc)
char *pointer[];
int nr, nc;
{
	static char *p, *q;
	int i;

	if ((p = (char *) calloc(nr * nc, sizeof(char))) == NULL) {
		error(-1 , "can't allocate space for bytemap");
	}
	q = p;
	for (i = 0; i < nr; i++) {
		pointer[i] = q;
		q += nc;
	}
		
#ifdef DEBUG
	printf("managed to allocate space for all %d cols of %d chars\n", nr, nc);
#endif
}


	/* try to allocate 'nr' rows of 'nc' int16's each.
	   the user must make sure that the array of pointers has 
	   'nr' elements 

	   If 'quickflag' == 0, then we will convolve the image and we'll
	   need a second chunk of memory for the convolved version, so allocate 
	   two arrays, one for the real image and one for the convolved one.
	   If 'quickflag' == 1, then we only need one, so DON'T allocate
	   any memory for the second one (we'll just set it to point to the
	   first). */

static void
allo_image(pointer1, pointer2, nr, nc, quickflag)
int16 *pointer1[];
int16 *pointer2[];
int nr, nc, quickflag;
{
	static int16 *p, *pp, *q;
	int i;

	if ((p = (int16 *) malloc(nr * nc * sizeof(int16))) == NULL)
		error(-1, "can't allocate space for image");
	pp = p;
	q = p;
	for (i = 0; i < nr; i++) {
		pointer1[i] = q;
		q += nc;
	}

	if (quickflag == 0) {
		if ((p = (int16 *) malloc(nr * nc * sizeof(int16))) == NULL)
			error(-1, "can't allocate space for convolved image");
		pp = p;
		q = p;
		for (i = 0; i < nr; i++) {
			pointer2[i] = q;
			q += nc;
		}
	}
}

	/* read in all the data from the FITS file and put it into the
	   'image' array.  Note that row 0 of the image array DOES 
	   correspond to the true row 0 of the image - if the
	   user has specified a box subset of the image, then row 'sc' of the
	   image array is the first row of that box. 

	   this is modified from the original, which only read in the box
	   of data if one was specified.  But that leads to code that's too
	   hard to maintain, and we've got memory to burn.  MWR 5/27/92. */


static int 
read_image(nrow, ncol)
int nrow, ncol;
{
	int i;

	for (i = 0; i < nrow; i++) {
		if (fits_get_data(fh, i, 0, image[i], ncol) == FITS_FAIL) {
			fprintf(stderr, "error reading data from row %d\n", i);
			exit(1);
		}
	}			

	return(0);
}


	/* just copy the image into the convolved image - for quick use.
	   we make the conv_image array pointers point to the image array
	   pointers.  */

static void 
copy_image()
{
	int i;

	for (i = 0; i < nrow; i++)
		conv_image[i] = image[i];
}


static void timeout_handler(x)
int x;
{
	fprintf(stderr,"stars: file=%s timeout=%d exceeded \n",
		fname, timeout_time);
	exit(1);
}

	/* all of the following should only be included if the
	   XWINDOWS flag is set */

#ifdef XWINDOWS

/*	
	causes the crosshair to appear in all windows. There is no good way to make
	sure that all windows receive the signal SHOW_CROSSHAIR; 
	all the routine does
	now is set the window state to SHOW_CROSSHAIR, wait for a few seconds, 
	and then re-set the window_state to NORMAL.  
	I hope it works out in practice..
	         - MWR 4/8/92

	after Dick's successful experiment with usleep() in MARKS.C, I've
    switched from 
	                  sleep(1)        sleep one second
	to
	                 usleep(50000)    sleep 0.05 seconds
	under most circumstances, this seemst to be enough.  But modify
	UNAPTIME as necessary.  
	          - MWR 10/3/92.


*/

#define UNAPTIME     50000

static Display *display;
static Atom atom_state, actual;
static int data[PROP_NITEMS];
static struct s_prop prop;

#define MAX_TRIES   1


	/* returns 0 if all goes well */

static int
do_crosshair(row, col)
int row, col;
{
	int i;
	static int flag = 0;

	if (flag == 1)
		return(1);

	if ((display = XOpenDisplay("")) == NULL) {
		flag = 1;
		fprintf(stderr,"you must be running X-windows");
		return(1);
	}
	atom_state = XInternAtom(display, ATOM_STATE, False);
	XGetWindowProperty(display, DefaultRootWindow(display), atom_state,
			(long) 0, (long) PROP_NITEMS, False, XA_INTEGER, &actual,
			&(prop.format), &(prop.nitems), &(prop.bytes), 
#if XGETUNSIGNED == 1
			(unsigned char **) &(prop.data));
#else
			(char **) &(prop.data));
#endif
	for (i = 0; i < PROP_NITEMS; i++)
		data[i] = prop.data[i];

	data[1] = STATE_CROSSHAIR;
	data[2] = row;
	data[3] = col;

	signal(SIGINT, sig_trap);
	signal(SIGQUIT, sig_trap);
	signal(SIGTERM, sig_trap);

	XChangeProperty(display, DefaultRootWindow(display), atom_state,
			XA_INTEGER, PROP_FORMAT, PropModeReplace, 
			(unsigned char *) data, PROP_NITEMS);
	XFlush(display);

	vista_usleep(UNAPTIME);

	data[1] = STATE_NORMAL;
	for (i = 2; i < PROP_NITEMS; i++)
		data[i] = 0;
	XChangeProperty(display, DefaultRootWindow(display), atom_state,
			XA_INTEGER, PROP_FORMAT, PropModeReplace, 
			(unsigned char *) data, PROP_NITEMS);

	XCloseDisplay(display);

	return(0);
}

	/* change the property back to NORMAL so that image windows don't 
	   wait forever for ackowledgment. */

static 
void
sig_trap(x)
int x;
{
	int i;

	XGetWindowProperty(display, DefaultRootWindow(display), atom_state,
			(long) 0, (long) PROP_NITEMS, False, XA_INTEGER, &actual,
			&(prop.format), &(prop.nitems), &(prop.bytes), 
#if XGETUNSIGNED == 1
			(unsigned char **) &(prop.data));
#else
			(char **) &(prop.data));
#endif

	for (i = 0; i < PROP_NITEMS; i++)
		data[i] = prop.data[i];
	data[1] = STATE_NORMAL;

	XChangeProperty(display, DefaultRootWindow(display), atom_state,
			XA_INTEGER, PROP_FORMAT, PropModeReplace, 
			(unsigned char *) data, PROP_NITEMS);

	XCloseDisplay(display);
	exit(0);

}

#endif 		/* XWINDOWS */


