feat: 完善日月食与月掩几何链路并扩展历法接口

- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑
- 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口
- 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界
- 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名
- 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验
- 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口
- 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
This commit is contained in:
2026-09-17 12:27:40 +08:00
parent 9ee2163cc7
commit 2bf8478639
428 changed files with 85981 additions and 7998 deletions
+333 -187
View File
@@ -4,11 +4,11 @@
[![Go Reference](https://pkg.go.dev/badge/b612.me/astro.svg)](https://pkg.go.dev/b612.me/astro)
自用多年的天文算法库,用于个人天文历法爱好、科普演示和轻量研究。
自用多年的天文算法库,用于个人天文历法爱好。
>📚 本项目主要用于天文算法学习与验证,计算结果满足业余爱好级别需求。
基于《天文算法》(Astronomical Algorithms)一书实现,提供历法转换、太阳/月亮/行星位置、日月食、月掩、升落、中天、月相、恒星、坐标变换、物理星历、研究公式和通用小天体轨道传播等功能。太阳和行星部分使用内置 VSOP87 解析项,月球部分使用内置 ELP2000/82 解析级数,不依赖外部 JPL 星历文件。
基于《天文算法》(Astronomical Algorithms)一书实现,覆盖范围见下方[功能概览](#功能概览)。太阳和行星部分使用内置 VSOP87 解析项,月球部分使用内置 ELP/MPP02(DE405 拟合)解析级数,不依赖外部 JPL 星历文件。
没有特殊标注时,本程序所提供的坐标均为瞬时天球坐标;角度单位默认是度,视直径/视半径单位是角秒,距离单位按函数名使用 AU 或 km。
@@ -42,7 +42,7 @@ go get b612.me/astro
## 功能概览
- 📅 **历法转换**:公历与农历互转(公元前721年-公元3000年或更久)、节气时刻
- 📅 **历法转换**:公历与农历互转(公元前721年-公元3000年)、节气时刻
- 🌞 **太阳计算**:天球位置、日出日落、日地距离、真太阳时、视高度角、视差角、日面物理参数(`P/B0/L0`)、视直径等
- 🌙 **月亮计算**:天球位置、月出月落、地月距离、月相、朔望时间、视直径、亮边位置角、视差角、地心/站心天平动、近远地点、交点、最大赤纬等
- 🪶 **轻量链路**:`lite/sun` 与 `lite/moon` 提供面向手表、前端、小程序和其它资源受限环境的轻量近似太阳/月亮算法,覆盖天球位置、升落和月相
@@ -50,7 +50,7 @@ go get b612.me/astro
- 🌘 **月掩**:按指定赤经赤纬搜索恒星月掩,按有限圆盘计算行星月掩,支持指定地点接触时刻、全球掩带、几何掩甚点和 SVG
- 🗺️ **地理输出**:日食、月食和月掩结果可编码为带时间数据与可选时间标记的 GeoJSON;全球 SVG 使用无行政边界海岸线,并支持等经纬和南北极投影
- 🪐 **行星计算**:七大行星天球位置、升落时间、合冲留、大距、水星/金星地心凌日等特殊天象时间、升交点/降交点、视直径/视半径、相位、视差角、节点、视星等与物理星历
- ⭐ **恒星计算**:指定天球坐标所属星座;同时包含9100颗恒星数据库,可计算升降时间、视差角和视高度角,获取指定日期的恒星坐标信息
- ⭐ **恒星计算**:指定天球坐标所属星座;同时内置 9100 颗恒星数据库,可计算升降时间、视差角和视高度角,获取指定日期的恒星坐标信息
- 🧭 **坐标工具**:黄道/赤道/地平坐标转换、站心坐标、恒星时、岁差、章动、角距离、大气折射、大气质量、视差角、银道坐标
- 🔭 **研究公式**:黑体辐射、会合周期、星等距离换算、望远镜极限星等、恒星半径/温度/光度换算、大气质量模型
- ☄️ **通用轨道**:给定小行星、彗星或假想天体轨道根数,计算日心/地心位置和站心视位置,并提供距日/距地距离、日距角、相位角、照明比例、H-G 视星等和轻量视双星位置角/角距计算
@@ -67,7 +67,7 @@ go get b612.me/astro
| `lite/sun` / `lite/moon` | 轻量太阳/月亮近似链路,面向分钟级升落、轻量天球位置和月相计算 |
| `eclipse` / `eclipse/svg` | 全局/局地日月食、日食中心线与偏食足迹、局地可见性筛选、局地示意图与全球见食图 SVG |
| `moon/svg` | 指定地点恒星/行星月掩视圆图,以及带掩带、中心线和时间标记的全球投影 SVG |
| `geojson` | 将日食、月食和月掩的既有地理结果编码为 RFC 7946 GeoJSON,投影与样式由应用负责 |
| `geojson` | 将日食、月食和月掩的既有地理结果编码为 RFC 7946 GeoJSON |
| `mercury` / `venus` | 水星、金星位置、升落、合日、留、大距、地心凌日、相位、视差角、视星等、视直径、节点和物理星历 |
| `mars` / `jupiter` / `saturn` / `uranus` / `neptune` | 外行星位置、升落、合冲、留、方照、相位、视差角、视星等、视直径、节点和物理星历 |
| `earth` | 地球轨道偏心率、近日点、远日点 |
@@ -76,23 +76,18 @@ go get b612.me/astro
| `orbit` | 通用日心二体圆锥曲线轨道传播,支持椭圆、近抛物、抛物和双曲轨道;另含相位/测光辅助和轻量视双星计算 |
| `sundial` | 真/平太阳时换算、太阳时角、平太阳时/区时时角、平面日晷几何、时间线/赤纬曲线采样、赤道/水平/垂直日晷特例 |
很多接口额外提供 `...N` 截断版本:
一些接口额外提供 `...N` 截断版本:
- `n < 0`:使用本仓库当前内置的全部解析项
- `n >= 0`:截断解析项,适合性能对比、粗算或算法研究
这里的“全部解析项”指package中已经内置的表项,不等同于外部发行版 VSOP/ELP 长表的全部原始数据。
大气质量接口的补充:
- `coord.Airmass...` 面向观测场景,既可以直接传入视高度角,也可以从真高度角先做折射修正再计算
- `formula.Airmass...` 只提供纯公式本身,不负责折射修正,适合已经在别处拿到视高度角或天顶距时直接调用
## 适用范围与精度
### 太阳与行星
太阳和行星使用内置 VSOP87 解析项,当前表项覆盖 **J2000 前后约 4000 年**。精度量级如下:
太阳和行星使用内置 VSOP87 解析项,当前表项覆盖 **J2000 前后约 4000 年**。下表列出相对完整 VSOP87 的截断误差量级:
| 目标 | 黄经/黄纬 | 距离 |
| --- | --- | --- |
@@ -104,20 +99,20 @@ go get b612.me/astro
| 天王星 | 约 `1"` | 约 `20 × 10^-6 AU` |
| 海王星 | 约 `1"` | 约 `40 × 10^-6 AU` |
这类精度适合常规天文历法、观测辅助、科普展示和个人研究。如果需要航天导航、掩星预报或严格动力学积分,应使用 JPL DE 等专业星历。
这类精度适合常规天文历法、观测辅助、科普展示和个人研究;航天导航、精确掩星预报和严格动力学积分不在该范围内,这类用途通常需要 JPL DE 等专业星历。
### 月球
月球使用内置的 ELP/MPP02 DE405 解析级数(截断版,保留主要周期项),库体积轻,不需要外部星历文件。它适合农历定朔、月相、升落、月食、业余月掩预报和常规位置计算;若需要极高精度月球测距、长期物理天平动或专业掩星,请以 JPL 星历或专门月球星历为准。
月球使用内置的 ELP/MPP02 DE405 解析级数(截断版,保留主要周期项),库体积轻,不需要外部星历文件。它适合农历定朔、月相、升落、月食、业余月掩预报和常规位置计算;极高精度月球测距、长期物理天平动和专业掩星超出该范围,这类用途以 JPL 星历或专门月球星历为准。
### Lite 轻量链路
`lite/sun` 和 `lite/moon` 是独立于 `sun` / `moon` 的近似实现。不依赖 VSOP87 或 ELP2000/82,适合 CPU / 内存受限环境。
`lite/sun` 和 `lite/moon` 是独立于 `sun` / `moon` 的近似实现。不依赖 VSOP87 或 ELP/MPP02,适合 CPU / 内存受限环境。
- `lite/sun`:简化太阳真黄经 / 视黄经公式 + 轻量赤道坐标转换
- `lite/moon`:Schlyter 风格月球近似(约 15 个摄动项)+ 轻量站心修正
- 升落搜索:固定步长扫描 + 二分,不走主链的高精度章动迭代
- 计算链路零堆分配(0 allocs/op),月球位置约 1µs,比主链快 20–60 倍
- 计算链路零堆分配(0 allocs/op);相对主链,位置与月相等纯求值接口约快 `8.3–27.3x`,升落接口约 `1.0–3.7x`
能力边界:
@@ -126,62 +121,63 @@ go get b612.me/astro
| `lite/sun` | 简化太阳真/视黄经 + 轻量赤道坐标转换 | `30` 分钟步长扫描 + 二分 | 日出日落、太阳高度角、表盘/前端周期刷新 |
| `lite/moon` | Schlyter / vFPS 月球近似 + 轻量站心修正 | `15` 分钟步长扫描 + 二分 | 月出月落、月相、月龄、轻量月球观测辅助 |
与主链 `sun` / `moon` 的误差(2026 全年,8 个站点;升落每 7 或 15 天取样,月相月龄每 6 小时):
与 `sun` / `moon` package的误差(2026 全年,8 个站点;升落每 7 或 15 天取样,月相月龄每 6 小时):
| 能力 | 平均绝对误差 | P95 | 最大绝对误差 | 备注 |
| --- | --- | --- | --- | --- |
| `lite/sun` 日出 | `0.02 min` | `0.04 min` | `0.31 min` | 样本中无事件存在性分歧 |
| `lite/sun` 日落 | `0.02 min` | `0.06 min` | `0.35 min` | `2` 个高纬样本在跨午夜“归属哪一天”上有语义差异 |
| `lite/moon` 月出 | `0.28 min` | `0.57 min` | `1.44 min` | 样本中无事件存在性分歧 |
| `lite/moon` 月落 | `0.36 min` | `0.86 min` | `1.24 min` | `1` 个高纬样本在“当天是否有月落”上与主链判断不同 |
| `lite/moon` `Phase()` | `0.00089` | `0.00185` | `0.00243` | 和 `moon.Phase` 对比 |
| 能力 | 平均绝对误差 | P95 | 最大绝对误差 | 备注 |
| --- | --- | --- | --- |-----------------------------------|
| `lite/sun` 日出 | `0.02 min` | `0.04 min` | `0.31 min` | 样本中无事件存在性分歧 |
| `lite/sun` 日落 | `0.02 min` | `0.06 min` | `0.35 min` | `2` 个高纬样本在跨午夜日期归属上有语义差异 |
| `lite/moon` 月出 | `0.28 min` | `0.57 min` | `1.44 min` | 样本中无事件存在性分歧 |
| `lite/moon` 月落 | `0.36 min` | `0.86 min` | `1.24 min` | `1` 个高纬样本在“当天是否有月落”上与主链判断不同 |
| `lite/moon` `Phase()` | `0.00089` | `0.00185` | `0.00243` | 与 `moon.Phase` 对比的结果 |
| `lite/moon` `PhaseAge()` | `0.003 d` | `0.010 d` | `0.014 d` | 约平均 4.3 分钟、P95 14.4 分钟、最大 20.2 分钟 |
| `lite/moon` 地心黄经 | `2.41'` | `6.82'` | `9.91'` | 相对主链月球位置 |
| `lite/moon` 地心黄纬 | `0.87'` | `1.83'` | `2.92'` | 相对主链月球位置 |
| `lite/moon` 地心黄经 | `2.41'` | `6.82'` | `9.91'` | 相对主链月球位置 |
| `lite/moon` 地心黄纬 | `0.87'` | `1.83'` | `2.92'` | 相对主链月球位置 |
本地 `Go testing.Benchmark` 参考值(绝对值因机器而异,相对趋势稳定):
`Go testing.Benchmark` 参考值(单机实测,仅供参考;绝对值因机器而异):
口径为 2026-01-01 20:00 CST、上海(`121.4737°E, 31.2304°N`)、`height=0`、`aero=true`,表中取 3 次中位数;每项先预热一次,懒加载缓存与首次分配不计入稳态单次开销。
| 接口 | 主链 | `lite` | 加速倍数 | 主链分配 | `lite` 分配 |
| --- | --- | --- | --- | --- | --- |
| `Sun ApparentRaDec` | `13.392 µs/op` | `231.0 ns/op` | `57.97x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Sun Altitude` | `16.405 µs/op` | `681.5 ns/op` | `24.09x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Sun RiseTime` | `202.994 µs/op` | `18.823 µs/op` | `10.78x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Moon ApparentRaDec` | `65.273 µs/op` | `1.035 µs/op` | `63.06x` | `297202 B/op, 70 allocs/op` | `0 B/op, 0 allocs/op` |
| `Moon Phase` | `40.264 µs/op` | `940.6 ns/op` | `42.83x` | `178321 B/op, 42 allocs/op` | `0 B/op, 0 allocs/op` |
| `Moon Altitude` | `44.883 µs/op` | `2.275 µs/op` | `19.73x` | `178321 B/op, 42 allocs/op` | `0 B/op, 0 allocs/op` |
| `Moon RiseTime` | `659.886 µs/op` | `77.600 µs/op` | `8.50x` | `2377613 B/op, 560 allocs/op` | `0 B/op, 0 allocs/op` |
| `Sun ApparentRaDec` | `5.888 µs/op` | `215.6 ns/op` | `27.3x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Sun Altitude` | `5.955 µs/op` | `625.9 ns/op` | `9.5x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Sun RiseTime` | `95.847 µs/op` | `25.648 µs/op` | `3.7x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Moon ApparentRaDec` | `16.520 µs/op` | `1.006 µs/op` | `16.4x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Moon Phase` | `15.139 µs/op` | `917.7 ns/op` | `16.5x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Moon Altitude` | `9.533 µs/op` | `1.150 µs/op` | `8.3x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
| `Moon RiseTime` | `120.545 µs/op` | `118.037 µs/op` | `1.0x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` |
需要日月食、物理天平动或高纬边界判定时,仍用主链 `sun` / `moon`。
主链与 `lite` 的差距随场景变化:位置、月相等纯求值接口约 `8.3–27.3x`;升落接口两边都要做时间搜索,差距缩小到 `1.0–3.7x`(`Moon RiseTime` 已接近持平)。加速倍数来自同一台机器上的对照,受机器影响小于绝对值。
日月食、物理天平动或高纬边界判定使用主链 `sun` / `moon`。
### 精度校验参考
下面这些函数曾与 JPL Horizons、NASA GSFC 等资料对照,可作为使用时判断结果量级的参考:
- 太阳/行星/月亮视直径:与外部基线最大差异从 `0.000002"` 到 `0.194598"` 不等,月亮因视差和距离变化更敏感
- 太阳/行星/月亮视直径:各天体与外部基线的最大差异从 `0.000002"` 到 `0.194598"` 不等,月亮因视差和距离变化更敏感
- 太阳物理星历 `P/B0/L0`:最大差异约 `0.003349° / 0.003986° / 0.047394°`
- 行星升/中天/落:已用 JPL Horizons 电视事件(TVH, Time-Varying Hourly)做对比校验;该基线按 1 分钟步长生成,当前结果与 Horizons 事件时间在分钟级上对齐
- 月出/月落:`aero=true` 按动态标准折射和实时月球视半径计算上缘过地平线。7 个地点、14 个海平面事件相对 JPL Horizons DE441 的平均/最大差异约 `0.30s / 0.75s`
- 月出/月落的其他口径:相对固定 `-0.8333°` 的 MET Norway(Skyfield 1.53 + DE440s)约 `38.77s / 76.22s`;相对未公开地平线口径的 IMCCE Miriade 平均约 `2m13.46s`,`61°N` 低仰角样本最大约 `6m41.82s`
- 月掩恒星全球路径:`2025-06-05` 月掩进贤增九(HR 4799)样例相对项目记录的参考值,掩始/掩甚/掩终差异约 `+2.30s / -1.37s / -3.12s`;掩甚经纬度差异约 `+0.0122° / +0.0105°`,掩带宽为 `3582.4 km`,与参考 `3571.9 km` 相差约 `10.5 km`
- 地球近日点/远日点:时刻最大差异约 `1m28.84s`,距离最大差异约 `0.000000039837 AU`
- 月球主链位置:当前算法为 ELP/MPP02 DE405 解析级数截断版;在 `-2000` 年四个 JPL/Horizons `JDTT` 样本上,相对 JPL/Horizons 的最大差异约为黄经 `219.6"`、黄纬 `25.8"`、距离 `34.3 km`
- 月球近地点/远地点:时刻最大差异约 `15m53.45s`,距离最大差异约 `39.758 km`
- 月球最大赤纬:时刻最大差异约 `2.43s`,赤纬最大差异约 `0.00006431°`
站心时角修复后的影响边界:月出/月落、指定地点月掩、月球站心坐标,以及地方月食结果中的食甚月高和可见性判断使用修复后的 UT 时角链。日食和月食的全球接触时刻、食甚、食分等主体结果来自各自的地心/贝塞尔几何,不经过该站心转换,因此现有 NASA 精度数据无需随之改写。
## 快速开始
### 历法转换与节气
本 package 支持公历与中国传统农历日期之间的相互转换,并提供节气信息。支持年份范围为公元前721年至公元3000年(部分现代算法可更久)。
本 package 支持公历与中国传统农历日期之间的相互转换,并提供节气信息。支持年份范围为公元前721年至公元3000年(公元前104年为历法表切换点)。
农历本质上是阴阳合历(Lunisolar Calendar),但为兼顾大众习惯与代码简洁性,相关函数命名采用 `Lunar` 而非更学术的 `Lunisolar`。
#### 历法说明
- **默认路由**:按年份自动选择,先秦段使用春秋/古六历重建,`-220..-104` 使用秦汉颛顼历,`-103..1912` 使用历表,`1913` 年后使用现代算法。
- **显式古历**:如果需要指定某一古历系统,请使用 `SolarToLunarWithCalendar` / `LunarToSolarWithCalendar` 这类 API。
- **数据来源**:古历部分主要参考《寿星天文历》;使用 [ytliu0教授的网站数据](https://ytliu0.github.io/ChineseCalendar/index_simp.html)做验证;现代段依据GB/T 33661-2017编排,通过 VSOP87、ELP定气定朔 。
- **数据来源**:古历部分主要参考《寿星天文历》;使用 [ytliu0教授的网站数据](https://ytliu0.github.io/ChineseCalendar/index_simp.html)做验证校验;现代段依据GB/T 33661-2017编排,通过 VSOP87、ELP定气定朔 。
- **节气**:`JieQi` 返回现代天文计算的节气时刻;`CalendricalJieQi` 返回历法相符节气日期。
---
@@ -207,7 +203,7 @@ go get b612.me/astro
本 package 主要面向中国历法,因此定气和定朔的计算默认采用北京时间(UTC+8)。对于使用其他时区的地区,若直接套用中国农历的编排规则,可能会产生日期偏差。
为方便探索与研究,本 package 提供了底层方法 `Solar` 和 `Lunar`,它们支持在**自定义时区**下,按照**现行中国农历算法(GB/T 33661-2017)**进行公历与农历的相互转换。
为方便探索与研究,本 package 提供了底层方法 `Solar` 和 `Lunar`,它们支持在**自定义时区**下,按照**现行中国农历算法(GB/T 33661-2017)** 进行公历与农历的相互转换。
如果只需北京时间下的标准转换,请直接使用封装好的 `SolarToLunar` 和 `LunarToSolar` 方法。
**示例**:农历规则要求冬至必须落在农历十一月。以1984年冬至为例,计算可得:
@@ -232,7 +228,7 @@ fmt.Println(calendar.Solar(1985, 1, 1, false, 7.0))
##### 5. Go 语言特别注意
⚠️ **Go 标准库 `time.Time` 在历法处理上与本程序存在差异:**
⚠️ Go 标准库 `time.Time` 在历法处理上与本程序存在差异:
- Go 语言在1582年10月15日之前使用逆推格里高利历,而非儒略历。若不使用 `Add` 方法,一般可正常使用。
- 因此,**在1582年10月15日之前,`time.Time.Weekday()` 返回结果与本程序计算结果不一致**。
@@ -246,9 +242,44 @@ fmt.Println(calendar.Solar(1985, 1, 1, false, 7.0))
weekday := int(calendar.Date2JDE(date)+1.5) % 7
// 0表示星期日,1表示星期一,……,6表示星期六
```
若在1582年之前使用 time.Time 的 Add 或 AddDate 方法,请注意其在某些年份可能不准确。
在 1582 年之前使用 `time.Time` 的 `Add` 或 `AddDate` 会经过逆推格里高利历,跨过儒略历独有的闰日时与儒略历相差一天。
例如:700年儒略历为闰年,而 Go 使用的逆推格里高利历中700年不是闰年。
##### 6. 儒略历独有的闰日(如 700-02-29)
1582 年以前"能被 100 整除但不能被 400 整除"的年份(如 100、700、1500 年)在儒略历中有 2 月 29 日,
而 Go 的`time.Time`使用逆推格里高利历,没有这一天(`time.Date(700, 2, 29, ...)` 会被规范化成 700-03-01)。本库承认 700-02-29 这一天存在,对应的约束如下:
- `Time.JulianOnly()`:该农历日是否只存在于儒略历(对应 JSON 字段 `julianOnly`);
- `Time.JDE()`:该日精确的儒略日;儒略历闰日比 `Solar()` 早一天,其余情况两者一致(对应 JSON 字段 `jde`)。
- `Time.Solar()` / `LunarTime.SolarDate`:库内标准输出,对于700-02-29Go标准库表示不出来的日期,固定返回为**后一天**(700-02-29 的后一天是 700-03-01),与 `basic.JDE2DateByZone` 的约定一致;
```go
julian, _ := calendar.SolarToLunarByYMD(700, 2, 29)
fmt.Println(julian.Solar().Format("2006-01-02"), julian.JulianOnly(), julian.JDE(), julian.Lunar().MonthDay())
// 0700-03-01 true 1.9767915e+06 二月初五
```
> 涉及这类日期时,不丢闰日的入口有两类:整型年月日入口 `SolarToLunarByYMD` / `LunarToSolarByYMD`,以及直接调用
> `basic.JDECalc(700, 2, 29)` 得到精确儒略日 `1976791.5`;先构造 `time.Time` 的那一步就会丢掉闰日。
##### 7. 同一农历日的多个公历候选
改历双纪年(王莽 9–23 年、魏明帝 237–240 年、武则天 689–700 年、唐肃宗 761–762 年)与太初改历交接
(公元前 104 年)会让同一个农历日对应两个合法公历日。`Solar()` 仍是库内默认选择,`SolarCandidates()`
返回全部候选、首个恒等于 `Solar()`:
```go
res, _ := calendar.LunarToSolarByYMD(700, 11, 1, false)
fmt.Println(res.Solar().Format("2006-01-02"))
fmt.Println(len(res.SolarCandidates()))
// 0700-12-15
// 2
```
非改历年份的农历日只有一个候选,`SolarCandidates()` 返回仅含 `Solar()` 的slice;儒略历独有的闰日也只返回
标准输出,因为它唯一合法的那一天无法表示成 `time.Time`,精确日期见 `JDE()`。
#### 历法转换
##### 公历转农历
@@ -272,14 +303,13 @@ weekday := int(calendar.Date2JDE(date)+1.5) % 7
3. `年份+月+日`:如 **`"二零二五年正月初一"`**(闰月前加"闰",适用于现代日期)
4. `年份+月+干支日`:如 **`"二零二五年正月戊戌日"`**
5. `阿拉伯数字+月+日`:可以将中文数字替换为阿拉伯数字,如 **`"2025年1月1日"`**,代表`二零二五年正月初一`
6. **注意历史场景**:历史上月份名称可能与现代不同(如武则天时期“正月”与“一月”代表不同月份),请使用汉字数字确保准确性
6. 历史场景:历史上月份名称可能与现代不同(如武则天时期“正月”与“一月”代表不同月份),这类场景下月份名称按汉字数字解释
> ⚠️ **特别提醒**:
> 农历年份与公历年份并非完全重合。例如:公历2025年1月28日(除夕)对应农历2024年腊月二十九,应传入 `"二零二四年腊月廿九"`。
> ⚠️ 农历年份与公历年份并非完全重合。例如:公历2025年1月28日(除夕)对应农历2024年腊月二十九,对应的字符串是 `"二零二四年腊月廿九"`。
###### 方式二:传入数字参数
- **参数**:年份 (`int`)、月份 (`int`)、日期 (`int`)、是否闰月 (`bool`)
- **特点**:简单直接,适用于现代农历日期转换
- **语义**:按农历年、月、日与闰月标志定位日期,适用于现代农历日期转换
##### 代码示例
@@ -336,6 +366,9 @@ func main() {
"ganzhiYear": "己未",
"ganzhiMonth": "丙子",
"ganzhiDay": "辛未",
"calendarSystem": "",
"calendarName": "",
"jde": 1808717.8389814815,
"dynasty": "魏",
"emperor": "魏明帝",
"nianhao": "景初",
@@ -355,7 +388,10 @@ func main() {
"ganzhiYear": "己未",
"ganzhiMonth": "丙子",
"ganzhiDay": "辛未",
"dynasty": "",
"calendarSystem": "",
"calendarName": "",
"jde": 1808717.8389814815,
"dynasty": "蜀",
"emperor": "蜀后主",
"nianhao": "延熙",
"yearOfNianhao": 2,
@@ -374,6 +410,9 @@ func main() {
"ganzhiYear": "己未",
"ganzhiMonth": "丙子",
"ganzhiDay": "辛未",
"calendarSystem": "",
"calendarName": "",
"jde": 1808717.8389814815,
"dynasty": "吴",
"emperor": "吴大帝",
"nianhao": "赤乌",
@@ -450,17 +489,15 @@ fmt.Printf("%d-%02d-%02d %v\n", date.Year(), int(date.Month()), date.Day(), err)
- `Altitude`:高度角,地平线为 `0°`,天顶为 `+90°`
- `Zenith`:天顶距,天顶为 `0°`,地平线为 `90°`
- **注意:旧版本中 `Zenith` 曾错误返回高度角;当前版本已修正,升级时请重点检查调用方**
- `Zenith` 与 `Altitude` 互补,两者相加为 `90°`
#### 日出日落/月出月落
> ⚠️ **重要说明**:
> 月球升降时间计算基于当天日期,升降时间点之间不一定具有连续性。
> ⚠️ 月球升降时间按当天日期计算,升降时间点之间不一定具有连续性。
>
> **可能出现的情况**:
> - 月亮可能在凌晨1点落下,中午12点再次升起,此时升起时间会晚于降落时间;要获取此场景晚上的月落时间,需要传入次日日期进行计算
> 例如月亮可能在凌晨1点落下、中午12点再次升起,此时升起时间会晚于降落时间;这一场景晚上的月落时间对应次日日期。
>
> **如需获取完整升降周期,需要自行通过判断升起时间是否在降落时间之后来确定后续的正确时间点**
> 完整的升降周期由升起时间与降落时间的先后关系决定:判断升起时间是否在降落时间之后,即可确定后续的正确时间点。
```go
package main
@@ -509,8 +546,8 @@ func main() {
2020-01-01 17:45:09.188657999 +0800 CST <nil>
2020-01-01 18:12:33.624035418 +0800 CST <nil>
2020-01-01 11:52:49.860912859 +0800 CST <nil>
2020-01-01 17:38:02.510787248 +0800 CST
2020-01-01 23:26:49.313593804 +0800 CST <nil>
2020-01-01 17:36:48.811488747 +0800 CST
2020-01-01 23:26:49.313553571 +0800 CST <nil>
```
@@ -571,9 +608,9 @@ func main() {
人马座
方位角: 120.19477090015224 高度角: 2.4014437419430097 天顶距: 87.59855625805699
0.983292937163176
赤经: 23h17m53.15s 赤纬: -10°19′18.57″
赤经: 23h18m56.24s 赤纬: -10°20′54.42″
宝瓶座
方位角: 67.84050700509859 高度角: -45.13425530765482 天顶距: 135.13425530765483
方位角: 67.63889332004852 高度角: -45.34916937173283 天顶距: 135.34916937173284
404238.6096080479
```
@@ -649,8 +686,8 @@ north=2026-01-02T08:10:49Z dec=28.266373
south=2026-01-16T05:15:14Z dec=-28.304184
libration lon=-1.278902 lat=-6.531444 pa=-9.967050
bright limb=267.364849
topo libration lon=-2.010562 lat=-5.912181 pa=-10.184664
topo bright limb=266.045494
topo libration lon=-1.736754 lat=-5.780730 pa=-10.072846
topo bright limb=266.038258
```
如果只关心某一时刻地球轨道偏心率,也可以直接调用:
@@ -675,7 +712,7 @@ fmt.Println(moon.AscendingNode(nodeDate), moon.DescendingNode(nodeDate))
以上面 `nodeDate := 2026-01-01 00:00:00 UTC` 的示例来说,输出结果是:
```text
340.9570862454423 160.95708624544227
340.95708624505863 160.9570862450587
```
#### 月相
@@ -711,7 +748,7 @@ func main() {
输出结果:
```
0.300041309608744 // 月面约有 30% 被太阳照亮
0.30004130960877884 // 月面约有 30% 被太阳照亮
上峨眉月 // 当前月相描述
2020-01-25 05:41:58.271192908 +0800 CST // 下一次朔月
2020-01-03 12:45:23.229190707 +0800 CST // 下一次上弦
@@ -778,21 +815,28 @@ func main() {
`SolarEclipsePartialFootprintsInfo` 还给出影锥与地球的全球接触:`P1/P4` 是半影外切,`P2/P3` 是半影内切;`U1/U4` 是本影或反本影外切,`U2/U3` 是内切。某次日食不存在的接触保持 `time.Time` 零值。`CentralBeginOnEarth` / `CentralEndOnEarth` 仍表示影轴进入和离开地球,不等同于 `U1/U4`。
需要结构化的瞬时中心影轮廓时,可在 `SolarEclipsePartialFootprintOptions` 中设置 `CentralShadowStep`;结果写入 `CentralShadowFootprints`。零值关闭该额外计算,SVG 入口则默认按 10 分钟采样。
需要结构化的瞬时中心影轮廓时,可在 `SolarEclipsePartialFootprintOptions` 中设置 `CentralShadowStep`;结果写入 `CentralShadowFootprints`。零值关闭该额外计算;SVG 入口同样只在正值时采样(小于一分钟按一分钟),零值或负值都不画。
需要在数据层直接取等时线时,可在同一个 `SolarEclipsePartialFootprintOptions` 中设置 `GreatestTimeValues` 或 `GreatestTimeStep`。`GreatestTimeValues []time.Time` 是**食甚时刻取值**,按绝对时刻使用(其 `Location` 不参与换算),最多保留 64 条:重复的时刻取值与偏食可见窗口之外的时刻取值会被跳过,其余按时间先后排序,超出时保留最早的 64 条;没有可用支路的时刻取值不会出现在结果里。它为空时改用 `GreatestTimeStep` 按间隔生成,间隔只在为正值时生效,且对齐到 UTC 整刻度;要按展示时区对齐,请自行生成时刻后传给 `GreatestTimeValues`。
结果写入 `SolarEclipsePartialFootprintsInfo.GreatestTimeContours`:`JDE` 是对应的力学时儒略日,`Time` 是该时刻取值在输入时区下的时刻(显式传入的时刻取值原样回显,按步长生成时由 `JDE` 换算并抹到毫秒,避免往返把整分截断成前一分钟),`Segments` 是该时刻的等时线支路。等时线只出现在日月盘面确有重叠且太阳在几何地平以上(不含蒙气差与半径修正)的地方,两端止于地平线或偏食可见域边界;纬度 ±88° 以上不再延拓,同一时刻可能有多条互不相连的支路。不请求时既有输出完全不变。
日食结果 `SolarEclipseInfo`、`LocalSolarEclipseInfo`,以及 `SolarEclipsePath` / `SolarEclipsePartialFootprintsInfo` 里的 `Eclipse` 字段还会附带沙罗序列信息:
- `HasSaros`:是否成功匹配到沙罗序列
- `Saros.Series`:NASA 沙罗系列号
- `Saros.Series`:`Verified=true` 时为 NASA 沙罗系列号,否则为推算的暂定系列号
- `Saros.Member`:这次日食在该系列中的第几个成员,从 `1` 开始
- `Saros.Count`:该沙罗系列的总成员数
- `Saros.Verified`:是否已与内置权威目录锚点核验;扩展表或范围外推算结果为 `false`
说明:
- 沙罗周期约为 `6585.321` 天,也就是 `223` 个朔望月,常写作约 `18 年 11 天 8 小时`。经过一个沙罗周期后,太阳、地球、月球的相对几何关系接近重复,因此会出现性质相近的一次日食。
- 沙罗周期约为 `6585.321` 天,也就是 `223` 个朔望月、约 `18 年 11 天 8 小时`;系列成员按此周期排列。
- 沙罗系列是一组按沙罗周期连续排列的日食事件;`Series` 标识该组,`Member` / `Count` 表示当前事件在该组中的序号和总数。
- 沙罗序列属于整场日食事件,不随观测地点改变,所以全局日食、站心日食、中心路径和偏食足迹中的对应值应当一致。
- 例如 `2024-04-08` 北美日全食属于 `Solar Saros 139` 的第 `30/71` 个成员。
- 内置 NASA 锚点优先使用正式编号;天文年份 `-3000` 至 `+6000` 年内(含首尾年,`0` 年为公元前 1 年)未被锚点覆盖的事件使用预计算扩展表,范围外才实时演算外推。预计算与实时推算结果的 `Verified` 都是 `false`,不应视为实际已发布编号。
- 扩展编号沿用 NASA 的 [Saros/Inex 编号关系](https://eclipse.gsfc.nasa.gov/SEsaros/SEperiodicity.html),成员按 Split-K 模型计算,计数覆盖完整系列,不在预计算年份边界截断。`3288-11-15` 的推算结果为系列 `202`、第 `1/71` 个成员。
- 例如 `2024-04-08` 北美日全食属于 `日食沙罗序列139` 的第 `30/71` 个成员。
##### 与 NASA 资料的时间对照
@@ -813,11 +857,11 @@ func main() {
- 全局日食资料通常给到秒,适合直接做秒级对照。
- 很多站心日食页面的初亏、复圆和本地食甚只公开到整分钟,因此这类资料只按分钟级核对,公开资料舍入造成的残差不按秒级误差解读。
- 下面的 2009 洋山和 2012 厦门示例主要展示接口调用和 SVG 输出;如果要把某个具体观测点的接触时刻用于正式发布,建议再拿该点的 NASA/IMCCE local circumstances 做逐项核对。
- 下面的 2009 洋山和 2012 厦门示例只展示接口调用与 SVG 输出,未承诺地方接触时刻的发布级精度;需要逐项核对时可与 NASA/IMCCE local circumstances 比对。
##### 2009 年长江大日食:长江口洋山附近
2009-07-22 是国内常说的“长江大日食”。下面示例选用上海东南方长江口洋山附近的观测点,接近中心线,食甚时日月中心非常接近,全食持续约 5 分 57 秒。
2009-07-22 “长江大日食”。下面示例选用上海东南方长江口洋山附近的观测点,接近中心线,全食持续约 5 分 57 秒。
```go
package main
@@ -863,15 +907,15 @@ func main() {
```text
true total // 洋山站点当天命中日食,食型为日全食
true {136 37 71} // Solar Saros 136,第 37/71 个成员
2009-07-22 08:23:54.85276848 +0800 CST // 初亏
2009-07-22 09:37:22.978325486 +0800 CST // 全食开始
2009-07-22 09:40:20.771768689 +0800 CST // 食甚
2009-07-22 09:43:19.611152708 +0800 CST // 全食结束
2009-07-22 11:03:13.974365293 +0800 CST // 复圆
5m56.632827222s // 全食持续时间
true {136 37 71 true} // Solar Saros 136,第 37/71 个成员,已核验
2009-07-22 08:23:54.852366149 +0800 CST // 初亏
2009-07-22 09:37:22.978486418 +0800 CST // 全食开始
2009-07-22 09:40:20.771366357 +0800 CST // 食甚
2009-07-22 09:43:19.610750377 +0800 CST // 全食结束
2009-07-22 11:03:13.974526226 +0800 CST // 复圆
5m56.632263959s // 全食持续时间
magnitude=1.076997 obscuration=1.000000 altitude=57.292 // 食分、遮掩比例、食甚太阳高度
greatest lon=144.1177 lat=24.2193 width=258.3km center=268 // 全局食甚点经纬度、食带宽度、中心线采样点数
greatest lon=144.1177 lat=24.2193 width=258.3km center=289 // 全局食甚点经纬度、食带宽度、中心线采样点数
```
##### 2012 年日环食:厦门示例
@@ -909,13 +953,13 @@ func main() {
```text
true annular // 厦门站点当天命中日食,食型为日环食
true {128 58 73} // Solar Saros 128,第 58/73 个成员
2012-05-21 05:08:12.683185637 +0800 CST // 初亏
2012-05-21 06:08:15.570583641 +0800 CST // 环食开始
2012-05-21 06:10:25.164288282 +0800 CST // 食甚
2012-05-21 06:12:34.763746261 +0800 CST // 环食结束
2012-05-21 07:20:55.029697716 +0800 CST // 复圆
4m19.19316262s // 环食持续时间
true {128 58 73 true} // Solar Saros 128,第 58/73 个成员,已核验
2012-05-21 05:08:12.683024704 +0800 CST // 初亏
2012-05-21 06:08:15.570422708 +0800 CST // 环食开始
2012-05-21 06:10:25.156724452 +0800 CST // 食甚
2012-05-21 06:12:34.764188826 +0800 CST // 环食结束
2012-05-21 07:20:55.029536783 +0800 CST // 复圆
4m19.193766118s // 环食持续时间
magnitude=0.933290 obscuration=0.872480 altitude=9.567 // 食分、遮掩比例、食甚太阳高度
```
@@ -1007,8 +1051,8 @@ func main() {
true 13460 // 洋山日全食 SVG 生成成功,长度 13460 字节
true 13377 // 厦门日环食 SVG 生成成功,长度 13377 字节
true total // 北京站点当天命中日食,食型为日全食
true {145 23 77} // Solar Saros 145,第 23/77 个成员
1m33.329527975s // 北京市区近似坐标下的全食持续时间
true {145 23 77 true} // Solar Saros 145,第 23/77 个成员,已核验
1m33.329527974s // 北京市区近似坐标下的全食持续时间
true 13424 // 北京日全食 SVG 生成成功,长度 13424 字节
```
@@ -1025,7 +1069,7 @@ true 13424 // 北京日全食 SVG 生成成功,长度 13424 字节
本库的月食判断与搜索能力统一放在 `eclipse` 包,返回结果会保持传入 `time.Time` 的时区。
常用接口:
- `LunarEclipseOnDate`:判断某个当地日期是否与整场月食重叠
- `LunarEclipseOnDate`:判断某个当地日期是否有月食
- `LastLunarEclipse` / `NextLunarEclipse` / `ClosestLunarEclipse`:搜索全局月食
- `LocalLunarEclipseOnDate`:判断某地当天是否能看到可见月食
- `LastLocalLunarEclipse` / `NextLocalLunarEclipse` / `ClosestLocalLunarEclipse`:搜索某地可见月食
@@ -1043,23 +1087,25 @@ true 13424 // 北京日全食 SVG 生成成功,长度 13424 字节
其中 `Saros` 的含义与日食部分相同:
- `Saros.Series`:NASA 月食沙罗系列号
- `Saros.Series`:`Verified=true` 时为 NASA 月食沙罗系列号,否则为推算的暂定系列号
- `Saros.Member`:这次月食在该系列中的第几个成员,从 `1` 开始
- `Saros.Count`:该沙罗系列的总成员数
- `Saros.Verified`:是否已与内置权威目录锚点核验;扩展表或范围外推算结果为 `false`
例如 `2028-12-31 / 2029-01-01` 这次跨年月全食属于 `Lunar Saros 125` 的第 `49/72` 个成员。
月食同样优先使用 NASA 锚点,天文年份 `-3000` 至 `+6000` 年内查扩展表,范围外才实时演算。推算成员按 Danjon 与 Chauvenet 检出的事件并集计数,因此不随调用的月食模型或观测地点改变;极浅成员可能与 NASA 目录不同,`Verified` 保持 `false`。
例如 `2028-12-31 / 2029-01-01` 这次跨年月全食属于 `月食沙罗序列125` 的第 `49/72` 个成员。
当前同时保留两套地影放大口径:
- **Danjon(默认,推荐)**:只对月球水平视差项乘 `1.01`,再与太阳视半径、太阳视差组合求影半径。NASA GSFC 当前月食目录与图页采用的也是这一路线,本库默认的 `LunarEclipseOnDate`、`LastLunarEclipse`、`NextLunarEclipse`、`ClosestLunarEclipse` 都使用它。
- **Danjon(默认)**:只对月球水平视差项乘 `1.01`,再与太阳视半径、太阳视差组合求影半径。NASA GSFC 当前月食目录与图页采用的也是这一路线,本库默认的 `LunarEclipseOnDate`、`LastLunarEclipse`、`NextLunarEclipse`、`ClosestLunarEclipse` 都使用它。
- **Chauvenet(兼容口径)**:先取 `0.99834 × 地球赤道半径`,再把整组影半径统一乘 `51/50`。这与传统旧历表口径更接近,适合做兼容性回归和旧结果对照。
两者的直接差异通常表现为:
- `Chauvenet` 给出的半影和本影都更大,半影食分通常比 `Danjon` 多约 `0.025`,本影食分通常多约 `0.005`
- 对边界月食而言,`Chauvenet` 更容易把结果推向“更深”的食型
- 若目的是与 NASA 目录、现代星历软件或当前主流月食资料对照,优先使用默认的 `Danjon`
- 若目的是兼容既有历史基线,可显式调用 `Chauvenet`
- 与 NASA 目录、现代星历软件或当前主流月食资料对照时,对应的是默认的 `Danjon`
- 兼容既有历史基线时,对应的是显式调用的 `Chauvenet`
##### 代码示例
@@ -1105,16 +1151,16 @@ func main() {
```text
total
true {125 49 72}
true {125 49 72 true}
2028-12-31 16:52:05.566135346 +0000 UTC
2.273989043382249 1.2461142882946992
2.2739890433790566 1.2461142882915068
2028-12-31 14:03:54.219463169 +0000 UTC
2028-12-31 15:07:42.115980684 +0000 UTC
2028-12-31 16:16:27.24464178 +0000 UTC
2028-12-31 17:27:46.214954853 +0000 UTC
2028-12-31 18:36:32.251235246 +0000 UTC
2028-12-31 19:40:11.52023971 +0000 UTC
2.2996033397593934 1.2511710895700923
2.2996033397562012 1.2511710895669002
true
total
2029-01-01 00:52:05.566135346 +0800 CST
@@ -1158,6 +1204,8 @@ total
##### 月食 SVG
`LunarEclipseSVG`、`LunarEclipseDetailedSVG` 与 `LunarEclipseMapSVG` 的默认模型与后缀入口口径见下文[全球见食图 SVG](#全球见食图-svg)。
默认月食 SVG 头部会自动带上沙罗序列;如果需要自定义更多文字,可以通过 `LunarEclipseSVGOptions` 覆写:
- `Title`:主标题
@@ -1301,13 +1349,28 @@ func main() {
```text
进贤增九 total
2025-06-05 19:14:01.095 CST 2025-06-05 20:02:06.357 CST 2025-06-05 20:50:10.740 CST
2025-06-05 19:14:01.062 CST 2025-06-05 20:02:06.296 CST 2025-06-05 20:50:10.697 CST
altitude=75.561 visible=true
2025-06-05 17:45:28.498 CST 2025-06-05 20:02:06.332 CST 2025-06-05 22:18:49.977 CST
greatest=121.566021 6.807046 width=3582.4km center=108
2025-06-05 17:45:28.475 CST 2025-06-05 20:02:06.300 CST 2025-06-05 22:18:49.945 CST
greatest=121.566140 6.807079 width=3582.4km center=108
```
`OccultationSearchOptions` 的零值使用默认搜索步长和安全余量;`MaxEvents > 0` 限制返回数量。`OccultationPathOptions.Step` 控制基础时间采样,`TargetSpacingKM` 按地面距离自适应加密中心线;过密请求超出确定性预算时返回 `ErrOccultationPathSamplingLimit`。
`OccultationSearchOptions` 的零值使用默认搜索步长和安全余量;`MaxEvents > 0` 限制返回数量。`OccultationPathOptions.Step` 控制基础时间采样,`TargetSpacingKM` 按地面距离自适应加密中心线;过密请求超出确定性预算时返回 `ErrOccultationPathSamplingLimit`。`RiseSetStep` 独立控制初掩、掩甚、终掩分别发生在月升/月落时的六类阶段线,零值使用 5 分钟;`DisableRiseSet` 可跳过这些阶段线。`DisableFootprints` 跳过体积较大的密集瞬时可见区要素,改用稀疏支撑样本合并成紧凑掩带;中心线、边界和六类升落阶段线仍保留,适合普通 GeoJSON 地图(首次渲染需合并一次,重复渲染走缓存)。`GreatestLimitSeparationKM` 是掩甚处南北限的地面间距,掩星图与详细版的"掩带宽"用它标注,与 `Greatest.WidthKM` 口径不同、不可互换。`IncludeFootprintTimeline` 可在紧凑掩带之外保留按 `FootprintTimelineStep` 采样的瞬时足迹,供时间轴选择当前时刻的可见区域。
`OccultationPathOptions.Algorithm` 控制恒星和行星全球路径的星历分支:零值或 `moon.OccultationPathAlgorithmOptimized` 默认使用经抽检的 30 分钟节点矢量插值,保留现有站心方程、连续包络和升落曲线;`moon.OccultationPathAlgorithmExact` 保留原有分支,候选可使用插值,最终求解仍使用全项星历。优化分支在抽检不合格时回退到原分支,超出插值时间窗时使用精确星历。抽检不是全时段严格误差证明;两个分支的几何目标相同,但不保证采样点或 GeoJSON 字节完全相同。此选项不影响仅查询事件、指定站点接触或独立单时刻月影接口,也不影响日月食。
两个分支的全球起止、掩甚标记和中心线宽度均保留全项星历计算。绘图时应传入完整返回路径,包括可见性轮廓;丢弃该轮廓会调用历史瞬时足迹回退逻辑,其边界不能替代完整解析可见集。
路径中的 `BandContours` 是静态掩带的接触包络,`VisibilityContours` 是月亮处于地平线以上时的可见时间包络;两者与 `Footprints` 的瞬时采样分别承担静态边界、可见性边界和时间轴细节,不应互相替代。
`OccultationPathOptions.GreatestTimeValues` / `GreatestTimeStep` 请求**掩甚时刻等时线**。与日食不同,`GreatestTimeValues []float64` 给的是力学时儒略日,最多保留 64 条(先去掉重复的时刻取值,按时间先后排序,超出时保留最早的 64 条),掩可见窗口之外或没有可用支路的时刻取值不会出现在结果里;它为空时改用 `GreatestTimeStep`,同样只在为正值时生效,且对齐到 UTC 整刻度。结果写入 `StarOccultationPath.GreatestTimeContours`(行星路径是同名字段),元素类型 `OccultationGreatestTimeContour` 的 `JDE`、`Time`、`Segments` 与日食同义:`Time` 在按步长生成时是原始对齐时刻,显式给出的时刻取值则由 `JDE` 换算并抹到毫秒,两者都落在 UTC 时区,而支路点的时刻仍按路径时区;日食公共层的 `Time` 则直接落在输入时区。边界口径同样一致:只出现在目标盘面与月面确有重叠且月亮在几何地平以上(不含蒙气差与半径修正)的地方,两端止于地平线或掩可见域边界,纬度 ±88° 以上不再延拓,同一时刻可能有多条互不相连的支路;不请求时既有输出不变。
```go
options := moon.OccultationPathOptions{
Algorithm: moon.OccultationPathAlgorithmExact, // 显式选择原分支;省略时使用优化分支
DisableFootprints: true,
}
```
#### 行星月掩
@@ -1346,14 +1409,14 @@ func main() {
```text
Saturn total true
2025-02-01 11:29:09.692 CST
2025-02-01 11:29:40.042 CST
2025-02-01 12:00:48.729 CST
2025-02-01 12:32:46.388 CST
2025-02-01 12:33:18.285 CST
2025-02-01 11:29:09.710 CST
2025-02-01 11:29:40.069 CST
2025-02-01 12:00:48.747 CST
2025-02-01 12:32:46.415 CST
2025-02-01 12:33:18.312 CST
```
`FindPlanetOccultationPaths` 的全球结果同时包含任意圆盘重叠的部分掩区域和整颗行星被遮住的全掩区域。`HasTotalBand` 表示是否存在全掩带,`GreatestTotalWidthKM` 是掩甚处全掩带宽;中心线、边界和瞬时足迹都带采样时刻。
`FindPlanetOccultationPaths` 的全球结果同时包含任意圆盘重叠的部分掩区域和整颗行星被遮住的全掩区域。`HasTotalBand` 表示是否存在全掩带,`GreatestTotalWidthKM` 是掩甚处全掩带宽;中心线、边界和启用时的瞬时足迹都带采样时刻。
#### 月掩 SVG
@@ -1372,33 +1435,19 @@ localSVGs, err := moonsvg.FindLocalStarOccultationSVGs(
moonsvg.LocalStarOccultationSVGOptions{Width: 920, Height: 700, Location: cst},
)
fmt.Println(err, len(localSVGs))
globalSVGs, err := moonsvg.FindStarOccultationSVGs(
start, end, target,
moon.OccultationPathOptions{Step: 5 * time.Minute, TargetSpacingKM: 200},
moonsvg.StarOccultationSVGOptions{
Width: 1200, Height: 800, Location: cst,
TimeLabelStep: 30 * time.Minute,
},
)
fmt.Println(err, len(globalSVGs))
```
本地图按指定观测者的站心几何绘制。全球图使用 Natural Earth `1:50m` 海岸线,不含行政边界;默认每 30 分钟在中心线上标记 `HH:MM`,高纬事件可自动切换极区投影。
下面两张图沿用前文 `2025-06-05` 月掩进贤增九(HR 4799)的样例。局地图的观测点为 `121.56601°E, 6.80706°N`,靠近全球几何掩甚点;图中的掩始、掩甚和掩终是该地点实际看到的站心接触时刻,并同时给出月面方向、白道、月高、方位和地平可见性。
本地图按指定观测者的站心几何绘制,下图沿用前文 `2025-06-05` 月掩进贤增九(HR 4799)的样例。局地图的观测点为 `121.56601°E, 6.80706°N`,靠近全球几何掩甚点;图中的掩始、掩甚和掩终是该地点实际看到的站心接触时刻,并同时给出月面方向、白道、月高、方位和地平可见性。
![2025 月掩进贤增九指定地点见掩图](doc/lunar-occultation-hr4799-2025-06-05-local.svg)
全球图展示同一事件的掩带边界、可见/几何中心线、全球阶段点以及每 30 分钟的中心线时间标记。全球掩始和掩终表示月影首次接触和最后离开地球,并不是上述指定地点的接触时刻。
![2025 月掩进贤增九全球见掩图](doc/lunar-occultation-hr4799-2025-06-05-global.svg)
### 天象地图与 GeoJSON
### 天象图与 GeoJSON
#### 全球见食图 SVG
`eclipse/svg` 可直接生成日食和月食全球图。日食图绘制完整偏食可见区、全食/环食中心带、中心线、全球阶段信息和中心线时间标记;同时显示食甚时的晨昏圈与日下点、影轴进出地球点、`P1-P4/U1-U4` 接触点、定时半影轮廓和本影/反本影轮廓。月食图绘制 P1/P4 可见半球、月出/月落过渡区和整场可见区。
`eclipse/svg` 可直接生成日食和月食全球图。日食图绘制完整偏食可见区、全食/环食中心带、中心线、全球阶段信息和中心线时间标记;同时显示初亏/食甚/复圆的日升日落线、太阳直射点、影轴进出地球点、`P1-P4/U1-U4` 接触点,以及默认关闭、按需打开的定时半影轮廓和本影/反本影轮廓。月食图绘制 P1/P4 可见半球、月出/月落过渡区和整场可见区。
`LunarEclipseDetailedSVG` 把上述两类月食图合成详细版式的一页:居中摘要(食甚、半影/本影食分、伽马、半影/本影半径、月距、沙罗序列)、左右两侧的日月地心坐标块、穿影示意图、历时 / 弧分比例尺 / 接触时刻三栏,以及下方的世界可见性底图与图例。地影几何由 `basic.LunarEclipseShadowGeometryAt` 给出,其中 **Gamma 用地球赤道半径、半影/本影半径用度**,换成地球半径要乘以月球处的地球视差。
```go
package main
@@ -1417,9 +1466,7 @@ func main() {
time.Date(2009, 7, 22, 12, 0, 0, 0, cst),
eclipsesvg.SolarEclipseMapSVGOptions{
Width: 1200, Height: 800, Location: cst,
TimeLabelStep: 30 * time.Minute,
PenumbralOutlineStep: 60 * time.Minute,
CentralShadowStep: 10 * time.Minute,
TimeLabelStep: 30 * time.Minute,
},
)
if ok {
@@ -1433,21 +1480,56 @@ func main() {
if ok {
_ = os.WriteFile("doc/lunar-eclipse-2029-01-01-global.svg", []byte(lunar), 0o644)
}
detailed, ok := eclipsesvg.LunarEclipseDetailedSVG(
time.Date(2029, 1, 1, 0, 0, 0, 0, cst),
eclipsesvg.LunarEclipseDetailedSVGOptions{Location: cst},
)
if ok {
_ = os.WriteFile("doc/lunar-eclipse-2029-01-01-detailed.svg", []byte(detailed), 0o644)
}
}
```
日食图右侧事件表按时间列出可用的 `P1-P4/U1-U4`、影轴进出地球和食甚时刻,空间允许时同时显示接触点经纬度;摘要还包含沙罗序列、食带宽、食甚点太阳高度/方位和中心食持续时间。图中的橙色虚线是带 `HH:MM` 标记的瞬时半影边界,灰色虚线是食甚时晨昏圈,棕色实线是本影或反本影瞬时轮廓。
图上的橙色长虚线是初亏/食甚/复圆分别发生在日出和日落时的六类阶段线;**瞬时半影与本影轮廓默认不画**,需要时用正的 `PenumbralOutlineStep` / `CentralShadowStep` 打开。紫色短虚线是 `MagnitudeValues` 指定的地方最大食分等值线(默认 0.2/0.4/0.6/0.8),蓝色实线是食甚时刻等时线。
`PenumbralOutlineStep` 与 `CentralShadowStep` 的零值分别使用 60 分钟和 10 分钟,负值关闭对应轮廓;显式设置仍可使用 30 分钟等其他间隔。`TimeLabelStep` 的零值为 30 分钟,负值关闭中心线时刻标记。
**食甚时刻等时线**(蓝色实线)默认不画:同一条线上的地点在同一时刻看到食甚,需要时用正的 `GreatestTimeStep` 打开,NASA 全球图的间隔是 30 分钟。这里的 `GreatestTimeStep` 属于 SVG 层,按**展示时区**(`Location`)对齐整刻度,与数据层的 UTC 对齐不同;`eclipse/svg` 不提供显式时刻取值入口,需要别的对齐刻度时请直接调用数据层并把时刻传给 `GreatestTimeValues`。
```go
solar, _ := eclipsesvg.SolarEclipseMapSVG(date, eclipsesvg.SolarEclipseMapSVGOptions{
Width: 1200, Height: 800, Location: cst,
GreatestTimeStep: 30 * time.Minute,
})
```
它不是在经纬度网格上逐点求食甚再描等值线,而是固定时刻后求解 `∂(日月中心角距²)/∂t = 0` 的零集,再沿曲线延拓,因此成本正比于曲线长度而不是可见域面积。等时线只画在日月盘面确有重叠且太阳在几何地平以上(不含蒙气差与半径修正)的地方,每条支路止于地平线或偏食可见域边界;纬度 ±88° 以上不再延拓,同一时刻可能有多条互不相连的支路。
`TimeLabelStep` 的零值为 30 分钟,负值关闭中心线时刻标记。`GreatestTimeStep` 的零值与负值都不画食甚时刻等时线(与核心层、`moon/svg` 一样必须显式请求),正值按展示时区对齐、小于一分钟时按一分钟处理,单次最多生成 64 条;30 分钟是 NASA 全球图的推荐间隔。`MagnitudeValues` 为 nil 时使用 0.2/0.4/0.6/0.8,显式空切片关闭,非空切片按给定电平绘制。
`SolarEclipseMapSVGOptions.EventsTitle` 覆盖“全球阶段”数据块的标题(该块给出食甚经纬度与地球范围的中心食始/终),为空时使用本地化默认标题;`MapTitle` 与 `Title` 分别覆盖地图分区标题与主标题。
日食图的画布下限是 **800×560**:宽度小于 800 或高度小于 560 时按文档回落到 960×640,更窄的横版画布上地图框会与右栏数据网格水平重叠、面板行距压到 1 px 以下。`PartialStep` 小于两分钟时按两分钟处理:偏食区填充是瞬时足迹的并集,成本随采样数成倍增长,而并集必须由一整条自洽的扫描序列生成,更密的请求不改变产物(1 秒步长实测 36.8 s / 951 MB,夹取后为 1.1 s / 30 MB,与默认请求逐字节相同)。月食详细版式按 `Height` 推导版面:640×420 与 800×600 容不下示意图与底图的下限而返回 `false`,1000×1414 与 1414×1000 正常出图。
`eclipse/svg` 的三个无后缀月食入口(`LunarEclipseSVG`、`LunarEclipseDetailedSVG`、`LunarEclipseMapSVG`)使用同一个默认模型:以 Danjon 为主,极浅半影按核心默认口径回退 Chauvenet,与 `LunarEclipseOnDate` 一致;带 `Danjon` / `Chauvenet` 后缀的入口强制指定模型。
可降级的图层用 `data-source` 标注实际几何来源,取值词表见 `eclipse/svg` 包注释:`partial-band-union`、`sampled-footprint-sweep`、`partial-band-contours`、`rise-set-phase-lines`、`magnitude-contours`、`greatest-time-isochrones`、`besselian-critical-envelope`、`paired-limit-chords`、`sampled-open-sweep`、`central-path-limits`、`penumbral-outlines`、`central-shadow-outlines`、`p1-p4-visibility-regions`、`p1-p4-horizon-boundaries`。`PenumbralOutlineStep` 与 `CentralShadowStep` **默认关闭**(零值或负值都不画瞬时半影/本影轮廓,它们会把地球盖住,NASA 全球图也没有这两族),正值给出采样间隔,小于一分钟时按一分钟。
日食和月掩的自动投影会在适合时选择北极或南极图;月食默认使用等经纬投影。投影仅影响 SVG 表达,不改变底层 WGS84 地理结果。
日月食通过 `EclipseMapProjectionEquirectangular`、`EclipseMapProjectionNorthPolar`、`EclipseMapProjectionSouthPolar` 强制投影;月掩使用对应的 `MapProjection...` 常量。
日月食通过 `EclipseMapProjectionEquirectangular`、`EclipseMapProjectionNorthPolar`、`EclipseMapProjectionSouthPolar` 强制投影;月掩使用对应的 `MapProjection...` 常量。`EclipseMapProjectionOrthographic` 给出 NASA 版式的**正射球面图**:视点取食甚点,只画朝向视点的半个地球,投影边界就是可见半球的大圆。
球面图不需要新增数据,也不需要第三方投影库:陆地由内置的等经纬底图在运行时反解回经纬度再正射投影,视界裁剪在地理坐标上按大圆求交、并沿视界弧补齐被切断的环;经纬网按球面采样后同样裁剪。
正射投影同时切换成 **NASA 摆法**的版式:球面居中放大,比例尺排在球面正下方,阶段信息改为三栏面板(半影接触 / 食甚点地方情况 / 本影接触),图例与页脚依次向下;其他投影保持原有版式。代价是 `1000×1414` 画布下整幅图约 1.0 s(等经纬图约 0.85 s),一个同尺寸的球面图 SVG 约 530 KB;耗时为单机实测参考值,绝对值因机器而异。
下面的全球图沿用前文局地 SVG 的事件日期。2009 长江大日食、2012 厦门日环食和 2035 北京日全食使用等经纬投影:
![2009 长江大日食全球见食图](doc/solar-eclipse-yangshan-2009-global.svg)
同一场日食的正射球面版式(`EclipseMapProjectionOrthographic`):
![2009 长江大日食正射球面图](doc/solar-eclipse-yangshan-2009-globe.svg)
![2012 厦门日环食全球见食图](doc/solar-eclipse-xiamen-2012-global.svg)
![2035 北京日全食全球见食图](doc/solar-eclipse-beijing-2035-global.svg)
@@ -1460,6 +1542,58 @@ func main() {
![2029 跨年月全食全球可见图](doc/lunar-eclipse-2029-01-01-global.svg)
月食还有把上面两类图合二为一的详细版式:居中摘要(食甚、半影/本影食分、伽马、半影/本影半径、月距、沙罗序列)、左右两侧的日月地心坐标块、穿影示意图、历时 / 弧分比例尺 / 接触时刻三栏,以及下方的世界可见性底图与图例,一页 `1000x1414`:
![2029 跨年月全食详细版式](doc/lunar-eclipse-2029-01-01-detailed.svg)
#### 月掩详细版式 SVG
`moon/svg` 的详细版式把整场月掩合成一页 `1000x1414`:居中摘要、日月与目标天体的地心/站心数据块、一张**正射球面**的全球掩带图(南北限、可见/几何中心线、掩甚点、初掩/掩甚/终掩阶段点与 30 分钟时间标记),以及页脚说明。球面视点取事件中心,只画朝向视点的半球,这一版式固定用正射球面,不接受其它投影;只需要单独的全球掩带地图时用前文“月掩 SVG”的 `StarOccultationPathSVG` / `FindStarOccultationSVGs`。页内数据分为月亮地心坐标、目标天体、掩带路径点、接触时刻、历表与常数、天平动六块;横版画布把数据块排在地图右侧两栏三行,竖版把数据块排在球面下方三栏两行。下图是 `2025-06-05` 月掩 HR 4799:
```go
package main
import (
"os"
"time"
"b612.me/astro/moon"
moonsvg "b612.me/astro/moon/svg"
)
func main() {
cst := time.FixedZone("CST", 8*3600)
star := moon.StarCoordinate{
ID: "HR 4799", RA: 189.1975, Dec: -5.831944444444,
Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: moon.CoordinateFrameJ2000,
ProperMotionRACosDecMasPerYear: -28, ProperMotionDecMasPerYear: -18,
}
paths, err := moon.FindStarOccultationPaths(
time.Date(2025, 6, 5, 0, 0, 0, 0, cst),
time.Date(2025, 6, 6, 0, 0, 0, 0, cst),
star,
moon.OccultationPathOptions{Step: 5 * time.Minute, TargetSpacingKM: 200},
)
if err == nil && len(paths) > 0 {
detailed, renderErr := moonsvg.StarOccultationDetailedSVG(
paths[0], star,
moonsvg.OccultationDetailedSVGOptions{Width: 1000, Height: 1414, Location: cst},
)
if renderErr == nil {
_ = os.WriteFile("doc/lunar-occultation-hr4799-2025-06-05-detailed.svg", []byte(detailed), 0o644)
}
}
}
```
![2025 月掩 HR 4799 详细版式](doc/lunar-occultation-hr4799-2025-06-05-detailed.svg)
页内的球面掩带图使用 Natural Earth `1:50m` 海岸线,不含行政边界。在 `moon.OccultationPathOptions` 上设置 `GreatestTimeStep` 会额外请求**掩甚时刻等时线**,含义与日食图上的蓝色等时线相同:固定时刻后求角距导数的零集并沿曲线延拓。它同样是可选项,不设置时输出不变。掩星全球可见窗口通常只有数小时(本页 HR 4799 样例为 4 小时 33 分),常用间隔比日食更密,为 15–30 分钟量级,间隔越大掩带上的等时线越少;点源恒星按日月中心角距定食甚,有限盘面行星按外接触度量,分别与库内 `StarOccultationInfo.Greatest`、`PlanetOccultationInfo.Greatest` 同口径。`moon/svg` 自己不提供等时线开关,只绘制路径结果里已有的 `GreatestTimeContours`,因此请求必须在计算路径时通过 `OccultationPathOptions` 提出,线的位置也由核心口径决定(`GreatestTimeStep` 对齐 UTC 整刻度);按展示时区对齐的入口,是把时刻换算成力学时儒略日后传给 `GreatestTimeValues`。全球掩始和掩终表示月影首次接触和最后离开地球,与指定地点的接触时刻无关。
- 画布与错误:详细版最小 `480x320`,并按画布推导版式,地图与数据块放不下时返回 `ErrInvalidOccultationDetailedSVGOptions`(`800x600`、`1000x1414`、`1414x1000` 均可出图,`640x420`、`900x400` 会被拒绝);单独的全球掩带地图最小 `640x480`,更小的画布返回 `ErrInvalidStarOccultationSVGOptions`。
图上标注的“掩带宽”是掩甚处南北限的地面间距 `GreatestLimitSeparationKM`(本例约 `3666.6 km`),与中心线横向宽度 `Greatest.WidthKM`(约 `3582.4 km`)口径不同、不可互换。掩甚时刻等时线要在路径层显式请求 `OccultationPathOptions.GreatestTimeStep`;使用 `DisableFootprints` 的紧凑掩带首次渲染会合并一次,之后同一路径走缓存。详细版式与固定地点图见前文“月掩 SVG”。
#### GeoJSON
`geojson` 接收已经计算好的日食、月食或月掩结果,返回 `[]byte`。这段字节是完整的 UTF-8 RFC 7946 `FeatureCollection` JSON,不是图片,也不是压缩数据,可以直接写入 `.geojson`、交给 `encoding/json`,或发送给前端地图组件。
@@ -1516,6 +1650,20 @@ func main() {
坐标统一为 WGS84 经度、纬度,跨反经线的线和面会拆分。带时路径的 `times` 属性与各段坐标逐点对齐;`WithTimeMarkers` 另加 `role=time-marker` 的 Point Feature,本地化 `label` 用于显示,`time` 始终是 UTC RFC 3339。
单时刻原语(拖动时间轴、"停下即精确")与站心搜索跨度:
- `eclipse.NewSolarEclipseShadowSolver(eclipse.SolarEclipseShadowSolverOptions{...})` 返回可复用句柄;`ShadowAt(time.Time)`(按 UTC 解释)或 `ShadowAtJDE(jdeTT)`(TT 语义)取该时刻的**全球本影足迹**,`StationStateAt` / `StationStateAtJDE` 取该时刻、该站点的**站心日月几何**(食分、遮蔽率、站心角距、日月视半径、太阳高度/方位、是否处于全食/环食)。两者都只算这一件事,不产生可见带、食分线、升落边界、南北界或中心线;本影不在地球上时返回空/零值而不是错误。
- `geojson.MarshalSolarEclipseShadowInstant(instant)` 只输出该时刻的阴影区域,以及被地平线切断时的物理边界;属性含 `time`、`source_boundary_closed`、`geometry_role`、`closure`、`delta_t_seconds`、`model`、`interp_signature`。本影用 `central-shadow-footprint` + `central-shadow-boundary`;把 `Kind` 设为 `SolarEclipseShadowPenumbra` 则输出半影(偏食区),角色为 `partial-footprint` + `partial-footprint-boundary`,默认参数与整包偏食采样一致(96 点 + 200 km 加密),因此同一时刻的结果与采样逐点一致(约 1e-12 度差)。没有阴影时返回空 FeatureCollection。
- 采样的 `partial-footprint` 也带 `source_boundary_closed`、`geometry_role`、`closure` 与 `interp_signature`;被地平线切断的序列端点补到地平圈擦地点,未补齐时该处的端点偏差为 `36–41 km` 量级。掩带的填充提示仍沿用旧封口,避免端点外扩改变极区面归属。
- 实测成本(原生构建,单机参考值,绝对值因机器而异):单时刻足迹(96 点)约 **64 µs**,站心瞬时约 **20 µs**;公开句柄构造只是夹取选项(≈0),首次查询时按最近朔月构造内部状态约 39 µs、锚点查询约 130 µs,随后按事件缓存;批处理约 75 µs/时刻。
- ΔT:`DeltaTSeconds` 显式指定时只作用于该句柄,且只改变地球自转相位——同一 TT 的几何不变,地面足迹沿经度平移 `0.4651·|ΔΔT|·cos(纬度)` 千米(见 `basic.DeltaTGroundShiftKM`);`<=0` 时使用进程级模型。两种情况下结果都回传实际使用的 ΔT。库不附带 ΔT 不确定度模型,请用该函数把外部的 ΔT 标准差换算成几何不确定度。
- 插值:整包的 `central-shadow-footprint` 与该时刻的单时刻导出都带 `interp_signature`(形如 `umbra-closed-seg1-pt97`,由物理边界的顶点数/分段数/闭合标志与绕极标志给出),**相同**的相邻时刻才适合按顶点插值;`closed` 翻转、段数变化(换日线拆分)、顶点数变化、空↔非空(U1/U4 附近)时必须改取精确几何。实测 2 分钟步长下中段质心移动 78–232 km,端点附近可达约 520 km。
- 批量:`ShadowBetween(start, end, step)` / `StationStatesBetween(...)` 按时间轴对齐返回整段,没有阴影的时刻是空条目。
- 掩星的单时刻足迹(`moon.StarOccultationFootprintAt` / `moon.PlanetOccultationFootprintsAt` → `geojson.MarshalStarOccultationFootprint` / `MarshalPlanetOccultationFootprints`)同样带 `delta_t_seconds`、`source_boundary_closed`、`geometry_role`、`interp_signature`;被月球地平切断时 `closure` 的 `kind` 是 `target-horizon`、`body` 是 `moon`,参照 `sublunar` 月下点而不是日下点。掩星子系统沿用进程级 ΔT,只回传实际用值,不提供显式覆盖。
- 站心搜索:`SearchLocalCentralSolarEclipse(date, lon, lat, height, eclipse.SolarEclipseLocalSearchOptions{Kind, MaxYears, Backward, Geometric, Model})` 返回 `(info, status)`,`status.Exhausted` 把"跨度内确实没有"与"找到了"分开;`MaxYears<=0` 使用与旧入口等价的默认跨度(6000 次候选步进 ≈ 992 年,因为候选会跳过非食季)。`SolarEclipseCandidates(start, end, options)` 只回时刻表(食甚时刻、食型、中心食类型、食分、伽马、可选沙罗序列),不含任何几何。
日食 GeoJSON 的中心影相关 role 是稳定契约:`role=central-shadow-footprint` 要么缺省、要么是 `Polygon`/`MultiPolygon`,永不出现线类型;被地平线切断时它仍输出该时刻地面本影(或反本影)覆盖的完整区域——物理边界延伸到两个地平擦地点,再由两擦地点之间的地平弧闭合,此时 `source_boundary_closed=false`,并由 `closure`(`kind`、`time`、`subsolar`)声明那段人工弧。只含物理边界曲线的折线另由 `role=central-shadow-boundary` 输出,调用方描它、填上面那个面即可,不会描出假的地平线边界。足迹收缩到零(U1/U4)时整条缺省,也不会退化成线。`source_boundary_closed=true` 表示边界由本影自身闭合,环上没有任何人工段。
`TimeMarkerOptions.Step` 的零值为 30 分钟,正值至少 1 分钟,每次导出最多 1440 个标记。GeoJSON 不携带底图、国家边界、样式或投影;Web Mercator、极区图、瓦片选择和政治边界由应用自行决定。
### 行星
@@ -1569,18 +1717,18 @@ func main() {
输出结果:
```
2019-11-11 23:21:42.048057317 +0800 CST // 水星上次下合
2021-03-26 14:57:43.01215589 +0800 CST // 金星下次上合
2019-11-01 04:31:38.999851942 +0800 CST // 水星上次由顺行转逆行的留
2020-06-25 02:07:41.549940705 +0800 CST // 金星下次由逆行转顺行的留
2019-10-20 11:50:28.734245896 +0800 CST // 水星上次东大距
2020-08-13 07:59:17.123789191 +0800 CST // 金星下次西大距
2020-01-01 10:02:34.172194004 +0800 CST <nil> // 西安当天金星升起时刻;无错误
2020-01-01 20:25:37.363712489 +0800 CST <nil> // 西安当天金星落下时刻;无错误
2019-11-11 23:21:41.971051096 +0800 CST // 水星上次下合
2021-03-26 14:57:42.052354216 +0800 CST // 金星下次上合
2019-11-01 04:31:49.749019145 +0800 CST // 水星上次由顺行转逆行的留
2020-06-25 02:07:41.599749326 +0800 CST // 金星下次由逆行转顺行的留
2019-10-20 12:01:37.740152478 +0800 CST // 水星上次东大距
2020-08-13 08:14:46.304587125 +0800 CST // 金星下次西大距
2020-01-01 10:02:34.172435402 +0800 CST <nil> // 西安当天金星升起时刻;无错误
2020-01-01 20:25:37.36411482 +0800 CST <nil> // 西安当天金星落下时刻;无错误
-4 // 金星视星等
49.98145049145023 // 金星相位角,单位度
0.8215177914415865 // 金星被照亮比例
255.63802093000768 // 金星亮面中心位置角,单位度
255.63802053541346 // 金星亮面中心位置角,单位度
1.2778819631550336 // 金地距离,单位 AU
0.7262651056423838 // 金日距离,单位 AU
```
@@ -1650,21 +1798,21 @@ func main() {
```text
true // 找到一次有效的地心水星凌日
2019-11-11 12:35:31.617325544 +0000 UTC // 一触:水星外切进入太阳圆面
2019-11-11 12:37:13.078211545 +0000 UTC // 二触:水星完全进入太阳圆面
2019-11-11 15:19:48.410291075 +0000 UTC // 凌甚:水星中心最接近太阳中心
2019-11-11 18:02:29.2267102 +0000 UTC // 三触:水星开始离开太阳圆面
2019-11-11 18:04:10.687676668 +0000 UTC // 四触:水星外切离开太阳圆面
2019-11-11 12:35:31.567597389 +0000 UTC // 一触:水星外切进入太阳圆面
2019-11-11 12:37:12.817581295 +0000 UTC // 二触:水星完全进入太阳圆面
2019-11-11 15:19:48.36056292 +0000 UTC // 凌甚:水星中心最接近太阳中心
2019-11-11 18:02:29.176982045 +0000 UTC // 三触:水星开始离开太阳圆面
2019-11-11 18:04:10.637948513 +0000 UTC // 四触:水星外切离开太阳圆面
5h28m39.070351124s // 一触到四触的地心凌日持续时间
75.92506897631685 // 凌甚时水星中心与太阳中心的最小角距离,单位角秒
968.8881520858397 // 凌甚时太阳视半径,单位角秒
4.978442860728242 // 凌甚时水星视半径,单位角秒
75.92400059923187 // 凌甚时水星中心与太阳中心的最小角距离,单位角秒
968.8881519533047 // 凌甚时太阳视半径,单位角秒
4.978442871670873 // 凌甚时水星视半径,单位角秒
true // 找到一次有效的地心金星凌日
2012-06-05 22:09:47.514281272 +0000 UTC // 一触:金星外切进入太阳圆面
2012-06-05 22:27:35.701768398 +0000 UTC // 二触:金星完全进入太阳圆面
2012-06-06 01:29:35.408823788 +0000 UTC // 凌甚:金星中心最接近太阳中心
2012-06-06 04:31:34.90493685 +0000 UTC // 三触:金星开始离开太阳圆面
2012-06-06 04:49:23.303366303 +0000 UTC // 四触:金星外切离开太阳圆面
2012-06-05 22:09:47.466886639 +0000 UTC // 一触:金星外切进入太阳圆面
2012-06-05 22:27:35.865356326 +0000 UTC // 二触:金星完全进入太阳圆面
2012-06-06 01:29:35.572371482 +0000 UTC // 凌甚:金星中心最接近太阳中心
2012-06-06 04:31:35.068444311 +0000 UTC // 三触:金星开始离开太阳圆面
2012-06-06 04:49:23.25597167 +0000 UTC // 四触:金星外切离开太阳圆面
6h39m35.789085031s // 一触到四触的地心凌日持续时间
```
@@ -1727,15 +1875,15 @@ func main() {
输出结果:
```
2020-10-14 07:25:50.262777507 +0800 CST // 火星下次冲日
2021-01-29 09:39:33.565426468 +0800 CST // 木星下次合日
2019-04-30 10:27:41.606289446 +0800 CST // 土星上次由顺行转逆行的留
saturn B=23.577026 Bp=23.266930 P=6.629811 dU=1.171016 major=34.133852 minor=13.652911 // 土星环 B、B'、P、dU、长轴、短轴
2020-01-11 15:23:07.378419935 +0800 CST // 天王星下次由逆行转顺行的留
2019-12-08 17:00:15.328663587 +0800 CST // 海王星上次东方照
2020-06-07 03:10:59.356176853 +0800 CST // 火星下次西方照
2020-01-01 04:41:29.622089266 +0800 CST <nil> // 西安当天火星升起时刻;无错误
2020-01-01 14:55:32.963870465 +0800 CST <nil> // 西安当天火星落下时刻;无错误
2020-10-14 07:25:50.441412627 +0800 CST // 火星下次冲日
2021-01-29 09:39:33.697994649 +0800 CST // 木星下次合日
2019-04-30 10:28:00.187439918 +0800 CST // 土星上次由顺行转逆行的留
saturn B=23.577025 Bp=23.266930 P=6.629811 dU=1.171016 major=34.133852 minor=13.652911 // 土星环 B、B'、P、dU、长轴、短轴
2020-01-11 15:23:23.360308706 +0800 CST // 天王星下次由逆行转顺行的留
2019-12-08 17:00:15.517960488 +0800 CST // 海王星上次东方照
2020-06-07 03:11:00.026179254 +0800 CST // 火星下次西方照
2020-01-01 04:41:29.621566236 +0800 CST <nil> // 西安当天火星升起时刻;无错误
2020-01-01 14:55:32.963508367 +0800 CST <nil> // 西安当天火星落下时刻;无错误
1.57 // 火星视星等
2.1844284956325937 // 地火距离,单位 AU
1.5897860004265403 // 日火距离,单位 AU
@@ -1789,7 +1937,7 @@ func main() {
```text
jupiter DS=54.342153 DE=1.436485 CMI=292.712909 CMII=276.309048 CMIII=147.241811 // 木星子日/子地赤纬,System I/II/III 中央经线,单位度
saturn B=-0.608046 Bp=-2.675677 P=4.480276 major=42.709920 minor=0.453246 // 土星环 B、B'、短轴位置角、外缘长短轴,角度单位度,长短轴单位角秒
saturn B=-0.608048 Bp=-2.675677 P=4.480276 major=42.709920 minor=0.453248 // 土星环 B、B'、短轴位置角、外缘长短轴,角度单位度,长短轴单位角秒
```
只需要中央经线时,可以单独调用 `CentralMeridians`:
@@ -1819,12 +1967,12 @@ fmt.Printf("uranus systemIII lon=%.6f lat=%.6f P=%.6f\n", ura3.SubEarthLongitude
- `LastGalileanPhenomenonEvent` / `NextGalileanPhenomenonEvent` / `ClosestGalileanPhenomenonEvent`:搜索整场现象区间
- `LastGalileanPhenomenonContactEvent` / `NextGalileanPhenomenonContactEvent` / `ClosestGalileanPhenomenonContactEvent`:搜索 IMCCE 风格的 D/F 接触事件
这里有两个容易混淆的口径,以木卫一凌日为例:
两个口径的区别如下,以木卫一凌日为例:
- `GalileanPhenomenonEvent` 把卫星看作一个点,判断“卫星圆心是否进入/离开木星圆面”。它返回整段凌日的起止区间,适合快速搜索现象和程序内部状态判断。
- `GalileanPhenomenonContactEvent` 把卫星自身的有限圆盘考虑进去,区分初亏到复圆的完整接触过程。它返回消失阶段(D)和再现阶段(R)各自的接触起止与模型中心穿越时刻,适合和 IMCCE 年表中的 `TR.D/TR.F/OC.D/OC.F/EC.D/EC.F/SH.D/SH.F` 逐项对照。
两个口径的差异在持续时间上最多约 7 分钟——这是模型定义不同造成的,不表示精度问题。用于观测预报或和公开年表逐项核对时,优先使用 `GalileanPhenomenonContactEvent`。
两个口径的差异在持续时间上最多约 7 分钟,差异来自模型定义不同。用于观测预报或与公开年表逐项核对时,取 `GalileanPhenomenonContactEvent`。
##### 代码示例
@@ -1875,15 +2023,15 @@ func main() {
输出结果:
```text
io x=-0.658543 y=-0.035608 front=true // 木卫一相对木星中心的 X/Y 偏移,单位木星半径;位于木星盘面前方
europa ra=110.769323 dec=22.335800 // 木卫二视赤经、视赤纬,单位度
io x=-0.675026 y=-0.032798 front=true // 木卫一相对木星中心的 X/Y 偏移,单位木星半径;位于木星盘面前方
europa ra=110.769133 dec=22.335828 // 木卫二视赤经、视赤纬,单位度
io transit=true occultation=false eclipse=false shadow=true // 木卫一正在凌日,且影子正在凌日
europa transit=false occultation=false eclipse=false shadow=false // 木卫二此刻无凌日、掩蔽、木星食或影凌
event valid=true sat=1 type=transit // 下一次有效事件为木卫一凌日
2026-01-16 16:32:47.785289883 +0000 UTC // 木卫一凌日开始
2026-01-16 17:40:43.882995843 +0000 UTC // 木卫一凌日中点
2026-01-16 18:48:40.519664883 +0000 UTC // 木卫一凌日结束
2h15m52.734375s // 木卫一凌日持续时间
2026-01-16 16:32:47.552742362 +0000 UTC // 木卫一凌日开始
2026-01-16 17:40:44.189371168 +0000 UTC // 木卫一凌日中点
2026-01-16 18:48:40.287077128 +0000 UTC // 木卫一凌日结束
2h15m52.734334766s // 木卫一凌日持续时间
contact valid=true sat=2 type=occultation // 下一次有效接触事件为木卫二掩蔽
2026-01-17 01:00:34.99533087 +0000 UTC // 木卫二掩蔽消失阶段开始
2026-01-17 01:02:31.714070141 +0000 UTC // 木卫二掩蔽消失阶段模型中心穿越
@@ -1903,14 +2051,14 @@ contact valid=true sat=2 type=occultation // 下一次有效接触事件为木
当前测试结果可概括为:
- `Satellites` 相对木星中心的位置,对 JPL Horizons 的样例最大偏差约为 `X=0.252"`、`Y=0.108"`。
- `Satellites` 相对木星中心的位置,对 JPL Horizons 的样例最大偏差约为 `X=0.054"`、`Y=0.048"`。
- `SatellitePhenomena` 的影凌影心偏移,对 JPL Horizons 的样例最大偏差约为 `X=0.051"`、`Y=0.016"`,现象布尔标志在样例中一致。
- `GalileanPhenomenonContactEvent` 对 IMCCE 2026 年表,当前样例中接触起止时刻最大偏差约 `72 s`,接触持续时间最大偏差约 `17 s`。
- `GalileanPhenomenonEvent` 不是 IMCCE 的 D/F 接触口径;如果拿它去直接对 IMCCE 起止时刻,当前样例会出现最多约 `7` 分钟的差异。这是事件定义不同,不应当按“时间精度差”解读。
- `GalileanPhenomenonContactEvent` 对 IMCCE 2026 年表(8 个样例、D1/D2/F1/F2 四个接触逐值对拍):接触时刻最大偏差约 `79 s`,接触持续时间最大偏差约 `17 s`,回归测试按 `120 s` / `25 s` 上限钉住。
- `GalileanPhenomenonEvent` 不是 IMCCE 的 D/F 接触口径;与 IMCCE 起止时刻直接对照时,当前样例的最大差异约 `7` 分钟,来源是事件定义不同。
### 恒星
1. 本程序自带9100颗恒星的数据库,能够自动计算自行
1. 本程序自带 9100 颗恒星的数据库(BSC / HR 编号 `1–9110`,视星等 `-1.46`~`7.96`),能够自动计算自行
```go
package main
@@ -1953,11 +2101,11 @@ func main() {
```
```
2019-12-31 19:22:56.176710426 +0800 CST // 天狼星升起时刻
2020-01-01 05:30:39.834894239 +0800 CST // 天狼星落下时刻
2019-12-31 19:22:56.144202053 +0800 CST // 天狼星升起时刻
2020-01-01 05:30:39.802506566 +0800 CST // 天狼星落下时刻
大犬座 // 天狼星所在星座
5h58m5.71s // 织女一在公元 13600 年的赤经
84°19′26.13″ // 织女一在公元 13600 年的赤纬
6h3m46.61s // 织女一在公元 13600 年的赤经
84°18′27.15″ // 织女一在公元 13600 年的赤纬
天狼 Sirius -1.46 // 最亮恒星表第一项:中文名、英文常用名、视星等
```
@@ -2010,13 +2158,13 @@ func main() {
```text
143.72223158223719 19.53512536790277
43.46959597099446 -17.686623571613737 107.68662357161374
143.99353431082105 18.7404068044953
144.2551242046188 18.790254631841993
manual az=281.869347 alt=24.489608 zen=65.510392 ha=73.866900
gal lon=0.000047 lat=-0.000079
apparent alt=10.093429
apparent alt=10.093428
```
`coord` 里的研究型接口不会自动代入当前日期的黄赤交角或恒星时,适合做“不同自转轴倾角”“手工指定时角”这类推演。常规观测计算仍建议使用 `EclipticToEquatorial`、`EquatorialToHorizontal` 等带 `time.Time` 的接口。
`coord` 里的研究型接口不会自动代入当前日期的黄赤交角或恒星时,适合做“不同自转轴倾角”“手工指定时角”这类推演。常规计算可用 `EclipticToEquatorial`、`EquatorialToHorizontal` 等带 `time.Time` 的接口。
观测辅助方面,`coord` 还提供了两类高频小工具:
@@ -2180,7 +2328,7 @@ fmt.Printf("alt=%.6f az=%.6f rise=%s\n", alt, az, rise.Format(time.RFC3339))
这些观测接口基于站心视坐标计算,适合直接拿去做小行星、彗星或自定义二体目标的升落和指向辅助。
另外,`orbit` 里还带了一个非常轻量的视双星求解器,直接按《天文算法》第 55 章的经典表观轨道公式输出位置角和角距:
`orbit` 里还带了一个视双星求解器,直接按《天文算法》第 55 章的经典表观轨道公式输出位置角和角距:
```go
gammaVir := orbit.VisualBinaryElements{
@@ -2238,9 +2386,7 @@ func main() {
- `HorizontalHourLineAngle`:给定纬度和时角,计算水平日晷相对午线的时线角
- `HorizontalHourLineAngleAt`:直接用时刻和经纬度求当前时线角
注意:
- `MeanSolarTimePoint` / `MeanSolarTimeLine` 中的 `date` 应位于目标地点的地方平太阳时区;最直接的来源就是 `MeanSolarTime(...)` 的返回值。
- `MeanSolarTimePoint` / `MeanSolarTimeLine` 的 `date` 位于目标地点的地方平太阳时区,通常是 `MeanSolarTime(...)` 的返回值。
- `ZoneTimePoint` / `ZoneTimeLine` 会忽略传入 `date` 的原有时分秒,只使用它的年月日与时区,再把钟面时间替换成参数 `zoneTimeHours`。
## 已实现
@@ -2252,8 +2398,8 @@ func main() {
- ✅ 地球偏心率、日地距离、近日点、远日点
- ✅ 真平恒星时、星座计算、常用坐标转换、大气折射、大气质量、视差角、银道坐标
- ✅ 七大行星坐标、距日距地距离、特殊天象、水星/金星地心凌日、物理星历、视直径、相位、视差角与节点
- ✅ 公农历转换(公元前104年-公元3000年)
- ✅ 9100+恒星数据库
- ✅ 公农历转换(公元前721年至公元3000年)
- ✅ 9100 颗恒星数据库
- ✅ 通用小天体轨道传播、H-G 视星等、视双星位置角/角距
- ✅ 黑体辐射、会合周期、星等、望远镜、大气质量等研究公式
- ✅ 真太阳时、平面日晷几何、水平日晷时线角