Note that there are some explanatory texts on larger screens.

plurals
  1. POWhere is the bug in these length-of-daylight/night approximations?
    primarykey
    data
    text
    <p>I am trying to make an approximation of the length of day from sunrise to sunset, and the length of night from sunset to sunrise. My present approximation is crude (it assumes yesterday and tomorrow have equivalent values to today), but for now I am not specifically concerned with pinpointing yesterday sunset, today sunrise, today sunset, and tomorrow sunrise (yet). My goal is a calculation based on twelve equal hours per night (twelve equal to each other, not equal to a standard hour or daytime hour), and twelve equal hours per day. </p> <p>What I am concerned with is that in my iOS app, the calculation is way off; a minute flies by in 5-6 (standard) seconds' time. When I use unmodified time, in other code from here, the clock moves at a standard pace, but when I try to get this code to feed the clock code, something is out of bounds.</p> <p>The code I've been working on, as an approximation, is:</p> <pre><code>NSDate *now = [[NSDate alloc] init]; NSDate *factory = [[NSDate alloc] init]; NSDate *summerSolstice2013 = [factory initWithTimeIntervalSinceReferenceDate:_referenceSummerSolstice]; double distanceAlong = [now timeIntervalSinceDate:summerSolstice2013]; double angleAlong = M_PI * 2 * distanceAlong / (2 * (_referenceWinterSolstice - _referenceSummerSolstice)); double currentHeight = cos(angleAlong) * _latitudeAngle + _tiltAngle; ... if (_secondsAreNatural) { _secondsAreShadowed = FALSE; double dayDuration = 12 * 60 * 60 + 12 * 60 * 60 * sin(currentHeight); double midday = fmod(24 * 60 * 60 * _longitudeAngle / (2 * M_PI) + 12 * 60 * 60, 24 * 60 * 60); double sunrise = midday - dayDuration / 2; double sunset = midday + dayDuration / 2; double seconds = fmod([now timeIntervalSinceReferenceDate], 24 * 60 * 60); double proportionAlong = 0; if (seconds &lt; sunrise) { _naturalSeconds = (seconds - sunset - 24 * 60 * 60) / (sunrise - sunset - 24 * 60 * 60); } else if (seconds &gt; sunset) { _naturalSeconds = 12 * 60 * 60 * (seconds - sunset) / (sunrise + 24 * 60 * 60 - sunset) + 18 * 60 * 60; } else { _naturalSeconds = 12 * 60 * 60 * (seconds - sunrise) / (sunset - sunrise) + 6 * 60 * 60; } } </code></pre> <p>Are there any problems (given that this approximation can probably be refined to any extent) you can pinpoint in this code?</p> <p>Thanks,</p> <p>--EDIT--</p> <p>The code I wrote above was fairly demanding in terms of the loose ends presented to someone reading it. I tried to take another pass, and rewrite it in simpler terms and with a purer mathematical model. I wrote, comments added:</p> <pre><code>NSDate *now = [[NSDate alloc] init]; NSDate *summerSolstice2013 = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:_referenceSummerSolstice]; double distanceAlong = [now timeIntervalSinceDate:summerSolstice2013]; // How far along are we, in seconds, since the reference date? double angleAlong = M_PI * 2 * distanceAlong / (2 * (_referenceWinterSolstice - _referenceSummerSolstice)); // What's the angle if 2 &amp;pi; radians corresponds to a whole year? double currentHeight = cos(angleAlong) * _latitudeAngle + _tiltAngle; // _latitudeAngle is the angle represented by our latitude; _tiltAngle is the angle of the earth's tilt. NSInteger day = 24 * 60 * 60; // 'day' could have been called secondsInADay, but it was mean to reduce the number of multiplicands represented in the code. // If we are in the endless day or endless night around the poles, leave the user with standard clock hours. if (currentHeight &gt; M_PI / 2) { _secondsAreShadowed = TRUE; } else if (currentHeight &lt; - M_PI / 2) { _secondsAreShadowed = TRUE; } // Otherwise, calculate the time this routine is meant to calculate. (This is the main intended use case.) else if (_secondsAreNatural) { _secondsAreShadowed = FALSE; // closestDay is intended to be the nearest midnight (or, in another hemisphere, midday), not exactly in hours offset from UTC, but in longitude offset from Greenwich. double closestDay; if (fmod(distanceAlong, day) &lt; .5 * day) { closestDay = distanceAlong - fmod(distanceAlong, day); } else { closestDay = day + distanceAlong - fmod(distanceAlong, day); } // As we go through the calculations, for the most part we keep up information on the previous and next days, which will to some degree be consulted at the end. double previousDay = closestDay - day; double nextDay = closestDay + day; // For the three days, what proportion of the way along are they from the solstices? double closestDayAngleAlong = M_PI * 2 * closestDay / (2 * (_referenceWinterSolstice - _referenceSummerSolstice)); double previousDayAngleAlong = M_PI * 2 * previousDay / (2 * (_referenceWinterSolstice - _referenceSummerSolstice)); double nextDayAngleAlong = M_PI * 2 * nextDay / (2 * (_referenceSummerSolstice - _referenceSummerSolstice)); // What angle are we placed by on the year's cycle, between _latitudeAngle + _tiltAngle and -latitudeAngle + _tiltAngle? double closestDayHeight = cos(closestDayAngleAlong) * _latitudeAngle + _tiltAngle; double previousDayHeight = cos(previousDayAngleAlong) * _latitudeAngle + _tiltAngle; double nextDayHeight = cos(nextDayAngleAlong) * _latitudeAngle + _tiltAngle; // Based on that, what are the daylight durations for the three twenty-four hour days? double closestDayDuration = day / 2 + (day / 2) * sin(closestDayHeight); double previousDayDuration = day / 2 + (day / 2) * sin(previousDayHeight); double nextDayDuration = day / 2 + (day / 2) * sin(nextDayHeight); // Here we use both morning and evening for the closest day, and the previous day's morning and the next day's evening. double closestDayMorning = closestDay + (day / 2) - (closestDayDuration / 2); double closestDayEvening = closestDay + (day / 2) + (closestDayDuration / 2); double previousDayEvening = previousDay + (day / 2) + (previousDayDuration / 2); double nextDayMorning = nextDay + (day / 2) + (nextDayDuration / 2); // We calculate the proportion along the day that we are between evening and morning (or morning and evening), along with the sooner endpoint of that interval. double proportion; double referenceTime; if (distanceAlong &lt; closestDayMorning) { proportion = (distanceAlong - previousDayEvening) / (closestDayMorning - previousDayEvening); referenceTime = previousDay + day * 3 / 4; } else if (distanceAlong &gt; closestDayEvening) { proportion = (distanceAlong - closestDayEvening) / (nextDayMorning - closestDayEvening); referenceTime = closestDay + day * 3 / 4; } else { proportion = (distanceAlong - closestDayMorning) / (closestDayEvening - closestDayMorning); referenceTime = closestDay + day * 1 / 4; } // Lastly, we take both that endpoint and the proportion of it, and we get the number of seconds according to the daylight / nighttime calculation intended. _naturalSeconds = referenceTime + proportion * day / 2; </code></pre> <p>I was hoping to make the code clearer and easier to grasp, and I think I did that, but it is displaying similar behavior to my previous attempt: the clock hands spin by at about ten times natural time when they should be within a factor of .8 to 1.2 of standard hours/minutes/seconds.</p> <p>Any advice? Has my edited code been any clearer either about what is intended or what is wrong?</p> <p>Thanks,</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload