2bf8478639
- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑 - 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口 - 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界 - 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名 - 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验 - 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口 - 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
218 lines
7.4 KiB
Go
218 lines
7.4 KiB
Go
package basic
|
||
|
||
import (
|
||
. "b612.me/astro/tools"
|
||
"math"
|
||
)
|
||
|
||
// StarHeight 星体的高度角
|
||
// 传入 jde时间、瞬时赤经、瞬时赤纬、经度、纬度、时区,jde时间应为时区时间
|
||
// 返回高度角,单位为度
|
||
func StarHeight(jde, ra, dec, lon, lat, timezone float64) float64 {
|
||
// 转换为世界时
|
||
utcJde := jde - timezone/24.0
|
||
// 计算视恒星时
|
||
st := Limit360(ApparentSiderealTime(utcJde)*15 + lon)
|
||
// 计算时角
|
||
hourAngle := Limit360(st - ra)
|
||
// 高度角、时角与天球座标三角转换公式
|
||
// sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle)
|
||
sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle)
|
||
return ArcSin(sinHeight)
|
||
}
|
||
|
||
// StarAzimuth 星体的方位角
|
||
// 传入 jde时间、瞬时赤经、瞬时赤纬、经度、纬度、时区,jde时间应为时区时间
|
||
// 返回方位角,单位为度,正北为0,度数顺时针增加,取值范围[0-360)
|
||
func StarAzimuth(jde, ra, dec, lon, lat, timezone float64) float64 {
|
||
// 转换为世界时
|
||
utcJde := jde - timezone/24.0
|
||
// 计算视恒星时
|
||
st := Limit360(ApparentSiderealTime(utcJde)*15 + lon)
|
||
// 计算时角
|
||
hourAngle := Limit360(st - ra)
|
||
// 三角转换公式
|
||
tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat))
|
||
azimuth := ArcTan(tanAzimuth)
|
||
if azimuth < 0 {
|
||
if hourAngle/15 < 12 {
|
||
return azimuth + 360
|
||
}
|
||
return azimuth + 180
|
||
}
|
||
if hourAngle/15 < 12 {
|
||
return azimuth + 180
|
||
}
|
||
return azimuth
|
||
}
|
||
|
||
// StarHourAngle 星体的时角
|
||
// 传入 jde时间、瞬时赤经、瞬时赤纬、经度、时区,jde时间应为时区时间
|
||
// 返回时角
|
||
func StarHourAngle(jde, ra, lon, timezone float64) float64 {
|
||
// 转换为世界时
|
||
utcJde := jde - timezone/24.0
|
||
// 计算视恒星时
|
||
st := Limit360(ApparentSiderealTime(utcJde)*15 + lon)
|
||
// 计算时角
|
||
return Limit360(st - ra)
|
||
}
|
||
|
||
// MeanSiderealTime 平恒星时
|
||
func MeanSiderealTime(jd float64) float64 {
|
||
return MeanSiderealTime2006(jd)
|
||
}
|
||
|
||
// ApparentSiderealTime 视恒星时,计算章动
|
||
func ApparentSiderealTime(jd float64) float64 {
|
||
return ApparentSiderealTime2006(jd)
|
||
}
|
||
|
||
// MeanSiderealTime1982 不含章动下的恒星时
|
||
func MeanSiderealTime1982(jd float64) float64 {
|
||
t := (jd - 2451545) / 36525
|
||
return (Limit360(280.46061837+360.98564736629*(jd-2451545.0)+0.000387933*t*t-t*t*t/38710000) / 15)
|
||
}
|
||
|
||
// ApparentSiderealTime1982 视恒星时,计算章动
|
||
func ApparentSiderealTime1982(jd float64) float64 {
|
||
tmp := MeanSiderealTime1982(jd)
|
||
dpsi, deps := Nutation2000B(jd)
|
||
return tmp + dpsi*math.Cos((Obliquity1980(jd)+deps)*math.Pi/180)/15
|
||
}
|
||
|
||
// EarthRotationAngle 计算地球自转角 (ERA)
|
||
// jd_ut1: UT1 时间的儒略日
|
||
// 返回值: 地球自转角 (弧度)
|
||
func EarthRotationAngle(jd_ut1 float64) float64 {
|
||
t := jd_ut1 - 2451545.0
|
||
frac := math.Mod(jd_ut1, 1.0)
|
||
|
||
era := math.Mod(math.Pi*2*(0.7790572732640+0.00273781191135448*t+frac), math.Pi*2)
|
||
if era < 0 {
|
||
era += math.Pi * 2
|
||
}
|
||
|
||
return era
|
||
}
|
||
|
||
// MeanSiderealTime2006 计算格林尼治平恒星时 (GMST)
|
||
// jd_ut1: UT1 时间的儒略日
|
||
// jd_tt: TT 时间的儒略日
|
||
// 返回值: 格林尼治平恒星时 (弧度)
|
||
func MeanSiderealTime2006(jd_ut1 float64) float64 {
|
||
jd_tt := TD2UT(jd_ut1, true)
|
||
t := (jd_tt - 2451545.0) / 36525.0
|
||
era := EarthRotationAngle(jd_ut1)
|
||
|
||
// 公式 2.12
|
||
gmst := math.Mod(era+(0.014506+4612.15739966*t+1.39667721*t*t+
|
||
-0.00009344*t*t*t+0.00001882*t*t*t*t)/60/60*math.Pi/180, math.Pi*2)
|
||
|
||
if gmst < 0 {
|
||
gmst += math.Pi * 2
|
||
}
|
||
|
||
return gmst * deg / 15
|
||
}
|
||
|
||
// ApparentSiderealTime2006 视恒星时,计算章动。
|
||
// 一次求值只要一份 IAU2000B 章动(黄经与交角在同一次展开里同时得到),并走有界记忆表,
|
||
// 避免月掩路径里的重复求值;ΔT 覆盖会使记忆表按世代失效。见 sidereal_memo.go。
|
||
// ApparentSiderealTime2006 computes apparent sidereal time. One evaluation needs only a single
|
||
// IAU2000B nutation expansion (longitude and obliquity come out of the same series) and is
|
||
// memoized so repeated occultation-path queries do not rerun it; a ΔT override invalidates the
|
||
// memo by generation. See sidereal_memo.go.
|
||
func ApparentSiderealTime2006(jd float64) float64 {
|
||
if value, ok := siderealMemoLoad(jd); ok {
|
||
return value
|
||
}
|
||
generation := siderealMemoGeneration()
|
||
tmp := MeanSiderealTime2006(jd)
|
||
dpsi, deps := Nutation2000B(jd)
|
||
value := tmp + dpsi*math.Cos((Obliquity1980(jd)+deps)*math.Pi/180)/15
|
||
siderealMemoStore(jd, value, generation)
|
||
return value
|
||
}
|
||
|
||
func StarRiseTime(jde, ra, dec, lon, lat, height, timezone float64, aero bool) (float64, error) {
|
||
return StarRiseSetTime(jde, ra, dec, lon, lat, height, timezone, aero, true)
|
||
}
|
||
|
||
func StarSetTime(jde, ra, dec, lon, lat, height, timezone float64, aero bool) (float64, error) {
|
||
return StarRiseSetTime(jde, ra, dec, lon, lat, height, timezone, aero, false)
|
||
}
|
||
|
||
func StarRiseSetTime(jde, ra, dec, lon, lat, height, timezone float64, aero, isRise bool) (float64, error) {
|
||
if !isFiniteFloat(jde) || !isFiniteFloat(ra) || !isFiniteFloat(dec) || !isFiniteFloat(lon) || !isFiniteFloat(lat) || !isFiniteFloat(height) || !isFiniteFloat(timezone) {
|
||
return 0, ErrInvalidObservationInput
|
||
}
|
||
//jde 世界时,非力学时,当地时区 0时,无需转换力学时
|
||
//ra,dec 瞬时天球座标,非J2000等时间天球坐标
|
||
jde = math.Floor(jde) + 0.5
|
||
targetAltitude := StandardAltitudeStar(aero, height, lat)
|
||
sct := StarCulminationTime(jde, ra, lon, timezone)
|
||
tmp := (Sin(targetAltitude) - Sin(dec)*Sin(lat)) / (Cos(dec) * Cos(lat))
|
||
if math.Abs(tmp) > 1 {
|
||
if StarHeight(sct, ra, dec, lon, lat, timezone) < 0 {
|
||
return 0, ErrNeverRise
|
||
}
|
||
return 0, ErrNeverSet
|
||
}
|
||
var estimateJD float64
|
||
if isRise {
|
||
estimateJD = sct - ArcCos(tmp)/15.0/24.0
|
||
} else {
|
||
estimateJD = sct + ArcCos(tmp)/15.0/24.0
|
||
}
|
||
var ok bool
|
||
estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 {
|
||
stDegree := StarHeight(prevJD, ra, dec, lon, lat, timezone) - targetAltitude
|
||
stDegreep := (StarHeight(prevJD+0.000005, ra, dec, lon, lat, timezone) - StarHeight(prevJD-0.000005, ra, dec, lon, lat, timezone)) / 0.00001
|
||
return stDegree / stDegreep
|
||
})
|
||
if !ok {
|
||
return 0, ErrInvalidObservationInput
|
||
}
|
||
return estimateJD, nil
|
||
}
|
||
|
||
func StarCulminationTime(jde, ra, lon, timezone float64) float64 {
|
||
if !isFiniteFloat(jde) || !isFiniteFloat(ra) || !isFiniteFloat(lon) || !isFiniteFloat(timezone) {
|
||
return math.NaN()
|
||
}
|
||
//jde 世界时,非力学时,当地时区 0时,无需转换力学时
|
||
//ra,dec 瞬时天球座标,非J2000等时间天球坐标
|
||
jde = math.Floor(jde) + 0.5
|
||
estimateJD := jde + Limit360(360-StarHourAngle(jde, ra, lon, timezone))/15.0/24.0*0.99726851851851851851
|
||
limitStarHA := func(jde, ra, lon, timezone float64) float64 {
|
||
ha := StarHourAngle(jde, ra, lon, timezone)
|
||
if ha < 180 {
|
||
ha += 360
|
||
}
|
||
return ha
|
||
}
|
||
var ok bool
|
||
estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 {
|
||
stDegree := limitStarHA(prevJD, ra, lon, timezone) - 360
|
||
stDegreep := (limitStarHA(prevJD+0.000005, ra, lon, timezone) - limitStarHA(prevJD-0.000005, ra, lon, timezone)) / 0.00001
|
||
return stDegree / stDegreep
|
||
})
|
||
if !ok {
|
||
return math.NaN()
|
||
}
|
||
return estimateJD
|
||
}
|
||
|
||
func StarAngularSeparation(ra1, dec1, ra2, dec2 float64) float64 {
|
||
//cos(d)=sinδ1 sinδ2 + cosδ1 cosδ2 cos(α1-α2)
|
||
d := Sin(dec1)*Sin(dec2) + Cos(dec1)*Cos(dec2)*Cos(ra1-ra2)
|
||
if math.Abs(d) >= 0.999999997 {
|
||
//d = √(Δα*cosδ)2+(Δδ)2
|
||
tmp1 := ((ra1 - ra2) * Cos((dec1+dec2)/2))
|
||
tmp2 := (dec1 - dec2)
|
||
return math.Sqrt(tmp1*tmp1 + tmp2*tmp2)
|
||
}
|
||
return ArcCos(d)
|
||
}
|