40

Does anyone know an algorithm to either calculate the moon phase or age on a given date or find the dates for new/full moons in a given year?

Googling tells me the answer is in some Astronomy book, but I don't really want to buy a whole book when I only need a single page.

Update:

I should have qualified my statement about googling a little better. I did find solutions that only worked over some subset of time (like the 1900's); and the trig based solutions that would be more computationally expensive than I'd like.

S Lott in his Python book has several algorithms for calculating Easter on a given year, most are less than ten lines of code and some work for all days in the Gregorian calendar. Finding the full moon in March is a key piece of finding Easter so I figured there should be an algorithm that doesn't require trig and works for all dates in the Gregorian calendar.

4

8 回答 8

23

前段时间我将一些代码移植到 Python 中。我打算只是链接到它,但事实证明它同时从网上掉下来了,所以我不得不把它擦掉并重新上传。请参阅从John Walker 的 moontool派生的moon.py。

我找不到关于这两个时间跨度准确的参考,但似乎作者非常严谨。这意味着是的,它确实使用了 trig,但我无法想象你会使用它来做什么,这会使其在计算上令人望而却步。Python 函数调用开销可能超过了三角操作的成本。计算机的计算速度非常快。

代码中使用的算法来自以下来源:

米乌斯,让。天文算法。里士满:Willmann-Bell,1991。ISBN 0-943396-35-2。

必备;如果你只买一本书,请确保是这本书。算法以数学方式呈现,而不是作为计算机程序,但实现本书中许多算法的源代码可以从 QuickBasic、Turbo Pascal 或 C 中的出版商单独订购。Meeus 提供了许多对调试至关重要的计算示例您的代码,并且经常提出几种算法,它们在准确性、速度、复杂性和长期(世纪和千年)有效性之间进行了不同的权衡。

达菲特 - 史密斯,彼得。实用天文学与您的计算器。第三版。剑桥:剑桥大学出版社,1981 年。ISBN 0-521-28411-2。

尽管标题中有计算器一词;如果您有兴趣开发计算行星位置、轨道、日食等的软件,这是一个有价值的参考。提供了比 Meeus 更多的背景信息,这有助于那些尚未精通天文学的人学习经常令人困惑的术语。给出的算法比 Meeus 提供的算法更简单、更不准确,但适用于大多数实际工作。

于 2010-03-28T01:03:41.020 回答
17

如果您像我一样,请尝试成为一名细心的程序员。因此,当您看到散布在互联网上的随机代码声称要解决一个复杂的天文问题时,您会感到紧张,但并没有解释为什么该解决方案是正确的。

您认为必须有权威的资料,例如包含仔细、完整的解决方案的书籍。例如:

米乌斯,让。天文算法。里士满:Willmann-Bell,1991。ISBN 0-943396-35-2。

达菲特 - 史密斯,彼得。实用天文学与您的计算器。第三版。剑桥:剑桥大学出版社,1981 年。ISBN 0-521-28411-2。

您信任可以纠正错误的广泛使用、经过良好测试的开源库(与静态网页不同)。那么,这里是一个基于PyEphem库的 Python 解决方案,它使用了Phases of the Moon接口。

#!/usr/bin/python
import datetime
import ephem

def get_phase_on_day(year,month,day):
  """Returns a floating-point number from 0-1. where 0=new, 0.5=full, 1=new"""
  #Ephem stores its date numbers as floating points, which the following uses
  #to conveniently extract the percent time between one new moon and the next
  #This corresponds (somewhat roughly) to the phase of the moon.

  #Use Year, Month, Day as arguments
  date=ephem.Date(datetime.date(year,month,day))

  nnm = ephem.next_new_moon    (date)
  pnm = ephem.previous_new_moon(date)

  lunation=(date-pnm)/(nnm-pnm)

  #Note that there is a ephem.Moon().phase() command, but this returns the
  #percentage of the moon which is illuminated. This is not really what we want.

  return lunation

def get_moons_in_year(year):
  """Returns a list of the full and new moons in a year. The list contains tuples
of either the form (DATE,'full') or the form (DATE,'new')"""
  moons=[]

  date=ephem.Date(datetime.date(year,01,01))
  while date.datetime().year==year:
    date=ephem.next_full_moon(date)
    moons.append( (date,'full') )

  date=ephem.Date(datetime.date(year,01,01))
  while date.datetime().year==year:
    date=ephem.next_new_moon(date)
    moons.append( (date,'new') )

  #Note that previous_first_quarter_moon() and previous_last_quarter_moon()
  #are also methods

  moons.sort(key=lambda x: x[0])

  return moons

print get_phase_on_day(2013,1,1)

print get_moons_in_year(2013)

这返回

0.632652265318

[(2013/1/11 19:43:37, 'new'), (2013/1/27 04:38:22, 'full'), (2013/2/10 07:20:06, 'new'), (2013/2/25 20:26:03, 'full'), (2013/3/11 19:51:00, 'new'), (2013/3/27 09:27:18, 'full'), (2013/4/10 09:35:17, 'new'), (2013/4/25 19:57:06, 'full'), (2013/5/10 00:28:22, 'new'), (2013/5/25 04:24:55, 'full'), (2013/6/8 15:56:19, 'new'), (2013/6/23 11:32:15, 'full'), (2013/7/8 07:14:16, 'new'), (2013/7/22 18:15:31, 'full'), (2013/8/6 21:50:40, 'new'), (2013/8/21 01:44:35, 'full'), (2013/9/5 11:36:07, 'new'), (2013/9/19 11:12:49, 'full'), (2013/10/5 00:34:31, 'new'), (2013/10/18 23:37:39, 'full'), (2013/11/3 12:49:57, 'new'), (2013/11/17 15:15:44, 'full'), (2013/12/3 00:22:22, 'new'), (2013/12/17 09:28:05, 'full'), (2014/1/1 11:14:10, 'new'), (2014/1/16 04:52:10, 'full')]
于 2013-09-06T22:48:50.107 回答
8

此外,pyephem — 科学级天文学例程[ PyPI ],它是一个 Python 包,但具有C 中的计算能力,这确实说明了

精度 < 0.05" 从 -1369 到 +2950。
使用表查找技术来限制对三角函数的调用。

于 2010-03-28T01:16:50.053 回答
3

Pyephem 默认使用协调通用 (UTC) 时间。我想要一个程序,它可以生成一个在太平洋时区准确的满月列表。下面的代码将计算给定年份的满月,然后使用 ephem.localtime() 方法进行调整以校准到所需的时区。它似乎也正确地考虑了夏令时。感谢 Richard,这段代码和他写的很相似。

#!/usr/bin/python
import datetime
import ephem
import os
import time

# Set time zone to pacific
os.environ['TZ'] = 'US/Pacific'
time.tzset()

print("Time zone calibrated to", os.environ['TZ'])

def get_full_moons_in_year(year):
    """
    Generate a list of full moons for a given year calibrated to the local time zone
    :param year: year to determine the list of full moons
    :return: list of dates as strings in the format YYYY-mm-dd
    """
    moons = []

    date = ephem.Date(datetime.date(year - 1, 12, 31))
    end_date = ephem.Date(datetime.date(year + 1, 1, 1))

    while date <= end_date:
        date = ephem.next_full_moon(date)

        # Convert the moon dates to the local time zone, add to list if moon date still falls in desired year
        local_date = ephem.localtime(date)
        if local_date.year == year:
            # Append the date as a string to the list for easier comparison later
            moons.append(local_date.strftime("%Y-%m-%d"))

    return moons

moons = get_full_moons_in_year(2015)
print(moons)

上面的代码将返回:

Time zone calibrated to US/Pacific
['2015-01-04', '2015-02-03', '2015-03-05', '2015-04-04', '2015-05-03', '2015-06-02', '2015-07-01', '2015-07-31', '2015-08-29', '2015-09-27', '2015-10-27', '2015-11-25', '2015-12-25']
于 2015-08-23T17:24:04.820 回答
2

我知道您正在寻找 Python,但如果您能理解 C#,那么有一个名为Chronos XP的开源项目可以很好地做到这一点。

于 2010-03-31T17:41:08.407 回答
2

如果您不需要高精度,您可以始终(ab)使用月球(或阴阳)日历类(例如,HijriCalendarChineseLunisolarCalendar在 Microsoft .NET 中)计算任何日期的(近似)月相,作为日历的“日-of-month”属性,作为阴历(或阴阳)日历日,始终对应于月相(例如,第 1 天是新月,第 15 天是满月等)

于 2010-04-08T02:01:46.133 回答
1

PyEphem现在已弃用 - 他们建议在新项目中更喜欢Skyfield 天文学库而不是 PyEphem。它的现代设计鼓励更好的 Python 代码,并使用 NumPy 来加速其计算。

月相定义为月球与太阳沿黄道的夹角。这个角度被计算为月球和太阳的黄道经度之差。

结果是新月的角度为 0°,第一季度为 90°,满月为 180°,最后一个季度为 270°

这里获取的代码

from skyfield.api import load
from skyfield.framelib import ecliptic_frame

ts = load.timescale()
t = ts.utc(2019, 12, 9, 15, 36)

eph = load('de421.bsp')
sun, moon, earth = eph['sun'], eph['moon'], eph['earth']

e = earth.at(t)
_, slon, _ = e.observe(sun).apparent().frame_latlon(ecliptic_frame)
_, mlon, _ = e.observe(moon).apparent().frame_latlon(ecliptic_frame)
phase = (mlon.degrees - slon.degrees) % 360.0

print('{0:.1f}'.format(phase))

输出

149.4
于 2021-11-06T10:46:32.367 回答