#include #include #ifdef X11 #include #endif #ifdef WIN32 #include "stonehen.h" #endif #include "TimeDate.h" #include #include #ifdef X11 #include #else #include #endif inline float sign(float a) {return a > 0.0 ? 1. : (a < 0.0 ? -1. : 0.);} inline float degrees(float a) {return a * 180.0 / M_PI;} inline float radians(float a) {return a * M_PI / 180.0;} inline float arcsin(float a) {return atan2(a, sqrt(1.-a*a));} /* Thirty days hath September... */ const int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; TimeDate::TimeDate(int hour, int minute, int second, long usecond) { read_time(); t.tm_hour = hour; t.tm_min = minute; t.tm_sec = second; usec = usecond; } TimeDate TimeDate::operator=(TimeDate a) { usec = a.usec; t = a.t; return *this; } TimeDate TimeDate::operator+(TimeDate a) { TimeDate td; td = *this + a.t; td.usec = usec + a.usec; return td; } TimeDate TimeDate::operator+(struct tm t2) { TimeDate td; td.usec = 0; td.t.tm_sec = t.tm_sec + t2.tm_sec; td.t.tm_min = t.tm_min + t2.tm_min; td.t.tm_hour = t.tm_hour + t2.tm_hour; td.t.tm_mday = t.tm_mday + t2.tm_mday; td.t.tm_mon = t.tm_mon + t2.tm_mon; td.t.tm_year = t.tm_year + t2.tm_year; td.t.tm_wday = t.tm_wday + t2.tm_wday; td.t.tm_yday = t.tm_yday + t2.tm_yday; return td; } TimeDate TimeDate::operator+=(TimeDate a) { /* This is slower than it needs to be */ *this = *this + a.t; return *this; } TimeDate TimeDate::operator-(TimeDate a) { TimeDate td; td.usec = usec - a.usec; td.t.tm_sec = t.tm_sec - a.t.tm_sec; td.t.tm_min = t.tm_min - a.t.tm_min; td.t.tm_hour = t.tm_hour - a.t.tm_hour; td.t.tm_mday = t.tm_mday - a.t.tm_mday; td.t.tm_mon = t.tm_mon - a.t.tm_mon; td.t.tm_year = t.tm_year - a.t.tm_year; td.t.tm_wday = t.tm_wday - a.t.tm_wday; td.t.tm_yday = t.tm_yday - a.t.tm_yday; return td; } TimeDate TimeDate::operator*(float f) { TimeDate td; td.usec = (long)((float)usec * f); td.t.tm_sec = (int)((float)t.tm_sec * f); td.t.tm_min = (int)((float)t.tm_min * f); td.t.tm_hour = (int)((float)t.tm_hour * f); td.t.tm_mday = (int)((float)t.tm_mday * f); td.t.tm_mon = (int)((float)t.tm_mon * f); td.t.tm_year = (int)((float)t.tm_year * f); td.t.tm_wday = (int)((float)t.tm_wday * f); td.t.tm_yday = (int)((float)t.tm_yday * f); return td; } TimeDate TimeDate::read_time() { #ifdef X11 struct timeval tv; #else LPSYSTEMTIME lpst; #endif time_t cal_time; struct tm *tmp; cal_time = time(NULL); tmp = localtime(&cal_time); #ifdef X11 gettimeofday(&tv); usec = tv.tv_usec; #else lpst = (LPSYSTEMTIME) malloc(sizeof(SYSTEMTIME)); GetSystemTime(lpst); usec = 1000 * lpst->wMilliseconds; #endif t = *tmp; return *this; } void TimeDate::print() { puts(asctime(&t)); } /* The sun_direction routine comes from an awk program by Stuart Levy * (slevy@geom.umn.edu) */ /* Day number of March 21st, roughly the vernal equinox */ const float equinox = mday[0] + mday[1] + 21; /* * Date E.T. [degrees] * Jan 1 0.82 * Jan 31 3.35 * Mar 1 3.08 * Mar 31 1.02 * Apr 30 -0.71 * May 30 -0.62 * Jun 29 0.87 * Jul 29 1.60 * Aug 28 0.28 * Sep 27 -2.28 * Oct 27 -4.03 * Nov 26 -3.15 * Dec 26 0.19 */ const float et[] = { .82, 3.35, 3.08, 1.02, -.71, -.62, .87, 1.60, .28, -2.28, -4.03, -3.15, .19, 2.02}; const float DEG = 180. / M_PI; Point TimeDate::sun_direction(float lon, float lat) { float yearday, eti, etoffset, ET, HA, LON, decl, altitude, azimuth; int etindex; Point dir; long tmp_usec; int m; tmp_usec = usec; usec = 0; *this = correct_smaller(); /* * hour angle of the sun (time since local noon): * HA = (time - noon) * 15 degrees/hour * + (observer''s east longitude - long. of time zone''s * central meridian * POSITIVE east, NEGATIVE west) * - (15 degrees if time above was daylight savings time, else 0) * - "equation of time" (depends on date, correcting the * hour angle for * the earth''s elliptical orbit and the slope of the ecliptic) */ yearday = (float)t.tm_mday + ((float)t.tm_hour + (float)t.tm_min / 60.0) / 24.0; for (m = 0; m < t.tm_mon; m++) yearday += mday[m]; /* * Index into equation-of-time table, tabulated at 30-day intervals * We''ve added an extra entry at the end of the et table, corresponding * to January 25th of the following year, to make interpolation work * throughout the year. * Use simple linear interpolation. */ eti = (yearday - 1.) / 30.; #ifdef X11 etoffset = eti - trunc(eti); etindex = (int)trunc(eti) + 1; #else etoffset = eti - (int)eti; etindex = (int)eti + 1; #endif ET = et[etindex - 1] + etoffset*(et[etindex+1 - 1] - et[etindex - 1]); /* The 90. puts us in the Central time zone */ HA = ((float)t.tm_hour + (float)t.tm_min/60. - 12.)*15. + lon + 90. - ET; /* * Sun''s declination: * sun''s longitude = (date - March 21) * 360 degrees / 365.25 days * [This ignores the earth''s elliptical orbit...] */ LON = (yearday - equinox) * 360. / 365.25; decl = DEG * arcsin( sin(LON/DEG) * sin(23.4/DEG) ); /* * Then a spherical triangle calculation to convert the Sun''s * hour angle and declination, and the observer''s latitude, * to give the Sun''s altitude and azimuth (angle east from north). */ altitude = DEG * arcsin( sin(decl/DEG)*sin(lat/DEG) + cos(decl/DEG)*cos(lat/DEG)*cos(HA/DEG) ); azimuth = DEG * atan2( -cos(decl/DEG)*sin(HA/DEG), sin(decl/DEG)*cos(lat/DEG) - cos(decl/DEG)*cos(HA/DEG)*sin(lat/DEG) ); /* printf("On %d/%d at %d:%02d, lat %g, lon %g\n", t.tm_mon + 1, t.tm_mday + 1, t.tm_hour, t.tm_min, lat, lon); printf("HA %.1f ET %.1f Sun lon %.1f decl %.1f alt %.1f az %.1f\n", HA,ET,LON,decl,altitude,azimuth); */ dir.pt[2] = sin(radians(altitude)); dir.pt[0] = cos(radians(azimuth)); dir.pt[1] = sin(radians(azimuth)); dir.pt[3] = 0; dir.unitize(); // dir.print(); usec = tmp_usec; return dir; } TimeDate TimeDate::correct_bigger() { TimeDate td; int days; td.t = t; td.usec = usec; td.t.tm_sec += (int)(td.usec / 1000000); td.usec %= 1000000; td.t.tm_min += td.t.tm_sec / 60; td.t.tm_sec %= 60; td.t.tm_hour += td.t.tm_min / 60; td.t.tm_min %= 60; if (td.t.tm_hour > 23) { days = td.t.tm_hour / 24; td.t.tm_hour %= 24; td.t.tm_mday += days; td.t.tm_wday = (td.t.tm_wday + days) % 7; td.t.tm_yday = (td.t.tm_yday + days) % 365; while (td.t.tm_mday > mday[td.t.tm_mon]) { td.t.tm_mday -= mday[td.t.tm_mon++]; if (td.t.tm_mon >= 12) { td.t.tm_year += td.t.tm_mon / 12; td.t.tm_mon %= 12; } } } return td; } TimeDate TimeDate::correct_smaller() { TimeDate td; td.t = t; td.usec = usec; while (td.usec < 0) { td.t.tm_sec--; td.usec += 1000000; } while (td.t.tm_sec < 0) { td.t.tm_min--; td.t.tm_sec += 60; } while (td.t.tm_min < 0) { td.t.tm_hour--; td.t.tm_min += 60; } while (td.t.tm_hour < 0) { td.t.tm_mday--; if (td.t.tm_wday) td.t.tm_wday--; else td.t.tm_wday = 6; td.t.tm_yday--; td.t.tm_hour += 24; } if (td.t.tm_mon < 0 || td.t.tm_year < 0 || td.t.tm_yday < 0) { fprintf(stderr, "Warning: day < 0 in TimeDate.c++.\n"); td.t.tm_mon = td.t.tm_year = td.t.tm_yday = 0; } return td.correct_bigger(); }