ios - 这些日光/夜晚长度近似值的错误在哪里?

标签 ios objective-c math time

我正在尝试估算从日出到日落的白天长度,以及从日落到日出的夜晚长度。我目前的近似值是粗略的(它假设昨天和明天的值与今天相同),但现在我并不特别关心精确定位昨天的日落、今天的日出、今天的日落和明天的日出(还)。我的目标是基于每晚十二个相等的小时(十二个彼此相等,不等于标准小时或白天小时)和每天十二个相等的小时来计算。

我担心的是,在我的 iOS 应用程序中,计算有偏差;一分钟在 5-6(标准)秒的时间内飞逝。当我使用未修改的时间时,在此处的其他代码中,时钟以标准速度移动,但当我尝试让此代码提供时钟代码时,出现了一些问题。

我一直在研究的代码,作为一个近似值,是:

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 < sunrise)
    {
        _naturalSeconds = (seconds - sunset - 24 * 60 * 60) / (sunrise - sunset - 24 * 60 * 60);
    }
    else if (seconds > 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;
    }
}

您是否可以在这段代码中查明任何问题(假设此近似值可能可以在任何程度上改进)?

谢谢,

--编辑--

我上面写的代码对于向阅读它的人呈现的未完成部分来说要求相当高。我试着再试一次,用更简单的术语和更纯粹的数学模型重写它。我写了,添加了评论:

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 &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 > M_PI / 2)
{
    _secondsAreShadowed = TRUE;
}
else if (currentHeight < - 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) < .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 < closestDayMorning)
    {
        proportion = (distanceAlong - previousDayEvening) / (closestDayMorning - previousDayEvening);
        referenceTime = previousDay + day * 3 / 4;
    }
    else if (distanceAlong > 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;

我希望让代码更清晰、更容易掌握,我想我做到了,但它显示出与我之前尝试类似的行为:时钟指针以自然时间的十倍左右旋转,而它们应该在标准小时/分钟/秒的 0.8 到 1.2 倍。

有什么建议吗?我编辑的代码是否更清楚地说明了意图或错误?

谢谢,

最佳答案

您的代码很难理解,但我会尽力为您提供一些提示:

  • 现有的库可以计算给定日期的太阳角/方位角和日出/日落。使用谷歌作为帮助,这里有一些相关资源:http://www.esrl.noaa.gov/gmd/grad/solcalc/如果您找不到任何有用的源代码,我可以发布一些。
  • 不要使用 double 来计算日期和时间。这令人困惑并导致错误。使用旨在存储日期的数据类型。
  • 对于您的代码,您说时间过得很快。由于最后一行的 referenceTime 和 day 是常数(至少半天),所以误差必须成比例。我认为你在那里混合了很多情况。插值应该从范围的开始到结束,所以在这种情况下

    比例 = (distanceAlong - previousDayEvening)/(closestDayMorning - previousDayEvening); referenceTime = previousDay + day * 3/4;

比例应该从 (previousDay + day * 3/4) 到 (closestDay + day * 3/4),或者换句话说,从 closestDay 的黄昏到黎明。但完全不清楚这种插值应该如何工作。

尝试画出不同情况的图表(我相信应该只有两种,一种用于白天,一种用于夜间)和相应的插值。

但是:您到底想达到什么目的?结果时间只是向前运行的时间,它实际上与纬度或经度或一天中的时间无关。所以要让时间流逝,你不需要知道太阳在哪里。

关于ios - 这些日光/夜晚长度近似值的错误在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19437569/

相关文章:

ios - 具有CIDetectorTypeQRCode的CIDetector在iPad上有效,但在iPhone和iPod上无效

ios - UITableViewCell selectedBackgroundView 自定义

objective-c - 返回通过 "convenience constructor"初始化的对象

java - 通过栈计算负数算术表达式

ios - 使用 UITableView 时应用程序崩溃

ios - 无法以编程方式设置 UINavigationItem 标题

objective-c - iOS的UITableView部分之间的空间不受控制吗?

objective-c - autorelease 在自动引用计数模式下不可用

javascript - 为 20 位数字间隔生成唯一且可逆的 ID

java - 如何围绕某个点旋转顶点?