Files
astro/basic/star.go
T

218 lines
7.5 KiB
Go
Raw Permalink Normal View History

2019-10-24 10:44:21 +08:00
package basic
import (
. "b612.me/astro/tools"
2022-05-10 22:24:10 +08:00
"math"
2019-10-24 10:44:21 +08:00
)
2022-05-06 12:42:01 +08:00
// StarHeight 星体的高度角
// 传入 jde时间、瞬时赤经、瞬时赤纬、经度、纬度、时区,jde时间应为时区时间
// 返回高度角,单位为度
func StarHeight(localJD, ra, dec, lon, lat, timezone float64) float64 {
2022-05-06 12:42:01 +08:00
// 转换为世界时
utcJD := localJD - timezone/24.0
2022-05-06 12:42:01 +08:00
// 计算视恒星时
st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon)
2022-05-06 12:42:01 +08:00
// 计算时角
2026-05-01 22:38:44 +08:00
hourAngle := Limit360(st - ra)
2022-05-06 12:42:01 +08:00
// 高度角、时角与天球座标三角转换公式
2026-05-01 22:38:44 +08:00
// sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle)
sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle)
2022-05-06 12:42:01 +08:00
return ArcSin(sinHeight)
}
2025-09-18 13:16:04 +08:00
// StarAzimuth 星体的方位角
2022-05-06 12:42:01 +08:00
// 传入 jde时间、瞬时赤经、瞬时赤纬、经度、纬度、时区,jde时间应为时区时间
// 返回方位角,单位为度,正北为0,度数顺时针增加,取值范围[0-360)
func StarAzimuth(localJD, ra, dec, lon, lat, timezone float64) float64 {
2022-05-06 12:42:01 +08:00
// 转换为世界时
utcJD := localJD - timezone/24.0
2022-05-06 12:42:01 +08:00
// 计算视恒星时
st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon)
2022-05-06 12:42:01 +08:00
// 计算时角
2026-05-01 22:38:44 +08:00
hourAngle := Limit360(st - ra)
2022-05-06 12:42:01 +08:00
// 三角转换公式
2026-05-01 22:38:44 +08:00
tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat))
azimuth := ArcTan(tanAzimuth)
if azimuth < 0 {
if hourAngle/15 < 12 {
return azimuth + 360
2022-05-06 12:42:01 +08:00
}
2026-05-01 22:38:44 +08:00
return azimuth + 180
2022-05-06 12:42:01 +08:00
}
2026-05-01 22:38:44 +08:00
if hourAngle/15 < 12 {
return azimuth + 180
2022-05-06 12:42:01 +08:00
}
2026-05-01 22:38:44 +08:00
return azimuth
2022-05-06 12:42:01 +08:00
}
2025-09-18 13:16:04 +08:00
// StarHourAngle 星体的时角
2022-05-06 12:42:01 +08:00
// 传入 jde时间、瞬时赤经、瞬时赤纬、经度、时区,jde时间应为时区时间
// 返回时角
func StarHourAngle(localJD, ra, lon, timezone float64) float64 {
2022-05-06 12:42:01 +08:00
// 转换为世界时
utcJD := localJD - timezone/24.0
2022-05-06 12:42:01 +08:00
// 计算视恒星时
st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon)
2022-05-06 12:42:01 +08:00
// 计算时角
return Limit360(st - ra)
}
2025-09-18 13:16:04 +08:00
// MeanSiderealTime 平恒星时
2026-05-01 22:38:44 +08:00
func MeanSiderealTime(jd float64) float64 {
return MeanSiderealTime2006(jd)
2019-10-24 10:44:21 +08:00
}
2022-05-12 15:55:48 +08:00
// ApparentSiderealTime 视恒星时,计算章动
2026-05-01 22:38:44 +08:00
func ApparentSiderealTime(jd float64) float64 {
return ApparentSiderealTime2006(jd)
2025-09-18 13:16:04 +08:00
}
// MeanSiderealTime1982 不含章动下的恒星时
2026-05-01 22:38:44 +08:00
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)
2025-09-18 13:16:04 +08:00
}
// ApparentSiderealTime1982 视恒星时,计算章动
2026-05-01 22:38:44 +08:00
func ApparentSiderealTime1982(jd float64) float64 {
tmp := MeanSiderealTime1982(jd)
dpsi, deps := Nutation2000B(jd)
return tmp + dpsi*math.Cos((Obliquity1980(jd)+deps)*math.Pi/180)/15
2025-09-18 13:16:04 +08:00
}
// 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
2019-10-24 10:44:21 +08:00
}
2025-09-18 13:16:04 +08:00
// MeanSiderealTime2006 计算格林尼治平恒星时 (GMST)
// jd_ut1: UT1 时间的儒略日
// jd_tt: TT 时间的儒略日
// 返回值: 格林尼治平恒星时 (弧度)
func MeanSiderealTime2006(jd_ut1 float64) float64 {
jd_tt := UT12TT(jd_ut1)
2025-09-18 13:16:04 +08:00
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.
2026-05-01 22:38:44 +08:00
func ApparentSiderealTime2006(jd float64) float64 {
if value, ok := siderealMemoLoad(jd); ok {
return value
}
generation := siderealMemoGeneration()
2026-05-01 22:38:44 +08:00
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
2025-09-18 13:16:04 +08:00
}
func StarRiseTime(localJD, ra, dec, lon, lat, height, timezone float64, aero bool) (float64, error) {
return StarRiseSetTime(localJD, ra, dec, lon, lat, height, timezone, aero, true)
2019-10-24 10:44:21 +08:00
}
func StarSetTime(localJD, ra, dec, lon, lat, height, timezone float64, aero bool) (float64, error) {
return StarRiseSetTime(localJD, ra, dec, lon, lat, height, timezone, aero, false)
2022-05-10 22:24:10 +08:00
}
func StarRiseSetTime(localJD, ra, dec, lon, lat, height, timezone float64, aero, isRise bool) (float64, error) {
if !isFiniteFloat(localJD) || !isFiniteFloat(ra) || !isFiniteFloat(dec) || !isFiniteFloat(lon) || !isFiniteFloat(lat) || !isFiniteFloat(height) || !isFiniteFloat(timezone) {
return 0, ErrInvalidObservationInput
}
// localJD 是本地民用日锚点(当地 0 时),不是力学时。
2022-05-10 22:24:10 +08:00
//ra,dec 瞬时天球座标,非J2000等时间天球坐标
localJD = math.Floor(localJD) + 0.5
2026-05-01 22:38:44 +08:00
targetAltitude := StandardAltitudeStar(aero, height, lat)
sct := StarCulminationTime(localJD, ra, lon, timezone)
2026-05-01 22:38:44 +08:00
tmp := (Sin(targetAltitude) - Sin(dec)*Sin(lat)) / (Cos(dec) * Cos(lat))
2022-05-10 22:24:10 +08:00
if math.Abs(tmp) > 1 {
if StarHeight(sct, ra, dec, lon, lat, timezone) < 0 {
2026-05-01 22:38:44 +08:00
return 0, ErrNeverRise
2022-05-10 22:24:10 +08:00
}
2026-05-01 22:38:44 +08:00
return 0, ErrNeverSet
2022-05-10 22:24:10 +08:00
}
2026-05-01 22:38:44 +08:00
var estimateJD float64
2022-05-10 22:24:10 +08:00
if isRise {
2026-05-01 22:38:44 +08:00
estimateJD = sct - ArcCos(tmp)/15.0/24.0
2022-05-10 22:24:10 +08:00
} else {
2026-05-01 22:38:44 +08:00
estimateJD = sct + ArcCos(tmp)/15.0/24.0
2022-05-10 22:24:10 +08:00
}
var ok bool
estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 {
2026-05-01 22:38:44 +08:00
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
2022-05-10 22:24:10 +08:00
}
2026-05-01 22:38:44 +08:00
return estimateJD, nil
2022-05-10 22:24:10 +08:00
}
func StarCulminationTime(localJD, ra, lon, timezone float64) float64 {
if !isFiniteFloat(localJD) || !isFiniteFloat(ra) || !isFiniteFloat(lon) || !isFiniteFloat(timezone) {
return math.NaN()
}
// localJD 是本地民用日锚点(当地 0 时),不是力学时。
2022-05-10 22:24:10 +08:00
//ra,dec 瞬时天球座标,非J2000等时间天球坐标
localJD = math.Floor(localJD) + 0.5
estimateJD := localJD + Limit360(360-StarHourAngle(localJD, ra, lon, timezone))/15.0/24.0*0.99726851851851851851
limitStarHA := func(localJD, ra, lon, timezone float64) float64 {
ha := StarHourAngle(localJD, ra, lon, timezone)
2022-05-10 22:24:10 +08:00
if ha < 180 {
ha += 360
}
return ha
}
var ok bool
estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 {
2026-05-01 22:38:44 +08:00
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()
2022-05-10 22:24:10 +08:00
}
2026-05-01 22:38:44 +08:00
return estimateJD
2019-10-24 10:44:21 +08:00
}
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)
}