- 新增日食、月食、本地可见性、中心线、半影区域、SVG 图示与沙罗周期信息 - 新增行星冲合、留、方照、物理星历、视直径、相位、亮肢角、轨道节点等计算 - 新增木星伽利略卫星位置、现象与接触事件计算 - 新增恒星星表、星座判定、自行修正与观测辅助能力 - 新增 coord、formula、orbit、sundial、lite/sun、lite/moon 等扩展包 - 完善农历年号、月相英文别名、视差角、大气质量、折射、日晷与双星计算 - 增加 NASA、JPL Horizons、IMCCE 等回归测试数据与基线测试 - 重构基础算法文件组织,补充大量公开 API 注释和语义回归测试 - 更新中文和英文 README,补充示例、精度说明、SVG 配图
137 lines
5.7 KiB
Go
137 lines
5.7 KiB
Go
package basic
|
|
|
|
import (
|
|
"math"
|
|
|
|
. "b612.me/astro/tools"
|
|
)
|
|
|
|
func orbitTopocentricObservation(jde, observerLon, observerLat, observerHeight, timezone float64, elements OrbitElements) (ra, dec, distance float64) {
|
|
utcJde := jde - timezone/24.0
|
|
return OrbitApparentTopocentricEquatorial(TD2UT(utcJde, true), observerLon, observerLat, observerHeight, elements)
|
|
}
|
|
|
|
// OrbitHeight 返回轨道目标在观测者所在地的视高度角,单位度。
|
|
func OrbitHeight(jde, observerLon, observerLat, timezone, observerHeight float64, elements OrbitElements) float64 {
|
|
ra, dec, _ := orbitTopocentricObservation(jde, observerLon, observerLat, observerHeight, timezone, elements)
|
|
st := Limit360(ApparentSiderealTime(jde-timezone/24.0)*15 + observerLon)
|
|
hourAngle := Limit360(st - ra)
|
|
sinHeight := Sin(observerLat)*Sin(dec) + Cos(dec)*Cos(observerLat)*Cos(hourAngle)
|
|
return ArcSin(sinHeight)
|
|
}
|
|
|
|
// OrbitAzimuth 返回轨道目标在观测者所在地的视方位角,按正北为 0°、向东增加。
|
|
func OrbitAzimuth(jde, observerLon, observerLat, timezone, observerHeight float64, elements OrbitElements) float64 {
|
|
ra, dec, _ := orbitTopocentricObservation(jde, observerLon, observerLat, observerHeight, timezone, elements)
|
|
st := Limit360(ApparentSiderealTime(jde-timezone/24.0)*15 + observerLon)
|
|
hourAngle := Limit360(st - ra)
|
|
tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(observerLat) - Tan(dec)*Cos(observerLat))
|
|
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
|
|
}
|
|
|
|
// OrbitHourAngle 返回轨道目标的站心视时角,单位度。
|
|
func OrbitHourAngle(jde, observerLon, observerLat, timezone, observerHeight float64, elements OrbitElements) float64 {
|
|
ra, _, _ := orbitTopocentricObservation(jde, observerLon, observerLat, observerHeight, timezone, elements)
|
|
st := Limit360(ApparentSiderealTime(jde-timezone/24.0)*15 + observerLon)
|
|
hourAngle := st - ra
|
|
if hourAngle < 0 {
|
|
hourAngle += 360
|
|
}
|
|
return hourAngle
|
|
}
|
|
|
|
// OrbitCulminationTime 返回轨道目标的中天时刻,输入输出均沿用本仓库现有观测函数的 JD 语义。
|
|
func OrbitCulminationTime(jde, observerLon, observerLat, timezone, observerHeight float64, elements OrbitElements) float64 {
|
|
jde = math.Floor(jde) + 0.5
|
|
estimateJD := jde + Limit360(360-OrbitHourAngle(jde, observerLon, observerLat, timezone, observerHeight, elements))/15.0/24.0*0.99726851851851851851
|
|
normalizedHourAngle := func(jde float64) float64 {
|
|
currentHourAngle := OrbitHourAngle(jde, observerLon, observerLat, timezone, observerHeight, elements)
|
|
if currentHourAngle < 180 {
|
|
currentHourAngle += 360
|
|
}
|
|
return currentHourAngle
|
|
}
|
|
for {
|
|
prevJD := estimateJD
|
|
hourAngleDelta := normalizedHourAngle(prevJD) - 360
|
|
hourAngleSlope := (normalizedHourAngle(prevJD+0.000005) - normalizedHourAngle(prevJD-0.000005)) / 0.00001
|
|
estimateJD = prevJD - hourAngleDelta/hourAngleSlope
|
|
if math.Abs(estimateJD-prevJD) <= 0.00001 {
|
|
break
|
|
}
|
|
}
|
|
return estimateJD
|
|
}
|
|
|
|
// OrbitRiseTime 返回轨道目标在给定当地日期的升起时刻。
|
|
func OrbitRiseTime(jde, observerLon, observerLat, timezone, aeroCorrection, observerHeight float64, elements OrbitElements) (float64, error) {
|
|
return orbitRiseDown(jde, observerLon, observerLat, timezone, aeroCorrection, observerHeight, elements, true)
|
|
}
|
|
|
|
// OrbitSetTime 返回轨道目标在给定当地日期的落下时刻。
|
|
func OrbitSetTime(jde, observerLon, observerLat, timezone, aeroCorrection, observerHeight float64, elements OrbitElements) (float64, error) {
|
|
return orbitRiseDown(jde, observerLon, observerLat, timezone, aeroCorrection, observerHeight, elements, false)
|
|
}
|
|
|
|
func orbitRiseDown(jde, observerLon, observerLat, timezone, aeroCorrection, observerHeight float64, elements OrbitElements, isRise bool) (float64, error) {
|
|
localTimezone := math.Round(observerLon / 15)
|
|
targetAltitude := StandardAltitudePlanet(aeroCorrection, observerHeight, observerLat)
|
|
|
|
culminationJD := OrbitCulminationTime(jde, observerLon, observerLat, localTimezone, observerHeight, elements)
|
|
if OrbitHeight(culminationJD, observerLon, observerLat, localTimezone, observerHeight, elements) < targetAltitude {
|
|
return 0, ErrNeverRise
|
|
}
|
|
if OrbitHeight(culminationJD-0.5, observerLon, observerLat, localTimezone, observerHeight, elements) > targetAltitude {
|
|
return 0, ErrNeverSet
|
|
}
|
|
|
|
_, dec, _ := orbitTopocentricObservation(culminationJD, observerLon, observerLat, observerHeight, localTimezone, elements)
|
|
cosHourAngle := (Sin(targetAltitude) - Sin(dec)*Sin(observerLat)) / (Cos(dec) * Cos(observerLat))
|
|
|
|
var eventJD float64
|
|
if math.Abs(cosHourAngle) <= 1 {
|
|
hourOffset := ArcCos(cosHourAngle) / 15
|
|
if isRise {
|
|
eventJD = culminationJD - hourOffset/24 - 25.0/24.0/60.0
|
|
} else {
|
|
eventJD = culminationJD + hourOffset/24 - 25.0/24.0/60.0
|
|
}
|
|
} else {
|
|
eventJD = culminationJD
|
|
steps := 0
|
|
for OrbitHeight(eventJD, observerLon, observerLat, localTimezone, observerHeight, elements) > targetAltitude {
|
|
steps++
|
|
if isRise {
|
|
eventJD -= 15.0 / 60.0 / 24.0
|
|
} else {
|
|
eventJD += 15.0 / 60.0 / 24.0
|
|
}
|
|
if steps > 48 {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
estimateJD := eventJD
|
|
for {
|
|
prevJD := estimateJD
|
|
altitudeDelta := OrbitHeight(prevJD, observerLon, observerLat, localTimezone, observerHeight, elements) - targetAltitude
|
|
altitudeSlope := (OrbitHeight(prevJD+0.000005, observerLon, observerLat, localTimezone, observerHeight, elements) - OrbitHeight(prevJD-0.000005, observerLon, observerLat, localTimezone, observerHeight, elements)) / 0.00001
|
|
estimateJD = prevJD - altitudeDelta/altitudeSlope
|
|
if math.Abs(estimateJD-prevJD) <= 0.00001 {
|
|
break
|
|
}
|
|
}
|
|
return estimateJD - localTimezone/24 + timezone/24, nil
|
|
}
|