Files
astro/doc/manual/sun-moon.md
T
b612 16c62a97d5 feat: 完善时标与天象几何计算并扩展输出接口
- 新增时标、ΔT 模型、质心时间与 UT1 支持
- 改进日月食、月掩、行星事件及路径边界计算
- 完善恒星三维自行与动态距离传播
- 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具
- 整理中英文手册、示例资源及回归测试
2026-09-23 18:55:12 +08:00

45 KiB
Raw Permalink Blame History

太阳与月亮

English | 返回 README

sun 与 moon 是主链,lite/sun 与 lite/moon 是独立近似实现。没有特殊说明时,角度单位为度,视直径与视半径为角秒,sun.EarthDistance 为 AU、moon.EarthDistance 为千米;观测接口的 time.Time 通常表示民用时刻,真太阳时的返回值另按地方太阳时解释,见时标手册。

本手册覆盖日月本体:位置、升落与中天、站心量、相位与朔望、近远地点与交点、最大赤纬、天平动、视直径与物理星历。日月食几何见日食与月食手册,月掩见月掩手册,站心几何与折射的通用换算见坐标工具。

目录

日出日落与月相

package main

import (
	"fmt"
	"log"
	"time"

	"b612.me/astro/moon"
	"b612.me/astro/sun"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2020, 1, 1, 8, 8, 8, 0, cst)
	lon, lat, height := 108.93, 34.27, 0.0
	rise, err := sun.RiseTime(date, lon, lat, height, true) //日出时间
	if err != nil {
		log.Fatal(err)
	}
	set, err := sun.SetTime(date, lon, lat, height, true) //日落时间
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(rise.Format(time.RFC3339), set.Format(time.RFC3339))
	fmt.Println(moon.Phase(date), moon.PhaseDesc(date)) //月相(被照亮比例)和月相描述
}

aero=true 采用含折射的升落判据。极昼、极夜或当天没有事件时,升落接口返回错误。moon.Phase 是受照比例,不是月龄。

API 参考

下表按能力分组列出四个包的导出接口。多数求值接口另有 ...N 截断变体,统一在截断项族里说明;表中所有 time.Time 参数都是绝对时刻,lon/lat 均为东正西负、北正南负。

sun

名称 用途 单位与口径
TrueLo / ApparentLo 太阳真黄经 / 视黄经 度,地心
TrueBo 太阳真黄纬 度,地心;库内不单独提供视黄纬
GeometricLo / MidFunc 太阳几何黄经 / 中心差 度
ApparentRa / ApparentDec / ApparentRaDec 太阳视赤经 / 视赤纬 度,地心
EclipticObliquity 黄赤交角 度;第二参数为 true 时加入交角章动
EclipticNutation / EclipticNutation1980 黄经章动 度,IAU 2000B / IAU 1980
AxialtiltNutation / AxialtiltNutation1980 交角章动 度,IAU 2000B / IAU 1980
RiseTime / SetTime 日出 / 日落 (time.Time, error);aero 与 height 见参数与返回值约定
DownTime 日落别名 已废弃,内部转调 SetTime
CulminationTime 上中天 time.Time
MorningTwilight / EveningTwilight 晨光始 / 暮光终 (time.Time, error);角度常用 -6 / -12 / -18 度
Altitude / Zenith / Azimuth / HourAngle 几何高度角 / 天顶距 / 方位角 / 时角 度,站心
ApparentAltitude / ApparentZenith 视高度角 / 视天顶距 度;需要气压 hPa 与气温 ℃
ParallacticAngle 视差角(天顶方向角) 度,有符号
ApparentSolarTime 真太阳时 time.Time,结果时区按经度换算
EquationTime 均时差 小时
Diameter / Semidiameter 视直径 / 视半径 角秒
EarthDistance 日地距离 AU
Physical 日面物理量 返回 PhysicalInfo,字段单位为度

moon

名称 用途 单位与口径
TrueLo / TrueBo / ApparentLo 地心真黄经 / 真黄纬 / 视黄经 度
TrueRa / TrueDec / TrueRaDec 地心真赤道坐标 度
GeocentricApparentRa / GeocentricApparentDec / GeocentricApparentRaDec 地心视赤道坐标 度
ApparentRa / ApparentDec / ApparentRaDec 站心视赤道坐标 度;需要观测者经纬度
Altitude / Zenith / Azimuth / HourAngle 站心高度角 / 天顶距 / 方位角 / 时角 度
ApparentAltitude / ApparentZenith 视高度角 / 视天顶距 度;需要气压 hPa 与气温 ℃
ParallacticAngle 视差角 度,有符号,显式依赖观测者经纬度
RiseTime / SetTime 月出 / 月落 (time.Time, error)
DownTime 月落别名 已废弃,内部转调 SetTime
CulminationTime 上中天 time.Time;需要经度与纬度
Phase / PhaseDesc 受照比例 / 中文月相描述 比例 [0,1] / 字符串
SunMoonLoDiff 日月视黄经差 度,[0,360)
ShuoYue / ShangXianYue / WangYue / XiaXianYue 以小数年为锚点的朔 / 上弦 / 望 / 下弦 time.Time,UTC
NewMoon / FullMoon / FirstQuarter / LastQuarter 上四相的英文 alias time.Time,UTC
Next* / Last* / Closest* 下一次 / 上一次 / 最近一次相位与最大赤纬 time.Time;结果保持输入时区
NextConjunctionWithPlanet / LastConjunctionWithPlanet / ClosestConjunctionWithPlanet 行星合月(赤经合) time.Time;目标用 ConjunctionPlanet 常量
PerigeesInMonth / ApogeesInMonth 指定年月内的近地点 / 远地点 []ApsisInfo,距离 km
MaximumNorthDeclinationsInMonth / MaximumSouthDeclinationsInMonth 指定年月内的最大北 / 南赤纬事件 []MaximumDeclinationInfo,赤纬为度
AscendingNode / DescendingNode 升交点 / 降交点黄经 度
Physical / TopocentricPhysical 地心 / 站心天平动与自转轴位置角 返回 PhysicalInfo,字段单位为度
BrightLimbPositionAngle / TopocentricBrightLimbPositionAngle 地心 / 站心亮边位置角 度
Diameter / Semidiameter 视直径 / 视半径 角秒
EarthDistance 地月距离 千米

moon 还提供月掩接口,其参数、结果和完整示例见月掩手册。

分组 导出接口与类型
事件与路径 FindStarOccultations, FindPlanetOccultations, FindBestStarOccultations, FindBestPlanetOccultations, FindStarOccultationPaths, FindPlanetOccultationPaths
瞬时足迹与视圆几何 StarOccultationFootprintAt, PlanetOccultationFootprintsAt, StarOccultationDiagram, PlanetOccultationDiagram
UT1 标签换算 StarOccultationInfoInUT1, StarOccultationPathInUT1, PlanetOccultationInfoInUT1, PlanetOccultationPathInUT1
恒星结果 StarOccultationInfo, StarOccultationPath, StarOccultationInstant
行星结果 PlanetOccultationInfo, PlanetOccultationPath, PlanetOccultationInstant, PlanetOccultationFootprint
路径数据 OccultationFootprint, OccultationPathPoint, OccultationGreatestTimeContour, OccultationRiseSetCurve
搜索与路径选项 OccultationSearchOptions, OccultationPathOptions, OccultationPathAlgorithm
视圆图数据 StarOccultationDiagramFrame, StarOccultationDiagramOptions, StarOccultationDiagramResult, PlanetOccultationDiagramFrame, PlanetOccultationDiagramOptions, PlanetOccultationDiagramResult
目标与坐标 StarData, StarCoordinate, StarCoordinateFromStarData, Observer, CoordinateFrame, CoordinateFrameICRS, CoordinateFrameJ2000, CoordinateFrameApparentOfDate
事件类型 OccultationPlanet, OccultationType, OccultationTotal, OccultationPartial, OccultationGrazing
路径算法 OccultationPathAlgorithmOptimized, OccultationPathAlgorithmExact
升落阶段 RiseSetPhase, RiseSetDirection, RiseSetPhaseStart, RiseSetPhaseGreatest, RiseSetPhaseEnd, RiseSetDirectionRise, RiseSetDirectionSet
错误 ErrInvalidOccultationInput, ErrOccultationPathSamplingLimit

行星目标常量为 OccultationMercury 至 OccultationNeptune。

lite/sun

名称 用途 单位与口径
TrueLo / ApparentLo 轻量真黄经 / 视黄经 度,地心
TrueRa / TrueDec / TrueRaDec 轻量真赤道坐标 度,地心
ApparentRa / ApparentDec / ApparentRaDec 轻量视赤道坐标 度,地心
Distance 轻量日地距离 AU
HourAngle / Azimuth / Altitude / Zenith 轻量时角 / 方位角 / 高度角 / 天顶距 度,站心
RiseTime / SetTime 轻量日出 / 日落 (time.Time, error)
ERR_SUN_NEVER_RISE / ERR_SUN_NEVER_SET 极夜 / 极昼 错误值

lite/moon

名称 用途 单位与口径
TrueLo / TrueBo 轻量地心真黄经 / 真黄纬 度
TrueRa / TrueDec / TrueRaDec 轻量地心真赤道坐标 度
ApparentRa / ApparentDec / ApparentRaDec 轻量站心视赤道坐标 度;需要观测者经纬度
HourAngle / Azimuth / Altitude / Zenith 轻量时角 / 方位角 / 高度角 / 天顶距 度,站心
SunMoonLoDiff / Phase / PhaseAge 轻量日月黄经差 / 受照比例 / 月龄 度 / [0,1] / 天
RiseTime / SetTime 轻量月出 / 月落 (time.Time, error)
ERR_MOON_NEVER_RISE / ERR_MOON_NEVER_SET / ERR_NOT_TODAY 极夜 / 极昼 / 事件不在当天 错误值

截断项族

sun 与 moon 的多数求值接口提供 ...N 变体:名字是原接口名加 N,参数在末尾多一个 n int,返回形状不变。n < 0 使用当前仓库内嵌的全部解析项,结果与不带 N 的版本一致;n >= 0 把级数截断到 n 项,适合性能对比、批量粗算或误差敏感性实验。

  • sun:TrueLoN、TrueBoN、AltitudeN、ZenithN、AzimuthN、HourAngleN、ParallacticAngleN、ApparentAltitudeN、ApparentZenithN、DiameterN、SemidiameterN、PhysicalN、RiseTimeN、SetTimeN、DownTimeN、CulminationTimeN、MorningTwilightN、EveningTwilightN、ApparentSolarTimeN
  • moon:TrueLoN、TrueBoN、AscendingNodeN、DescendingNodeN、DiameterN、SemidiameterN、PhysicalN、TopocentricPhysicalN、BrightLimbPositionAngleN、TopocentricBrightLimbPositionAngleN

站心赤道坐标(ApparentRa / ApparentDec / ApparentRaDec)、月亮的升落(RiseTime / SetTime)与相位族没有 N 变体,它们不受截断开关控制。

常用场景

今天的日出日落与晨昏朦影

fmt.Println(sun.MorningTwilight(date, lon, lat, -6)) // 民用晨光始
fmt.Println(sun.RiseTime(date, lon, lat, height, true))
fmt.Println(sun.SetTime(date, lon, lat, height, true))
fmt.Println(sun.EveningTwilight(date, lon, lat, -6)) // 民用暮光终
2020-01-01 07:22:28.138198256 +0800 CST <nil>
2020-01-01 07:49:52.591398954 +0800 CST <nil>
2020-01-01 17:45:09.366609156 +0800 CST <nil>
2020-01-01 18:12:33.801986575 +0800 CST <nil>

朦影角度换成 -12 / -18 就是航海与天文朦影;aero = true 按蒙气差与视半径修正后的地平求升落,与几何地平的差别见升落与中天。

月出月落与此刻的月亮高度

rise, _ := moon.RiseTime(date, lon, lat, height, true)
set, _ := moon.SetTime(date, lon, lat, height, true)
fmt.Println(rise)
fmt.Println(set)
fmt.Println(moon.Altitude(date, lon, lat), moon.Azimuth(date, lon, lat))
2020-01-01 11:52:50.042243599 +0800 CST
2020-01-01 23:26:49.498263895 +0800 CST
-45.349728852972675 67.63824603392399

月球升落按当地自然日计算,升落之间可以没有连续性,date 之后的完整周期要看升起与落下时刻的先后关系,细节见日出日落/月出月落;高度角为负说明月亮在地平线下,这里 -45.35° 就是此刻看不见。

月相与下次朔望弦

fmt.Println(moon.Phase(date), moon.PhaseDesc(date)) // 受照比例与月相名
fmt.Println(moon.NextShuoYue(date))                 // 下次朔
fmt.Println(moon.NextWangYue(date))                 // 下次望
0.30004130960877884 上峨眉月
2020-01-25 05:41:58.271192908 +0800 CST
2020-01-11 03:21:17.159625291 +0800 CST

Next* / Last* / Closest* 是下一次 / 上一次 / 最近一次三种检索口径,上弦与下弦分别是 moon.NextShangXianYue / moon.NextXiaXianYue;结果保持输入时区,四种相位与朔望月的完整口径见月相。

视直径、地月距离与天平动

fmt.Println(moon.Diameter(date), moon.EarthDistance(date))
p := moon.Physical(date)
fmt.Println(p.LibrationLongitude, p.LibrationLatitude, p.PositionAngle)
1774.6658461637385 404238.6096080479
0.7655535663486027 6.382898400777244 -23.672356410246774

Diameter 单位角秒、EarthDistance 单位千米;1774.67″(约 29.6′)对应约 40.4 万 km 的远地点附近,比平均视直径小约 5%。Physical 给地心天平动,站心版本 TopocentricPhysical 与字段口径见天平动与亮边位置角。

地心量与站心量的区别

geoRa, geoDec := moon.GeocentricApparentRaDec(date) // 地心视位置
topRa, topDec := moon.ApparentRaDec(date, lon, lat) // 站心视位置
fmt.Printf("geocentric %.4f %.4f\n", geoRa, geoDec)
fmt.Printf("topocentric %.4f %.4f\n", topRa, topDec)
fmt.Printf("delta dRA=%.4f dDec=%.4f\n", topRa-geoRa, topDec-geoDec)
fmt.Println(sun.ApparentRaDec(date)) // 太阳只给地心量
geocentric 349.2322 -9.9506
topocentric 349.7343 -10.3485
delta dRA=0.5021 dDec=-0.3978
280.8950939694744 -23.05840775453492

月亮离得近,站心与地心能差半度左右(这里赤经差 0.50°、赤纬差 0.40°),合起来比一个月面视直径还大,所以给观测者用的位置必须走 moon.ApparentRaDec;太阳在 1 AU 外视差可忽略,只提供地心量(当然也可以调用站心坐标转换接口得到站心位置=-=)。口径定义见观测角语义。

主链与 lite 的误差对照

fmt.Println(sun.Altitude(date, lon, lat), litesun.Altitude(date, lon, lat)) // 主链 / 轻量几何高度角
fmt.Println(moon.Phase(date), litemoon.Phase(date))                         // 受照比例
fmt.Println(litemoon.PhaseAge(date))                                        // 轻量月龄(天)
fmt.Println(litesun.RiseTime(date, lon, lat, height, true))                 // 轻量日出
2.40091496867759 2.403576774819768
0.30004130960877884 0.2978124633132848
5.42608394367707
2020-01-01 07:49:51.69717729 +0800 CST <nil>

轻量链路与主链形状同构、精度不同:同一时刻的高度角只差 0.0027° 量级,月相只差 0.0022;2026 全年 8 站点统计下 lite/sun 日出平均绝对误差 0.02 min(P95 0.04 min、最大 0.31 min),lite/moon 月出 0.28 min(P95 0.57 min、最大 1.44 min),lite/moon 的 Phase() 最大绝对误差 0.00243。

完整对照与截断误差见与主链的差异与误差量级与 README 的 Lite 轻量链路。

观测角语义

  • Altitude:高度角,地平线为 0°,天顶为 +90°
  • Zenith:天顶距,天顶为 0°,地平线为 90°
  • Zenith 与 Altitude 互补,两者相加为 90°
  • Azimuth:方位角,正北为 0°,向东增加,取值范围 [0°, 360°)
  • HourAngle:时角,上中天为 0°,向西(下午)增大,归一化到 [0°, 360°)
  • ParallacticAngle:视差角(天顶方向角),有符号,单位度;方向定义与站心几何的统一口径见坐标工具
  • Altitude / Azimuth 是几何中心高度与方位,不含大气折射和视半径修正;ApparentAltitude / ApparentZenith 加入大气折射,需要传入气压(hPa)与气温(℃)
  • 太阳的赤道坐标是地心量;月亮的 TrueRaDec 与 GeocentricApparentRaDec 是地心量,ApparentRa / ApparentDec / ApparentRaDec 是站心量,必须给出观测者经纬度

综合示例:日月升落与位置

下面两个完整示例覆盖最常用的入口,公共前置是西安(108.93°E, 34.27°N)与 2020-01-01 08:08:08 CST。后续各节的片段都省略这类公共变量,只保留与本组能力相关的语句。

日出日落/月出月落

⚠️ 月球升降时间按当天日期计算,升降时间点之间不一定具有连续性。

例如月亮可能在凌晨1点落下、中午12点再次升起,此时升起时间会晚于降落时间;这一场景晚上的月落时间对应次日日期。

完整的升降周期由升起时间与降落时间的先后关系决定:判断升起时间是否在降落时间之后,即可确定后续的正确时间点。

package main

import (
	"fmt"
	"time"

	"b612.me/astro/moon"
	"b612.me/astro/sun"
)

func main() {
	// 以陕西省西安市为例,设置西安市经纬度,设置地平高度为0米
	var lon, lat, height float64 = 108.93, 34.27, 0
	cst := time.FixedZone("CST", 8*3600)
	// 指定 2020-01-01 08:08:08 CST,所有"今日"语义都以这个本地自然日为基准。
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)
	// 西安市2020年1月1日民用晨朦影开始时间
	// 民用朦影,太阳位于地平线下6度,航海朦影=地平线下12度,天文朦影=地平线下18度
	fmt.Println(sun.MorningTwilight(date, lon, lat, -6))
	// 西安市2020年1月1日日出时间,按动态标准折射和实时太阳视半径计算上缘过地平线
	fmt.Println(sun.RiseTime(date, lon, lat, height, true))
	// 西安市2020年1月1日太阳上中天时间
	fmt.Println(sun.CulminationTime(date, lon))
	// 西安市2020年1月1日日落时间,按动态标准折射和实时太阳视半径计算上缘过地平线
	fmt.Println(sun.SetTime(date, lon, lat, height, true))
	// 西安市2020年1月1日民用昏朦影结束时间
	fmt.Println(sun.EveningTwilight(date, lon, lat, -6))

	// 西安市2020年1月1日月出时间,按动态标准折射和实时月球视半径计算上缘过地平线
	fmt.Println(moon.RiseTime(date, lon, lat, height, true))
	// 西安市2020年1月1日月亮上中天时间
	fmt.Println(moon.CulminationTime(date, lon, lat))
	// 西安市2020年1月1日月落时间,按动态标准折射和实时月球视半径计算上缘过地平线
	fmt.Println(moon.SetTime(date, lon, lat, height, true))
}

输出结果:

2020-01-01 07:22:27.960488498 +0800 CST <nil>
2020-01-01 07:49:52.413689196 +0800 CST <nil>
2020-01-01 12:47:35.933117866 +0800 CST
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:36:48.811488747 +0800 CST
2020-01-01 23:26:49.313553571 +0800 CST <nil>

日月位置

package main

import (
	"fmt"
	"time"

	"b612.me/astro/moon"
	"b612.me/astro/star"
	"b612.me/astro/sun"
	"b612.me/astro/tools"
)

func main() {
	// 以陕西省西安市为例,设置西安市经纬度,设置地平高度为0米
	var lon, lat float64 = 108.93, 34.27
	cst := time.FixedZone("CST", 8*3600)
	// 指定观测时刻。
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)
	// 太阳此刻的视黄经,单位度。
	fmt.Println(sun.ApparentLo(date))
	// 此刻黄赤交角,第二个参数 true 表示使用真黄赤交角。
	fmt.Println(sun.EclipticObliquity(date, true))
	//太阳此刻视赤经、视赤纬
	ra, dec := sun.ApparentRaDec(date)
	fmt.Println("赤经:", tools.Format(ra/15, 1), "赤纬:", tools.Format(dec, 0))
	//太阳当前所在星座
	fmt.Println(star.Constellation(ra, dec, date))
	//此刻西安市的太阳方位角、高度角、天顶距
	fmt.Println("方位角:", sun.Azimuth(date, lon, lat), "高度角:", sun.Altitude(date, lon, lat), "天顶距:", sun.Zenith(date, lon, lat))
	//此刻日地距离,单位为天文单位(AU)
	fmt.Println(sun.EarthDistance(date))

	//月亮此刻站心视赤经、视赤纬
	ra, dec = moon.ApparentRaDec(date, lon, lat)
	fmt.Println("赤经:", tools.Format(ra/15, 1), "赤纬:", tools.Format(dec, 0))
	//月亮当前所在星座
	fmt.Println(star.Constellation(ra, dec, date))
	//此刻西安市的月亮方位角、高度角、天顶距
	fmt.Println("方位角:", moon.Azimuth(date, lon, lat), "高度角:", moon.Altitude(date, lon, lat), "天顶距:", moon.Zenith(date, lon, lat))
	//此刻地月距离,单位为千米
	fmt.Println(moon.EarthDistance(date))
}

输出结果:

280.01526210031136
23.4362178391013
赤经: 18h43m34.82s 赤纬: -23°3′30.27″
人马座
方位角: 120.19477090015224 高度角: 2.4014437419430097 天顶距: 87.59855625805699
0.983292937163176
赤经: 23h18m56.24s 赤纬: -10°20′54.42″
宝瓶座
方位角: 67.63889332004852 高度角: -45.34916937173283 天顶距: 135.34916937173284
404238.6096080479

太阳

本节片段省略公共前置:cst := time.FixedZone("CST", 8*3600)、date := time.Date(2026, 1, 1, 12, 0, 0, 0, cst)、var lon, lat float64 = 108.93, 34.27(西安),并假定已导入 fmt、time、sun、moon。

位置

太阳位置接口只依赖绝对时刻,不依赖观测者。真黄纬由 TrueBo 给出,库内不单独提供视黄纬;EclipticObliquity 的第二个参数决定是否加入交角章动。

// 太阳真黄经、视黄经与真黄纬,单位度。
fmt.Println(sun.TrueLo(date), sun.ApparentLo(date), sun.TrueBo(date))
// 太阳视赤经与视赤纬。
ra, dec := sun.ApparentRaDec(date)
fmt.Println("视赤经:", ra, "视赤纬:", dec)
fmt.Println(sun.ApparentRa(date), sun.ApparentDec(date))
// 黄赤交角、几何黄经与中心差。
fmt.Println(sun.EclipticObliquity(date, true), sun.GeometricLo(date), sun.MidFunc(date))

输出结果:

280.742671383543 280.7383965677222 0.00018280886212040676
视赤经: 281.6786097810291 视赤纬: -23.00369182413533
281.6786097810291 -23.00387403948847
23.438148552330773 280.83169623098 -0.08703499790144194

同一族接口都提供 ...N 截断变体。下面的对照用 n = 8 截断,n < 0 时结果与不带 N 的版本逐位一致:

// n<0 使用全部内嵌 VSOP 项,n>=0 截断解析项。
fmt.Println(sun.TrueLo(date), sun.TrueLoN(date, 8))
fmt.Println(sun.Altitude(date, lon, lat), sun.AltitudeN(date, lon, lat, 8))
fmt.Println(sun.Diameter(date), sun.DiameterN(date, 8))

输出结果:

280.742671383543 280.7439900413756
31.61569462953789 31.615524473491835
1950.9979407481142 1950.9994358395434

升落与中天

RiseTime / SetTime 以 date 所在时区的当地自然日为锚点,返回值保持同一个时区;height 是按椭球高(大地高)解读的观测点高程,单位米。aero 为 true 时按动态标准大气折射与实时视半径计算上缘过地平线,为 false 时只做几何中心高度过地平线的判定。

朦影接口把目标高度角作为参数:民用朦影 -6°、航海朦影 -12°、天文朦影 -18°,晨昏两侧分别是 MorningTwilight 与 EveningTwilight。

// 民用、航海、天文晨朦影的目标高度角。
for _, angle := range []float64{-6, -12, -18} {
	t, err := sun.MorningTwilight(date, lon, lat, angle)
	fmt.Println(angle, t.Format("15:04:05"), err)
}
// 上中天与日出;极区无事件时 err 非空。
fmt.Println(sun.CulminationTime(date, lon).Format("15:04:05"))
t, err := sun.RiseTime(date, lon, lat, 0, true)
fmt.Println(t.Format("15:04:05"), err)

输出结果:

-6 07:22:47 <nil>
-12 06:51:31 <nil>
-18 06:21:00 <nil>
12:47:50
07:50:10 <nil>

站心量与视差角

Altitude / Zenith / Azimuth / HourAngle 走几何链路,不含折射;ApparentAltitude / ApparentZenith 加入大气折射,需要给气压与气温;ParallacticAngle 是有符号的视差角。站心几何与折射的更一般换算(含大气折射、站心赤道坐标)见坐标工具。

fmt.Println(sun.Azimuth(date, lon, lat), sun.Altitude(date, lon, lat), sun.Zenith(date, lon, lat))
fmt.Println(sun.ApparentAltitude(date, lon, lat, 1010, 10), sun.ApparentZenith(date, lon, lat, 1010, 10))
// 时角与有符号视差角。
fmt.Println(sun.HourAngle(date, lon, lat), sun.ParallacticAngle(date, lon, lat))

输出结果:

167.09774780715728 31.61569462953789 58.38430537046211
31.643010360459822 58.356989639540174
348.07820699607544 -11.564174033740159

真太阳时与均时差

ApparentSolarTime 返回给定经度处的真太阳时,结果时区是按经度换算出来的固定偏移时区,不是调用方传入的时区;EquationTime 给出同一时刻的均时差,单位小时。sundial.TrueSolarTime 与 ApparentSolarTime 口径相同,sundial 另提供地方平太阳时与日晷几何,见日晷与真太阳时。

// 真太阳时;结果时区按经度换算。
fmt.Println(sun.ApparentSolarTime(date, lon).Format("2006-01-02 15:04:05 -0700"))
// 均时差,单位小时。
fmt.Println(sun.EquationTime(date))
// sundial.TrueSolarTime 与其口径相同。
fmt.Println(sundial.TrueSolarTime(date, lon).Format("2006-01-02 15:04:05 -0700"))

输出结果:

2026-01-01 11:12:18 +0715
-0.05674946079069686
2026-01-01 11:12:18 +0715

物理与视直径

sun.Physical 返回 PhysicalInfo:P 是太阳北极位置角,B0 是日面中心的太阳纬度,L0 是日面中心的卡林顿经度,单位都是度。Diameter / Semidiameter 给视直径与视半径,单位角秒;EarthDistance 给日地距离,单位 AU。

// 视直径、视半径(角秒)与日地距离(AU)。
fmt.Println(sun.Diameter(date), sun.Semidiameter(date), sun.EarthDistance(date))
// 日面物理量 P/B0/L0,单位度。
p := sun.Physical(date)
fmt.Println(p.P, p.B0, p.L0)

输出结果:

1950.9979407481142 975.4989703740571 0.9833237486528845
1.979086377118846 -3.0131029209723916 296.9595333604375

日、月与七大行星的同名接口形状一致,可以并排对照:

fmt.Println(sun.Diameter(date), sun.Semidiameter(date))
fmt.Println(sun.Physical(date))
fmt.Println(moon.Diameter(date), moon.Semidiameter(date))
fmt.Println(mars.Diameter(date), mars.Semidiameter(date))

地球轨道极值

日地距离的极值由 earth 包给出,时间按 UTC,距离单位 AU;只关心某一时刻的轨道偏心率时直接调 EarthEccentricity:

// 2026 年地球近日点、远日点,时间为 UTC,距离单位 AU。
peri := earth.Perihelion(2026)
aphe := earth.Aphelion(2026)
fmt.Printf("earth perihelion=%s distance=%.9fAU\n", peri.Time.Format(time.RFC3339), peri.Distance)
fmt.Printf("earth aphelion=%s distance=%.9fAU\n", aphe.Time.Format(time.RFC3339), aphe.Distance)

输出结果:

earth perihelion=2026-01-03T17:15:35Z distance=0.983302050AU
earth aphelion=2026-07-06T17:31:24Z distance=1.016643936AU
fmt.Printf("earth e=%.9f\n", earth.EarthEccentricity(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)))

月亮

本节的片段沿用太阳一节的公共前置,date 仍是 2026-01-01 12:00:00 CST、观测点是西安。

位置

月亮的赤道坐标分三层:TrueRaDec 是地心真位置,GeocentricApparentRaDec 是地心视位置,ApparentRaDec 是站心视位置。黄道侧只有地心量:TrueLo 真黄经、TrueBo 真黄纬、ApparentLo 视黄经。

// 地心真赤道坐标。
fmt.Println(moon.TrueRaDec(date))
// 地心视赤道坐标。
fmt.Println(moon.GeocentricApparentRaDec(date))
// 站心视赤道坐标。
fmt.Println(moon.ApparentRaDec(date, lon, lat))
// 真黄经、真黄纬与视黄经。
fmt.Println(moon.TrueLo(date), moon.TrueBo(date), moon.ApparentLo(date))

输出结果:

66.66309709020791 26.830370234236764
66.66476688311091 26.830608930005567
67.0275694157259 25.982665403390747
69.21422925147913 5.060516750865828 69.21574418700706

升落与中天

月球升落与太阳一样以当地自然日为锚点:RiseTime / SetTime 返回当天的事件时刻,CulminationTime 返回当天上中天时刻。月球的升降之间可以没有连续性,date 之后的完整周期要靠升起时间与降落时间的先后关系决定;当某个事件落在查询日期之外时,升落接口返回 ERR_NOT_TODAY。

// 月出、上中天与月落,按当天日期计算。
rise, err := moon.RiseTime(date, lon, lat, 0, true)
fmt.Println(rise.Format("15:04:05"), err)
fmt.Println(moon.CulminationTime(date, lon, lat).Format("15:04:05"))
set, err := moon.SetTime(date, lon, lat, 0, true)
fmt.Println(set.Format("15:04:05"), err)

输出结果:

16:17:11 <nil>
00:03:16
06:41:37 <nil>

月相

Phase 返回 [0,1] 的受照比例,PhaseDesc 返回中文月相名,SunMoonLoDiff 返回归一化到 [0,360) 的日月视黄经差(朔附近接近 0°、望附近接近 180°)。Next* / Last* / Closest* 是三种检索口径,结果保持输入时区;四个相位同时提供拼音名与英文 alias,例如 ShuoYue / NewMoon、WangYue / FullMoon、ShangXianYue / FirstQuarter、XiaXianYue / LastQuarter。

package main

import (
	"fmt"
	"time"

	"b612.me/astro/moon"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	// 指定观测时刻。
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)
	//月亮此刻被照亮的比例(月相)
	fmt.Println(moon.Phase(date))
	//月相具体描述
	fmt.Println(moon.PhaseDesc(date))
	//下次朔月时间;也可用 moon.NextNewMoon(date)
	fmt.Println(moon.NextShuoYue(date))
	//下次上弦月时间;也可用 moon.NextFirstQuarter(date)
	fmt.Println(moon.NextShangXianYue(date))
	//下次望月时间;也可用 moon.NextFullMoon(date)
	fmt.Println(moon.NextWangYue(date))
	//下次下弦月时间;也可用 moon.NextLastQuarter(date)
	fmt.Println(moon.NextXiaXianYue(date))
}

输出结果:

0.30004130960877884 // 月面约有 30% 被太阳照亮
上峨眉月 // 当前月相描述
2020-01-25 05:41:58.271192908 +0800 CST // 下一次朔月
2020-01-03 12:45:23.229190707 +0800 CST // 下一次上弦
2020-01-11 03:21:17.159625291 +0800 CST // 下一次望月,也就是满月
2020-01-17 20:58:23.396406769 +0800 CST // 下一次下弦

Last*、Closest* 与按小数年锚点求解的 ShuoYue / FullMoon 等返回 UTC;ClosestConjunctionWithPlanet 求最近一次行星合月,目标用 ConjunctionPlanet 常量给出:

// 朔与望的上一次、最近一次。
fmt.Println(moon.LastShuoYue(date), moon.ClosestShuoYue(date))
fmt.Println(moon.LastWangYue(date), moon.ClosestWangYue(date))
// 上弦与下弦。
fmt.Println(moon.LastFirstQuarter(date), moon.ClosestLastQuarter(date))
// 以小数年为锚点的解,结果为 UTC。
fmt.Println(moon.ShuoYue(2025.5).Format(time.RFC3339), moon.FullMoon(2025.5).Format(time.RFC3339))
// 最近一次行星合月(赤经合)。
fmt.Println(moon.ClosestConjunctionWithPlanet(date, moon.ConjunctionJupiter))

输出结果:

2025-12-20 09:43:19.074603617 +0800 CST 2025-12-20 09:43:19.074603617 +0800 CST
2025-12-05 07:14:03.670351803 +0800 CST 2026-01-03 18:02:53.55531156 +0800 CST
2025-12-28 03:09:50.141303837 +0800 CST 2026-01-10 23:48:22.03346461 +0800 CST
2025-06-25T10:31:35Z 2025-07-10T20:36:46Z
2026-01-04 05:59:19.9425897 +0800 CST

近地点与远地点

PerigeesInMonth / ApogeesInMonth 返回指定公历月内的全部近地点与远地点事件,元素是 ApsisInfo,含 Time(UTC)与 Distance(km),一个月可能有零个、一个或多个事件。

// 2026 年 1 月的月球近地点、远地点,距离单位 km。
perigees := moon.PerigeesInMonth(2026, time.January)
apogees := moon.ApogeesInMonth(2026, time.January)
fmt.Printf("moon perigee=%s distance=%.1fkm count=%d\n", perigees[0].Time.Format(time.RFC3339), perigees[0].Distance, len(perigees))
fmt.Printf("moon apogee=%s distance=%.1fkm count=%d\n", apogees[0].Time.Format(time.RFC3339), apogees[0].Distance, len(apogees))

输出结果:

moon perigee=2026-01-01T21:44:24Z distance=360348.1km count=2
moon apogee=2026-01-13T20:47:13Z distance=405437.9km count=1

交点

月球也提供升交点和降交点黄经,适合做食季、轨道几何和月球轨道研究:

nodeDate := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
fmt.Println(moon.AscendingNode(nodeDate), moon.DescendingNode(nodeDate))

这里的“升交点 / 降交点”与行星章节中的定义相同:

  • AscendingNode:月球轨道从黄道南侧穿到黄道北侧时的黄经
  • DescendingNode:月球轨道从黄道北侧穿到黄道南侧时的黄经
  • 单位都是度;同一时刻两者通常相差约 180°

以上面 nodeDate := 2026-01-01 00:00:00 UTC 的示例来说,输出结果是:

340.95708624505863 160.9570862450587

最大赤纬

月球赤纬在一个交点月内达到南北极值,MaximumDeclinationInfo 含 Time(事件时刻)与 Declination(该时刻的地心赤纬,度)。逐月接口返回当月全部事件,Next* / Last* / Closest* 按时刻检索:

// 最近最大北赤纬与上一次最大南赤纬。
north := moon.ClosestMaximumNorthDeclination(date)
south := moon.LastMaximumSouthDeclination(date)
fmt.Println(north.Time.Format(time.RFC3339), north.Declination)
fmt.Println(south.Time.Format(time.RFC3339), south.Declination)
// 下一次最大北赤纬与当月全部事件。
fmt.Println(moon.NextMaximumNorthDeclination(date).Time.Format(time.RFC3339))
events := moon.MaximumNorthDeclinationsInMonth(2026, time.January)
fmt.Println(len(events))
for _, event := range events {
	fmt.Println(event.Time.Format(time.RFC3339), event.Declination)
}

输出结果:

2026-01-02T16:10:49+08:00 28.266373428242343
2025-12-20T07:06:57+08:00 -28.23514705130737
2026-01-02T16:10:49+08:00
2 2026-01-02T08:10:49Z 28.266373428242343

同一组事件的逐月列表口径如下(2026-01-01 00:00:00 UTC 输入):

// 2026 年 1 月的月球最大北/南赤纬。
north := moon.MaximumNorthDeclinationsInMonth(2026, time.January)
south := moon.MaximumSouthDeclinationsInMonth(2026, time.January)
fmt.Printf("north=%s dec=%.6f\n", north[0].Time.Format(time.RFC3339), north[0].Declination)
fmt.Printf("south=%s dec=%.6f\n", south[0].Time.Format(time.RFC3339), south[0].Declination)

输出结果:

north=2026-01-02T08:10:49Z dec=28.266373
south=2026-01-16T05:15:14Z dec=-28.304184

天平动与亮边位置角

Physical 返回地心天平动,TopocentricPhysical 返回站心天平动,二者都是 PhysicalInfo,含光学、物理与总天平动的经纬分量以及自转轴位置角 PositionAngle,单位度。亮边位置角的 0° 从月面北点起、向东增加。

// 地心天平动与自转轴位置角。
p := moon.Physical(date)
fmt.Println(p.LibrationLongitude, p.LibrationLatitude, p.PositionAngle)
// 站心天平动与自转轴位置角。
topo := moon.TopocentricPhysical(date, lon, lat, 0)
fmt.Println(topo.LibrationLongitude, topo.LibrationLatitude, topo.PositionAngle)
// 地心与站心亮边位置角。
fmt.Println(moon.BrightLimbPositionAngle(date), moon.TopocentricBrightLimbPositionAngle(date, lon, lat, 0))

输出结果:

-0.9680924808747591 -6.547834757841939 -9.025022841390472
-0.7780085060449551 -5.659649431558411 -8.883877708625544
269.08333384819935 267.8559531949645

站心量的完整示例(上海,height = 4 m):

// 月球天平动和自转轴位置角。
physical := moon.Physical(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
fmt.Printf("libration lon=%.6f lat=%.6f pa=%.6f\n", physical.LibrationLongitude, physical.LibrationLatitude, physical.PositionAngle)
// 月亮明亮边缘位置角;0° 从月面北点起,向东增加。
fmt.Printf("bright limb=%.6f\n", moon.BrightLimbPositionAngle(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)))
// 上海站心看到的月球天平动、自转轴位置角和亮边位置角。
topo := moon.TopocentricPhysical(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), 121.4737, 31.2304, 4)
fmt.Printf("topo libration lon=%.6f lat=%.6f pa=%.6f\n", topo.LibrationLongitude, topo.LibrationLatitude, topo.PositionAngle)
fmt.Printf("topo bright limb=%.6f\n", moon.TopocentricBrightLimbPositionAngle(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), 121.4737, 31.2304, 4))

输出结果:

libration lon=-1.278902 lat=-6.531444 pa=-9.967050
bright limb=267.364849
topo libration lon=-1.736754 lat=-5.780730 pa=-10.072846
topo bright limb=266.038258

视直径与地月距离

Diameter / Semidiameter 给视直径与视半径(角秒),EarthDistance 给地月距离(千米)。三者都只依赖绝对时刻:

// 视直径、视半径(角秒)与地月距离(千米)。
fmt.Println(moon.Diameter(date), moon.Semidiameter(date), moon.EarthDistance(date))

输出结果:

1986.4975069969655 993.2487534984828 360488.4234539985

轻量链路

lite/sun 与 lite/moon 是独立于主链的近似实现:不依赖 VSOP87 或主链的 ELP2000/82 级数,面向 CPU / 内存受限环境,调用方式与主链同形。升落搜索用固定步长扫描加二分,不走主链的高精度章动迭代。

package main

import (
	"fmt"
	"time"

	litemoon "b612.me/astro/lite/moon"
	litesun "b612.me/astro/lite/sun"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2026, 1, 1, 20, 0, 0, 0, cst)

	fmt.Println(litesun.Altitude(date, 121.4737, 31.2304))
	fmt.Println(litesun.RiseTime(date, 121.4737, 31.2304, 0, true))

	fmt.Println(litemoon.Phase(date))
	fmt.Println(litemoon.PhaseAge(date))
	fmt.Println(litemoon.RiseTime(date, 121.4737, 31.2304, 0, true))
}

下面的片段沿用这一节的公共前置:上海(121.4737°E, 31.2304°N)与 2026-01-01 20:00:00 CST。

lite/sun

轻量太阳链路提供黄经、赤道坐标、距离、地平坐标与升落,但不提供视直径、日面物理量与朦影接口。Distance 是日地距离(AU):需要注意,主链同名能力叫 EarthDistance,轻量链路叫 Distance。

// 轻量太阳真黄经、视黄经与日地距离(AU)。
fmt.Println(litesun.TrueLo(date), litesun.ApparentLo(date), litesun.Distance(date))
// 轻量视赤经与视赤纬。
ra, dec := litesun.ApparentRaDec(date)
fmt.Println(ra, dec)
// 轻量时角、方位角、高度角与天顶距。
fmt.Println(litesun.HourAngle(date, 121.4737, 31.2304), litesun.Azimuth(date, 121.4737, 31.2304), litesun.Altitude(date, 121.4737, 31.2304), litesun.Zenith(date, 121.4737, 31.2304))
// 轻量升落。
fmt.Println(litesun.RiseTime(date, 121.4737, 31.2304, 0, true))
fmt.Println(litesun.SetTime(date, 121.4737, 31.2304, 0, true))

输出结果:

281.0835889076667 281.0793650422643 0.9833163427233701
282.0475744673639 -22.973803828458102
120.58011725187828 263.4579582386061 -37.07676039520773 127.07676039520773
2026-01-01 06:52:24.6475178 +0800 CST <nil>
2026-01-01 17:02:44.014452695 +0800 CST <nil>

lite/moon

轻量月球链路提供少量摄动项的月球位置、轻量站心修正、月相与月龄、升落,但不提供天平动、视直径、地月距离与交点。PhaseAge 返回月龄(天),只在轻量链路提供:

// 轻量真黄经与真黄纬。
fmt.Println(litemoon.TrueLo(date), litemoon.TrueBo(date))
// 轻量地心真赤道坐标。
ra, dec := litemoon.TrueRaDec(date)
fmt.Println(ra, dec)
// 轻量站心视赤道坐标。
fmt.Println(litemoon.ApparentRaDec(date, 121.4737, 31.2304))
// 日月黄经差、受照比例与月龄。
fmt.Println(litemoon.SunMoonLoDiff(date), litemoon.Phase(date), litemoon.PhaseAge(date))
// 轻量地平坐标。
fmt.Println(litemoon.Altitude(date, 121.4737, 31.2304), litemoon.Azimuth(date, 121.4737, 31.2304), litemoon.Zenith(date, 121.4737, 31.2304))

输出结果:

74.25630740893 5.078407838127742
72.2518040645064 27.549605139478064
72.74256784654426 27.432483413326057
153.17694236666568 0.9462021494002484 12.565014741082448
63.55513206820331 90.53047230027812 26.444867931796693

与主链的差异与误差量级

轻量链路与主链的差别集中在实现与误差上,接口形状基本一致。位置与月相等纯求值接口相对主链约快 8.3–27.3x,升落接口约 1.0–3.7x,计算链路零堆分配;升落搜索步长为 lite/sun 30 分钟、lite/moon 15 分钟。与 sun / moon 的误差(2026 全年,8 个站点)如下,数据见适用范围与精度:

能力 平均绝对误差 P95 最大绝对误差
lite/sun 日出 0.02 min 0.04 min 0.31 min
lite/sun 日落 0.02 min 0.06 min 0.35 min
lite/moon 月出 0.28 min 0.57 min 1.44 min
lite/moon 月落 0.36 min 0.86 min 1.24 min
lite/moon Phase() 0.00089 0.00185 0.00243
lite/moon PhaseAge() 0.003 d 0.010 d 0.014 d
lite/moon 地心黄经 2.41' 6.82' 9.91'
lite/moon 地心黄纬 0.87' 1.83' 2.92'

两个包都不提供 ...N 截断族。太阳侧的极夜 / 极昼错误是 ERR_SUN_NEVER_RISE / ERR_SUN_NEVER_SET;月球侧除 ERR_MOON_NEVER_RISE / ERR_MOON_NEVER_SET 外还有 ERR_NOT_TODAY,语义与主链一致。

参数与返回值约定

单位与角口径

  • 角度一律为度,RA、Lon、Azimuth 归一化到 [0°, 360°),赤纬与黄纬取值 [−90°, 90°]
  • 视直径与视半径为角秒;sun.EarthDistance 为 AU,moon.EarthDistance 为千米
  • Phase 是 [0,1] 的受照比例,PhaseAge(轻量链路)为天,EquationTime 为小时
  • 距离类极值:earth.Perihelion / earth.Aphelion 为 AU,ApsisInfo.Distance 为千米

时标

观测输入与事件时刻采用民用时间约定;本库将 1972-01-01 以前的读数按 UT1 处理。ApparentSolarTime 另返回地方太阳时读数。time.Time 携带的时区只影响“当地自然日”的划分与返回值的时区,不改变绝对时刻。UT1 与图内时标的完整声明见时标声明。

高度与 aero

  • 升落接口的 height 是按椭球高(大地高)解读的观测点高程,单位米,不是正高;换算约定见观测点高度约定
  • aero = true:按动态标准大气折射与实时视半径计算上缘过地平线;aero = false:只做几何中心高度过地平线的判定
  • ApparentAltitude / ApparentZenith 的 pressureHPa、temperatureC 是观测时的气压(hPa)与气温(℃)

零值与越界

  • 极夜时升落接口返回 ERR_SUN_NEVER_RISE / ERR_MOON_NEVER_RISE;极昼时返回 ERR_SUN_NEVER_SET / ERR_MOON_NEVER_SET,此时第一个返回值为零值 time.Time
  • 朦影不存在时返回 ERR_TWILIGHT_NOT_EXISTS
  • 月亮升降事件落在查询日期之外时返回 ERR_NOT_TODAY,对应上面“月出月落按当天日期计算”的语义
  • DownTime / DownTimeN 是 SetTime 的废弃别名,ERR_SUN_NEVER_DOWN / ERR_MOON_NEVER_DOWN 是极昼错误的废弃别名,新代码不应使用
  • ...N 中 n < 0 用全部内嵌项、n >= 0 截断;n 只影响级数长度,不改变返回单位与时区

精度与适用范围

太阳与行星用内置 VSOP87 解析项,月球用内置 ELP2000/82 风格截断级数,覆盖 J2000 前后约 4000 年,无需外部星历文件。截断误差、月球链路的能力边界和 lite 链路的量化误差见适用范围与精度。本手册的太阳 / 月球接口适合历法、观测辅助、科普与业余预报;航天导航、精确掩星预报和严格动力学积分需要 JPL DE 等专业星历。