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

187 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package basic
import "math"
// DeltaTModel 标识一个可选的已发布 ΔT 模型 / a selectable published ΔT model.
type DeltaTModel string
const (
// DeltaTModelDefault 内置默认模型:Stephenson–Morrison–Hohenkerk 2016 + Morrison 2021 的样条与积分 lod。
// DeltaTModelDefault is the built-in model: the SMH2016 + Morrison 2021 spline and integrated lod.
DeltaTModelDefault DeltaTModel = ""
// DeltaTModelSMH2016 显式选择内置的 SMH2016 + Morrison 2021 模型。
// DeltaTModelSMH2016 selects the built-in SMH2016 + Morrison 2021 model explicitly.
DeltaTModelSMH2016 DeltaTModel = "smh2016"
// DeltaTModelMS2004 Morrison & Stephenson 2004 的长期抛物线 ΔT = −20 + 32u²(u = (y−1820)/100),单位秒。
// DeltaTModelMS2004 is the Morrison & Stephenson 2004 long-term parabola in seconds.
DeltaTModelMS2004 DeltaTModel = "ms2004"
// DeltaTModelEspenakMeeus2006 Espenak & Meeus 2006 的分段多项式,基于 M&S2004,未加配对修正 c。
// DeltaTModelEspenakMeeus2006 is the Espenak & Meeus 2006 piecewise polynomial without the pairing term c.
DeltaTModelEspenakMeeus2006 DeltaTModel = "espenak_meeus_2006"
// DeltaTModelNASACanon2006 是 Espenak–Meeus 再加配对修正 c = −0.000012932(y−1955)²(y < 1955),
// 即 NASA 五千年目录实际印刷的 ΔT 口径,配对的是 ELP2000/82 的 −25.858″/cy²。
// DeltaTModelNASACanon2006 adds the pairing term c for y < 1955, the convention printed by NASA's canon.
DeltaTModelNASACanon2006 DeltaTModel = "nasa_canon_2006"
// DeltaTModelManual 表示当前生效的是 SetDeltaTFn 注入的任意函数,不是命名模型。
// DeltaTModelManual reports that an arbitrary function injected with SetDeltaTFn is active.
DeltaTModelManual DeltaTModel = "manual"
)
// deltaTModelFunction 把命名模型包成 ΔT 钩子的签名;isJulianDay 为假时入参是十进制年。
func deltaTModelFunction(model DeltaTModel, keepObserved bool) func(float64, bool) float64 {
return func(date float64, isJulianDay bool) float64 {
if math.IsNaN(date) || math.IsInf(date, 0) {
return math.NaN()
}
if isJulianDay {
return DeltaTModelSeconds(model, date, keepObserved)
}
if keepObserved {
year := math.Floor(date)
start := JDCalc(int(year), 1, 1)
end := JDCalc(int(year)+1, 1, 1)
jd := start + (date-year)*(end-start)
if seconds, ok := deltaTMonthlyAt(jd); ok {
return seconds
}
}
return deltaTModelSecondsAtYear(model, date)
}
}
// DeltaTModelSeconds 按命名模型求 ΔT(秒),入参为 UT 儒略日。
//
// keepObserved 为真时,落在逐月实测表覆盖段内的时刻一律返回实测值,模型只接管表外;
// 这是"实测优先"的推荐用法,也是内置默认模型的结构。为假时全程使用模型,仅用于口径对照。
// DeltaTModelSeconds evaluates a named model. With keepObserved the monthly observed table wins
// wherever it covers the instant and the model only takes over outside it.
func DeltaTModelSeconds(model DeltaTModel, jd float64, keepObserved bool) float64 {
if math.IsNaN(jd) || math.IsInf(jd, 0) {
return math.NaN()
}
switch model {
case DeltaTModelDefault, DeltaTModelSMH2016:
if keepObserved {
// 与内置默认逐位一致(含实测段与样条表尾的常值锚定)。
return deltaTModelSecondsAtUT(jd)
}
return deltaTSplineAtJDE(jd)
}
if keepObserved {
if seconds, ok := deltaTMonthlyAt(jd); ok {
return seconds
}
}
return deltaTModelSecondsAtYear(model, deltaTYearAtJDE(jd))
}
// SetDeltaTModel 安装命名模型;keepObserved 为真时保留实测表覆盖段(推荐),未知模型返回 false 且不改动现状。
// SetDeltaTModel installs a named model, keeping the observed span when keepObserved is true;
// an unknown model returns false and leaves the current model untouched.
func SetDeltaTModel(model DeltaTModel, keepObserved bool) bool {
if !knownDeltaTModel(model) {
return false
}
if model == DeltaTModelDefault || model == DeltaTModelSMH2016 {
if keepObserved {
// 内置模型本身就是"实测优先",直接用它可以避免表尾锚定出现差异。
installDeltaTFn(DefaultDeltaTv2, model, true)
return true
}
installDeltaTFn(deltaTModelFunction(model, false), model, false)
return true
}
installDeltaTFn(deltaTModelFunction(model, keepObserved), model, keepObserved)
return true
}
// GetDeltaTModel 返回当前生效的模型与是否保留实测段;SetDeltaTFn 注入的任意函数报 DeltaTModelManual。
// GetDeltaTModel returns the active model and whether the observed span is kept; an arbitrary
// function injected with SetDeltaTFn reports DeltaTModelManual.
func GetDeltaTModel() (DeltaTModel, bool) {
deltaTFnMu.RLock()
defer deltaTFnMu.RUnlock()
return activeDeltaTModel, activeDeltaTKeepObserved
}
func knownDeltaTModel(model DeltaTModel) bool {
switch model {
case DeltaTModelDefault, DeltaTModelSMH2016, DeltaTModelMS2004,
DeltaTModelEspenakMeeus2006, DeltaTModelNASACanon2006:
return true
}
return false
}
// deltaTModelSecondsAtYear 按十进制年求模型值;实测段是否介入由调用方决定。
func deltaTModelSecondsAtYear(model DeltaTModel, year float64) float64 {
switch model {
case DeltaTModelMS2004:
u := (year - 1820) / 100
return -20 + 32*u*u
case DeltaTModelEspenakMeeus2006:
return espenakMeeus2006Seconds(year)
case DeltaTModelNASACanon2006:
value := espenakMeeus2006Seconds(year)
if year < 1955 {
// 配对修正:把 −26.0″/cy² 口径的 ΔT 换到 −25.858″/cy²(1955 起原子时使其与月球历表无关)。
d := year - 1955
value += -0.000012932 * d * d
}
return value
}
return DeltaTSplineY(year)
}
// espenakMeeus2006Seconds 是 Espenak & Meeus 2006 对 −1999..+3000 的分段多项式(秒),
// 自变量 y = year + (month−0.5)/12;分段边界处两段取值一致(−500 处由 17203.7 强制连续)。
func espenakMeeus2006Seconds(y float64) float64 {
switch {
case y < -500:
u := (y - 1820) / 100
return -20 + 32*u*u
case y < 500:
u := y / 100
return 10583.6 + u*(-1014.41+u*(33.78311+u*(-5.952053+u*(-0.1798452+u*(0.022174192+u*0.0090316521)))))
case y < 1600:
u := (y - 1000) / 100
return 1574.2 + u*(-556.01+u*(71.23472+u*(0.319781+u*(-0.8503463+u*(-0.005050998+u*0.0083572073)))))
case y < 1700:
t := y - 1600
return 120 - 0.9808*t - 0.01532*t*t + t*t*t/7129
case y < 1800:
t := y - 1700
return 8.83 + t*(0.1603+t*(-0.0059285+t*(0.00013336-t/1174000)))
case y < 1860:
t := y - 1800
return 13.72 + t*(-0.332447+t*(0.0068612+t*(0.0041116+t*(-0.00037436+
t*(0.0000121272+t*(-0.0000001699+t*0.000000000875))))))
case y < 1900:
t := y - 1860
return 7.62 + t*(0.5737+t*(-0.251754+t*(0.01680668+t*(-0.0004473624+t/233174))))
case y < 1920:
t := y - 1900
return -2.79 + t*(1.494119+t*(-0.0598939+t*(0.0061966-t*0.000197)))
case y < 1941:
t := y - 1920
return 21.20 + t*(0.84493+t*(-0.076100+t*0.0020936))
case y < 1961:
t := y - 1950
return 29.07 + t*(0.407-t/233+t*t/2547)
case y < 1986:
t := y - 1975
return 45.45 + t*(1.067-t/260-t*t/718)
case y < 2005:
t := y - 2000
return 63.86 + t*(0.3345+t*(-0.060374+t*(0.0017275+t*(0.000651814+t*0.00002373599))))
case y < 2050:
t := y - 2000
return 62.92 + t*(0.32217+t*0.005589)
case y < 2150:
u := (y - 1820) / 100
return -20 + 32*u*u - 0.5628*(2150-y)
}
u := (y - 1820) / 100
return -20 + 32*u*u
}