Files
astro/basic/precession.go
T
b612 044f2f4381 feat: 引入可切换星历模型并扩展天象观测链路
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算
- 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询
- 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出
- 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解
- 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤
- 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持
- 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理
- 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
2026-10-15 23:54:15 +08:00

187 lines
4.7 KiB
Go

package basic
import (
"math"
"b612.me/astro/internal/ltp"
)
// Precess 函数实现了基于 Explanatory Supplement 2nd ed. table 3.211.1 的岁差计算
// ra0, dec0: 初始赤经和赤纬(弧度)
// startJD: 初始儒略日
// targetJD: 目标儒略日
// 返回: 目标历元的赤经和赤纬(弧度)
func PrecessIAU1976(ra0, dec0, startJD, targetJD float64) (float64, float64) {
// 定义常量
ra0 *= rad
dec0 *= rad
t := (targetJD - startJD) / 36525.0
t0 := (startJD - 2451545.0) / 36525.0
// Explanatory Supplement 2nd ed. table 3.211.1
zeta := ((2306.2181+1.39656*t0-0.000139*t0*t0)*t +
(0.30188-0.000344*t0)*t*t + 0.017998*t*t*t) / 3600.0 * rad
z := ((2306.2181+1.39656*t0-0.000139*t0*t0)*t +
(1.09468+0.000066*t0)*t*t + 0.018203*t*t*t) / 3600.0 * rad
theta := ((2004.3109-0.85330*t0-0.000217*t0*t0)*t -
(0.42665+0.000217*t0)*t*t - 0.041833*t*t*t) / 3600.0 * rad
termA := math.Cos(dec0) * math.Sin(ra0+z)
termB := math.Cos(theta)*math.Cos(dec0)*math.Cos(ra0+z) - math.Sin(theta)*math.Sin(dec0)
termC := math.Sin(theta)*math.Cos(dec0)*math.Cos(ra0+z) + math.Cos(theta)*math.Sin(dec0)
raMinZ := math.Atan2(termA, termB)
dec := math.Asin(termC)
ra := raMinZ + zeta
return ra / rad, dec / rad
}
// 常量定义
const (
AS2R = 4.848136811095359935899141e-6 // 角秒到弧度的转换因子
D2PI = 6.283185307179586476925287 // 2π
EPS0 = 84381.406 * AS2R // J2000.0历元的黄赤交角(弧度)
)
// 赤道坐标结构体
type Equatorial struct {
RA float64 // 赤经(度)
Dec float64 // 赤纬(度)
}
// 三维向量
type Vector3 [3]float64
// 三维矩阵
type Matrix3 [3][3]float64
func ltpPECL(epj float64) Vector3 { return Vector3(ltp.EclipticPole(epj)) }
func ltpPEQU(epj float64) Vector3 { return Vector3(ltp.EquatorPole(epj)) }
func pxp(a, b Vector3) Vector3 { return Vector3(ltp.Cross(ltp.Vector3(a), ltp.Vector3(b))) }
func pn(p Vector3) (Vector3, float64) {
u, w := ltp.Normalize(ltp.Vector3(p))
return Vector3(u), w
}
// 计算长期岁差矩阵
func ltpPMAT(epj float64) Matrix3 {
// 计算赤道极向量和黄极向量
peqr := ltpPEQU(epj)
pecl := ltpPECL(epj)
// 计算春分点向量
v := pxp(peqr, pecl)
eqx, _ := pn(v)
// 计算中间行向量
v = pxp(peqr, eqx)
// 形成旋转矩阵
return Matrix3{
{eqx[0], eqx[1], eqx[2]},
{v[0], v[1], v[2]},
{peqr[0], peqr[1], peqr[2]},
}
}
// 计算包含GCRS框架偏差的长期岁差矩阵
func ltpPBMAT(epj float64) Matrix3 {
// 框架偏差 (IERS Conventions 2010, Eqs. 5.21 and 5.33)
dx := -0.016617 * AS2R
de := -0.0068192 * AS2R
dr := -0.0146 * AS2R
// 计算基本岁差矩阵
rp := ltpPMAT(epj)
// 应用偏差
return Matrix3{
{
rp[0][0] - rp[0][1]*dr + rp[0][2]*dx,
rp[0][0]*dr + rp[0][1] + rp[0][2]*de,
-rp[0][0]*dx - rp[0][1]*de + rp[0][2],
},
{
rp[1][0] - rp[1][1]*dr + rp[1][2]*dx,
rp[1][0]*dr + rp[1][1] + rp[1][2]*de,
-rp[1][0]*dx - rp[1][1]*de + rp[1][2],
},
{
rp[2][0] - rp[2][1]*dr + rp[2][2]*dx,
rp[2][0]*dr + rp[2][1] + rp[2][2]*de,
-rp[2][0]*dx - rp[2][1]*de + rp[2][2],
},
}
}
// 将赤道坐标转换为直角坐标向量
func raDecToVector(ra, dec float64) Vector3 {
raRad := ra * math.Pi / 180
decRad := dec * math.Pi / 180
cosDec := math.Cos(decRad)
return Vector3{
math.Cos(raRad) * cosDec,
math.Sin(raRad) * cosDec,
math.Sin(decRad),
}
}
// 将直角坐标向量转换为赤道坐标
func vectorToRaDec(v Vector3) (float64, float64) {
dec := math.Asin(v[2]) * 180 / math.Pi
ra := math.Atan2(v[1], v[0]) * 180 / math.Pi
if ra < 0 {
ra += 360
}
return ra, dec
}
// 应用长期岁差转换
func Precess(ra, dec, jdFrom, jdTo float64) (float64, float64) {
// 将儒略日转换为儒略纪元
epjFrom := 2000.0 + (jdFrom-2451545.0)/365.25
epjTo := 2000.0 + (jdTo-2451545.0)/365.25
// 计算从起始历元到J2000.0的逆矩阵
rpFrom := ltpPMAT(epjFrom)
rpFromInv := Matrix3{
{rpFrom[0][0], rpFrom[1][0], rpFrom[2][0]},
{rpFrom[0][1], rpFrom[1][1], rpFrom[2][1]},
{rpFrom[0][2], rpFrom[1][2], rpFrom[2][2]},
}
// 计算从J2000.0到目标历元的矩阵
rpTo := ltpPMAT(epjTo)
// 计算复合旋转矩阵
var rpFinal Matrix3
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
rpFinal[i][j] += rpTo[i][k] * rpFromInv[k][j]
}
}
}
// 将赤道坐标转换为直角坐标向量
vector := raDecToVector(ra, dec)
// 应用旋转矩阵
rotatedVector := Vector3{
rpFinal[0][0]*vector[0] + rpFinal[0][1]*vector[1] + rpFinal[0][2]*vector[2],
rpFinal[1][0]*vector[0] + rpFinal[1][1]*vector[1] + rpFinal[1][2]*vector[2],
rpFinal[2][0]*vector[0] + rpFinal[2][1]*vector[1] + rpFinal[2][2]*vector[2],
}
// 将直角坐标向量转换回赤道坐标
return vectorToRaDec(rotatedVector)
}