/* praytimer.c * * Program to compute Islamic prayer hour schedules. * It generates a file which should be processed by TeX. * * Copyright (c) 1987--1992 Kamal Abdali * * Permission for nonprofit use of this software and its documentation * is hereby granted without fee, provided that the above copyright notice * appear in all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that the name * of Kamal Abdali not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior permission. * * Author: * Kamal Abdali * P.O. Box 65207 * Washington, DC 20035 * */ #include #include #include "praytimer.h" #ifdef THINK_C #include #endif /* * The program takes geographical and time data from standard input * and produces TeX code for the schedule on the standard output. * The following command line arguments control the calculation method: * * -i Interactive mode. User prompted for data at the terminal. * -a angle Sun's angle of depression at Fajr in degrees (usually 15 or 18) * -t time Time interval from Fajr to sunrise in minutes (usually 90) * -f fiqh Value should be S(hafii) or H(anafi) * -r ratio Shadow ratio at Asr. (Usually 2 for Hanafi, 1 for others) * * NOTE: It is an error to specify both -a and -t or both -f and -r. * DEFAULT is as if called with "-a 15 -r 1" * * Data on standard input must contain (in given order) * Name of location (upto 30 caharacters) * Latitude degrees and minutes, and N or S to specify north or south * Longitude degrees and minutes, and E or W to specify east or west * Time Zone in hours (Decimal for fractional hour zones, negative if * West of Greenwich) * Y or 1 if Daylight Saving Time adjustment needed. N or 0, otherwise. * Year in the range 1900..2200, or 0 for a perpetual schedule * Data items should be separated by whitespace, but the name must be on * a separate line by itself because it may contain spaces or punctuation. * * Input may contain data for more than one location. * An interactive session may be ended by typing the End-of-File character * (e.g. CTRL-D in UNIX) when prompted for Name. */ main(argc, argv) short argc; char* argv[]; { register char *cp; short optA = 0, optF = 0, optR = 0, optT = 0, interactive = 0; char fiqh = 'S'; double ratio = 1.0, depr = 15.0, intvl = 90.0; extern double atof(/* const char* s */); /* In case not declared in math.h */ fajrByInterval = 0; fajrInterval = 90.0; fajrDepr = 15.0; asrShadowRatio = 1.0; #ifdef THINK_C argc = ccommand(&argv); #endif while(--argc){ cp = *++argv; if (*cp++ == '-' && *cp) { while (*cp) { switch(*cp++) { case 'i': /* Interactive data */ interactive = 1; break; case 'a': /* Sun's depression angle at Fajr in degrees */ depr = atof(*cp ? cp : *++argv); optA = 1; if (*cp) {*cp = '\0';} else {--argc;} break; case 't': /* Time interval from Fajr to Sunrise */ intvl = atof(*cp ? cp : *++argv); optT = 1; if (*cp) {*cp = '\0';} else {--argc;} break; case 'r': /* Shadow ratio at 'Asr */ ratio = atof(*cp ? cp : *++argv); optR = 1; if (*cp) {*cp = '\0';} else {--argc;} break; case 'f': /* Fiqh for 'Asr. Value to be H(anafi) or S(hafii) */ if (*cp) { fiqh = *cp; *cp = '\0'; } else {fiqh = **++argv; --argc;} if (fiqh != 'H' && fiqh != 'S') { fprintf(stderr, "praytimer: command line arg -f must have value H(anafi) or S(hafii)\n"); exit(1); } /* Set 'Asr shadow ratio according to the chosen fiqh: 1 for S, 2 for H */ ratio = (fiqh == 'S' ? 1.0 : 2.0); optF = 1; break; default: fprintf(stderr, "Usage: praytimer [-i] [-f fiqh] [-r ratio] [-a angle] [-t time]\n"); exit(1); } } } } if (optF && optR) { fprintf(stderr, "praytimer: invalid command line arg combination (can't have both -f and -r)\n"); exit(1); } if (optF || optR) asrShadowRatio = ratio; if (optA && optT) { fprintf(stderr, "praytimer: invalid command line arg combination (can't have both -a and -t)\n"); exit(1); } if (optT) { fajrByInterval = 1; fajrInterval = intvl; } if (optA) { fajrByInterval = 0; fajrDepr = depr; } while (getData(interactive)) { /* For perpetual, use 1994. (Need middle year of 4-year leap cycle */ computeConstants(year==0 ? 1994 : year); /* find beginning and ending days for daylight saving time */ dayLight(&leap, hasDayLt, &beginDayLight, &endDayLight); makeSchedule(); } exit(0); } /* * Obtain name and geographical data for the location for which * the schedule is desired. If INTERACTIVE is true, then * the user is prompted for the info at the terminal. * Otherwise, the info is obtained from the standard input. * Note: the values to control the calculation method are * to be given as command line arguments, not as input. */ short getData(interactive) short interactive; { short maxNameLength, chCount, badData; char ch, str[10]; if (interactive) /* Data to be read from terminal */ fprintf(stderr, "Location name (30 chars or less)? "); maxNameLength = 30; /* ARBITRARY */ chCount = 0; /* Read Name. First skip over leading whitespaces */ while ((ch = getchar()) != EOF && (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')); if (ch == EOF) return(0); name[chCount++] = ch; while (--maxNameLength > 0 && (ch = getchar()) != EOF && ch != '\n') { name[chCount++] = ch; } if (ch == EOF) return(0); name[chCount++] = '\0'; do { if (interactive) fprintf(stderr, "Latitude (degrees, minutes, N/S)? "); if (badData = (scanf("%hd%hd%s", &latd, &latm, str) != 3 || latd < 0 || latd > 90 || latm < 0 || latm > 59 || ((ch = str[0]) != 'N' && ch != 'n' && ch != 'S' && ch != 's'))) { fprintf(stderr, "Illegal data for geographical latitude!\n"); if (!interactive) return(0); } } while (badData && interactive); latIsS = ch == 'S' || ch == 's'; do { if (interactive) fprintf(stderr, "Longitude (degrees, minutes, E/W)? "); if (badData = (scanf("%hd%hd%s", &longd, &longm, str) != 3 || longd < 0 || longd > 180 || longm < 0 || longm > 59 || ((ch = str[0]) != 'E' && ch != 'e' && ch != 'W' && ch != 'w'))) { fprintf(stderr, "Illegal data for geographical longitude!\n"); if (!interactive) return(0); } } while (badData && interactive); longIsW = ch == 'W' || ch == 'w'; do { if (interactive) fprintf(stderr, "Time Zone in decimal hours (negative if West of Greenwich)? "); if (badData = (scanf("%lf", &timeZone) != 1 || timeZone <= -12.0 || timeZone > 12.0)) { fprintf(stderr, "Illegal data for time zone!\n"); if (!interactive) return(0); } } while (badData && interactive); if (interactive) fprintf(stderr, "Adjust for daylight saving time (Y/N)? "); /* ch = getchar(); */ /* Following works with terminal I/O without special settings*/ scanf("%s", str); ch = str[0]; hasDayLt = ch == 'Y' || ch == 'y' || ch == '1'; do { if (interactive) fprintf(stderr, "Year (1900 thru 2200, or 0 for a perpetual schedule)? "); if (badData = (scanf("%hd", &year) != 1 || (year != 0 && (year < 1900 || year > 2200)))) { fprintf(stderr, "Illegal data for year! Try again\n"); if (!interactive) return(0); } } while (badData && interactive); latitude = deg2rad(dm2deg(latd,latm)); if (latIsS) latitude = -latitude; longitude = deg2rad(dm2deg(longd,longm)); if (!longIsW) longitude = -longitude; direc = qibla() * DPR; return(1); } /* * Schedule computation section. * Prayer hours are computed basically following the algorithms given in * "Prayer Schedules for North America", American Trust Publications, * Indianapolis, Indiana, 1978, Appendices A and B. * */ void makeSchedule () { register short i; short ndays0, ndays1; ndmnth[1] = 28+leap; computeHours(0,365+leap); /* schedule computed. now print it out */ ndays0 = 0; texDcl(1); for (i=0; i<12; i++) { if (i%4 == 0) titleTex(); ndays1 = ndays0+ndmnth[i]; displayTex(ndays0,ndays1,1); headrTex(i); /* One page has four monthly tables. */ /* Start new page on months 0, 4, 8 */ /* Odd months underneath even months */ if (i%2 == 0) printf("&\n"); if (i%2 != 0 && (i+1)%4 != 0) printf("\\cr\\noalign{\\hskip0.5in}\n"); if ((i+1)%4 == 0) printf("\\cr}\\vfill\\eject\n"); ndays0 = ndays1; } printf("\\bye\n"); } /* * Computes times for range of days first..last-1. */ void computeHours(first, last) short first; short last; { double coaltn,time0[6],coalt[6]; register double t; register short i,k,l; /* Approximate times of fajr,shuruq,asr,maghrib,isha */ time0[0] = 4.0; time0[1] = 6.0; time0[3] = 15.0; time0[4] = 18.0; time0[5] = 20.0; /* Coaltitudes of sun at fajr,shuruq,maghrib,isha */ coalt[0] = deg2rad((double)(90+fajrDepr)); coalt[1] = deg2rad(90.83); coalt[4] = coalt[1]; coalt[5] = coalt[0]; /* Get approximate times for the first day specified. */ /* Later on, each day's times used as approximate times for next day */ t = noontime(first,&coaltn); coalt[3] = atan(asrShadowRatio+tan(coaltn)); time0[1] = (((t = tempus(first,coalt[1],time0[1])) < 24.0) ? t : 6.0); time0[3] = (((t = tempus(first,coalt[3],time0[3])) < 24.0) ? t : 15.0); time0[4] = (((t = tempus(first,coalt[4],time0[4])) < 24.0) ? t : 18.0); if (fajrByInterval) { time0[0] = time0[1] - fajrInterval/60.0; time0[5] = time0[4] + fajrInterval/60.0; } else { time0[0] = (((t = tempus(first,coalt[0],time0[0])) < 24.0) ? t : 4.0); time0[5] = (((t = tempus(first,coalt[5],time0[5])) < 24.0) ? t : 20.0); } /* compute times for the whole range of days */ for (l=first, i=1; l59 && year==0) k = l-1; tim[l][2] = noontime(k+1,&coaltn); coalt[3] = atan(asrShadowRatio+tan(coaltn)); time0[1] = (((tim[l][1] = t = tempus(k+1,coalt[1],time0[1])) < 24.0) ? t : 6.0); time0[3] = (((tim[l][3] = t = tempus(k+1,coalt[3],time0[3])) < 24.0) ? t : 15.0); time0[4] = (((tim[l][4] = t = tempus(k+1,coalt[4],time0[4])) < 24.0) ? t : 18.0); if (fajrByInterval) { tim[l][0] = time0[0] = time0[1] - fajrInterval/60.0; tim[l][5] = time0[5] = time0[4] + fajrInterval/60.0; } else { time0[0] = (((tim[l][0] = t = tempus(k+1,coalt[0],time0[0])) < 24.0) ? t : 4.0); time0[5] = (((tim[l][5] = t = tempus(k+1,coalt[5],time0[5])) < 24.0) ? t : 20.0); } } /* correct for daylight saving time */ if (endDayLight) { for (i = beginDayLight-1; i < endDayLight; i++) for (k=0; k<6; k++) tim[i][k] += 1.0; } } /* * Computes astro constants for Jan 0 of given year */ void computeConstants(year) short year; { /* ndays = time from 12 hr(noon), Jan 0, 1900 to 0 hr, Jan 0 of year */ /* t = same in julian centuries (units of 36525 days) */ /* obl = obliquity of ecliptic */ /* perigee = sun's longitude at perigee */ /* eccy = earth's eccentricity */ /* dmanom,delsid = daily motion (change) in */ /* sun's anomaly, sidereal time */ /* anom0,sidtm0 = sun's mean anomaly, */ /* sidereal time, all at 0 hr, jan 0 of year year */ /* c1,c2 = coefficients in equation of center */ register double t; long ndays; double obl, eccy; ndays = ((long) (year-1900))*365+(year-1901)/4; t = (ndays-0.5)/36525.0; obl = deg2rad(dms2deg(23L,27,8.26)-dms2deg(0L,0,46.845)*t); cosobl = cos(obl); sinobl = sin(obl); eccy = 0.01675104-4.180e-5*t-1.26e-7*t*t; perigee = deg2rad(FMOD(dms2deg(281L,13,15.0)+dms2deg(1L,43,9.03)*t+ dms2deg(0L,0,1.63)*t*t,360.0)); dmanom = deg2rad(dms2deg(35999L,2,59.10)/36525.0); anom0 = deg2rad(FMOD(dms2deg(358L,28,33.0)-dms2deg(0L,0,0.54)*t*t+ FMOD(dms2deg(35999L,2,59.10)*t,360.0),360.0)); delsid = hms2h(2400,3,4.542)/36525.0; sidtm0 = FMOD(hms2h(6,38,45.836)+FMOD(hms2h(2400,3,4.542)*t,24.0),24.0); c1 = eccy*(2-eccy*eccy/4); c2 = 5*eccy*eccy/4; } /* * Double-duty function for leap year and Daylight Saving dates info. * Finds whether year is leap (sets leap = 1 if yes, 0 if no). * If hasDayLt is non-zero, then also computes the day numbers of the * start and end of Daylight Savings Time. * Sets begin = Day no. of the first Sunday of April, and * finish = Day no. of the Saturday before the last Sunday of October. */ void dayLight(leap, hasDayLt, begin, finish) short* leap; short hasDayLt; short* begin; short* finish; { short m4,m1,jan0,napr1,noct31,apr1,oct31; m4 = year%400; m1 = year%100; *leap = !(year%4 != 0 || m1 == 0 && m4 != 0); if (hasDayLt==0) { /* No adjustment for Daylight Saving Time (year zero for perpetual) */ *begin = 367; *finish = 0; return; } if (year==0) { /* Daylight Saving Time in perpetual calendar. April 1 thru Oct 31 */ *begin = 92; /* April 1, 31+29+31+1 */ *finish = *begin+213; /* Oct 31, -1+30+31+30+31+31+30+31 */ return; } /* Non-zero year. for annual calendar */ /* jan0,apr1,oct31 = day of week on those dates (fri=0,sat=1,sun=2,...) */ /* napr1,noct31 = Day no. in year on those dates */ jan0 = (m4/100*124+1+m1+m1/4-*leap) % 7; napr1 = 91 + *leap; /* 31+28+*leap+31+1 */ noct31 = 304 + *leap; /* 365+*leap-31-30 */ apr1 = (napr1+jan0) % 7; oct31 = (noct31+jan0) % 7; *begin = napr1+2-apr1; if ( *begin < napr1 ) *begin += 7; *finish = noct31+2-oct31; if ( *finish > noct31 ) *finish -= 7; *finish = *finish-1; } /* * Place sun's coaltitude at noon in coaltn, * and return time of noon for day no. nday of year */ double noontime(nday, coaltn) short nday; double* coaltn; { /* slong = sun's true longitude at noon */ /* ra = sun's right ascension, decl = sun's declination */ /* ha = sun's hour angle west */ /* locmt = local mean time of phenomenon */ register double t; double longh,days,anomaly,slong,sinslong,ra,decl,locmt; longh = longitude*HPR; days = nday+(12.0+longh)/24.0; anomaly = anom0+dmanom*days; slong = perigee+anomaly+c1*sin(anomaly)+c2*sin(anomaly*2); sinslong = sin(slong); ra = atan2(cosobl*sinslong,cos(slong))*HPR; if (ra<0.0) ra += 24.0; decl = asin(sinobl*sinslong); locmt = ra-delsid*days-sidtm0; t = locmt+longh+timeZone; if (t<0.0) t += 24.0; if (t>24.0) t -= 24.0; *coaltn = FABS(latitude-decl); return(t); } /* * Returns time on day no. nday of year when sun's coaltitude is coalt. * If no such time, then returns a large number. * time0 is approximate time of phenomenon */ double tempus(nday, coalt, time0) short nday; double coalt; double time0; { /* slong = true longitude */ /* ra = sun's right ascension, sindcl = sin(sun's declination) */ /* ha = sun's hour angle west */ /* locmt = local mean time of phenomenon */ double longh,days,anomaly,slong,sinslong,ra,sindcl,cosha,ha,locmt; register double t; longh = longitude*HPR; days = nday+(time0+longh)/24.0; anomaly = anom0+dmanom*days; slong = perigee+anomaly+c1*sin(anomaly)+c2*sin(anomaly*2); sinslong = sin(slong); ra = atan2(cosobl*sinslong,cos(slong))*HPR; if (ra<0.0) ra += 24.0; sindcl = sinobl*sinslong; cosha = (cos(coalt)-sindcl*sin(latitude))/ (sqrt(1.0-sindcl*sindcl)*cos(latitude)); /* if cos(ha)>1, then time cannot be evaluated */ if (FABS(cosha)>1.0) return(1.0e7); ha = acos(cosha)*HPR; if (time0<12.0) ha = 24.0-ha; locmt = ha+ra-delsid*days-sidtm0; t = locmt+longh+timeZone; if (t<0.0) t += 24.0; if (t>24.0) t -= 24.0; return(t); } /* * Returns the direction of qibla in radians. Eastward from north is positive. */ double qibla() { /* lat0, long0 are Makkah's latitude and longitude in radians */ double lat0 = 0.3739077, long0 = -0.69504828, dflong; dflong = longitude-long0; return( atan2(sin(dflong), cos(latitude)*tan(lat0)-sin(latitude)*cos(dflong)) ); } /* * TeX code generation for schedule */ /* * Some TeX code to include. * n = 1 or 2 for two different sections of the schedule. */ void texDcl(n) short n; { switch (n) { case 1: printf("\\hsize=7.5in \\vsize=11in\n"); printf("\\hoffset=-0.5in \\voffset=-0.4in\n"); printf("\\font\\eightrm=cmr8 \\font\\eightbf=cmbx8\n"); printf("\\font\\emph=cmti8\n"); printf("\\def\\tabrule{\\noalign{\\hrule}}\n"); printf("\\def\\tabhead{\n"); printf("\\noalign{\\medskip} \\tabrule\n"); printf("&&&&\\omit\\hidewidth Fajr\\hidewidth\n"); printf("&&\\omit\\hidewidth Shuruq\\hidewidth\n"); printf("&&\\omit\\hidewidth Zuhr\\hidewidth\n"); printf("&&\\omit\\hidewidth Asr\\hidewidth\n"); printf("&&\\omit\\hidewidth Maghrib\\hidewidth\n"); printf("&&\\omit\\hidewidth Isha\\hidewidth&\\cr\n"); printf("&&\\omit\\hidewidth Date\\hidewidth\n"); printf("&&\\omit\\hidewidth Dawn\\hidewidth\n"); printf("&&\\omit\\hidewidth Sunrise\\hidewidth\n"); printf("&&\\omit\\hidewidth Noon\\hidewidth\n"); printf("&&\\omit\\hidewidth Afnoon\\hidewidth\n"); printf("&&\\omit\\hidewidth Sunset\\hidewidth\n"); printf("&&\\omit\\hidewidth Night\\hidewidth&\\cr\n"); printf("\\tabrule}\n"); printf("\\def\\monthtable#1#2{\\vbox{\\tabskip=0pt \\offinterlineskip\n"); printf(" \\halign to3.25in{\\strut##&\n"); printf(" \\vrule##\\tabskip=1em plus2em& \\hfil##\\hfil&\n"); printf(" \\vrule##& \\hfil##\\hfil& \\vrule##& \\hfil##\\hfil&\n"); printf(" \\vrule##& \\hfil##\\hfil& \\vrule##& \\hfil##\\hfil&\n"); printf(" \\vrule##& \\hfil##\\hfil& \\vrule##& \\hfil##\\hfil&\n"); printf(" \\vrule##\\tabskip=0pt\\cr\n"); printf(" &\\multispan{15}\\hfil{\\eightbf #1}\\hfil\\cr\n"); printf(" #2 \\tabhead\\chart\\tabrule}}}\n"); break; case 2: printf("\\bigskip\n\\eightrm \\normalbaselineskip=9pt\n"); printf("\\setbox\\strutbox=\\hbox{\\vrule height7pt\n"); printf(" depth2pt width0pt}%%\n"); printf("\\valign to9in{#\\bigskip & \\vfil# \\cr\n"); break; } } /* * Print title material. */ void titleTex() { double abszon, absqib; short qibd, qibm, zoneH, zoneM; char sgnlat, sgnlng, sgnzon, sgnqib; sgnlat = (latIsS ? dir[3] : dir[2]); sgnlng = (longIsW ? dir[1] : dir[0]); abszon = FABS(timeZone); zoneH = abszon; zoneM = 10.0*(abszon-zoneH)+0.5; sgnzon = (timeZone<0 ? '-' : '+'); absqib = FABS(direc); qibd = absqib; qibm = 60.0*(absqib-qibd)+0.5; if (qibm>=60) { qibm = 0; qibd += 1; } sgnqib = (direc<0 ? dir[1] : dir[0]); printf("\\tenrm\n"); if (year!=0) { printf("\\centerline{%4d A.D. Prayer Schedule for {\\tenbf ",year); } else { printf("\\centerline{Perpetual Prayer Schedule for {\\tenbf "); } printf("%s}}\n\\smallskip\n",name); printf("\\centerline{Latitude =$%3d^\\circ%02d'$ %c\\ \\ \\ \\ \\ ", latd,latm,sgnlat); printf("Longitude = $%3d^\\circ%02d'$ %c}\n",longd,longm,sgnlng); printf("\\centerline{Zone Time = GMT %c%2dh",sgnzon,zoneH); if (zoneM) printf("%3dm",zoneM); printf("\\ \\ \\ \\ \\ Qibla = $%3d^\\circ%02d'$ %c (From N)}\n", qibd,qibm,sgnqib); texDcl(2); } /* * Print times for range of days (in the year) first..last-1. * Days in month range from startdate to startdate+last-first * to keep all monthly tables of same length. * Blank rows are printed for remaining rows numbered thru 31. */ void displayTex(first, last, startdate) short first; short last; short startdate; { short hour,minute; double t; register short i,j,l; printf("\\def\\chart{\n"); for (l=first, i=startdate; l360.0) { printf(" * "); } else if (t<0.0) { printf(" "); } else { /* time conversion to am and pm hours and rounded minutes */ hour = t; minute = 60.0*(t-hour)+0.5; if (minute>=60) { minute = 0; hour += 1; } if (hour>12) hour -= 12; printf("%3d:%02d",hour,minute); } } printf("&\\cr\n"); } /* Blank out times for remaining day nos. thru 31 */ for (i=last-first+1; i<32; i++) { printf("&&%2d",i); for (j=0; j<6; j++) { printf("&& "); } printf("&\\cr\n"); } printf("}\n"); } /* * Print header for monthly section n in schedule. */ void headrTex(n) short n; { printf("\\monthtable{"); switch (n) { case 0: printf("J A N U A R Y}{}\n"); break; case 1: printf("F E B R U A R Y}{}\n"); break; case 2: printf("M A R C H}{}\n"); break; case 3: printf("A P R I L}{"); if (year==0 && hasDayLt!=0) { printf("\n"); printf(" &\\multispan{15}\\hfil{({\\emph Subtract one hour}\n"); printf(" from all times before first Sunday)}\\hfil\\cr}\n"); } else { printf("}\n"); } break; case 4: printf("M A Y}{}\n"); break; case 5: printf("J U N E}{}\n"); break; case 6: printf("J U L Y}{}\n"); break; case 7: printf("A U G U S T}{}\n"); break; case 8: printf("S E P T E M B E R}{}\n"); break; case 9: printf("O C T O B E R}{"); if (year==0 && hasDayLt!=0) { printf("\n"); printf(" &\\multispan{15}\\hfil{({\\emph Subtract one hour}\n"); printf(" from all times starting on last Sunday)}\\hfil\\cr}\n"); } else { printf("}\n"); } break; case 10: printf("N O V E M B E R}{}\n"); break; case 11: printf("D E C E M B E R}{}\n"); break; } } /* * Utility functions * */ double deg2rad(degree) double degree; { return(degree*RPD); } double dm2deg(degree, minute) short degree; short minute; { return((double) degree + minute/60.0); } double dms2deg(degree, min, sec) long degree; short min; double sec; { return((double) degree + min/60.0 + sec/3600.0); } double hms2h(hour, min, sec) short hour; short min; double sec; { return((double) hour + min/60.0 + sec/3600.0); }