如何用Skyfield计算指定时间地点的站心新月宽度?
计算指定时间地点的站心新月宽度(Skyfield间接实现方案)
核心逻辑
Skyfield没有直接计算站心新月宽度的API,但可以通过站心天体位置计算+相位角+视直径几何推导实现。站心计算的核心是将地心位置转换为观测者所在经纬度、海拔的视角坐标,再结合月球亮面的几何关系推导宽度。
分步公式与实现
1. 获取站心的日月位置
首先需要计算观测者(指定经纬度、海拔)在目标时间的站心天体位置:
- 用Skyfield的
Topos创建观测者对象,包含纬度、经度、海拔参数 - 通过
observer_geo = earth + observer构建观测者的地心位置,再调用observe().apparent()得到站心的太阳、月球视位置(自动消除光行差和视差)
2. 计算站心相位角
相位角i是从月球中心看,太阳与观测者的张角,这是决定新月亮面大小的核心参数,通过向量点积推导的公式:
cos(i) = [(S - M) · (O - M)] / (||S - M|| × ||O - M||)
其中:
S:站心太阳的ICRF坐标向量(单位:AU)M:站心月球的ICRF坐标向量(单位:AU)O:观测者的地心ICRF坐标向量(单位:AU)
3. 站心月球视直径
通过Skyfield的moon.apparent().diameter()直接获取站心视直径(单位:角秒),转换为弧度方便后续计算。
4. 新月宽度计算
新月的可见亮面宽度(垂直于日月连线方向的视厚度)公式:
宽度(角秒)= 视直径(角秒)× sin(i/2)
如果需要亮弧的视长度(新月两端的角距离),可近似用:
弧长(角秒)= 视直径(角秒)× i(弧度转角秒)
Skyfield代码示例
from skyfield.api import load, Topos import math # 初始化时间与观测者 ts = load.timescale() target_time = ts.utc(2024, 5, 15, 12, 0, 0) # 替换为目标时间 observer = Topos('39.9042 N', '116.4074 E', elevation_m=50) # 替换为目标经纬度、海拔 # 加载星历数据 eph = load('de421.bsp') sun, moon, earth = eph['sun'], eph['moon'], eph['earth'] # 计算站心视位置 observer_geo = earth + observer sun_app = observer_geo.at(target_time).observe(sun).apparent() moon_app = observer_geo.at(target_time).observe(moon).apparent() # 获取坐标向量 sun_vec = sun_app.position.au moon_vec = moon_app.position.au observer_vec = observer_geo.at(target_time).position.au # 计算相位角(弧度) sun_to_moon = sun_vec - moon_vec obs_to_moon = observer_vec - moon_vec dot_product = sun_to_moon @ obs_to_moon norm_product = math.hypot(*sun_to_moon) * math.hypot(*obs_to_moon) cos_i = dot_product / norm_product phase_angle = math.acos(cos_i) # 计算站心视直径(弧度) moon_diam_rad = math.radians(moon_app.diameter() / 3600) # 计算新月宽度(转换回角秒) new_width_arcsec = math.degrees(math.sin(phase_angle/2) * moon_diam_rad) * 3600 print(f"站心新月宽度:{new_width_arcsec:.2f} 角秒")
关键注意点
- 必须调用
apparent()方法,否则得到的是地心位置,无法体现观测者的视差影响 - 相位角的计算必须基于站心位置,高纬度或近地平线的观测场景下,站心与地心结果差异明显
- 新月宽度在朔前后变化极快,时间精度建议控制在10分钟以内
内容的提问来源于stack exchange,提问作者orchid
相关产品推荐
相关产品推荐

