astro/basic/moon_phase.go
starainrt 3ffdbe0034
feat: 扩展天文计算能力
- 新增日食、月食、本地可见性、中心线、半影区域、SVG 图示与沙罗周期信息
- 新增行星冲合、留、方照、物理星历、视直径、相位、亮肢角、轨道节点等计算
- 新增木星伽利略卫星位置、现象与接触事件计算
- 新增恒星星表、星座判定、自行修正与观测辅助能力
- 新增 coord、formula、orbit、sundial、lite/sun、lite/moon 等扩展包
- 完善农历年号、月相英文别名、视差角、大气质量、折射、日晷与双星计算
- 增加 NASA、JPL Horizons、IMCCE 等回归测试数据与基线测试
- 重构基础算法文件组织,补充大量公开 API 注释和语义回归测试
- 更新中文和英文 README,补充示例、精度说明、SVG 配图
2026-05-01 22:38:44 +08:00

210 lines
7.3 KiB
Go

package basic
import (
"math"
. "b612.me/astro/tools"
)
func MoonPhase(jd float64) float64 {
moonBo := HMoonTrueBo(jd)
sunLo := HSunApparentLo(jd)
moonLo := HMoonApparentLo(jd)
tmp := Cos(moonBo) * Cos(sunLo-moonLo)
earthSunDistance := Distance(jd) * 149597870.691
i := earthSunDistance * Sin(ArcCos(tmp)) / (HMoonAway(jd) - earthSunDistance*tmp)
i = ArcTan(i)
if i < 0 {
i += 180
}
if i > 180 {
i -= 180
}
k := (1 + Cos(i)) / 2
return k
}
func SunMoonSeek(jde float64, degree float64) float64 {
p := HMoonApparentLo(jde) - (HSunApparentLo(jde)) - degree
for p < -180 {
p += 360
}
for p > 180 {
p -= 360
}
return p
}
func CalcMoonSHByJDE(jde float64, phaseType int) float64 {
phaseType = phaseType * 180
estimateJD := jde
for {
prevJD := estimateJD
stDegree := SunMoonSeek(prevJD, float64(phaseType))
stDegreep := (SunMoonSeek(prevJD+0.000005, float64(phaseType)) - SunMoonSeek(prevJD-0.000005, float64(phaseType))) / 0.00001
estimateJD = prevJD - stDegree/stDegreep
if math.Abs(estimateJD-prevJD) <= 0.00001 {
break
}
}
return estimateJD
}
func CalcMoonSH(year float64, phaseType int) float64 {
jde := CalcMoonS(year, phaseType)
phaseType = phaseType * 180
estimateJD := jde
for {
prevJD := estimateJD
stDegree := SunMoonSeek(prevJD, float64(phaseType))
stDegreep := (SunMoonSeek(prevJD+0.000005, float64(phaseType)) - SunMoonSeek(prevJD-0.000005, float64(phaseType))) / 0.00001
estimateJD = prevJD - stDegree/stDegreep
if math.Abs(estimateJD-prevJD) <= 0.00001 {
break
}
}
return estimateJD
}
/*
* C=0朔月时刻 =1 望月
*/
func CalcMoonS(year float64, phaseType int) float64 {
k := math.Floor((year - 2000) * 12.36827)
if phaseType == 1 {
k += 0.5
}
T := k / 1236.85
jde := 2451550.09765 + 29.530588853*k + 0.0001337*T*T - 0.000000150*T*T*T + 0.00000000073*T*T*T*T
//太阳平近点角:
M := Limit360(2.5534 + 29.10535669*k - 0.0000218*T*T - 0.00000011*T*T*T)
//月亮的平近点角:
N := Limit360(201.5643 + 385.81693528*k + 0.0107438*T*T + 0.00001239*T*T*T - 0.000000058*T*T*T*T)
//月亮的纬度参数:
F := Limit360(160.7108 + 390.67050274*k - 0.0016341*T*T - 0.00000227*T*T*T + 0.000000011*T*T*T*T)
//月亮轨道升交点经度:
O := Limit360(124.7746 - 1.56375580*k + 0.0020691*T*T + 0.00000215*T*T*T)
E := 1 - 0.002516*T - 0.0000074*T*T
//die(E." ".M." ".N." ".F." ".O);
angles := []float64{N, M, 2 * N, 2 * F, N - M, N + M, 2 * M, N - 2*F, N + 2*F, 2*N + M, 3 * N, M + 2*F, M - 2*F, 2*N - M, O, N + 2*M, 2*N - 2*F, 3 * M, N + M - 2*F, 2*N + 2*F, N + M + 2*F, N - M + 2*F, N - M - 2*F, 3*N + M, 4 * N}
var coeffs []float64
if phaseType == 0 {
coeffs = []float64{-0.40720, 0.17241 * E, 0.01608, 0.01039, 0.00739 * E, -0.00514 * E, 0.00208 * E * E, -0.00111, -0.00057, 0.00056 * E, -0.00042, 0.00042 * E, 0.00038 * E, -0.00024 * E, -0.00017, -0.00007, 0.00004, 0.00004, 0.00003, 0.00003, -0.00003, 0.00003, -0.00002, -0.00002, 0.00002}
} else {
coeffs = []float64{-0.40614, 0.17302 * E, 0.01614, 0.01043, 0.00734 * E, -0.00515 * E, 0.00209 * E * E, -0.00111, -0.00057, 0.00056 * E, -0.00042, 0.00042 * E, 0.00038 * E, -0.00024 * E, -0.00017, -0.00007, 0.00004, 0.00004, 0.00003, 0.00003, -0.00003, 0.00003, -0.00002, -0.00002, 0.00002}
}
var correction float64
for idx, angle := range angles {
correction += Sin(angle) * coeffs[idx]
}
//die(tmp);
A1 := 299.77 + 0.107408*k - 0.009173*T*T
A2 := 251.88 + 0.016321*k
A3 := 251.83 + 26.651886*k
A4 := 349.42 + 36.412478*k
A5 := 84.66 + 18.206239*k
A6 := 141.74 + 53.303771*k
A7 := 207.14 + 2.453732*k
A8 := 154.84 + 7.306860*k
A9 := 34.52 + 27.261239*k
A10 := 207.19 + 0.121824*k
A11 := 291.34 + 1.844379*k
A12 := 161.72 + 24.198154*k
A13 := 239.56 + 25.513099*k
A14 := 331.55 + 3.592518*k
planetaryCorrection := 325*Sin(A1) + 165*Sin(A2) + 164*Sin(A3) + 126*Sin(A4) + 110*Sin(A5) + 62*Sin(A6) + 60*Sin(A7) + 56*Sin(A8) + 47*Sin(A9) + 42*Sin(A10) + 40*Sin(A11) + 37*Sin(A12) + 35*Sin(A13) + 23*Sin(A14)
planetaryCorrection /= 1000000
jde = jde + planetaryCorrection + correction
return jde
}
func CalcMoonXHByJDE(jde float64, quarterType int) float64 {
if quarterType == 0 {
quarterType = 90
} else {
quarterType = -90
}
estimateJD := jde
for {
prevJD := estimateJD
stDegree := SunMoonSeek(prevJD, float64(quarterType))
stDegreep := (SunMoonSeek(prevJD+0.000005, float64(quarterType)) - SunMoonSeek(prevJD-0.000005, float64(quarterType))) / 0.00001
estimateJD = prevJD - stDegree/stDegreep
if math.Abs(estimateJD-prevJD) <= 0.00001 {
break
}
}
return estimateJD
}
func CalcMoonXH(year float64, quarterType int) float64 {
jde := CalcMoonX(year, quarterType)
if quarterType == 0 {
quarterType = 90
} else {
quarterType = -90
}
estimateJD := jde
for {
prevJD := estimateJD
stDegree := SunMoonSeek(prevJD, float64(quarterType))
stDegreep := (SunMoonSeek(prevJD+0.000005, float64(quarterType)) - SunMoonSeek(prevJD-0.000005, float64(quarterType))) / 0.00001
estimateJD = prevJD - stDegree/stDegreep
if math.Abs(estimateJD-prevJD) <= 0.00001 {
break
}
}
return estimateJD
}
func CalcMoonX(year float64, quarterType int) float64 {
k := math.Floor((year-2000)*12.36827) + 0.25
if quarterType == 1 {
k += 0.5
}
T := k / 1236.85
jde := 2451550.09765 + 29.530588853*k + 0.0001337*T*T - 0.000000150*T*T*T + 0.00000000073*T*T*T*T
//太阳平近点角:
M := Limit360(2.5534 + 29.10535669*k - 0.0000218*T*T - 0.00000011*T*T*T)
//月亮的平近点角:
N := Limit360(201.5643 + 385.81693528*k + 0.0107438*T*T + 0.00001239*T*T*T - 0.000000058*T*T*T*T)
//月亮的纬度参数:
F := Limit360(160.7108 + 390.67050274*k - 0.0016341*T*T - 0.00000227*T*T*T + 0.000000011*T*T*T*T)
//月亮轨道升交点经度:
O := Limit360(124.7746 - 1.56375580*k + 0.0020691*T*T + 0.00000215*T*T*T)
E := 1 - 0.002516*T - 0.0000074*T*T
//die(E." ".M." ".N." ".F." ".O);
ZQ := []float64{N, M, N + M, 2 * N, 2 * F, N - M, 2 * M, N - 2*F, N + 2*F, 3 * N, 2*N - M, M + 2*F, M - 2*F, N + 2*M, 2*N + M, O, N - M - 2*F, 2*N + 2*F, N + M + 2*F, N - 2*F, N + M - 2*F, 3 * M, 2*N - 2*F, N - M + 2*F, M + 3*N}
MN := []float64{-0.62801, 0.17172 * E, -0.01183 * E, 0.00862, 0.00804, 0.00454 * E, 0.00204 * E * E, -0.00180, -0.00070, -0.00040, -0.00034 * E, 0.00032 * E, 0.00032 * E, -0.00028 * E * E, 0.00027 * E, -0.00017, -0.00005, 0.00004, -0.00004, 0.00004, 0.00003, 0.00003, 0.00002, 0.00002, -0.00002}
var correction float64
for idx, angle := range ZQ {
correction += Sin(angle) * MN[idx]
}
W := 0.00306 - 0.00038*E*Cos(M) + 0.00026*Cos(N) - 0.00002*Cos(N-M) + 0.00002*Cos(N+M) + 0.00002*Cos(2*F)
A1 := 299.77 + 0.107408*k - 0.009173*T*T
A2 := 251.88 + 0.016321*k
A3 := 251.83 + 26.651886*k
A4 := 349.42 + 36.412478*k
A5 := 84.66 + 18.206239*k
A6 := 141.74 + 53.303771*k
A7 := 207.14 + 2.453732*k
A8 := 154.84 + 7.306860*k
A9 := 34.52 + 27.261239*k
A10 := 207.19 + 0.121824*k
A11 := 291.34 + 1.844379*k
A12 := 161.72 + 24.198154*k
A13 := 239.56 + 25.513099*k
A14 := 331.55 + 3.592518*k
planetaryCorrection := 325*Sin(A1) + 165*Sin(A2) + 164*Sin(A3) + 126*Sin(A4) + 110*Sin(A5) + 62*Sin(A6) + 60*Sin(A7) + 56*Sin(A8) + 47*Sin(A9) + 42*Sin(A10) + 40*Sin(A11) + 37*Sin(A12) + 35*Sin(A13) + 23*Sin(A14)
planetaryCorrection /= 1000000
//die(tmp2);
//die(JDE." ".tmp." ".tmp2." ".W);
jde = jde + planetaryCorrection + correction
if quarterType == 0 {
jde += W
} else {
jde -= W
}
return jde
}