
	/* some miscellaneous functions used by several programs */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include "bait.h"
#include "pf.h"

#ifdef VERBOSE
extern char *progname;
#endif

static int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

int read_date( /* char *str, int *day, int *month, int *year */ );
int since_1950( /* int day, month, year */ );
int read_min_priority( /* int *priority */ );
int read_max_priority( /* int *priority */ );
int read_timestr( /* char *str, double *fhour */ );
int write_timestr( /* double fhour, char *str */ );
int mag2exptime( /* double magnitude, sigtonoise, char *filter, 
                         double *exptime */ );
void error( /* int level, char *message */ );
int read_longitude( /* double *longitude */ );
int read_latitude( /* double *latitude */ );
int read_halimits( /* double *east_limit, *west_limit */ );
int read_min_priority( /* int *priority */ );
int read_max_priority( /* int *priority */ );
char * find( /* char *lval, *rval */ );
int keyword( /* char *lval, *rval */ );	
void str_to_lower( /* char *string */ );
void str_to_upper( /* char *string */ );

	/* translate the pf_string (i.e. one that still has single-quote marks
	   around it) of form "DD/MM/YYYY" into numerical values for
	   the day, month and year. returns -1 if there's a problem, or 0
	   if all went well. */ 

int 
read_date(val, day, month, year)
char *val;
int *day, *month, *year;
{
	char str[PF_LEN + 1];

	if (pf_valtostr(val, str) < 0)
		return(-1);
	if (is_date(str) != 0) 
		return(-1);
	if (sscanf(str, "%d/%d/%d", day, month, year) != 3) 
		return(-1);
	return(0);
}

	/* return the number of days between the given date and Jan 0, 1950 */

int 
since_1950(day, month, year)
int day, month, year;
{
	int i, sum;

	sum = 0;
	for (i = 1950; i < year; i++) {
		sum += 365;
		if ((i % 4 == 0) && (i % 400 != 0))
			sum++;
	}
	for (i = 1; i < month; i++) {
		sum += days[i - 1];
		if ((i == 2) && (year % 4 == 0) && (year % 400 != 0))
			sum++;
	}
	sum += day;
	return(sum);
}

	/* convert a time, which was expressed as a string of form HH:MM:SS, 
	   to a single quantity which is simply the fractional hours
	   seconds since 00:00:00.  If there is some problem, return -1. 
	   Otherwise, return 0. */

int
read_timestr(str, fhour)
char *str;
double *fhour;
{
	int hour, minute, second;

	if (sscanf(str, "%2d:%2d:%2d", &hour, &minute, &second) != 3)
		return(-1);
	if ((hour < 0) || (hour > 23) || (minute < 0) || (minute > 59) ||
	    (second < 0) || (second > 59))
		return(-1);

	*fhour = (double)hour + (double)minute/60.0 + (double)second/3600.0;
	return(0);
}

	/* convert a time from fractional hours to a string of the form
       HH:MM:SS, exactly 8 characters long, plus a null character at the
	   end. return 0. */

int
write_timestr(fhour, str)
double fhour;
char *str;
{
	int hour, minute, second;

	hour = fhour;		/* simple truncation */
	minute = (fhour - hour)*60.0;	
	second = ((fhour - hour) - minute/60.0)*3600.0;
	sprintf(str, "%02d:%02d:%02d", hour, minute, second);
	return(0);
}




	/* convert the given magnitude and filter combination to an exposure time.
	   this is VERY approximate, so don't count on it too much.

	   exposure times based on the following rough figure: through a filter 
	   which is 1000 Angstroms wide (from 4000-5000 Angstroms),

	        vega (star mag 0)   has   1.25e6  photons/(sq.cm*s)
 
       the calculation ignores spectral shape, uses ONLY the width. for
	   details of the calculation, see "A Brief Look at the Problems of
	   Guiding," Michael Richmond, Proc. Electronically-Oriented Astronomers 
	   Seminar 3, 1990.

	   returns the 0 if all went well, or -1 if there is some problem 
	   (invalid filter, etc.). The calculated exposure time is placed
	   in variable pointed to by the final argument. */

#ifndef PI
#define PI      3.14159
#endif  PI

#define VEGACOUNTS    1.24e6		/* # counts/(sq.cm*s) per 1000 Angstroms */
                            		/*   for a canonical zero mag star */
#define FWHM             3.0		/* fwhm of stellar images (in arcsec) */ 
#define SKYMAG          17.0		/* sky brightness, mag/square arcsec */
#define QE               0.30		/* efficiency of mirrors, windows, etc. */
#define SIG2NOISE      100.0		/* default value for signal-to-noise */
#define WELLFRAC         0.50		/* max safe fraction of fullwell capacity */

int
mag2exptime(magnitude, sigtonoise, filter, exptime)
double magnitude, sigtonoise, *exptime;
char *filter;
{
	int i, filtcount, nfilter;
	double scale, pixsize1, pixsize2, time, npixels, shutmin, area;
	double well, arc_pix, nstar, nsky, nr, nd, f;
	double x, y, a, b, c, z;
	double eqwidth, lambda;
	char val[PF_LEN + 1], buf[PF_LEN + 1];
	pf_handle pf_config;
	
	if (sigtonoise == 0.0)
		sigtonoise = SIG2NOISE;

	if (is_filter(filter) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: invalid filter ..%s..\n", 
				progname, filter);
#endif
		return(-1);
	}

	if ((pf_config = pf_open(CONFIG_FILE, 1, PF_EXIST)) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't open config file %s\n", 
				progname, CONFIG_FILE);
#endif
		return(-1);
	}
	if ((pf_getval(pf_config, "TELAREA", val) == NULL) ||
	    (pf_valtodouble(val, &area) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't read TELAREA from config file\n",
				progname);
#endif
		pf_close(pf_config);
		return(-1);
	}
	if ((pf_getval(pf_config, "TELSCALE", val) == NULL) ||
	    (pf_valtodouble(val, &scale) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't read TELSCALE from config file\n",
				progname);
#endif
		pf_close(pf_config);
		return(-1);
	}
	if ((pf_getval(pf_config, "CCDREADN", val) == NULL) ||
	    (pf_valtodouble(val, &nr) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't read CCDREADN from config file\n",
				progname);
#endif
		pf_close(pf_config);
		return(-1);
	}
	if ((pf_getval(pf_config, "CCDDARKC", val) == NULL) ||
	    (pf_valtodouble(val, &nd) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't read CCDDARKC from config file\n",
				progname);
#endif
		pf_close(pf_config);
		return(-1);
	}
	if ((pf_getval(pf_config, "CCDPIX", val) == NULL) ||
	    (pf_valtostr(val, buf) == PF_ERROR) ||
	    (sscanf(buf, "%lf %lf", &pixsize1, &pixsize2) != 2)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't read CCDPIX from config file\n",
				progname);
#endif
		pf_close(pf_config);
		return(-1);
	}
	/* use the geometric mean  of the pixel sizes in each dimension as 
	   the effective size */
	pixsize1 = sqrt(pixsize1*pixsize2);

	if ((pf_getval(pf_config, "CCDWELL", val) == NULL) ||
	    (pf_valtodouble(val, &well) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't read CCDWELL from config file\n",
				progname);
#endif
		pf_close(pf_config);
		return(-1);
	}
	if ((pf_getval(pf_config, "SHUTMIN", val) == NULL) ||
	    (pf_valtodouble(val, &shutmin) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't read SHUTMIN from config file\n",
				progname);
#endif
		pf_close(pf_config);
		return(-1);
	}
	if ((pf_getval(pf_config, "NFILTERS", val) == NULL) ||
	    (pf_valtoint(val, &nfilter) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't read NFILTERS from config file\n",
				progname);
#endif
		pf_close(pf_config);
		return(-1);
	}
	for (i = 0, filtcount = 0; (i < 99) && (filtcount < nfilter); i++) {
		sprintf(buf, "FILTER%02d", i);
		if (pf_getval(pf_config, buf, val) != NULL) {
			filtcount++;
			if (pf_valtostr(val, buf) == PF_ERROR) {
#ifdef VERBOSE
				fprintf(stderr, "%s.mag2exptime: invalid filter name ..%s..\n",
						progname, val);
#endif
				pf_close(pf_config);
				return(-1);
			}
			if (strncmp(filter, buf, strlen(filter)) == 0) {
				break;
			}
		}
	}
	if (sscanf(buf, "%s %lf %lf", val, &lambda, &eqwidth) != 3) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: bad filter parameters ..%s..\n",
				progname, buf);
#endif
		pf_close(pf_config);
		return(-1);
	}

	if (pf_close(pf_config) == PF_ERROR) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: can't pf_close config file %s\n",
				progname, CONFIG_FILE);
#endif
		return(-1);
	}

	/* okay, at this point we have
	         1. telescope collecting area in sq. cm.
	         2. telescope plate scale (arcsec/mm)
	         3. CCD read noise (electrons per pixel)
	         4. CCD dark current (electrons/pix per second)
	         5. CCD scale (microns/pixel)
	         6. object magnitude
	         7. filter equivalent width
	   so we can calculate something which might give us the canonical 
	   signal to noise ratio.  

	   in the future, maybe use a more sophisticated manner of 
	   sampling or calculating the night sky brightness and seeing. */

	nstar = VEGACOUNTS*pow(10.0, -0.4*magnitude)*(eqwidth/1000.0);
	nsky = VEGACOUNTS*pow(10.0, -0.4*SKYMAG)*(eqwidth/1000.0);

	/* arcseconds per pixel */
	arc_pix = (scale/1000.0)*pixsize1;

	/* f = # of pix over which star spreads */
	f = PI*(FWHM*FWHM/arc_pix*arc_pix);	

	a = (nstar*nstar)*QE*QE*area*area;
	b = -(sigtonoise*sigtonoise)*(nd + QE*area*(nstar + nsky*f));
	c = -(sigtonoise*sigtonoise)*f*nr*nr;
	z = b*b - 4*a*c;
	if (z < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.mag2exptime: mag %lf, filter %s -> imag. time\n",
				progname, magnitude, filter);
#endif	
		return(-1);
	}	
	x = (-b + sqrt(z))/(2*a);

	/* so 'x' is how long it takes to get the desired Signal-to-Noise
	   ratio. But now, let's check to see if we come close to saturating
	   the chip. If so, cut the exposure so that it should come to only
	   WELLFRAC of the total well capacity, for safety's sake.  in our
	   calculations, ASSUME that each pixel inside the FWHM gets an equal
	   share of the total number of photons coming from the star.  */

	/* this is total number of electrons in each pixel per second
	   (ignoring readnoise, which had BETTER be negligible here!)  */
	y = nd + (nsky + nstar/f)*(arc_pix*arc_pix);
	/* so this is how long it takes to fill the well to WELLFRAC of its
	   total capacity */
	y = (well*WELLFRAC)/y;
	
	/* pick 'x', the time that give desired S/N, if possible - but if it
	   would saturate the chip, choose the time 'y' instead */
	time = (x < y ? x : y);

	if (time < shutmin)
		*exptime = shutmin;	
	else
		*exptime = time;

	return(0);
}


	/*	writes error message to stderr then exits if 'err' is non-zero */

void 
error(err, message)
int err;
char *message;
{

	fprintf(stderr,"\n%s\n",message);
	if (err)
		exit(err);
}

	/* read the value of the observer's longitude (in decimal hours
	   west of Greenwich) into the passed variable. return 0 if all
	   OK, or -1 if there is some problem. */

int
read_longitude(longitude)
double *longitude;
{
	char val[PF_LEN + 1];
	pf_handle pf_config;
	static double l = -1.0;

	if (l != -1.0) {
		*longitude = l;
		return(0);
	}
		
	if ((pf_config = pf_open(CONFIG_FILE, 1, PF_EXIST)) == PF_ERROR) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_longitude: can't open config file ..%s..\n", progname,
				CONFIG_FILE);
#endif
		return(-1);
	}
	if ((pf_getval(pf_config, "OBSLONGH", val) == NULL) ||
	    (pf_valtodouble(val, &l) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_longitude: can't read OBSLONGH \n", progname);
#endif
		if (pf_close(pf_config) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.read_longitude: can't close config file ..%s..\n",
					progname, CONFIG_FILE);
#endif
		}
		return(-1);
	}	
	if (pf_close(pf_config) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_longitude: can't close config file ..%s..\n",
				progname, CONFIG_FILE);
#endif
		return(-1);
	}
	
	*longitude = l;
	return(0);
}

	/* read the value of the observer's latitude (in decimal degrees
	   north of equator) into the passed variable. return 0 if all
	   OK, or -1 if there is some problem. */

int
read_latitude(latitude)
double *latitude;
{
	char val[PF_LEN + 1];
	static double lat = 100.0;
	pf_handle pf_config;

	if (lat != 100.0) {
		*latitude = lat;
		return(0);
	}
		
	if ((pf_config = pf_open(CONFIG_FILE, 1, PF_EXIST)) == PF_ERROR) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_latitude: can't open config file ..%s..\n", progname,
				CONFIG_FILE);
#endif
		return(-1);
	}
	if ((pf_getval(pf_config, "OBSLAT", val) == NULL) ||
	    (pf_valtodouble(val, &lat) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_latitude: can't read OBSLAT \n", progname);
#endif
		if (pf_close(pf_config) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.read_latitude: can't close config file ..%s..\n",
					progname, CONFIG_FILE);
#endif
		}
		return(-1);
	}	
	if (pf_close(pf_config) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_latitude: can't close config file ..%s..\n",
				progname, CONFIG_FILE);
#endif
		return(-1);
	}
	*latitude = lat;

	return(0);
}

	/* read the value of the observatory hour limits (in decimal degrees)
	   into the passed variables. return 0 if all OK, or -1 if there is 
	   some problem. */

int
read_halimits(east, west)
double *east, *west;
{
	char val[PF_LEN + 1];
	static double el = 400.0, wl = 400.0;
	pf_handle pf_config;

	if (el != 400.0) {
		*east = el;
		*west = wl;
		return(0);
	}
		
	if ((pf_config = pf_open(CONFIG_FILE, 1, PF_EXIST)) == PF_ERROR) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_halimits: can't open config file ..%s..\n", progname,
				CONFIG_FILE);
#endif
		return(-1);
	}
	if ((pf_getval(pf_config, "TELLIME", val) == NULL) ||
	    (pf_valtodouble(val, &el) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_halimits: can't read TELLIME \n", progname);
#endif
		if (pf_close(pf_config) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.read_halimits: can't close config file ..%s..\n",
					progname, CONFIG_FILE);
#endif
		}
		return(-1);
	}	
	if ((pf_getval(pf_config, "TELLIMW", val) == NULL) ||
	    (pf_valtodouble(val, &wl) == PF_ERROR)) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_halimits: can't read TELLIMW \n", progname);
#endif
		if (pf_close(pf_config) < 0) {
#ifdef VERBOSE
			fprintf(stderr, "%s.read_halimits: can't close config file ..%s..\n",
					progname, CONFIG_FILE);
#endif
		}
		return(-1);
	}	
	if (pf_close(pf_config) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_halimits: can't close config file ..%s..\n",
				progname, CONFIG_FILE);
#endif
		return(-1);
	}
	*east = el;
	*west = wl;

	return(0);
}

	/* put the least-important priority into the passed integer. return 0
	   if all went well, or -1 if there was a problem */

int
read_min_priority(priority)
int *priority;
{
	char val[PF_LEN + 1];
	pf_handle pf_config;

	if ((pf_config = pf_open(CONFIG_FILE, 1, PF_EXIST)) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_min_priority: can't open config file %s\n",
				progname, CONFIG_FILE);
#endif
		return(-1);
	}
	if (pf_getval(pf_config, "MINPRIOR", val) == NULL) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_min_priority: can't read MINPRIOR from config file %s\n", 
				progname, CONFIG_FILE);
#endif
		return(-1);
	}
	if (pf_valtoint(val, priority) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_min_priority: bad value %s for MINPRIOR in config file %s\n", 
				progname, val, CONFIG_FILE);
#endif
		return(-1);
	}
	return(0);
}

	/* put the most-important priority into the passed integer. return 0
	   if all went well, or -1 if there was a problem */

int
read_max_priority(priority)
int *priority;
{
	char val[PF_LEN + 1];
	pf_handle pf_config;

	if ((pf_config = pf_open(CONFIG_FILE, 1, PF_EXIST)) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_max_priority: can't open config file %s\n",
				progname, CONFIG_FILE);
#endif
		return(-1);
	}
	if (pf_getval(pf_config, "MAXPRIOR", val) == NULL) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_max_priority: can't read MAXPRIOR from config file %s\n", 
				progname, CONFIG_FILE);
#endif
		return(-1);
	}
	if (pf_valtoint(val, priority) < 0) {
#ifdef VERBOSE
		fprintf(stderr, "%s.read_max_priority: bad value %s for MAXPRIOR in config file %s\n", 
				progname, val, CONFIG_FILE);
#endif
		return(-1);
	}
	return(0);
}


/*	
	finds the right hand value of the equality
	returns a pointer to first character after equal sign
	or NULL if not successful
*/


char *
find(lval, rval)
char *lval, *rval;
{
	int len;
	
	len = strlen(lval);
	if ((strncmp(lval, rval, len) == 0) && (rval[len] == '='))
		return(rval + len + 1);
	return(NULL);	
}

int 
keyword(lval, rval)		/* returns non-zero when 'lval' is found in 'rval' */
char *lval, *rval;
{
	return(!strcmp(lval, rval));
}


	/* turn the given string into the lower-case version of itself */

void
str_to_lower(str)
char *str;
{
	static int diff = 'A' - 'a';
	char *p;

	for (p = str; *p != '\0'; p++) {
		if (isupper(*p))
			*p -= diff;
	}
}

	/* turn the given string into the upper-case version of itself */

void
str_to_upper(str)
char *str;
{
	static int diff = 'A' - 'a';
	char *p;

	for (p = str; *p != '\0'; p++) {
		if (isupper(*p))
			*p += diff;
	}
}
