65.9K
CodeProject 正在变化。 阅读更多。
Home

Sidreal 时间计算器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (7投票s)

2012 年 9 月 14 日

CPOL

16分钟阅读

viewsIcon

45093

downloadIcon

842

Windows Phone 的恒星时计算器。

引言

目前有许多在各种移动设备上运行的应用,可以让你将设备指向天空,识别出各种天体。我发现,根据用户的地理位置和设备的朝向来识别天体的能力非常迷人。当我看到物理硬件基于这些信息做出响应时,我更觉得它很迷人;我拥有的望远镜使用 GPS(获取我的位置和当前时间)并利用这些信息自动移动到所需的方向以观测选定的天体。

我现在对它是如何工作的有了相当的了解。其中相当一部分计算都基于时间,另一部分则基于坐标转换。通过正确的时间转换,你将获得足够的信息来确定恒星的方向。(要确定行星和月亮的方向需要做更多工作,但恒星相对于太阳系的视运动几乎没有变化)。我只想先谈谈时间,因为有关时间的信息足以写一整篇文章。

目录

术语和时间单位

时间通常以某种周期性过程或事件来描述,其时间单位来自于对这些周期/事件的计数。这可以是晶体振动、某个天体的经过,或其他任何事件。历史上,太阳和月亮的视运动一直被用作我们时间系统的基础周期事件。我们都使用源自这些事件的时间单位;小时、分钟、秒、年、月,以及上午、下午、公元、公元前等术语,还有度。让我们来剖析一下这些术语背后的物理事件。

罗马历

据说罗马历是由罗马的创始人罗穆路斯于公元前 753 年左右发明的。该历法有 10 个月,春分是第一个月。该历法有 304 天,外加一些冬季的额外天数,这些天数从 12 月一直延伸到下个月,但并不属于历法中的任何月份。

儒略历

儒略历是罗马历的改进版本。它有 365 天,分为 12 个月。每 4 年会增加一天闰日。这听起来非常像我们今天使用的民用历法,唯一的区别是我们不将能被 400 整除但不能被千年整除的年份定为闰年。随着时间的推移,太阳和季节事件会逐渐移动到历法的其他部分。格里高利历纠正了这个问题。

格里高利历、天文年和儒略日

格里高利历(也称为西方历或民用历)是当今世界绝大多数人所熟知、喜爱和使用的历法的名称。该历法的命名人是教皇格雷戈里十三世。地球绕太阳公转的次数是 365 次加上一些分数单位(大约 0.2524)。儒略历试图通过每 4 年引入一个闰年来纠正这一点。这种做法减缓了季节在历法上迁移的速度,但并未完全阻止。教皇格雷戈里十三世对历法的贡献是,如果年份能被 400 整除且是千年年份,则不设闰年。儒略历的最后一天是 1582 年 10 月 4 日星期四。紧随其后的一天是格里高利历的第一天;1582 年 10 月 15 日星期五。10 月 5 日至 14 日(含)这个日期范围不存在——在进行跨越这个边界的时间转换时,这一点需要牢记。

儒略日

另一个常用的历法是计算自公元前 4713 年 1 月 1 日中午以来的天数。这也被称为儒略日。公元前 4713 年 1 月 1 日中午是儒略日 0。公元前 4713 年 1 月 1 日和公元前 4714 年 1 月 2 日之间的午夜是儒略日 0.5。请注意,一天中的时间作为分数单位是儒略日的一部分。对于最近的日期,用于表示儒略日的数字已超过 2,400,000 百万。为了避免处理不必要的庞大数字,还有修正儒略日(MJD),它计算自 1858 年 11 月 17 日午夜以来的天数。请注意,MJD 从午夜开始,而 JD 从中午开始。因此,这两种日期表示法中的时间单位在小数点后数字上会有 0.5 的差异。您可能还会听到儒略日号(Julian Date Number),这只是儒略日的整数部分。这种形式的日期对于天文计算具有特殊意义。

公元、公元前和天文年

跟踪年份的系统有一个奇怪之处,那就是没有零年。基于基督教宗教耶稣基督的推定出生日期,日历的第一年是公元 1 年(AD = Anno Domino,拉丁语意为“主的年份”,也写作“CE”代表公元年)。紧随其后的年份是公元前 1 年(BC = Before Christ。有时写作 BCE 代表公元前)。在进行天文计算时,没有人愿意处理缺少零的问题。因此,还有一个天文年的概念。天文年大部分与我们当前跟踪年份的系统一致。所以 1984 AD 也是天文年 1984。当查看公元前的年份时,差异就显现出来了。公元前 1 年是天文年 0。公元前 2 年是天文年 -1,依此类推。

太阳日

太阳日是指太阳在天空中运行并回到其起始点的时间段。我说“视”是因为虽然我们知道这个现象是由于地球自转,但太阳的运动仍以地心说(日出、日落等)的方式来描述。太阳的路径每天略有不同,所以它实际上并没有回到其起始点。因此,子午线被用作起点。子午线是围绕地球从北极延伸到南极的假想圆。太阳和其他天体在子午线上达到它们在天空中的最高点,然后从上升转为下降。这条线也用于将一天分为两半。一旦太阳经过子午线,时间就被标记为下午(post meridian,P.M.)。当它经过地球的另一侧的这条线时,我们说它在子午线之前。拉丁语中的“之前”是“ante”,因此它被称为 A.M.(Ante Meridian 的缩写)。当太阳在子午线上时,它处于一天中最高点。这被称为“太阳正午”。太阳正午不一定发生在当地时间为下午 12:00 的同一时刻。太阳到达这一点的时间会有轻微变化,我们倾向于在民用时钟上忽略这种变化。

太阳绕观测者运行的路径通常分为 24 个单位。请注意,如果将一个圆的 360 度除以 24,则得到 15。这 24 个相等的单位称为小时(换句话说,一个小时的旋转是 15 度)。这 15 度也可以分为 60 个单位(分钟)。一分钟的旋转是 15/60,即 0.25 度。您可能已经猜到,下一级划分是将一分钟分为 60 个相等的部分(秒),其中包含 0.26/60 度。使用小时、分钟和秒(HMS)作为旋转单位的一个推论是,每过一个小时,您可以近似计算夜空中天体在单位时间内将行进的旋转距离;在 2 小时内,一个天体将行进 60 度。我说近似是因为如果以高精度测量,您会发现太阳和月亮在这么长的时间内似乎移动的量略有不同于 60 度。对于随意观察,这种差异不会显现。

恒星日

如果您以太阳为参考来测量旋转距离,那么地球似乎需要 24 小时才能完成一次完整旋转。但这并不完全正确。地球每天绕太阳轨道移动约 1 度。所以太阳不应被用于精确测量地球旋转了多远。但其他任何恒星都可以。其他恒星足够远,以至于无论地球在其轨道上的哪个位置,它们的视位置都相同。

恒星日和太阳日的区别。

选择一颗恒星(太阳除外,任何恒星都可以)。每次地球自转时,该恒星都会到达子午线。如果您使用手表来测量一颗恒星再次到达子午线所需的时间,您会发现它不是正好 24 小时。它是 23 小时 56 分 4 秒。用这种方法测量的时间是恒星日。因为这些天比 24 小时少,所以一个恒星年中的太阳日数量大约是 366.25 而不是 365.25。由于在任何给定的太阳日中,恒星日比太阳日短,因此存在一个恒星时间范围会在太阳日内出现两次。

时区

我们的世界时系统基于格林威治时间。格林威治位于零度经线上。在其子午线上的天体事件观测曾是我们计时系统的基础。其子午线也称为本初子午线。为了方便起见,我们也有本地时间的概念,这是通过在格林威治时间的基础上加上一定的小时和分钟来计算的。地球被分为 40 个共享本地时间的区域。这些区域或时区,通常与格林威治标准时间(GMT)之间有一定的小时时间差。有些时区也以小时加上 30 分钟为偏移。从最正偏移到最负偏移的差值为 26 小时。平均而言,时区之间的差异可以通过它们的经度来推断(回想一下一个小时是 15 度旋转)。但是,时区线并不是直的。为了避免将小地理区域划分为多个时区,时区边界会与该地理区域的边界重合。

地球的星体运动

恒星的相对位置是固定的。对于想非常讲究技术的人来说,恒星正以我们难以置信的速度相对于我们的位置或它们所在的星系而移动。但它们离我们太远了,以至于它们的运动对我们来说是无法察觉的,这使得我们可以在几百年这个短时间内将它们视为静止的天体。有几个因素会影响恒星相对于地球上观测者的方向。

在这些运动中,对观测者影响最大的是地球的自转。它的影响可以通过太阳、月亮和其他天体在我们天空中运行的视轨迹直接观察到。如果您用望远镜观察一个天体,它的运动会更加明显,除非您拥有一个自动调整的电动望远镜;当您观察一个天体时,它会在一分钟或更短的时间内偏离望远镜的视野。这是我最关心的一种运动。

地球在绕太阳运行时每天前进约 1 度。随着每一天的过去,由于太阳的竞争光照而变得无法观测的天球部分会略微移动。这意味着某些恒星在一年中的某些时段将不可见。尽管它们的方位仍然可以确定(除了日食),但您白天无法观测到这些恒星。另外请注意,这会影响日出、日落的时间,以及一天中可见阳光的小时数(冬季白天时间较短)。目前,对于我的一般情况,我不太关心恒星在一天中何时可见。由于我只在时间和天气意外允许时使用望远镜,所以我不怎么提前计划。如果您对此感兴趣,我建议您首先研究一下关于各种晨昏(例如:民用、航海、天文)的定义。

第三种运动发生在约 25,700 年的周期中。它导致地球自转轴指向的方向发生轻微的圆形漂移。可以通过随时间变化的坐标空间调整来解决。但在这篇文章中我不想讨论坐标转换。只是为了让您好奇,地球自转轴指向的变化大约每 71 年发生 1 度,所以我们可以暂时忽略这种运动,它不会对我们的结果产生显著影响。

地方恒星时

由于地球相对于太阳的方向不断变化,我们不希望使用太阳日来计算恒星相对于地球的位置。我们需要的是恒星时。要获得恒星时,我们需要知道儒略日。我们将从民用(格里高利)日期中获取儒略日。我创建了一组用于获取这些日期的扩展。在计算格里高利日期时,您需要能够计算出一天中小数部分表示的进度。中午 12:00 为一天中的 0.5,18:00 为一天中的 0.75,依此类推。这些可以很容易地从日期或时间计算得出。

 static double ToFractionalDay(this TimeSpan sourceTime)
{
    return sourceTime.TotalHours / 24d;
}

 static double ToFractionalDay(this DateTime sourceDate)
 {
     return sourceDate.TimeOfDay.ToFractionalDay();
 }

这些被写成扩展方法,因为我认为调用语法更简洁。现在我们知道了一天中的进度,就可以利用这些信息来计算儒略日。

public static double ToJulianDate(this DateTime  sourceDate)
{
    double y, m, c;
    if (sourceDate.Month <= 2)
    {
        y = sourceDate.Year - 1;
        m = sourceDate.Month + 2;
    }
    else
    {
        y = sourceDate.Year;
        m = sourceDate.Month;
    }

    double leapDayCount  = (sourceDate > GregorianReformDate) ? 
                (2 - Math.Floor(y / 100) + Math.Floor(y/400) ) : 0;
    if (sourceDate.Year < 0)
        c = (int)(365.25 * (double)sourceDate.Year - 0.75);
    else
        c = (int)(365.25 * (double)sourceDate.Year);
    double d = Math.Floor(30.6001 * (m + 1));
    var retVal = leapDayCount +c+ d + sourceDate.Day + 1720994.5;
    return retVal + sourceDate.ToFractionalDay();;
}

有一件事我还没有提到。所有这些计算都以零度经线为中心,并基于 GMT 时区,不考虑夏令时。如果您想调整结果以计算您的时区相对于可观测宇宙其他部分的方向,您需要根据您的经度进行调整。如果您的经度在 GMT 以西,请用负数表示,否则用正数。将此数字除以 15,然后加到恒星时上。我住在东经 84 度以西。所以要获得地方恒星时,我执行以下操作。

localSiderealTimeClock.CurrentTime = DateTime.Now.ToUniversalTime().ToSiderealTime().
                                     Add(TimeSpan.FromHours(-84d/15d));

地方恒星时描述了您相对于春分点(♈)方向的自转位移。虽然空间中没有“上”,但从太阳到地球在春分点时的连线所形成的方向是几个天体坐标系的基础(黄道坐标系,基于地球绕太阳的轨道;赤道坐标系,基于地球的自转)。

修正用户时钟的偏差

用户有意或无意地可能会将时钟设置成不正确的时间。避免这些问题的一种方法是使用 NTP(网络时间协议)。我之前写过关于获取 NTP 时间的文章。您可以在此处阅读。虽然可以持续轮询 NTP 源获取时间,但我每隔几分钟才获取一次。当我获取 NTP 时间时,用户时钟与 NTP 时间源之间的差异会被保存并添加到用户时钟的值中。期望的是,在 NTP 时间刷新之间,用户时钟会可靠地计算秒数而不会有显著的漂移(如果不会,那么用户就需要一台新设备!)。

NtpClient _ntpClient;
TimeSpan _ntpOffset;
DateTime _lastNtpRefresh = DateTime.MinValue;
TimeSpan _ntpRefreshPeriod = TimeSpan.FromMinutes(1);

public MainViewModel()
{
    _ntpClient = new NtpClient();
    _ntpClient.TimeReceived += 
         new EventHandler<NtpClient.TimeReceivedEventArgs>(_ntpClient_TimeReceived);
    //Default the difference to zero and provisionally assume the user's
    //clock is correct until we receive information of otherwise
    _ntpOffset = TimeSpan.Zero;
}

void _ntpClient_TimeReceived(object sender, NtpClient.TimeReceivedEventArgs e)
{
    _lastNtpRefresh = DateTime.Now;
    DateTime NtpTime = e.CurrentTime;
    // NTP time is always in universal time, so we need to adjust the system clock 
    // to universal before getting the time offset. 
    _ntpOffset = NtpTime.Subtract(DateTime.Now.ToUniversalTime());
}

//Use this method to get time adjusted for NTP offset.
DateTime GetDate()
{  
     return DateTime.Now.Add(_ntpOffset);
}

显示时间

如果您看过显示多个时区时间的时钟,那么分钟和秒的数字在大多数时区都是相同的。当同时查看民用时间和恒星时,情况并非如此。秒数将不同步。由于个人偏好(我只是觉得这令人不快),我正在同步更新秒数。我制作了两个用于显示时间的控件;一个模拟时钟和一个数字时钟。两者都可以以 12 小时或 24 小时格式显示时间。

 

恒星时钟的数字显示。

使用模拟时钟显示 24 小时制时间对许多人来说可能很新。我查看了几张 24 小时模拟时钟的在线图片。有些以顶部为午夜开始,有些以底部为午夜开始。我选择将午夜(0 点)放在时钟的底部。这样,中午就位于时钟的顶部。虽然我已经有一个类似圆形仪表盘的时钟,但我打算将其从用户控件更改为模板控件,并公开新的选项来渲染它。(源代码中可见即将进行的更改的线索)。

 
模拟时钟显示
 
选项屏幕

帮助文件

在尝试其他功能时,我为应用程序包含了一个帮助 HTML 文件。帮助文件存储在应用程序中作为内容,但在应用程序首次运行时会解压。为了防止每次应用程序运行时不必要地解压文件,它会检查文件是否已存在,然后再进行解压。

public class ContentUnpacker
{
   
    static string[] ContentFileList = { "About.html", "459441/Sidereal.png", "appTimes.png", "settings.png" }; 
    public static void UnpackAllFiles()
    {
        IsolatedStorageFile sourceArchive = IsolatedStorageFile.GetUserStoreForApplication();
        if (!sourceArchive.DirectoryExists("Content"))
            sourceArchive.CreateDirectory("Content");


        foreach (string s in ContentFileList)
        {
            string targetName = String.Format("Content/{0}", s);
            string sourceName = String.Format("Content/{0}", s);
            if(!sourceArchive.FileExists(targetName))
            {
                var outStream = sourceArchive.CreateFile(targetName);
                var contentStream = Application.GetResourceStream
                                      (new Uri(sourceName, UriKind.Relative));
                using (var br = new BinaryReader(contentStream.Stream))
                {
                    var length = (int)br.BaseStream.Length;
                    outStream.Write(br.ReadBytes(length), 0, length);
                }
            }   
        }
    }
}

关于页面只包含一个 Web 浏览器元素,它被赋予了帮助文件的 URL。关于页面背后的所有代码如下。

public partial class AboutPage : PhoneApplicationPage
{
    public AboutPage()
    {
        InitializeComponent();
    }

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
        aboutBrowser.Navigate(new Uri("Content/About.html", UriKind.Relative));
    }
}

下一步

我设想了许多应用程序,有些与天文学相关,有些则不相关,它们将用到这个功能。一个与天文学不相关的例子是我曾想过做一个增强现实应用程序,我想让应用程序根据太阳的位置来为屏幕上投影的模型着色。我有一个天文相关的应用程序,可以使用一个房间,四面墙上都有投影仪和屏幕。出于好玩,我想让控制四面墙上投影仪的计算机互相通信,并显示太阳系的 360 度视图。获取恒星时是实现这些其他应用程序的垫脚石,但不是最终目标。我计划在这些想法实现时继续撰写关于它们进展的文章。

图示和插图

修订历史 

  • 2012-09-14 - 初次发布
© . All rights reserved.