./sunwait/0.8/0000775000175000017500000000000012533057105011315 5ustar ianian./sunwait/0.8/sunwait.cpp0000775000175000017500000012717012533056525013533 0ustar ianian// sunwait.cpp : Defines the entry point for the console application. // // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv CHANGE ME // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv CHANGE ME const double VERSION=0.8; // <<<<<<<<< CHANGE ME // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ CHANGE ME // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ CHANGE ME /* copyright (c) 2000,2004 Daniel Risacher */ /* minor changes courtesy of Dr. David M. MacMillan */ /* major changes courtesy of Ian Craig (2012-13) */ /* Licensed under the Gnu General Public License */ // Who When Ver What // IFC 2013-07-13 0.4 Working version of "sunwait", ported to Windows. // IFC 2014-12-01 0.5 The linux port was not working - the windows sleep is for seconds, the linux is miliseconds. // I'm porting my home power control to Linux, so I've reworked the whole program to suit. // IFC 2014-12-08 0.6 Add timezone handling for output - if required // IFC 2015-04-29 0.7 Timezone and DST fixes - and for date line timings // IFC 2015-05-27 0.8 Resolve 'dodgy day' and cleanup // #include #include #include #include #include #include // Windows #if defined _WIN32 || defined _WIN64 #include #endif // Linux #if defined __linux__ #include #endif #include "sunwait.h" #include "sunriset.h" #include "print.h" using namespace std; /* ** Define global: ** 'runStruct' structure allows pretty much everything to be carted simply around functions. ** 'targetStruct' structure is the interface to the sunrise calcs. Compact, and allows multi-results to be used. ** It can be a bit naughty on side-effects, so I use 'const' where-ever I can. */ runStruct gRun; runStruct *pRun; void print_version () { printf ("Sunwait for Windows. Version %f (IFC).\n", VERSION); printf ("Code Contributors: P.Schlyter, D.Risacher, D.MacMillan and I.Craig.\n"); printf ("\n"); printf (" Sunset is the instant at which the upper edge of the Sun disappears below the horizon.\n"); printf (" Civil twilight is the period from sunset until the geometric centre of the sun is 6° below the horizon.\n"); printf (" Nautical twilight is the period when the geometric centre of the sun is between 6° and 12° below the horizon.\n"); printf (" Astronomical twilight is the period when the geometric centre of the sun is between 12° and 18° below the horizon.\n"); printf (" Night is period when the geometric center of the sun falls 18° below the horizon.\n"); } /* ** It's very useful to have this here */ void print_usage () { printf ("Calculate sunrise and sunset times for the current or targetted day.\n"); printf ("The times can be adjusted either for twilight or fixed durations.\n"); printf ("\n"); printf ("The program can either: wait for sunrise or sunset (function: wait),\n"); printf (" or return the time (GMT or local) the event occurs (function: list),\n"); printf (" or report the day length and twilight timings (function: report),\n"); printf (" or simply report if it is DAY or NIGHT (function: poll).\n"); printf ("\n"); printf ("You should specify the latitude and longitude of your target location.\n"); printf ("\n"); printf ("\n"); printf ("Usage: sunwait [major options] [minor options] [twilight type] [rise|set] [offset] [latitude] [longitude]\n"); printf ("\n"); printf ("Major options, either:\n"); printf (" poll Returns immediately indicating DAY or NIGHT. See 'program exit codes'. Default.\n"); printf (" wait Sleep until specified event occurs. Else exit immediate.\n"); printf (" list [X] Report twilight times for next 'X' days (inclusive). Default: %u.\n", DEFAULT_LIST); printf (" report Generate a report about the days sunrise and sunset timings.\n"); printf ("\n"); printf ("Minor options, any of:\n"); printf (" [no]debug Print extra info and returns in one minute. Default: nodebug.\n"); printf (" [no]version Print the version number. Default: noversion.\n"); printf (" [no]help Print this help. Default: nohelp.\n"); printf (" [no]gmt Print times in GMT or local-time. Default: nogmt.\n"); printf ("\n"); printf ("Twilight types, either:\n"); printf (" daylight Top of sun just below the horizon. Default.\n"); printf (" civil Civil Twilight. -6 degrees below horizon.\n"); printf (" nautical Nautical twilight. -12 degrees below horizon.\n"); printf (" astronomical Astronomical twilight. -18 degrees below horizon.\n"); printf (" angle [X.XX] User-specified twilight-angle (degrees). Default: 0.\n"); printf ("\n"); printf ("Sunrise/sunset. Only useful with major-options: 'wait' and 'list'. Any of: (default: both)\n"); printf (" rise Wait for the sun to rise past specified twilight & offset.\n"); printf (" set Wait for the sun to set past specified twilight & offset.\n"); printf ("\n"); printf ("Offset:\n"); printf (" offset [MM|HH:MM] Time interval (+ve towards noon) to adjust twilight calculation.\n"); printf ("\n"); printf ("Target date. Only useful with major-options: 'report' or 'list'. Default: today\n"); printf (" d [DD] Set the target Day-of-Month to calculate for. 1 to 31.\n"); printf (" m [MM] Set the target Month to calculate for. 1 to 12.\n"); printf (" y [YYYY] Set the target Year to calculate for. 2000 to 2099.\n"); printf ("\n"); printf ("latitude/longitude coordinates: floating-point degrees, with [NESW] appended. Default: Bingham, England.\n"); printf ("\n"); printf ("Exit (return) codes:\n"); printf (" %1d OK: exit from 'wait' or 'list' only.\n", EXIT_OK); printf (" %1d Error.\n", EXIT_ERROR); printf (" %1d Exit from 'poll': it is DAY or twilight.\n", EXIT_DAY); printf (" %1d Exit from 'poll': it is NIGHT (after twilight).\n", EXIT_NIGHT); printf ("\n"); printf ("Example 1: sunwait wait rise offset -1:15:10 51.477932N 0.000000E\n"); printf ("Wait until 1 hour 15 minutes 10 secs before the sun rises in Greenwich, London.\n"); printf ("\n"); printf ("Example 2: sunwait list 7 civil 55.752163N 37.617524E\n"); printf ("List civil sunrise and sunset times for today and next 6 days. Moscow.\n"); printf ("\n"); printf ("Example 3: sunwait poll exit angle 10 54.897786N -1.517536E\n"); printf ("Indicate by program exit-code if is Day or Night using a custom twilight angle of 10 degrees above horizon. Washington, UK.\n"); printf ("\n"); printf ("Example 4: sunwait list 7 gmt sunrise angle 3\n"); printf ("List next 7 days sunrise times, custom +3 degree twilight angle, default location.\n"); printf ("Uses GMT; as any change in daylight saving over the specified period is not considered.\n"); printf ("\n"); printf ("Note that program uses C library functions to determine time and localtime.\n"); printf ("Error for timings are estimated at: +/- 4 minutes.\n"); printf ("\n"); } void myToLower (char *arg) { for (unsigned int i=0; i < strlen (arg); i++) arg[i] = tolower (arg[i]); } void myToLower (const int argc, char *argv[]) { for (int i=1; i < argc; i++) myToLower (argv [i]); } boolean myIsNumber (const char *arg) { bool digitSet = false; for (int i=0; ; i++) { switch (arg[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digitSet = true; break; case '-': case '+': if (digitSet) return false; break; case '\0': return digitSet; break; // Exit OK default: return false; // Exit Error } } return false; /* Shouldn't get here */ } boolean myIsSignedNumber (const char *arg) { bool digitSet = false; for (int i=0; ; i++) { switch (arg[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digitSet = true; break; case '+': case '-': if (i>0) return false; break; /* Sign only at start */ case '\0': return digitSet; break; // Exit OK default: return false; // Exit Error } } return false; /* Shouldn't get here */ } boolean myIsSignedFloat (const char *arg) { bool digitSet = false; for (int i=0; ; i++) { switch (arg[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digitSet = true; break; case '.': break; /* Can be anywhere (but in front of sign), or not there */ case '+': case '-': if (i>0) return false; break; /* Sign only at start */ case '\0': return digitSet; break; // Exit OK default: return false; // Exit Error } } return false; /* Shouldn't get here */ } boolean myIsSignedFloat (const char *pArg, double *pDouble) { double number = 0; int exponent = 0; bool negative = false; bool exponentSet = false; for (int i=0; ; i++) { switch (pArg[i]) { case '0': number = (number*10) + 0; exponentSet?exponent++:true; break; case '1': number = (number*10) + 1; exponentSet?exponent++:true; break; case '2': number = (number*10) + 2; exponentSet?exponent++:true; break; case '3': number = (number*10) + 3; exponentSet?exponent++:true; break; case '4': number = (number*10) + 4; exponentSet?exponent++:true; break; case '5': number = (number*10) + 5; exponentSet?exponent++:true; break; case '6': number = (number*10) + 6; exponentSet?exponent++:true; break; case '7': number = (number*10) + 7; exponentSet?exponent++:true; break; case '8': number = (number*10) + 8; exponentSet?exponent++:true; break; case '9': number = (number*10) + 9; exponentSet?exponent++:true; break; case '.': case ',': exponentSet = true; exponent = 0; // May be: N36.513679 (not right, but it'll do) break; case '+': if (i>0) return false; // Sign only at start negative = false; break; case '-': if (i>0) return false; // Sign only at start negative = true; break; case '\0': /* Exit */ /* Place decimal point in number */ if (exponentSet && exponent > 0) number = number / pow (10, (double) exponent); if (negative) number = -number; *pDouble = number; return true; /* All done */ break; default: return false; } } return false; /* Shouldn't get to here */ } boolean isBearing (runStruct *pRun, const char *pArg) { double bearing = 0; int exponent = 0; bool negativeBearing = false; bool exponentSet = false; char compass = 'X'; for (int i=0; ; i++) { switch (pArg[i]) { case '0': bearing = (bearing*10) + 0; exponentSet?exponent++:true; break; case '1': bearing = (bearing*10) + 1; exponentSet?exponent++:true; break; case '2': bearing = (bearing*10) + 2; exponentSet?exponent++:true; break; case '3': bearing = (bearing*10) + 3; exponentSet?exponent++:true; break; case '4': bearing = (bearing*10) + 4; exponentSet?exponent++:true; break; case '5': bearing = (bearing*10) + 5; exponentSet?exponent++:true; break; case '6': bearing = (bearing*10) + 6; exponentSet?exponent++:true; break; case '7': bearing = (bearing*10) + 7; exponentSet?exponent++:true; break; case '8': bearing = (bearing*10) + 8; exponentSet?exponent++:true; break; case '9': bearing = (bearing*10) + 9; exponentSet?exponent++:true; break; case '.': case ',': exponentSet = true; exponent = 0; // May be: N36.513679 (not right, but it'll do) break; case '+': if (i>0) return false; // Sign only at start negativeBearing = false; break; case '-': if (i>0) return false; // Sign only at start negativeBearing = true; break; case 'n': case 'N': compass = 'N'; exponentSet = true; break; // Can support 36N513679 (not right, but it'll do) case 'e': case 'E': compass = 'E'; exponentSet = true; break; case 's': case 'S': compass = 'S'; exponentSet = true; break; case 'w': case 'W': compass = 'W'; exponentSet = true; break; case ' ': /* Exit */ case '\0': /* Exit */ /* Fail, if the compass has not been set */ if (compass == 'X') return false; /* Place decimal point in bearing */ if (exponentSet && exponent > 0) bearing = bearing / pow (10, (double) exponent); /* Fix-up bearing so that it is in range zero to just under 360 */ bearing = revolution (bearing); bearing = negativeBearing ? 360 - bearing : bearing; /* Fix-up bearing to Northings or Eastings only */ if (compass == 'S') { bearing = 360 - bearing; compass = 'N'; } else if (compass == 'W') { bearing = 360 - bearing; compass = 'E'; } /* It's almost done, assign bearing to appropriate global */ if (compass == 'N') pRun->latitude = fixLatitude (bearing); else if (compass == 'E') pRun->longitude = fixLongitude (bearing); else return false; return true; /* All done */ break; default: return false; } } return false; /* Shouldn't get to here */ } boolean isOffset (runStruct *pRun, const char *pArg) { int colon = 0, number0 = 0, number1 = 0, number2 = 0; bool negativeOffset = false; double returnOffset = 0.0; for (int i=0; ; i++) { switch (pArg[i]) { case '0': number0 = (number0*10) + 0; break; case '1': number0 = (number0*10) + 1; break; case '2': number0 = (number0*10) + 2; break; case '3': number0 = (number0*10) + 3; break; case '4': number0 = (number0*10) + 4; break; case '5': number0 = (number0*10) + 5; break; case '6': number0 = (number0*10) + 6; break; case '7': number0 = (number0*10) + 7; break; case '8': number0 = (number0*10) + 8; break; case '9': number0 = (number0*10) + 9; break; case ':': number2 = number1; number1 = number0; number0 = 0; colon++; break; case '+': break; case '-': if (i>0) return false; // Sign only at start negativeOffset = true; break; case '\0': /* Exit */ if (colon==0) returnOffset = number0/60.0; else if (colon==1) returnOffset = number1 + number0/60.0; else if (colon==2) returnOffset = number2 + number1/60.0 + number0/3600.0; else return false; if (negativeOffset) { returnOffset = -returnOffset; } pRun->offsetHour = returnOffset; return true; /* <-- Hopefully, exit here <-- */ break; default: return false; } } return false; /* Shouldn't get here */ } /* ** time_t converted to struct tm. Using GMT (UTC) time. */ void myUtcTime (const time_t *pTimet, struct tm *pTm) { /* Windows code: Start */ #if defined _WIN32 || defined _WIN64 errno_t err; err = _gmtime64_s (pTm, pTimet); if (err) { printf ("Error: Invalid Argument to _gmtime64_s ().\n"); exit (EXIT_ERROR); } #endif /* Windows code: End */ /* Linux code: Start */ #if defined __linux__ gmtime_r (pTimet, pTm); #endif /* Linux code: End */ } /* ** time_t converted to struct tm. Using local time. */ void myLocalTime (const time_t *pTimet, struct tm *pTm) { /* Windows code: Start */ #if defined _WIN32 || defined _WIN64 errno_t err; err = _localtime64_s (pTm, pTimet); if (err) { printf ("Error: Invalid Argument to _gmtime64_s ().\n"); exit (EXIT_ERROR); } #endif /* Windows code: End */ /* Linux code: Start */ #if defined __linux__ localtime_r (pTimet, pTm); #endif /* Linux code: End */ } /* ** A "struct tm" time can be different from UTC because of TimeZone ** or Daylight Savings. This function gives the difference - unit: hours. ** ** Usage: ** Add the UTC bias to convert from local-time to UTC. ** ptrTm is set */ double getUtcBiasHours (const time_t *pTimet) { struct tm utcTm; double utcBiasHours = 0.0; // Populate "struct tm" with UTC data for the given day myUtcTime (pTimet, &utcTm); /* Windows code: Start */ #if defined _WIN32 || defined _WIN64 struct tm utcNoonTm, localNoonTm; // Keep to the same day given, but go for noon. Daylight savings changes usually happen in the early hours. // mktime() changes the values in "struct tm", so I need to use a private one anyway. utcTm.tm_hour = 12; utcTm.tm_min = 0; utcTm.tm_sec = 0; // Now convert this time to time_t (which is always, by definition, UTC), // so I can run both of the two functions I can use that differentiate between timezones, using the same UTC moment. time_t noonTimet = mktime (&utcTm); // Unfortunately this is noonTimet is local time. It's the best I can do. // If it was UTC, all locations on earth are within the same day at noon. // (Because UTC = GMT. Noon GMT +/- 12hrs nestles upto, but not across, the dateline) // Local-time 'days' (away from GMT) will probably cross the date line. myLocalTime (&noonTimet, &localNoonTm); // Generate 'struct tm' for local time myUtcTime (&noonTimet, &utcNoonTm); // Generate 'struct tm' for UTC // This is not nice, but Visual Studio does not support "strftime(%z)" (get UTC bias) like linux does. // I'll figure out the UTC bias by comparing readings of local time to UTC for the same moment. // Although localTm and utcTm may be different, some secret magic ensures that mktime() will bring // them back to the same time_t value (as it should: they identify the same moment, just in different timezones). // I'll just have to work out the utcBias using the differing 'struct tm' values. It isn't pretty. utcBiasHours = (localNoonTm.tm_hour - utcNoonTm.tm_hour) + (localNoonTm.tm_min - utcNoonTm.tm_min) / 60.0; // The day may be different between the two times, especially if the local timezone is near the dateline. // Rollover of tm_yday (from 365 to 0) is a further problem, but no bias is ever more than 24 hours - that wouldn't make sense. if (localNoonTm.tm_year > utcNoonTm.tm_year) utcBiasHours += 24.0; // Local time is in a new year, utc isn't: so local time is a day ahead else if (localNoonTm.tm_year < utcNoonTm.tm_year) utcBiasHours -= 24.0; // Local time is in old year, utc is new year: so local time is a day behind else utcBiasHours += (localNoonTm.tm_yday - utcNoonTm.tm_yday)*24.0; // Year has not changed, so we can use tm_yday normaly #endif /* Windows code: End */ /* Linux code: Start */ #if defined __linux__ char buffer [80]; signed long int tmpLong = 0; mktime (&utcTm); // Let "mktime()" do it's magic strftime (buffer, 80, "%z", &utcTm); if (strlen (buffer) > 0 && myIsNumber (buffer)) { tmpLong = atol (buffer); utcBiasHours = (int)(tmpLong/100 + (tmpLong%100)/60.0); } #endif /* Linux code: End */ return utcBiasHours; } /* ** Debug: What's the time (include timezone)? */ void myDebugTime (const char * pTitleChar, const time_t *pTimet) { if (pRun->debug == ONOFF_ON) { struct tm tmpLocalTm, tmpUtcTm; char utcBuffer [80]; char localBuffer [80]; // Convert current time to struct tm for UTC and local timezone myLocalTime (pTimet, &tmpLocalTm); myUtcTime (pTimet, &tmpUtcTm); strftime ( utcBuffer, 80, "%c %Z", &tmpUtcTm); printf ("Debug: %s utcTm: %s\n", pTitleChar, utcBuffer); strftime (localBuffer, 80, "%c %Z", &tmpLocalTm); printf ("Debug: %s localTm: %s\n", pTitleChar, localBuffer); // Difference between UTC and local TZ strftime ( utcBuffer, 80, "%Z", &tmpUtcTm); strftime (localBuffer, 80, "%Z", &tmpLocalTm); printf ("Debug: %s UTC bias (add to %s to get %s) hours: %f\n", pTitleChar, utcBuffer, localBuffer, getUtcBiasHours (pTimet)); } } /* ** Return time_t of midnight UTC on the day given ** In effect, this function is going to shave upto 24 hours off a time ** returning 00:00 UTC on the day given. */ time_t getMidnightUTC (const time_t *pTimet, const runStruct *pRun) { struct tm tmpTm; // Convert target "struct tm" to time_t. It'll be set to midnight local time, on the target day. myUtcTime (pTimet, &tmpTm); // Set to the start of the day tmpTm.tm_hour = 0; tmpTm.tm_min = 0; tmpTm.tm_sec = 0; // Fiddle with other "struct tm" fields to get things right tmpTm.tm_wday = 0; // mktime will ignore this when calculating time_t as it contradicts days and months tmpTm.tm_yday = 0; // mktime will ignore this when calculating time_t as it contradicts days and months tmpTm.tm_isdst = -1; // -1 means: mktime() must work it out. 0=DST not in effect. 1=DST in effect. (Daylight Savings) // Shave off (add) UTC offset, so that time_t is converted from midnight local-time to midnight UTC on the target day tmpTm.tm_sec += myRound (pRun->utcBiasHours * 3600.0); // Let mktime() do it's magic return mktime (&tmpTm); } /* ** A utility that helps determine if the given targetStruct is DAY or NIGHT ** This isn't the end of the matter, as DAY from yesterday or tomorrow ** could slip past midnight into the target day. Near the dateline ** a UTC day features midday NIGHT. The sun is high near midnight UTC. ** ** THE TARGET DATE MUST BE THE CURRENT DATE (on 1st call, as this function iterates) */ OnOff isDay (const runStruct *pRun) { // If the time is before sunrise or after sunset, I need to know that // we're not in the daylight of either the neighbouring days. targetStruct yesterday; yesterday.twilightAngle = pRun->twilightAngle; yesterday.daysSince2000 = pRun->now2000 - 1; targetStruct today; today.twilightAngle = pRun->twilightAngle; today.daysSince2000 = pRun->now2000; targetStruct tomorrow; tomorrow.twilightAngle = pRun->twilightAngle; tomorrow.daysSince2000 = pRun->now2000 + 1; sunriset (pRun, &yesterday); sunriset (pRun, &today); sunriset (pRun, &tomorrow); yesterday.southHourUTC -= 24.0; tomorrow.southHourUTC += 24.0; // Get current time (hours from UTC midnight of current day). // Difftime() returns "seconds", we want "hours". time_t midnightUTC = getMidnightUTC (&pRun->nowTimet, pRun); double nowHourUTC = difftime (pRun->nowTimet, midnightUTC) / (3600.0); // Get the time (0-24) of sunrise and set, of the three days double riseHourUTCYesterday = getOffsetRiseHourUTC (pRun, &yesterday); double setHourUTCYesterday = getOffsetSetHourUTC (pRun, &yesterday); double riseHourUTCToday = getOffsetRiseHourUTC (pRun, &today); double setHourUTCToday = getOffsetSetHourUTC (pRun, &today); double riseHourUTCTomorrow = getOffsetRiseHourUTC (pRun, &tomorrow); double setHourUTCTomorrow = getOffsetSetHourUTC (pRun, &tomorrow); // Figure out if we're between sunrise and sunset (with offset) of any of the three days return ( (nowHourUTC >= riseHourUTCYesterday && nowHourUTC <= setHourUTCYesterday) || (nowHourUTC >= riseHourUTCToday && nowHourUTC <= setHourUTCToday ) || (nowHourUTC >= riseHourUTCTomorrow && nowHourUTC <= setHourUTCTomorrow ) ) ? ONOFF_ON : ONOFF_OFF; } /* ** ** >>>>> main() <<<<< ** */ int main (int argc, char *argv[]) { pRun = &gRun; // Set Default values pRun->latitude = DEFAULT_LATITUDE; pRun->longitude = DEFAULT_LONGITUDE; pRun->offsetHour = 0.0; pRun->utcBiasHours = 0.0; pRun->functionVersion = ONOFF_OFF; pRun->functionUsage = ONOFF_OFF; pRun->functionWait = ONOFF_OFF; pRun->functionPoll = ONOFF_OFF; pRun->functionReport = ONOFF_OFF; pRun->functionList = ONOFF_OFF; pRun->debug = ONOFF_OFF; pRun->reportSunrise = ONOFF_OFF; pRun->reportSunset = ONOFF_OFF; pRun->utc = ONOFF_OFF; pRun->twilightAngle = TWILIGHT_ANGLE_DAYLIGHT; // For target year int yearInt = NOT_SET; int monInt = NOT_SET; int mdayInt = NOT_SET; // Return code int exitCode = EXIT_OK; // // DO NOT PUT CODE BEFORE "debug" ARGUMENT IS KNOWN (UNTIL AFTER COMMAND LINE PARSED) // /* ** ** Parse command line arguments ** */ // Change to all lowercase, just to make life easier ... myToLower (argc, argv); // Look for debug being activated ... for (int i=1; i < argc; i++) if (!strcmp (argv [i], "debug")) pRun->debug = ONOFF_ON; // For each argument for (int i=1; i < argc; i++) { char *arg = argv[i]; // Echo argument, if in debug if (pRun->debug == ONOFF_ON) printf ("Debug: argv[%d]: >%s<\n", i, arg); // Strip any hyphen from arguments, but not negative signs of numbers if (arg[0] == '-' && arg[1] != '\0' && !isdigit(arg[1])) *arg++; // Normal help or version info if (!strcmp (arg, "v") || !strcmp (arg, "version")) pRun->functionVersion = ONOFF_ON; else if (!strcmp (arg, "nv") || !strcmp (arg, "noversion")) pRun->functionVersion = ONOFF_OFF; else if (!strcmp (arg, "?") || !strcmp (arg, "usage") || !strcmp (arg, "h") || !strcmp (arg, "help")) pRun->functionUsage = ONOFF_ON; else if (!strcmp (arg, "nh" ) || !strcmp (arg, "nousage") || !strcmp (arg, "nohelp")) pRun->functionUsage = ONOFF_OFF; // Major Options else if (!strcmp (arg, "wait")) pRun->functionWait = ONOFF_ON; else if (!strcmp (arg, "poll")) pRun->functionPoll = ONOFF_ON; else if (!strcmp (arg, "report")) pRun->functionReport = ONOFF_ON; else if (!strcmp (arg, "list")) { // List should have a parameter attached pRun->functionList = ONOFF_ON; if (i+1listDays = atoi (argv [++i]); // Note: ++i else pRun->listDays = DEFAULT_LIST; } // Minor Options // Print output in GMT or local-time else if (!strcmp (arg, "gmt") || !strcmp (arg, "utc")) pRun->utc = ONOFF_ON; else if (!strcmp (arg, "nogmt") || !strcmp (arg, "noutc")) pRun->utc = ONOFF_OFF; // Debug mode else if (!strcmp (arg, "debug")) pRun->debug = ONOFF_ON; else if (!strcmp (arg, "nodebug")) pRun->debug = ONOFF_OFF; // Poll. Is it DAY or NIGHT? else if (!strcmp (arg, "e") || !strcmp (arg, "er") || !strcmp (arg, "exit") || !strcmp (arg, "poll") || !strcmp (arg, "exitreport")) pRun->functionPoll = ONOFF_ON; else if (!strcmp (arg, "ne") || !strcmp (arg, "ner") || !strcmp (arg, "noexit") || !strcmp (arg, "nopoll") || !strcmp (arg, "noexitreport")) pRun->functionPoll = ONOFF_OFF; // Specify twilight angle else if (!strcmp (arg, "sun") || !strcmp (arg, "day") || !strcmp (arg, "light") || !strcmp (arg, "normal") || !strcmp (arg, "visible") || !strcmp (arg, "daylight")) pRun->twilightAngle = TWILIGHT_ANGLE_DAYLIGHT; else if (!strcmp (arg, "civil") || !strcmp (arg, "civ")) pRun->twilightAngle = TWILIGHT_ANGLE_CIVIL; else if (!strcmp (arg, "nautical") || !strcmp (arg, "nau") || !strcmp (arg, "naut")) pRun->twilightAngle = TWILIGHT_ANGLE_NAUTICAL; else if (!strcmp (arg, "astronomical") || !strcmp (arg, "ast") || !strcmp (arg, "astr") || !strcmp (arg, "astro")) pRun->twilightAngle = TWILIGHT_ANGLE_ASTRONOMICAL; else if (!strcmp (arg, "a") || !strcmp (arg, "angle") || !strcmp (arg, "twilightangle") || !strcmp (arg, "twilight")) { if (i+1twilightAngle = atof (argv [++i]); // Note: "++i" else pRun->twilightAngle = TWILIGHT_ANGLE_DAYLIGHT; } // Specify target date (default is today) else if (!strcmp (arg, "y") && i+1reportSunrise = ONOFF_ON; else if (!strcmp (arg, "sunset") || !strcmp (arg, "set") || !strcmp (arg, "dusk") || !strcmp (arg, "sundown") || !strcmp (arg, "down")) pRun->reportSunset = ONOFF_ON; // Specify latitude and longitude - quite complex, so I've handled it elsewhere else if (isBearing (pRun, arg)) {} /* Functionality in "isBearing()" */ // Don't know what in on the command line, so complain. else printf ("Error: Unknown command-line argument: %s\n", arg); } /* ** ** Analyse command line, check for errors and fill in gaps ** */ if (pRun->debug == ONOFF_ON) { if (pRun->utc == ONOFF_ON) printf ("Debug: All output to use UTC.\n"); else printf ("Debug: All output to use local timezone (nogmt).\n"); } /* ** Get: current time (time_t is UTC, always) and bias of local-time from UTC */ time (&pRun->nowTimet); if (pRun->debug == ONOFF_ON) myDebugTime ("Now", &pRun->nowTimet); pRun->utcBiasHours = getUtcBiasHours (&pRun->nowTimet); if (pRun->debug == ONOFF_ON) printf ("Debug: UTC Bias (hours): %f\n", pRun->utcBiasHours); pRun->now2000 = daysSince2000 (&pRun->nowTimet); if (pRun->debug == ONOFF_ON) printf ("Debug: Now: Days since 2000: %lu\n", pRun->now2000); /* ** Get: Target Date */ { struct tm targetTm; // Populate targetTm :- // Get the target day (as "struct tm") for "now" - the default // I'll get the local-time day, as it'll make sense with the user, unless UTC was asked for // if (pRun->utc == ONOFF_ON) myUtcTime (&pRun->nowTimet, &targetTm); // User wants UTC else myLocalTime (&pRun->nowTimet, &targetTm); // User gets local timezone // // Parse "target" year, month and day [adjust target] // if (yearInt != NOT_SET) { if (yearInt < 0 || yearInt > 99) { printf ("Error: \"Year\" must be between 0 and 99: %u\n", yearInt); exit (EXIT_ERROR); } targetTm.tm_year = yearInt + 100; } if (pRun->debug == ONOFF_ON) printf ("Debug: Target year set to: %u\n", targetTm.tm_year); if (monInt != NOT_SET) { if (monInt < 1 || monInt > 12) { printf ("Error: \"Month\" must be between 1 and 12: %u\n", monInt); exit (EXIT_ERROR); } targetTm.tm_mon = monInt-1; // We need month 0 to 11, not 1 to 12 } if (pRun->debug == ONOFF_ON) printf ("Debug: Target mon set to: %u\n", targetTm.tm_mon); if (mdayInt != NOT_SET) { if (mdayInt < 1 || mdayInt > 31) { printf ("Error: \"Day of month\" must be between 1 and 31: %u\n", mdayInt); exit (EXIT_ERROR); } targetTm.tm_mday = mdayInt; } if (pRun->debug == ONOFF_ON) printf ("Debug: Target mday set to: %u\n", targetTm.tm_mday); // Set target time to the start of the UTC day targetTm.tm_hour = 0; targetTm.tm_min = 0; targetTm.tm_sec = 0; // Fiddle with other "struct tm" fields to get things right targetTm.tm_wday = 0; // mktime will ignore this when calculating time_t as it contradicts days and months targetTm.tm_yday = 0; // mktime will ignore this when calculating time_t as it contradicts days and months targetTm.tm_isdst = -1; // -1 means: mktime() must work it out. 0=DST not in effect. 1=DST in effect. (Daylight Savings) // Convert target "struct tm" to time_t. It'll be set to midnight local time, on the target day. pRun->targetTimet = mktime (&targetTm); // Shave off (add) UTC offset, so that time_t is converted from midnight local-time to midnight UTC on the target day targetTm.tm_sec += myRound (pRun->utcBiasHours * 60.0 * 60.0); // All done pRun->targetTimet = mktime (&targetTm); // <<<<<< The important bit done <<< targetTimet is set to midnight UTC if (pRun->debug == ONOFF_ON) myDebugTime ("Target", &pRun->targetTimet); } pRun->target2000 = daysSince2000 (&pRun->targetTimet); if (pRun->debug == ONOFF_ON) printf ("Debug: Target: Days since 2000: %lu\n", pRun->target2000); /* ** Check: Latitude and Longitude */ if (pRun->latitude == NOT_SET) { if (pRun->debug == ONOFF_ON) printf ("Debug: latitude not set. Default applied.\n"); pRun->latitude = DEFAULT_LATITUDE; /* The Buttercross, Bingham, England */ } if (pRun->longitude == NOT_SET) { if (pRun->debug == ONOFF_ON) printf ("Debug: longitude not set. Default applied.\n"); pRun->longitude = DEFAULT_LONGITUDE; /* The Buttercross, Bingham, England */ } // /* Co-ordinates must be in 0 to 360 range */ // IFC 2014-12-02: Removed as done in isBearing() // pRun->latitude = revolution (pRun->latitude); // IFC 2014-12-02: Removed as done in isBearing() // pRun->longitude = revolution (pRun->longitude); // IFC 2014-12-02: Removed as done in isBearing() if (pRun->debug == ONOFF_ON) { printf ("Debug: Co-ordinates - Latitude: %fN\n", pRun->latitude); printf ("Debug: Co-ordinates - Longitude: %fE\n", pRun->longitude); } /* ** Check: Twilight Angle */ if (pRun->twilightAngle == NOT_SET) { if (pRun->debug == ONOFF_ON) printf ("Debug: twilight angle not set. Default: daylight.\n"); pRun->twilightAngle = TWILIGHT_ANGLE_DAYLIGHT; } if (pRun->twilightAngle <= -90 || pRun->twilightAngle >= 90) { printf ("Error: Twilight angle must be between -90 and +90 (-ve = below horizon), your setting: %f\n", pRun->twilightAngle); pRun->twilightAngle = TWILIGHT_ANGLE_DAYLIGHT; } if (pRun->debug == ONOFF_ON) { if (pRun->twilightAngle == TWILIGHT_ANGLE_DAYLIGHT) printf ("Debug: Twilight - Daylight\n"); else if (pRun->twilightAngle == TWILIGHT_ANGLE_CIVIL) printf ("Debug: Twilight - Civil\n"); else if (pRun->twilightAngle == TWILIGHT_ANGLE_NAUTICAL) printf ("Debug: Twilight - Nautical\n"); else if (pRun->twilightAngle == TWILIGHT_ANGLE_ASTRONOMICAL) printf ("Debug: Twilight - Astronomical\n"); else printf ("Debug: Twilight - Custom angle (degrees): %f\n", pRun->twilightAngle); } /* ** Check: Offset */ if (pRun->debug == ONOFF_ON) printf ("Debug: User specified offset (hours): %f\n", pRun->offsetHour); /* ** Check: Major-option or Function */ // IF no function requested THEN default to "usage" if ( pRun->functionList == ONOFF_OFF && pRun->functionPoll == ONOFF_OFF && pRun->functionUsage == ONOFF_OFF && pRun->functionVersion == ONOFF_OFF && pRun->functionWait == ONOFF_OFF && pRun->functionReport == ONOFF_OFF ) pRun->functionUsage = ONOFF_ON; /* ** Check: Sunrise or sunset. IF neither set THEN set both */ if (pRun->reportSunrise == ONOFF_OFF && pRun->reportSunset == ONOFF_OFF) { pRun->reportSunrise = ONOFF_ON; pRun->reportSunset = ONOFF_ON; } /* ** OK - we're all done figuring out what to do - let's do it */ if (pRun->functionVersion == ONOFF_ON) { if (pRun->debug == ONOFF_ON) printf ("Debug: Function selected: Version\n"); print_version (); exitCode = EXIT_OK; } if (pRun->functionUsage == ONOFF_ON) { if (pRun->debug == ONOFF_ON) printf ("Debug: Function selected: Usage\n"); print_usage (); exitCode = EXIT_OK; } if (pRun->functionReport == ONOFF_ON) { if (pRun->debug == ONOFF_ON) printf ("Debug: Function selected: Report\n"); generate_report (pRun); exitCode = EXIT_OK; } if (pRun->functionList == ONOFF_ON) { if (pRun->debug == ONOFF_ON) printf ("Debug: Function selected: List\n"); print_list (pRun); exitCode = EXIT_OK; } if (pRun->functionWait == ONOFF_ON) { if (pRun->debug == ONOFF_ON) printf ("Debug: Function selected: Wait\n"); exitCode = wait (pRun); } if (pRun->functionPoll == ONOFF_ON) { if (pRun->debug == ONOFF_ON) printf ("Debug: Function selected: Poll\n"); exitCode = poll (pRun); if (exitCode == EXIT_DAY) printf ("DAY\n"); else if (exitCode == EXIT_NIGHT) printf ("NIGHT\n"); else if (exitCode == EXIT_OK) printf ("OK\n"); else if (exitCode == EXIT_ERROR) printf ("ERROR\n"); } exit (exitCode); } /* ** Simply check if we think now/current-time is night OR day (day includes twilight) */ inline int poll (const runStruct *pRun) { return isDay (pRun) == ONOFF_ON ? EXIT_DAY : EXIT_NIGHT; } /* ** Wait until sunrise or sunset occurs on the target day. ** That sounds simple, until you start to consider longitudes near the dateline. ** Midnight UTC can lands later and later within a local day for large longitudes. ** Latitudes heading towards the poles can either shrink the day length to nothing or the whole day, depending on the season. ** A user-specified offset messes around with daylength too. ** Exit immediately if its a polar day or midnight sun (including offset). */ int wait (const runStruct *pRun) { /* ** Calculate start/end of twilight for given twilight type/angle. ** For latitudes near poles, the sun might not pass through specified twilight angle that day. ** For big longitudes, it's quite likely the sun is up at midnight UTC: this means we have to calculate successive days. */ targetStruct yesterday; yesterday.twilightAngle = pRun->twilightAngle; yesterday.daysSince2000 = pRun->target2000 - 1; targetStruct today; today.twilightAngle = pRun->twilightAngle; today.daysSince2000 = pRun->target2000; targetStruct tomorrow; tomorrow.twilightAngle = pRun->twilightAngle; tomorrow.daysSince2000 = pRun->target2000 + 1; sunriset (pRun, &yesterday); sunriset (pRun, &today); sunriset (pRun, &tomorrow); yesterday.southHourUTC -= 24; tomorrow.southHourUTC += 24; // Calculate duration (seconds) from "now" to "midnight UTC on the target day". [difftime (end, beginning)] long waitMidnightUTC = static_cast (difftime (pRun->targetTimet, pRun->nowTimet)); // Calculate duration to wait for each day's rise and set (seconds) // (targetTimet is set to midnight on the target day) long waitRiseYesterday = waitMidnightUTC + static_cast ( 3600.0 * getOffsetRiseHourUTC (pRun, &yesterday) ); long waitSetYesterday = waitMidnightUTC + static_cast ( 3600.0 * getOffsetSetHourUTC (pRun, &yesterday) ); long waitRiseToday = waitMidnightUTC + static_cast ( 3600.0 * getOffsetRiseHourUTC (pRun, &today) ); long waitSetToday = waitMidnightUTC + static_cast ( 3600.0 * getOffsetSetHourUTC (pRun, &today) ); long waitRiseTomorrow = waitMidnightUTC + static_cast ( 3600.0 * getOffsetRiseHourUTC (pRun, &tomorrow) ); long waitSetTomorrow = waitMidnightUTC + static_cast ( 3600.0 * getOffsetSetHourUTC (pRun, &tomorrow) ); // Determine next sunrise and sunset // (we may be in DAY, so the next event is sunset - followed by sunrise) long waitRiseSeconds = 0; long waitSetSeconds = 0; if (waitRiseYesterday > 0) { waitRiseSeconds = waitRiseYesterday; waitSetSeconds = waitSetYesterday; } else if (waitSetYesterday > 0) { waitRiseSeconds = waitRiseToday; waitSetSeconds = waitSetYesterday; } else if (waitRiseToday > 0) { waitRiseSeconds = waitRiseToday; waitSetSeconds = waitSetToday; } else if (waitSetToday > 0) { waitRiseSeconds = waitRiseTomorrow; waitSetSeconds = waitSetToday; } else if (waitRiseTomorrow > 0) { waitRiseSeconds = waitRiseTomorrow; waitSetSeconds = waitSetTomorrow; } else if (waitSetTomorrow > 0) { waitRiseSeconds = 0; waitSetSeconds = waitSetTomorrow; } // Is it currently DAY or NIGHT? OnOff isDay = ONOFF_OFF; if (waitRiseYesterday > 0) { isDay = ONOFF_OFF; } else if (waitSetYesterday > 0) { isDay = ONOFF_ON; } else if (waitRiseToday > 0) { isDay = ONOFF_OFF; } else if (waitSetToday > 0) { isDay = ONOFF_ON; } else if (waitRiseTomorrow > 0) { isDay = ONOFF_OFF; } else if (waitSetTomorrow > 0) { isDay = ONOFF_ON; } // Determine if the day is "normal" (where the rises and sets) or "polar" ("midnight sun" or "polar night") bool exitPolar = false; if (waitSetYesterday > 0) { double diurnalArc = diurnalArcWithOffset (pRun, &yesterday); exitPolar = diurnalArc <= 0.0 || diurnalArc >= 24.0; } else if (waitSetToday > 0) { double diurnalArc = diurnalArcWithOffset (pRun, &today); exitPolar = diurnalArc <= 0.0 || diurnalArc >= 24.0; } else { double diurnalArc = diurnalArcWithOffset (pRun, &tomorrow); exitPolar = diurnalArc <= 0.0 || diurnalArc >= 24.0; } if (exitPolar) { if (pRun->debug == ONOFF_ON) printf ("Debug: Polar region or large offset: No sunrise today, there's nothing to wait for!\n"); return EXIT_ERROR; } // Get next rise or set time UNLESS the opposite event happens first (unless less than 6 hours to required event) // IF both rise and set requested THEN wait for whichever is next long waitSeconds = 0; if (pRun->reportSunrise == ONOFF_ON && pRun->reportSunset == ONOFF_OFF) { if (isDay == ONOFF_OFF || waitRiseSeconds < 6*60*60) waitSeconds = waitRiseSeconds; } else if (pRun->reportSunrise == ONOFF_OFF && pRun->reportSunset == ONOFF_ON) { if (isDay == ONOFF_ON || waitSetSeconds < 6*60*60) waitSeconds = waitSetSeconds; } else { waitSeconds = waitRiseSeconds < waitSetSeconds ? waitRiseSeconds : waitSetSeconds; } // Don't wait if event has passed (or next going to occur soon [6hrs]) if (waitSeconds <= 0) { if (pRun->debug == ONOFF_ON) printf ("Debug: Event already passed today, can't wait for that!\n"); return EXIT_ERROR; } // // In debug mode, we don't want to wait for sunrise or sunset. Wait a minute instead. // if (pRun->debug == ONOFF_ON) { printf("Debug: Wait reduced from %li to 10 seconds.\n", waitSeconds); waitSeconds = 10; } else if (pRun->functionPoll == ONOFF_ON) waitSeconds += 60; // Make more sure that a subsequent POLL works properly (wink ;-) /* ** Sleep (wait) until the event is expected */ /* Windows code: Start */ #if defined _WIN32 || defined _WIN64 waitSeconds *= 1000; // Convert hours to milliseconds for Windows Sleep ((DWORD) waitSeconds); // Windows-only . waitSec is tested positive or zero #endif /* Windows code: End */ /* Linux code: Start */ #if defined __linux__ sleep (waitSeconds); // Linux-only (seconds OK) #endif /* Linux code: End */ return EXIT_OK; } ./sunwait/0.8/sunwait.h0000775000175000017500000000636512533056525013202 0ustar ianian// // sunwait - sunwait.h // // 08-12-2014 IFC 0.6 Add timezone handling for output, if required // 02-05-2015 IFC 0.7 Fix timezone and DST problems // 06-05-2015 IFC 0.8 Fix polar calculations // #include #ifndef SUNWAIT_H #define SUNWAIT_H #define DEFAULT_LIST 1 /* in 'List' mode, report these number days */ #define DEFAULT_ANGLE 0.83 /* Default twighlight angle. */ #define DEFAULT_LATITUDE 52.952308 #define DEFAULT_LONGITUDE 359.048052 /* The Buttercross, Bingham, England */ #define boolean bool #define NOT_SET 9999999 #define SET 1111111 typedef enum { ONOFF_ON , ONOFF_OFF } OnOff; typedef struct { double latitude; // Degrees N - Global position double longitude; // Degrees E - Global position double offsetHour; // Unit: Hours. Adjust sunrise or sunset by this amount, towards midday. double twilightAngle; // Degrees. -ve = below horizon. Twilight angle requested by user. time_t nowTimet; // Time this program is run time_t targetTimet; // Midnight (00:00am) UTC on target day unsigned long now2000; // Days from 1/1/2000 to "now" unsigned long target2000; // Days from 1/1/2000 to "target" OnOff functionVersion; // User wants program version number reported OnOff functionUsage; // User wants program usage info reported OnOff functionReport; // User wants a report generated OnOff functionList; // User wants a list of sunrise or sunset times OnOff functionPoll; // User wants a "NIGHT" or "DAY" return code/text OnOff functionWait; // User wants the program to wait until sunrise or sunset OnOff utc; // Printed output is in GMT/UTC (on) or localtime (off) OnOff debug; // Is debug output required OnOff reportSunrise; // Report sun rising OnOff reportSunset; // Report sun setting unsigned int listDays; // How many days should sunrise/set be listed for. (function: List) double utcBiasHours; // Add to UTC to get local-time (hours) } runStruct; typedef struct { // "Input" data double twilightAngle; // Degrees. -ve = below horizon. Can be: daylight, civil, nautical, astronomical, or custom. unsigned long daysSince2000; // The sunrise calculation needs this: days from start of 2000 to targetTimet // "Output" data double diurnalArc; // Target day: The time it takes the sun to travel across the sky. "southHourUTC" is mid-way. double southHourUTC; // Target day: Sun directly south - time (hours) from targetTimet (midnight, target Day UTC) } targetStruct; #define EXIT_OK 0 #define EXIT_ERROR 1 #define EXIT_DAY 2 #define EXIT_NIGHT 3 #define DAYS_TO_2000 365*30+7 // Number of days from 'C' time epoch (1/1/1970 to 1/1/2000) [including leap days] // Functions void myUtcTime (const time_t * ptrTimet, struct tm * ptrTm); void myLocalTime (const time_t * ptrTimet, struct tm * ptrTm); OnOff isDay (const runStruct *pRun); int poll (const runStruct *pRun); int wait (const runStruct *pRun); #endif ./sunwait/0.8/sunriset.cpp0000775000175000017500000003050412533056525013707 0ustar ianian/* ** sunriset.c - computes Sun rise/set times, including twilights ** Written as DAYLEN.C, 1989-08-16 ** Modified to SUNRISET.C, 1992-12-01 ** (c) Paul Schlyter, 1989, 1992 ** Released to the public domain by Paul Schlyter, December 1992 */ /* ** ** Who When Ver What ** IFC 04-12-2014 0.5 General fixes to "sunwait for windows" and linux port ** IFC 08-12-2014 0.6 Add timezone for output of timings ** IFC 29-04-2015 0.7 Fix for timezone (esp near date line) and more debug ** IFC 2015-05-27 0.8 Resolve 'dodgy day' and cleanup ** */ #include #include // Linux #include #include #include #include "sunwait.h" #include "sunriset.h" using namespace std; /************************************************************************/ /* Note: Eastern longitude positive, Western longitude negative */ /* Northern latitude positive, Southern latitude negative */ /* */ /* >>> Longitude value IS critical in this function! <<< */ /* */ /* days = Days since 2000 plus fraction to local noon */ /* altit = the altitude which the Sun should cross */ /* Set to -35/60 degrees for rise/set, -6 degrees */ /* for civil, -12 degrees for nautical and -18 */ /* degrees for astronomical twilight. */ /* upper_limb: non-zero -> upper limb, zero -> center */ /* Set to non-zero (e.g. 1) when computing rise/set */ /* times, and to zero when computing start/end of */ /* twilight. */ /* */ /* Both times are relative to the specified altitude, */ /* and thus this function can be used to comupte */ /* various twilight times, as well as rise/set times */ /* Return Codes: */ /* 0 = sun rises/sets this day. Success. */ /* Times held at *trise and *tset. */ /* +1 = Midnight Sun. Fail. */ /* Sun above the specified "horizon" all 24 hours. */ /* *trise set to time when the sun is at south, */ /* minus 12 hours while *tset is set to the south */ /* time plus 12 hours. "Day" length = 24 hours */ /* -1 = Polar Night. Fail. */ /* Sun is below the specified "horizon" all 24hours. */ /* "Day" length = 0 hours, *trise and *tset are */ /* both set to the time when the sun is at south. */ /* */ /************************************************************************/ void sunriset (const runStruct *pRun, targetStruct *pTarget) { double sr; /* solar distance, astronomical units */ double sra; /* sun's right ascension */ double sdec; /* sun's declination */ double sradius; /* sun's apparent radius */ double siderealTime; /* local sidereal time */ double altitude; /* sun's altitude: angle to the sun relative to the mathematical (flat-earth) horizon */ double diurnalArc = 0.0; /* the diurnal arc, hours */ double southHour = 0.0; /* Hour UTC the sun is directly south (or north for southern Hemisphere) of lat/long position */ /* compute sideral time at 00:00 UTC of target day for this longitude. */ siderealTime = revolution (GMST0(pTarget->daysSince2000) + 180.0 + pRun->longitude); // 180 = 0 hour UTC is measured 180 degrees from dateline /* compute sun's ra + decl at this moment */ sun_RA_dec (pTarget->daysSince2000, &sra, &sdec, &sr ); /* compute time when sun is directly south - in hours UTC. "12.00" == noon. "15" == 180degrees/12hours [degrees per hour] */ southHour = 12.0 - rev180 (siderealTime - sra)/15.0; /* compute the sun's apparent radius, degrees */ sradius = 0.2666 / sr; // Apparent angular radius of sun is 0.2666/distance in AU (deg) /* Do correction for upper limb ('top' of sun) only, for "daylight" sunrise or set. Otherwise calculate for centre of sun */ if (pTarget->twilightAngle == TWILIGHT_ANGLE_DAYLIGHT) altitude = pTarget->twilightAngle - sradius; else altitude = pTarget->twilightAngle; /* compute the diurnal arc that the sun traverses to reach the specified altitide altit: */ double cost = (sind(altitude) - sind(pRun->latitude) * sind(sdec)) / (cosd(pRun->latitude) * cosd(sdec)); if (abs(cost) < 1.0) diurnalArc = 2*acosd(cost)/15.0; /* Diurnal arc, hours */ else if (cost>=1.0) diurnalArc = 0.0; // Polar Night else diurnalArc = 24.0; // Midnight Sun if (pRun->debug == ONOFF_ON) { printf ("Debug: sunriset.cpp: Sun directly south: %f UTC, Dirunal Arc = %f hours\n", southHour, diurnalArc); printf ("Debug: sunriset.cpp: Days since 2000: %li\n", pTarget->daysSince2000); if (diurnalArc >= 24.0) printf ("Debug: sunriset.cpp: No rise or set: Midnight Sun\n"); if (diurnalArc <= 0.0) printf ("Debug: sunriset.cpp: No rise or set: Polar Night\n"); } // Error Check - just make sure odd things don't happen (causing trouble further on) if (diurnalArc > 24.0) diurnalArc = 24.0; if (diurnalArc < 0.0) diurnalArc = 0.0; /* Apply values */ pTarget->southHourUTC = southHour; pTarget->diurnalArc = diurnalArc; } void sunpos (const double d, double *lon, double *r) /******************************************************/ /* Computes the Sun's ecliptic longitude and distance */ /* at an instant given in d, number of days since */ /* 2000 Jan 0.0. The Sun's ecliptic latitude is not */ /* computed, since it's always very near 0. */ /******************************************************/ { double M, /* Mean anomaly of the Sun */ w, /* Mean longitude of perihelion */ /* Note: Sun's mean longitude = M + w */ e, /* Eccentricity of Earth's orbit */ E, /* Eccentric anomaly */ x, y, /* x, y coordinates in orbit */ v; /* True anomaly */ /* Compute mean elements */ M = revolution (356.0470 + 0.9856002585 * d); w = 282.9404 + 4.70935E-5 * d; e = 0.016709 - 1.151E-9 * d; /* Compute true longitude and radius vector */ E = M + e * RADIAN_TO_DEGREE * sind(M) * (1.0 + e * cosd(M)); x = cosd (E) - e; y = sqrt (1.0 - e*e) * sind(E); *r = sqrt (x*x + y*y); /* Solar distance */ v = atan2d (y, x); /* True anomaly */ *lon = revolution (v + w); /* True solar longitude, made 0..360 degrees */ } void sun_RA_dec (const double d, double *RA, double *dec, double *r) { double lon, obl_ecl; double xs, ys, zs; double xe, ye, ze; /* Compute Sun's ecliptical coordinates */ sunpos (d, &lon, r); /* Compute ecliptic rectangular coordinates */ xs = *r * cosd(lon); ys = *r * sind(lon); zs = 0; /* because the Sun is always in the ecliptic plane! */ /* Compute obliquity of ecliptic (inclination of Earth's axis) */ obl_ecl = 23.4393 - 3.563E-7 * d; /* Convert to equatorial rectangular coordinates - x is unchanged */ xe = xs; ye = ys * cosd(obl_ecl); ze = ys * sind(obl_ecl); /* Convert to spherical coordinates */ *RA = atan2d(ye, xe); *dec = atan2d(ze, sqrt(xe*xe + ye*ye)); } // // Utility functions // // Reduce angle to within 0..359.999 degrees double revolution (const double x) { double remainder = fmod (x, (double) 360.0); return remainder < (double) 0.0 ? remainder + (double) 360.0 : remainder; } // Reduce angle to -179.999 to +180 degrees double rev180 (const double x) { double y = revolution (x); return y <= (double) 180.0 ? y : y - (double) 360.0; } // Fix angle to 0-359.999 double fixLongitude (const double x) { return revolution (x); } // Fix angle to 0-89.999 and -0.001 to -89.999 double fixLatitude (const double x) { // Make angle 0 to 359.9999 double y = revolution (x); if (y <= (double) 90.0) ; else if (y <= (double) 180.0) y = (double) 180.0 - y; else if (y <= (double) 270.0) y = (double) 180.0 - y; else if (y <= (double) 360.0) y = y - (double) 360.0; // Linux compile of sunwait doesn't like 90, Windows is OK. // Let's just wiggle things a little bit to make things OK. if (y == (double) 90.0) y = (double) 89.9999999; else if (y == (double) -90.0) y = (double) -89.9999999; return y; } // Time must be between 0:00 amd 23:59 double fix24 (const double x) { double remainder = fmod (x, (double) 24.0); return remainder < (double) 0.0 ? remainder + (double) 24.0 : remainder; } /*******************************************************************/ /* This function computes GMST0, the Greenwhich Mean Sidereal Time */ /* at 0h UT (i.e. the sidereal time at the Greenwhich meridian at */ /* 0h UT). GMST is then the sidereal time at Greenwich at any */ /* time of the day. I've generalized GMST0 as well, and define it */ /* as: GMST0 = GMST - UT -- this allows GMST0 to be computed at */ /* other times than 0h UT as well. While this sounds somewhat */ /* contradictory, it is very practical: instead of computing */ /* GMST like: */ /* */ /* GMST = (GMST0) + UT * (366.2422/365.2422) */ /* */ /* where (GMST0) is the GMST last time UT was 0 hours, one simply */ /* computes: */ /* */ /* GMST = GMST0 + UT */ /* */ /* where GMST0 is the GMST "at 0h UT" but at the current moment! */ /* Defined in this way, GMST0 will increase with about 4 min a */ /* day. It also happens that GMST0 (in degrees, 1 hr = 15 degr) */ /* is equal to the Sun's mean longitude plus/minus 180 degrees! */ /* (if we neglect aberration, which amounts to 20 seconds of arc */ /* or 1.33 seconds of time) */ /* */ /*******************************************************************/ inline double GMST0 (const double d) { /* Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr */ /* L = M + w, as defined in sunpos(). Since I'm too lazy to */ /* add these numbers, I'll let the C compiler do it for me. */ /* Any decent C compiler will add the constants at compile */ /* time, imposing no runtime or code overhead. */ return revolution ((180.0 + 356.0470 + 282.9404) + (0.9856002585 + 4.70935E-5) * d); } unsigned long daysSince2000 (const time_t *pTimet) { struct tm tmpTm; myUtcTime (pTimet, &tmpTm); unsigned int yearsSince2000 = tmpTm.tm_year - 100; // Get year, but tm_year starts from 1900 // Calucate number of leap days, but - // yearsSince2000 - 1 // Don't include this year as tm_yday includes this year's leap day in the next bit unsigned int leapDaysSince2000 = (unsigned int) floor ((yearsSince2000-1)/4) // Every evenly divisible 4 years is a leap-year - (unsigned int) floor ((yearsSince2000-1)/100) // Except centuries + (unsigned int) floor ((yearsSince2000-1)/400) // Unless evenlt divisible by 400 + 1; // 2000 itself was a leap year with the 400 rule (a fix for 0/400 == 0) return (yearsSince2000 * 365) + leapDaysSince2000 + tmpTm.tm_yday; } /* ** Utility functions */ long myRound (const double d) { return d > 0.0 ? (int) (d + 0.5) : (int) (d - 0.5) ; } long myTrunc (const double d) { return (d>0) ? (int) floor(d) : (int) ceil(d) ; } double myAbs (const double d) { return (d>0) ? d : -d ; } int hours (const double d) { return myTrunc (d); } int minutes (const double d) { return myTrunc (fmod (myAbs (d) * 60, 60)); } ./sunwait/0.8/print.cpp0000775000175000017500000002256712533056525013201 0ustar ianian /* ** print.cpp (of sunwait) ** ** Who Ver When What ** IFC 0.5 04-12-2014 Fix my 1st release of sunwait for windows and port to linux ** IFC 0.6 08-12-2014 Add timezone for output of timings ** IFC 0.7 30-04-2015 Fix timexone and DST trouble - and problems near dateline ** IFC 0.8 2015-05-27 Resolve 'dodgy day' and cleanup ** */ #include #include #include #include #include "sunwait.h" #include "sunriset.h" #include "print.h" static const char* cTo = " to "; static const char* cComma = ", "; #define NO_OFFSET 0.0 inline double myDayLength (const double pDouble1, const double pDouble2) { return myAbs (pDouble1 - pDouble2); } inline double myDayLength (const targetStruct *pTarget) { return pTarget->diurnalArc; } // The user-specified offset reduces the diurnal arc, at sunrise AND sunset. // But make sure dawn aways is before dusk. The offset can mess that up. double diurnalArcWithOffset1 (const double pDiurnalArc, const double pOffset) { double arcWithOffset = pDiurnalArc - pOffset - pOffset; if (arcWithOffset >= 24.0) return 24.0; if (arcWithOffset <= 0.0) return 0.0; return arcWithOffset; } // Simpler to use form double diurnalArcWithOffset (const runStruct *pRun, const targetStruct *pTarget) { return diurnalArcWithOffset1 (pTarget->diurnalArc, pRun->offsetHour); } // What time, in hours UTC, is the offset sunrise? double getOffsetRiseHourUTC1 (const double pSouthHourUTC, const double pDiurnalArc, const double pOffsetHour) { return pSouthHourUTC - diurnalArcWithOffset1 (pDiurnalArc, pOffsetHour)/2.0; } // Simpler to use form double getOffsetRiseHourUTC (const runStruct *pRun, const targetStruct *pTarget) { return getOffsetRiseHourUTC1 (pTarget->southHourUTC, pTarget->diurnalArc, pRun->offsetHour); } // What time, in hours UTC, is the offset sunset? double getOffsetSetHourUTC1 (const double pSouthHourUTC, const double pDiurnalArc, const double pOffsetHour) { return pSouthHourUTC + diurnalArcWithOffset1 (pDiurnalArc, pOffsetHour)/2.0; } // Simpler to use form double getOffsetSetHourUTC (const runStruct *pRun, const targetStruct *pTarget) { return getOffsetSetHourUTC1 (pTarget->southHourUTC, pTarget->diurnalArc, pRun->offsetHour); } void print_a_time ( const OnOff pGmt_OnOff , const time_t *pMidnightTimet , const double pEventHour ) { struct tm tmpTm; char tmpBuffer [80]; // Convert current time to struct tm for UTC or local timezone if (pGmt_OnOff == ONOFF_ON) { myUtcTime (pMidnightTimet, &tmpTm); tmpTm.tm_min += (int) (pEventHour * 60.0); time_t x = mktime (&tmpTm); myUtcTime (&x, &tmpTm); } else { myLocalTime (pMidnightTimet, &tmpTm); tmpTm.tm_min += (int) (pEventHour * 60.0); mktime (&tmpTm); } strftime (tmpBuffer, 80, "%H:%M", &tmpTm); printf ("%s", tmpBuffer); } void print_a_sun_time ( const OnOff pGmt_OnOff , const time_t *pMidnightTimet , const double pEventHour , const double pOffsetDiurnalArc ) { // A positive offset reduces the diurnal arc if (pOffsetDiurnalArc <= 0.0 || pOffsetDiurnalArc >= 24.0) printf ("--:--"); else print_a_time (pGmt_OnOff, pMidnightTimet, pEventHour); } void print_times ( const OnOff pGmt , const OnOff pSunrise , const OnOff pSunset , const time_t pMidnightTimet , const double pSouthHour , const double pDiurnalArc , const double pOffset , const char *pSeparator ) { double offsetDiurnalArc = diurnalArcWithOffset1 (pDiurnalArc, pOffset); double riseHour = getOffsetRiseHourUTC1 (pSouthHour, pDiurnalArc, pOffset); double setHour = getOffsetSetHourUTC1 (pSouthHour, pDiurnalArc, pOffset); if (pSunrise == ONOFF_ON) print_a_sun_time (pGmt, &pMidnightTimet, riseHour, offsetDiurnalArc); if (pSunrise == ONOFF_ON && pSunset == ONOFF_ON) printf ("%s", pSeparator); if (pSunset == ONOFF_ON) print_a_sun_time (pGmt, &pMidnightTimet, setHour, offsetDiurnalArc); if (offsetDiurnalArc >= 24.0) printf (" (Midnight sun)"); else if (offsetDiurnalArc <= 0.0) printf (" (Polar night)"); printf ("\n"); } inline void print_times ( const runStruct *pRun , const targetStruct *pTarget , const double pOffsetHour , const char *pSeparator ) { print_times ( pRun->utc , pRun->reportSunrise , pRun->reportSunset , pRun->targetTimet , pTarget->southHourUTC , pTarget->diurnalArc , pOffsetHour , pSeparator ); } inline void print_twilight ( const double pDayLength , const double pTwilightLength ) { printf ( "%2.2d:%2.2d hours (twilight: %2.2d:%2.2d hours)\n" , hours (pDayLength), minutes (pDayLength) , hours (pTwilightLength), minutes (pTwilightLength) ); } void generate_report (const runStruct *pRun) { /* ** Generate and save sunrise and sunset times for target */ targetStruct tmpTarget; tmpTarget.twilightAngle = pRun->twilightAngle; tmpTarget.daysSince2000 = pRun->target2000; sunriset (pRun, &tmpTarget); double twilightAngleTarget = tmpTarget.twilightAngle; /* ** Now generate the report */ struct tm nowTm; struct tm targetTm; char buffer [80]; if (pRun->utc == ONOFF_ON) { myUtcTime (&pRun->nowTimet, &nowTm); myUtcTime (&pRun->targetTimet, &targetTm); } else { myLocalTime (&pRun->nowTimet, &nowTm); myLocalTime (&pRun->targetTimet, &targetTm); } printf ("\n"); strftime (buffer, 80, "%d-%b-%Y %H:%M %Z", &nowTm); printf (" Current Date and Time: %s\n", buffer); printf ("\n\nTarget Information ...\n\n"); printf (" Location: %10.6fN, %10.6fE\n" , pRun->latitude , pRun->longitude ); strftime (buffer, 80, "%d-%b-%Y", &nowTm); printf (" Date: %s\n", buffer); strftime (buffer, 80, "%Z", &nowTm); printf (" Timezone: %s\n", buffer); printf (" Sun directly north/south: "); print_a_time (pRun->utc, &pRun->targetTimet, tmpTarget.southHourUTC); printf ("\n"); if (pRun->offsetHour != NO_OFFSET) { printf ( " Offset: %2.2d:%2.2d hours\n" , hours (pRun->offsetHour) , minutes (pRun->offsetHour) ); } if (tmpTarget.twilightAngle == TWILIGHT_ANGLE_DAYLIGHT) printf(" Twilight angle: %5.2f degrees (daylight)\n", twilightAngleTarget); else if (tmpTarget.twilightAngle == TWILIGHT_ANGLE_CIVIL) printf(" Twilight angle: %5.2f degrees (civil)\n", twilightAngleTarget); else if (tmpTarget.twilightAngle == TWILIGHT_ANGLE_NAUTICAL) printf(" Twilight angle: %5.2f degrees (nautical)\n", twilightAngleTarget); else if (tmpTarget.twilightAngle == TWILIGHT_ANGLE_ASTRONOMICAL) printf(" Twilight angle: %5.2f degrees (astronomical)\n", twilightAngleTarget); else printf(" Twilight angle: %5.2f degrees (custom angle)\n", twilightAngleTarget); printf (" Day with twilight: "); print_times (pRun, &tmpTarget, NO_OFFSET, cTo); if (pRun->offsetHour != NO_OFFSET) { printf (" Day with twilight & offset: "); print_times (pRun, &tmpTarget, pRun->offsetHour, cTo); } printf (" It is: %s\n", isDay (pRun) == ONOFF_ON ? "Day (or twilight)" : "Night"); /* ** Generate times for different types of twilight */ targetStruct daylightTarget; daylightTarget.twilightAngle = TWILIGHT_ANGLE_DAYLIGHT; daylightTarget.daysSince2000 = tmpTarget.daysSince2000; sunriset (pRun, &daylightTarget); targetStruct civilTarget; civilTarget.twilightAngle = TWILIGHT_ANGLE_CIVIL; civilTarget.daysSince2000 = tmpTarget.daysSince2000; sunriset (pRun, &civilTarget); targetStruct nauticalTarget; nauticalTarget.twilightAngle = TWILIGHT_ANGLE_NAUTICAL; nauticalTarget.daysSince2000 = tmpTarget.daysSince2000; sunriset (pRun, &nauticalTarget); targetStruct astronomicalTarget; astronomicalTarget.twilightAngle = TWILIGHT_ANGLE_ASTRONOMICAL; astronomicalTarget.daysSince2000 = tmpTarget.daysSince2000; sunriset (pRun, &astronomicalTarget); printf ("\nGeneral Information (no offset) ...\n\n"); printf (" Times ... Daylight: "); print_times (pRun, &tmpTarget, NO_OFFSET, cTo); printf (" with Civil twilight: "); print_times (pRun, &civilTarget, NO_OFFSET, cTo); printf (" with Nautical twilight: "); print_times (pRun, &nauticalTarget, NO_OFFSET, cTo); printf (" with Astronomical twilight: "); print_times (pRun, &astronomicalTarget, NO_OFFSET, cTo); printf ("\n"); printf (" Duration ... Day length: %2.2d:%2.2d hours\n", hours ( daylightTarget.diurnalArc), minutes ( daylightTarget.diurnalArc)); printf (" with civil twilight: %2.2d:%2.2d hours\n", hours ( civilTarget.diurnalArc), minutes ( civilTarget.diurnalArc)); printf (" with nautical twilight: %2.2d:%2.2d hours\n", hours ( nauticalTarget.diurnalArc), minutes ( nauticalTarget.diurnalArc)); printf (" with astronomical twilight: %2.2d:%2.2d hours\n", hours (astronomicalTarget.diurnalArc), minutes (astronomicalTarget.diurnalArc)); printf ("\n"); } void print_list (const runStruct *pRun) { targetStruct tmpTarget; tmpTarget.daysSince2000 = pRun->target2000; tmpTarget.twilightAngle = pRun->twilightAngle; for (unsigned int day=0; day < pRun->listDays; day++) { sunriset (pRun, &tmpTarget); print_times ( pRun , &tmpTarget , pRun->offsetHour , cComma ); tmpTarget.daysSince2000++; } } ./sunwait/0.8/sunriset.h0000775000175000017500000000373512533056525013362 0ustar ianian// // sunwait - sunriset.h // // 08-12-2014 IFC 0.6 Add timezone to output // 02-05-2015 IFC 0.7 Fix timezone and DST issues // 2015-05-27 IFC 0.8 Resolve 'dodgy day' and cleanup // #ifndef SUNRISET_H #define SUNRISET_H #include "sunwait.h" #include "print.h" /* Sunrise/set is considered to occur when the Sun's upper limb (upper edge) is 50 arc minutes below the horizon */ /* (this accounts for the refraction of the Earth's atmosphere). */ /* Civil twilight starts/ends when the Sun's center is 6 degrees below the horizon. */ /* Nautical twilight starts/ends when the Sun's center is 12 degrees below the horizon. */ /* Astronomical twilight starts/ends when the Sun's center is 18 degrees below the horizon. */ #define TWILIGHT_ANGLE_DAYLIGHT -50.0/60.0 #define TWILIGHT_ANGLE_CIVIL -6.0 #define TWILIGHT_ANGLE_NAUTICAL -12.0 #define TWILIGHT_ANGLE_ASTRONOMICAL -18.0 /* Some conversion factors between radians and degrees */ #define RADIAN_TO_DEGREE ( 180.0 / PI ) #define DEGREE_TO_RADIAN ( PI / 180.0 ) /* The trigonometric functions in degrees */ #define sind(x) (sin((x)*DEGREE_TO_RADIAN)) #define cosd(x) (cos((x)*DEGREE_TO_RADIAN)) #define tand(x) (tan((x)*DEGREE_TO_RADIAN)) #define atand(x) (RADIAN_TO_DEGREE*atan(x)) #define asind(x) (RADIAN_TO_DEGREE*asin(x)) #define acosd(x) (RADIAN_TO_DEGREE*acos(x)) #define atan2d(y,x) (RADIAN_TO_DEGREE*atan2(y,x)) #define PI 3.1415926535897932384 void sunriset (const runStruct *pRun, targetStruct *pTarget); double revolution (const double x); double rev180 (const double x); double fixLongitude (const double x); double fixLatitude (const double x); double fix24 (const double x); double GMST0 (const double d); void sun_RA_dec (double d, double *RA, double *dec, double *r); unsigned long daysSince2000 (const time_t *pTimet); long myRound (const double d); long myTrunc (const double d); double myAbs (const double d); int hours (const double d); int minutes (const double d); #endif ./sunwait/0.8/print.h0000775000175000017500000000161312533056525012633 0ustar ianian// // sunwait - print.h // // 08-12-2014 IFC 0.6 No changes from 0.5 // 02-05-2015 IFC 0.7 No changes from 0.5, still // 2015-05-27 IFC 0.8 Resolve 'dodgy day' and cleanup // #ifndef PRINT_H #define PRINT_H #include "sunwait.h" #include "sunriset.h" void generate_report (const runStruct *pRun); void print_list (const runStruct *pRun); double diurnalArcWithOffsetX (const double pDiurnalArc, const double pOffset); double diurnalArcWithOffset (const runStruct *pRun, const targetStruct *pTarget); double getOffsetRiseHourUTCX (const double pSouthHourUTC, const double pDiurnalArc, const double pOffsetHour); double getOffsetRiseHourUTC (const runStruct *pRun, const targetStruct *pTarget); double getOffsetSetHourUTCX (const double pSouthHourUTC, const double pDiurnalArc, const double pOffsetHour); double getOffsetSetHourUTC (const runStruct *pRun, const targetStruct *pTarget); #endif ./sunwait/0.8/makefile0000775000175000017500000000065512533056525013033 0ustar ianian# sunwait # # 08/12/2014 IFC 0.6 No changes since 0.5 # 02/05/2015 IFC 0.7 No changes since 0.5, still # C=gcc CFLAGS=-c -Wall LDFLAGS= -lm -lstdc++ SOURCES=sunwait.cpp sunriset.cpp print.cpp sunwait.h sunriset.h print.h OBJECTS=$(SOURCES:.cpp=.o) EXECUTABLE=sunwait all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(C) $(OBJECTS) -o $@ $(LDFLAGS) .cpp.o: $(C) $(CFLAGS) $< -o $@ clean: rm *.o sunwait