☰
从几行 Python 开始:用太阴星历查询天体、日月食和中国历法
2026/10/19 2:11:12 网站建设 项目流程

这篇介绍太阴星历的原生完整版。底层是 C++,Python 通过 pybind11 调用;Dart 则有对应的 FFI 包。

与js-ephemeris-lite、ephemeris_lite这类纯语言轻量星历库不同,太阴原生库可以读取 NASA/JPL 的 BSP/SPK 星历,也可以使用 OPM2 压缩数据。Python wheel 已经随附一套 DE442 衍生数据,普通查询不用先找 DLL 或下载一个大型 BSP 文件。

先从 Python 用起来:

  • Python 仓库:py-ephemeris
  • C++ 内核:taiyin-ephemeris
  • Dart 原生绑定:ephemeris

一、安装,算一个太阳位置

PyPI 发行包名是py-ephemeris,代码中的导入名是taiyin。

python-mpipinstallpy-ephemeris
importtaiyin eph=taiyin.Ephemeris()ctx=eph.create_context()# 这里明确使用 UT1 儒略日。ut1=taiyin.JulianDate.from_double(2460409.25)sun,result_flags=ctx.position.at_ut1(taiyin.Body.sun,ut1)longitude,latitude,distance=sunprint('太阳黄经:',longitude,'度')print('太阳黄纬:',latitude,'度')print('地心距离:',distance,'AU')print('执行标记:',result_flags)

这次计算的太阳黄经约为19.3862°,距离约为1.00150 AU。

新建 context 默认开启光行时、年周光行差和太阳引力偏折,所以这是普通视位置口径,不是未经改正的几何位置。后面要做数值对照时,除了星历数据,还要对齐这些设置。

上下文可以重复使用,没必要每算一颗天体就新建一次。下面的例子继续沿用eph和ctx。

二、需要速度,加一个 flag

sun_state,speed_flags=ctx.position.at_ut1(taiyin.Body.sun,ut1,flags=(taiyin.PositionFlag.speed,),)lon,lat,radius,lon_rate,lat_rate,radius_rate=sun_stateprint('黄经速度:',lon_rate,'度/日')print('距离变化率:',radius_rate,'AU/日')

不加speed返回三个坐标值,加了以后附上三个速度值。要笛卡尔状态,则使用state_at_ut1()等状态接口,不要把球坐标的三个数直接当 XYZ。

这里的result_flags属于这一次调用,可以报告回退等执行情况。致命错误会抛出异常,不是让用户从一个看似正常的坐标里猜有没有失败。

ctx.last_diagnostic用来调试最近一次调用,但在并发中可能被别的调用覆盖。要把结果和执行情况配对,应使用同次返回的 flags,或失败时异常对象携带的信息。

三、查 2024 年的下一次日食和月食

start=taiyin.AstroDateTime(2024,1,1).to_julian_date()solar,solar_flags=ctx.eclipses.next_solar_at_ut1(start)lunar,lunar_flags=ctx.eclipses.next_lunar_at_ut1(start)solar_clock,clock_flags=ctx.time.calendar_from_ut1(solar.maximum)print('日食类型:',solar.kinds)print('食甚 JD(UT1):',solar.maximum.to_double())print('食甚 UT1 日期:',solar_clock.year,solar_clock.month,solar_clock.day)print('食甚 UT1 时刻:',solar_clock.hour,solar_clock.minute,solar_clock.second)print('月食类型:',lunar.kinds)print('月食食甚 JD(UT1):',lunar.maximum.to_double())

这里找到的日食是2024 年 4 月 8 日的日全食,月食是3 月 25 日的半影月食。日食食甚的 UT1 儒略日约为2460409.262039814。

calendar_from_ut1()只是把 UT1 坐标还原成年月日时分秒,没把它变成 UTC。想要 UTC 民用时刻,应使用相应的 UTC 转换接口,并确认 EOP 数据或估算策略。

全球搜索回答“什么时候发生这次日食”。如果问“我的地点能看见多少”,还要配置观测者,并调用地方日食接口。完整版也提供接触时刻、日食路线和相关几何数据;画地图、选择投影和行政区匹配由应用负责。

四、农历和恒星也在基础包里

lunar_date,calendar_flags=ctx.chinese_calendar.from_solar(taiyin.SolarDate(2025,1,29))print(lunar_date.year,lunar_date.month,lunar_date.day,lunar_date.isLeap)# 2025 1 1 Falseantares,star_flags=ctx.stars.at_ut1('antares',ut1)print('心宿二坐标:',antares.coordinates)

默认 wheel 会载入 lite 恒星表,包含 2,057 颗恒星和 12,242 个别名。也可以加入自己的星表。输入常用名时很方便,批量整理资料时则建议保存明确的标识,避免不同星表的别名冲突。

农历、节气、干支以及四柱属于基础包。完整八字分析和紫微斗数分别在py-ephemeris-bazi、py-ephemeris-ziwei中,按需安装即可。

原生完整版目前没有黄历宜忌模块。农历日期是历法计算,宜忌、吉神凶煞属于另一层传统规则资料;JS 仓库里的huangli-lite仍在完善,不能当成太阴原生库已经提供的功能。

五、输入是 Python datetime,怎么办

实际业务通常拿到的是带时区的datetime,不是儒略日。可以先确定唯一的 UTC 瞬间,再交给时间服务。

fromdatetimeimportdatetime,timezone instant=datetime(2024,4,8,18,0,tzinfo=timezone.utc)utc=taiyin.JulianDate.from_datetime(instant)# 这个例子明确允许:缺少所需 EOP 时,走时间尺度估算回退。ctx.time.set_allow_utc_out_of_range_estimate(True)estimated_ut1,time_flags=ctx.time.utc_to_ut1(utc)position,position_flags=ctx.position.at_ut1(taiyin.Body.sun,estimated_ut1,)print(position)print('时间执行标记:',time_flags)

from_datetime()拒绝没有时区的 naive datetime,不会默默采用服务器时区。Unix 时间戳也有入口,单位遵循 Python 习惯,用秒。

上面的True不是提高精度的开关。数据不足时,它允许用已配置的 ΔT 模型走估算路线,并用timeScaleFallback标记报告。需要严格 UTC/UT1 换算时,不要开启这个回退,应准备覆盖日期的地球定向数据。*_at_ut1()则始终把输入当 UT1,不受该开关重新解释。

天体位置主要需要 TT/TDB,地球旋转需要 UT1,民用时间通常给 UTC。接口名字里区分这些时间尺度,是为了让这几件事不被一个裸浮点数掩盖。

六、随包有什么数据,还能加什么

当前 Python wheel 随附约 1550~2650 年的 DE442 衍生主要天体 OPM2、选定小行星、部分形心修正、开普勒回退数据和 lite 恒星表。不随包附带 DE441。

需要长年代计算,可以另下全量 DE441 OPM2 数据包,也可以加入原始 NASA/JPL BSP/SPK、卫星和小天体 SPK。

下面是添加文件的配置示意,路径需要换成自己真实存在的文件:

custom_eph=taiyin.Ephemeris(source_paths=['/path/to/de441.bsp','/path/to/satellites',],)custom_ctx=custom_eph.create_context()

不指定data_root时保留默认随包数据,source_paths追加文件或目录。想把主目录也换掉,再显式设置data_root。

检查实际注册了什么,可以这样看:

frompathlibimportPathforsourceineph.registered_data_sources:print(source.format,Path(source.source).name)

注册列表能确认文件被识别,但不代表每次计算都选中了其中某个文件。具体路线还取决于天体、日期覆盖和优先级;需要复现结果时,把配置、数据版本以及本次诊断一起记录。

七、精度数字怎么读

OPM2 的典型还原差约为0.001 角秒,意思是压缩产品相对源 DE441/DE442 的还原差,不是整个库所有输出的统一误差。

通过公开 Python 接口,对随包 OPM2 与原始 DE442 做过 1,280 个样本的对照。对象为太阳、月球和行星系统质心,比较地心几何方向:RMS 为0.0001924″,样本最大值为0.0014786″。不包含站心、大气折射或物理行星形心重建。

日食另有事件层的校验。2024 年 4 月 8 日日食,相对紫金山天文台取整到秒的历书表,食甚差约+0.24 秒,检查的五个全球事件时刻差均在约两秒内。其他事件不能直接套用这一组统计。

详细口径、样本与性能记录放在 Python 精度与性能文档。选型时可以先看结果,再看测试到底比较了什么。

八、C++ 和 Dart 用户从哪里接入

这些例子调用的都是同一个 C++ 内核。原生程序可以链接 C++ 库,其他语言可以走版本化 C ABI;Dart Native 用ephemeris包,通过 FFI 调用。

Dart 原生包随附支持平台的动态库和 lite 恒星表,但不附带 Python wheel 里的精密行星 OPM2。需要使用精密数据时,应给它配置文件路径。也不要和纯 Dart、支持 Web 的ephemeris_lite混淆。

Python 应用通常可以复用一个已经配置好的 context。主要只读计算在原生工作期间释放 GIL,但是否加速仍要看任务粒度和数据缓存竞争;不要一边计算一边修改配置或关闭对象。长运行程序建议用with或在结束时调用ctx.close()。

原创代码采用 MPL-2.0,具体第三方来源与条款见NOTICE。先从默认数据把一个真实任务跑通,再按需要添加星历、观测地点和更细的模型配置,会比一上来把所有开关都打开省心一些。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询