home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
calcultr
/
sun.arc
/
SUN.C
next >
Wrap
C/C++ Source or Header
|
1988-03-17
|
21KB
|
722 lines
/***************************************************************************
**
** SUN.C Version 1.0 Michael Schwartz December 25, 1984
**
** This program uses the USNO algorithm to calculate sunrise and sunset
** times from standard lattitudes and longitudes. It has been set up as
** system independently as possible, with the exception being a function
** which gets the current (default) date and day of the year. It is meant
** to be particularly useful for Jewish persons in out-of-the-way locations
** who might need to know the time of candle_lighting or havdalah. With
** little difficulty, z'man k'riath shema can be calculated and added to the
** routines.
**
** Inquiries can reach me at PO BOX 24536, Denver, Colorado 80224
** (a place no longer 'out of the way')
**
** Ref: Almanac for Computers, pub. by US NAVAL OBSERVATORY
** Usage:
** sun [-c] [-l[lat]] [-L[long]] [-M] [-m[1-12|a]] [-d[day]]
** [-t<S|C|N|A>] [-<12|24>]
** Latitude in deg,min'sec" N; Longitude in deg,min'sec" W
**
** Accuracy: The USNO claims accuracy withing 2 minutes except at extreme
** northern or southern lattitudes. Comparison to local NWS charts for
** sunrise and sunset (which are cheap and easy to come by) shows that with
** the double precision calculations, the charts produced by this program
** are no more than 1 minute removed from those charts in lattitudes lower
** than 41 degrees. Candle lighting times agree with those on popular
** calendars also to the 1 minute accuracy.
**
** The program, unlike its fortran predecessor has a number of
** important options and defaults. It is capable of getting today's
** sunrise and sunset at a default location (now Denver, of course),
** producing a calendar-like table of a month or a year, and allowing the
** user to produce reams of data without reinvoking the program.
**
** Permission is granted to distribute and use this program as desired.
**
** Bugs: Automatic calculation of non-default time zones will not produce
** correct local times (using the APPROXIMATE_TZ define). Otherwise,
** when lattitudes and longitudes are specified, the result is in Zulu (UT).
** No method of calculating DST or aligning years to Fridays/Saturdays
** or holidays has been provided in the MS-DOS version: However,
** as more MS-DOS compilers support the ANSI time standards, the MS-DOS
** option may not be necessary for your compiler.
**************************************************************************/
#include <stdio.h>
#ifdef UNIX
#define LONG int
#ifdef MSDOS
#undef MSDOS
#endif
#else
#define LONG long
#define MSDOS
#define FUNC_8086 sysint21
#define INT_8086 bdos
/*
** The last two are currently set for the CI-86 compiler. They are the
** machine interrupt for function calls (function 21) and the general
** interrupt with return from AL respectively. Change for other compilers.
** In fact, modern compilers should all be able to use the UNIX def.
*/
#endif
#define TWELVE 1
#define TWENTYFOUR 0
#define SUNSET 0
#define CIVIL_TWILIGHT 1
#define NAUTICAL_TWILIGHT 2
#define ASTRONOMICAL_TWILIGHT 3
int myclock = TWELVE;
/* Chooses a 12 hour clock */
static char *location = "Denver, Colorado (East Side)";
/* Defines the location for the report header */
int longdeg=104, longmin=54, longsec=16, latdeg=39, latmin=41, latsec=45;
/* Lattitude and longitude of east Denver, Colorado */
int TZ = -7;
/* This is the difference between MST and GMT */
#define BAD_ARG -1
#define NOT_IMPLEMENTED -2
#define BAD_MONTH -3
#define BAD_DATE -4
#define NORMAL 0
#define NO_SUNSET 1
#define NO_SUNRISE 2
static char *months[] = {
"Nonesuch",
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
static int count[] = {
1,32,60,91,121,152,182,213,244,274,305,335,366
};
/*
** Note -- this is for a 365 day year. The best possible approximation.
** However, if one asks for 2/29, the sheet will still show it.
*/
int cal_opt, month_opt, day_opt, multi_opt, lat_opt, long_opt, ang_opt=0;
int j_opt;
int month_num, month_day, first_day, last_day;
main(argc,argv)
int argc;
char *argv[];
{
double xsunrise, xsunset;
int j=0, day, day_adj, status;
/*
** day_adj and status are used for those embarrasing places where the sun
** doesn't rise and set once a day as in the middle latitudes.
** day_adj will be -1 if today's event really happened yesterday, and
** 1 if today's event won't happen until tomorrow.
** e.g, if today's sunrise occurred at 11:00PM yesterday.
** status will be 0 if NORMAL, NO_SUNRISE or NO_SUNSET if that event doesn't
** happen at all on that day (the range of an acos has been violated)
*/
char *sunrise, *sunset, *candle_light, *havdalah, *timeadj();
char *get_date(); /* Puts current date in a string of fixed length */
#define LIGHT -18
#define HAV 42
/* The offsets from sunset for candle lighting and havdalah */
/* Default is today */
first_day = last_day = get_current_day();
/* Handle args */
while (++j < argc)
{
if (argv[j][0] != '-')
{
prerror(BAD_ARG,argv[j]);
prusage();
}
else
{
switch (argv[j][1])
{
case 'c':
cal_opt++; break;
case 'M':
multi_opt++;
day_opt++; first_day = last_day = get_day("\0");
break;
case 'm':
month_opt++; month_num = get_month(argv[j]+2); break;
case 'd':
day_opt++;
first_day = last_day = get_day(argv[j]+2); break;
case 'l':
lat_opt++;
get_angle("Latitude",&latdeg,&latmin,&latsec,argv[j]+2); break;
case 'L':
long_opt++;
get_angle("Longitude",&longdeg,&longmin,&longsec,argv[j]+2);
#ifdef APPROXIMATE_TZ
TZ = -((longdeg*60 + longmin) + 450)/900; /* Approximation */
#else
TZ = 0; /* Use GMT */
#endif
break;
case 'j':
j_opt++; break;
case '1': /* Assume arg is -12 */
myclock = TWELVE; break;
case '2': /* Assume arg is -24 */
myclock = TWENTYFOUR; break;
case 't': /* Alternate sun angle for sunrise/set calculation */
if (argv[j][2] == 'S') /* Normal */
ang_opt=SUNSET;
else if (argv[j][2] == 'C') /* Civil Twilight */
ang_opt=CIVIL_TWILIGHT;
else if (argv[j][2] == 'N')
ang_opt=NAUTICAL_TWILIGHT;
else if (argv[j][2] == 'A')
ang_opt=ASTRONOMICAL_TWILIGHT;
else
prerror(BAD_ARG,argv[j]);
break;
default:
prerror(BAD_ARG,argv[j]);
}
}
}
if (cal_opt && (lat_opt || long_opt) && !multi_opt) /* New location */
get_new_location_name();
if (cal_opt && !multi_opt) prheader();
for (day=first_day;day<=last_day;day++)
{
status = suntime(&xsunrise,&xsunset,day);
/* Note: an "adjusted hourlength" is (xsunset - xsunrise)/12, unless this is
** less than 0; in that case, add 2. (24/12)
*/
if (status & NO_SUNRISE)
sunrise = "No sunrise today";
else
sunrise = timeadj("Sunrise at", xsunrise,0 ,&day_adj);
if (status & NO_SUNSET)
{
sunset = "No sunset today";
if (j_opt)
{
candle_light = "Candle lighting at **:**";
havdalah = "Havdalah at **:**";
}
}
else
{
sunset = timeadj("Sunset at", xsunset ,0 ,&day_adj);
if (j_opt)
{
candle_light = timeadj("Candle lighting at",xsunset ,LIGHT,&day_adj);
havdalah = timeadj("Havdalah at", xsunset ,HAV ,&day_adj);
}
}
if (cal_opt)
{
if (j_opt)
printf("%-12s %s %s %s %s\n",get_date(),
sunrise, candle_light, sunset, havdalah);
else
printf("%-12s %s %s\n",get_date(),sunrise,sunset);
}
else
{
if (j_opt)
printf("%s %d\n\t%s %s\n\t%s %s\n",
months[month_num],month_day,sunrise, sunset, candle_light, havdalah);
else
printf("%s %2d\n\t%s\t\t%s\n",
months[month_num],month_day,sunrise,sunset);
}
if (day == count[month_num]-1) /* We did last day of a month */
{
month_num++; month_day=1;
if (cal_opt && !multi_opt)
{
putchar('\f');
prheader();
}
}
else
{
if (cal_opt && !multi_opt)
{
if (!(month_day%10))
{
for (j=0;j<80;j++)
putchar('-');
putchar('\n');
}
}
month_day++;
}
if (!(status & NO_SUNRISE))
free(sunrise);
if (!(status & NO_SUNSET))
{
free(sunset);
if (j_opt)
{
free(candle_light); free(havdalah);
}
}
}
if (multi_opt)
exit(main(argc,argv));
}
/******************************************************************
C SUBROUTINES
C *****************************************************************
c this program will calCULATE SUNSET TIMES FOR EAST DENVER.
C LAMBDA = 104,54'30" W Longitude
C PHI = 39,42'0"N Latitude
C FOR 1978, M=.985600t -3.251 -- M is the Sun's mean anomaly
C L=M + 1.916 SIN(M) + .020SIN(2M) + 282.565 -- L is Sun's TRUE long.
C TANa = .91746 TAN L -- a is Sun's right ascension
C SIN DELTA = .39782 SIN L -- Sun's declination
C COS h = (COSz - SIN DELTA*SIN PHI)/(COS DELTA*COS PHI)
C T = h + a - 0.065710t -6.620 -- h is local hour angle, a now in hours
C UT = T + LAMBDA --UT is GMT. LAMBDA is now in hours (=deg/15)
C
C WHERE Z is the zenith distance at phenomena in question. Samples:
c Z=90,50' REGULAR SUNRISE/SET (Atmospheric refraction + disk diameter)
C Z=96, CIVIL TWILIGHT
C Z=102 NAUTICAL TWILIGHT
C Z=106 ASTRONOMICAL TWILIGHT
*************************************************************************/
#define PI 3.14159265
#define TODEC(a,b,c) ((double)a + (double)b/60.0 + (double)c/3600.0)
#define DEGRAD (PI/180.0)
#define RADDEG (180.0/PI)
#define D 0.91746
#define E 0.39782
#define M(x) (0.985600*x - 3.251)
#define L(x) (x + 1.916*sin(DEGRAD*x) + 0.020*sin(2*DEGRAD*x) + 282.565)
#define ADJ(x) (-0.065710*x - 6.620)
suntime(sunrise,sunset,day)
double *sunrise, *sunset;
int day;
/******************************************************************
** --- This is where the REAL work is done ---
** This routine will seem FORTRANy. An attempt has been made to
** mirror the procedure outlined in the USGS publication; very little
** optimization has been done, so that the connection between the
** publication and the software will be extremely clear.
** Thus, all angles are preserved in degrees (degrees are converted to
** hours by the observation that 360 degrees = 24 hours -- so 15.0
** degrees are 1 hour).
******************************************************************/
{
double lohr, lambda, phi, xm, xl, zm, zl, a, aa, ahr;
double sind, sindel, cosd, cosdel, h, hh, hhr, hphr, t, tprime;
double hprime, tt, ttt, ut, uut, aahr;
double cosphi, sinphi, cosz;
double sin(),cos(),acos(),tan(),atan(),sqrt();
double fabs();
int retval=NORMAL;
/*********************************************************************
** lambda - longitude in degrees phi - lattitude in degrees
** [sin|cos][d|del] - functions of pseudo-angle delta
**********************************************************************/
switch(ang_opt)
{
case SUNSET:
cosz = cos(DEGRAD*TODEC(90,50,0)); break;
case CIVIL_TWILIGHT:
cosz = cos(DEGRAD*TODEC(96,0,0)); break;
case NAUTICAL_TWILIGHT:
cosz = cos(DEGRAD*TODEC(102,0,0)); break;
case ASTRONOMICAL_TWILIGHT:
cosz = cos(DEGRAD*TODEC(106,0,0)); break;
}
lambda = TODEC(longdeg,longmin,longsec);
lohr = lambda/15.0;
phi = TODEC(latdeg,latmin,latsec);
cosphi = cos(DEGRAD*phi);
sinphi = sin(DEGRAD*phi);
t = (double)day + (18.0 + lohr)/24.0;
tprime = (double)day + (6.0 + lohr)/24.0;
xm = M(t);
zm = M(tprime);
xl = L(xm);
zl = L(zm);
/* XM,XL ARE BOTH IN DEGREES. */
a = RADDEG*atan( D*tan(DEGRAD*xl) ); /* Conversion to and from radians */
aa = RADDEG*atan( D*tan(DEGRAD*zl) );
/******* readjustment of angles a and aa *****************/
if ( fabs(a+360.0-xl) > 90.0 ) a += 180.0;
if (a > 360.0) a -= 360.0;
if ( fabs(aa+360.0-zl) > 90.0 ) aa += 180.0;
if (aa > 360.0) aa -= 360.0;
ahr = a/15.0;
sindel = E*sin(DEGRAD*xl);
cosdel = sqrt(1.0 - sindel*sindel); /* cos delta must ALWAYS be >0 */
h = (cosz - sindel*sinphi)/( cosdel*cosphi );
aahr= aa/15.0;
sind = E*sin(DEGRAD*zl);
cosd = sqrt(1.0 - sind*sind);
hh= (cosz - sind *sinphi)/( cosd *cosphi );
if (fabs(h) <= 1.0)
h = RADDEG*acos(h);
else
retval |= NO_SUNSET;
if (fabs(hh) <= 1.0)
hh= RADDEG*acos(hh);
else
retval |= NO_SUNRISE;
hhr = h/15.0;
tt = hhr + ahr + ADJ(t);
ut = tt + lohr;
hprime = 360.0 - hh; /* Puts sunrise in correct quadrant */
hphr= hprime/15.0;
ttt = hphr+ aahr + ADJ(tprime);
uut = ttt + lohr;
#ifdef DEBUG
fprintf(stderr,"DEBUG: phi %f lambda %f\n",phi,lambda);
fprintf(stderr,"DEBUG: t %f tprime %f\n",t,tprime);
fprintf(stderr,"DEBUG: xl %f zl %f\n",xl,zl);
fprintf(stderr,"DEBUG: a %f aa %f\n",a,aa);
fprintf(stderr,"DEBUG: h %f hh %f hprime %f\n",h, hh, hprime );
fprintf(stderr,"DEBUG: hhr %f hphr %f\n",hhr,hphr);
fprintf(stderr,"DEBUG: tt %f ttt %f\n",tt,ttt);
fprintf(stderr,"DEBUG: ut %f uut %f\n",ut,uut);
#endif
*sunset = ut + TZ;
*sunrise = uut+ TZ;
return(retval);
}
/**********************************************************************
SUBROUTINE TIMEADJ(TIME,HR,MIN,MINADJ,DAYADJ)
**********************************************************************/
char *timeadj(mess,time,minadj,dayadj)
char *mess;
double time;
int minadj, *dayadj;
{
char *calloc();
static char *str;
int hour, min, strlen();
int num = strlen(mess)+9;
time += (double)minadj/60.0;
if (time < 0)
{
time += 24.0;
*dayadj -= 1;
}
hour = time; /* Type conversion causes truncation */
min = (int)(( time - (double)hour )*60.0 + 0.5);
if (min >= 60)
{
hour += 1;
min -= 60;
}
if (hour > 24)
{
hour -= 24;
*dayadj += 1;
}
if (myclock==TWELVE) /* Check for 12 hour clock */
if ( hour > 12)
hour -= 12;
str = calloc(num,sizeof(char));
sprintf(str,"%s %2d:%02d",mess,hour,min);
return(str);
}
/***************
New functions
***************/
get_current_day()
/* This is a system dependent function which will get the current day of
** the year, and initialize the month-num and month-day
*/
{
int day;
#ifdef MSDOS
struct regval { int ax,bx,cx,dx,si,di,ds,es; } regs;
regs.ax = 0x2a00; /* MS-DOS get date function request */
FUNC_8086(®s,®s); /* Now DH has month, DL has day */
month_num = regs.dx>>8;
month_day = regs.dx&0xff;
day = count[month_num-1] - 1 + month_day;
#endif
#ifdef UNIX
#include <time.h>
time_t tp;
struct tm *localtime(),*t;
time(&tp);
t = localtime(&tp);
month_num = t->tm_mon + 1;
month_day = t->tm_mday;
day = t->tm_yday;
if (!lat_opt && !long_opt)
{
TZ = -timezone / 3600; /* Timezone is in seconds */
if (t->tm_isdst) TZ++;
}
#endif
return(day);
}
#include <ctype.h>
get_day(argin)
char *argin;
/* Prompt if *argin NULL. Otherwise parse for mm/dd or num */
/* Gets the day of interest, possibly parsed from argin--if not, prompts */
/* Also used as the final exit function if multi_opt */
{
char temp[6], *cp;
int tnum,j;
LONG atoi();
if (*argin == NULL) /* Prompt for input */
{
argin = temp;
get_string(argin,
"Enter date in mm/dd format: (<CR> to exit): ",5);
/* This is the exit for multi_opt */
if (!*argin) exit(0);
/* If we got a NULL then exit */
}
cp = argin;
tnum = 0;
while (isdigit(*cp) && *cp)
tnum = 10*tnum + *cp++ - '0';
if (*cp) /* We have a non-digit literal */
{
month_num = tnum;
month_day = atoi(cp+1);
if (month_num < 1 || month_num > 12)
prerror(BAD_DATE,argin);
if (month_day < 1 || month_day > 366)
prerror(BAD_DATE,argin);
return(count[tnum-1] - 1 + month_day);
}
else
{
/* The date is a single integer, and thus the number of a day of the year */
month_num = 1;
for (j=0; j<12; j++)
if (tnum >= count[j]) month_num = j+1;
month_day = tnum - count[month_num-1] + 1;
if (tnum < 1 || tnum > 366)
prerror(BAD_DATE,argin);
return (tnum);
}
}
prerror(errnum,mess)
int errnum;
char *mess;
{
static char *messages[] = {
"***Not used***",
"Bad argument |%s|\n",
"Option |%s| not yet implemented\n",
"Bad argument |%s| to -m[onth] flag: **Fatal**\n",
"Bad date |%s|. Heap may be corrupted.\n"
};
fprintf(stderr,messages[-errnum],mess);
return(errnum);
}
get_angle(mess,degrees,minutes,seconds,argin)
char *mess, *argin;
int *degrees, *minutes, *seconds;
/* Gets the angle of interest, either parsed from argin or by prompts */
{
char temp[12],*cc,*degp,*minp,*secp,*strchr();
static char *zero = "0";
LONG atoi();
if (*argin == NULL) /* Must prompt for angle */
{
fprintf(stderr,"Enter %s as deg,min'sec\": ",mess);
fflush(stderr);
/* Use stderr so as not to mess up a redirection of output */
get_string(temp," ",11);
temp[11]='\0'; /* That's right, I don't trust myself */
argin = temp;
}
if ( (cc = strchr(argin,',') ) > 0)
{
degp = argin; *cc = '\0'; minp = cc+1;
}
else
{
minp = argin;
degp = zero;
}
if ( (cc = strchr(minp,'\'') ) > 0)
{
*cc = '\0'; secp = cc + 1;
}
else
{
secp = minp;
minp = zero;
}
if ( (cc = strchr(secp,'"') ) > 0)
{
*cc = '\0';
}
else
{
secp = zero;
}
*degrees = atoi(degp);
*minutes = atoi(minp);
*seconds = atoi(secp);
}
static char only_date_space[16];
char *get_date()
/* Converts mm/dd into a string of month day of fixed length */
{
sprintf(only_date_space,"%s %2d",months[month_num],month_day);
return(only_date_space);
}
prheader()
/* Used to put page headers for the cal_opt */
{
printf("\n ");
printf("Sunrise and Sunset times for %s\n\n\n",location);
}
get_month(argin)
char *argin;
{
LONG atoi();
if (*argin == NULL)
{
first_day = count[month_num-1];
last_day = count[month_num] -1;
month_day = 1;
return(month_num);
}
if (*argin == 'a')
{
first_day = 1;
last_day = 366;
month_num = 1;
month_day = 1;
return(month_num);
}
month_num = atoi(argin);
if ((month_num < 1) || (month_num > 12) )
{
prerror(BAD_MONTH,argin);
exit(-1);
}
first_day = count[month_num-1];
last_day = count[month_num] - 1;
month_day = 1;
return(month_num);
}
get_new_location_name()
{
char *calloc();
location = calloc(51,sizeof(char));
/* Don't use up this space unless we need it (For multi, use realloc?) */
get_string(location,
"Enter new location (Title for report): ",50);
}
prusage()
{
printf("Usage:\n");
printf("sun [-d[day]] [-c] [-M] [-l[lat]] [-L[long]]\n");
printf(" [-m[1-12|a]] [-<12|24>] [-t<S|C|N|A>] [-j]\n\n");
printf(" -m month\t-c calendar output\t-M prompt multiple days\n");
printf(" -12 - twelve hour clock. -24 - twenty four hour clock\n");
printf(" -t: S regular S-nrise/set. C Civil. N Nautical. A Astronomical\n");
printf(" -j: Jewish - print candle lighting and havdalah times\n");
printf("If no values are specified, the d, l and L options will prompt\n");
printf("Default for date is today, default for m is this month.\n");
printf("Default is for a 12 hour clock, regular sunrise/set.\n");
exit(-1);
}
get_string(buffer,mess,maxlen)
char *buffer, *mess;
int maxlen;
{
/* Returns 1 if stuff read, 0 if read had error or EOF */
/* Common routine should work for both MS-DOS and UNIX */
int n_write; /* In case we wish to check status */
char *start=buffer;
char c;
n_write = write(2,mess,strlen(mess)); /* STDERR, so not mess up output */
while (read(0,&c,1) == 1) /* Until EOF or error */
{
if (c == '\b' || c == 127) /* BS or DEL */
{
if (buffer-start) buffer--;
}
else if (c == '\t') /* HT */
*buffer++ = c;
else if (c == '\n' || c == '\r') /* Don't care about mode for NL */
{
*buffer = '\0';
return(1);
}
else if ( (buffer - start) >= maxlen || c < 32)
continue; /* Ignore CTRLs and flush overage to NL */
else
*buffer++ = c;
}
return(0); /* Get here only if read fails */
}