121 lines
4.7 KiB
Go
121 lines
4.7 KiB
Go
|
|
package basic
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"math"
|
||
|
|
|
||
|
|
"b612.me/astro/internal/ephem"
|
||
|
|
"b612.me/astro/planet"
|
||
|
|
)
|
||
|
|
|
||
|
|
func instanceValue(instances []*Instance) *Instance {
|
||
|
|
if len(instances) == 0 {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return instances[0]
|
||
|
|
}
|
||
|
|
|
||
|
|
func instanceModel(inst *Instance) ephem.Model {
|
||
|
|
if inst == nil {
|
||
|
|
return ephem.Active()
|
||
|
|
}
|
||
|
|
return inst.Model()
|
||
|
|
}
|
||
|
|
|
||
|
|
// PlanetCoordinateWithInstance 指定星历实例的行星日心经纬或距离 / heliocentric planetary coordinate or distance with an explicit instance.
|
||
|
|
func PlanetCoordinateWithInstance(index, coordinate int, jde float64, inst *Instance) float64 {
|
||
|
|
return PlanetCoordinateNWithInstance(index, coordinate, jde, -1, inst)
|
||
|
|
}
|
||
|
|
|
||
|
|
// PlanetCoordinateNWithInstance 指定星历实例的行星日心经纬或距离(n 变体) / n-parameterised heliocentric planetary coordinate or distance with an explicit instance.
|
||
|
|
func PlanetCoordinateNWithInstance(index, coordinate int, jde float64, n int, inst *Instance) float64 {
|
||
|
|
if inst == nil {
|
||
|
|
return planet.WherePlanetN(index, coordinate, jde, n)
|
||
|
|
}
|
||
|
|
if inst.Model() != ephem.Custom {
|
||
|
|
return planet.WherePlanetModelN(inst.Model(), index, coordinate, jde, n)
|
||
|
|
}
|
||
|
|
value, ok := planetHeliocentricCoordinateWithInstance(index, coordinate, jde, inst)
|
||
|
|
if !ok {
|
||
|
|
return math.NaN()
|
||
|
|
}
|
||
|
|
return value
|
||
|
|
}
|
||
|
|
|
||
|
|
func boundVectorApparent(target ephem.Body, jde float64, n int, inst *Instance) (lo, bo, tau, distAU float64, ok bool) {
|
||
|
|
lo, bo, tau, distAU, ok, err := vectorApparentWithInstance(target, jde, n, inst)
|
||
|
|
if err != nil {
|
||
|
|
return math.NaN(), math.NaN(), math.NaN(), math.NaN(), true
|
||
|
|
}
|
||
|
|
return lo, bo, tau, distAU, ok
|
||
|
|
}
|
||
|
|
|
||
|
|
func instanceStateEcliptic(target ephem.Body, jde float64, inst *Instance) (lo, bo, distAU float64, ok bool) {
|
||
|
|
return instanceStateEclipticRelative(target, ephem.Earth, jde, jde, inst)
|
||
|
|
}
|
||
|
|
|
||
|
|
func instanceStateEclipticRelative(target, center ephem.Body, stateJDE, eclipticJDE float64, inst *Instance) (lo, bo, distAU float64, ok bool) {
|
||
|
|
// 几何状态捷径只给自定义引擎,内置模型必须走与包级相同的级数与视位置管线。
|
||
|
|
if inst == nil || inst.Model() != ephem.Custom {
|
||
|
|
return 0, 0, 0, false
|
||
|
|
}
|
||
|
|
state, _, err := inst.State(target, center, stateJDE)
|
||
|
|
if err != nil {
|
||
|
|
return 0, 0, 0, false
|
||
|
|
}
|
||
|
|
distanceKM := math.Sqrt(state[0]*state[0] + state[1]*state[1] + state[2]*state[2])
|
||
|
|
if distanceKM == 0 || math.IsNaN(distanceKM) || math.IsInf(distanceKM, 0) {
|
||
|
|
return 0, 0, 0, false
|
||
|
|
}
|
||
|
|
direction := [3]float64{state[0] / distanceKM, state[1] / distanceKM, state[2] / distanceKM}
|
||
|
|
lo, bo = icrfToMeanEcliptic(direction, eclipticJDE)
|
||
|
|
return lo, bo, distanceKM / astronomicalUnitKM, true
|
||
|
|
}
|
||
|
|
|
||
|
|
func icrfToMeanEcliptic(direction [3]float64, jde float64) (lo, bo float64) {
|
||
|
|
matrix := ltpPBMAT(2000 + (jde-2451545)/365.25)
|
||
|
|
var equatorial [3]float64
|
||
|
|
for axis := range equatorial {
|
||
|
|
equatorial[axis] = matrix[axis][0]*direction[0] + matrix[axis][1]*direction[1] + matrix[axis][2]*direction[2]
|
||
|
|
}
|
||
|
|
sine, cosine := math.Sincos(Obliquity1980(jde) * math.Pi / 180)
|
||
|
|
eclipticY := equatorial[1]*cosine + equatorial[2]*sine
|
||
|
|
eclipticZ := -equatorial[1]*sine + equatorial[2]*cosine
|
||
|
|
lo = math.Atan2(eclipticY, equatorial[0]) * 180 / math.Pi
|
||
|
|
bo = math.Atan2(eclipticZ, math.Hypot(equatorial[0], eclipticY)) * 180 / math.Pi
|
||
|
|
return math.Mod(lo+720, 360), bo
|
||
|
|
}
|
||
|
|
|
||
|
|
func instanceApparent(target ephem.Body, jde float64, n int, inst *Instance) (lo, bo, tau, distAU float64, ok bool, err error) {
|
||
|
|
if inst == nil || n >= 0 && inst.Model() != ephem.Custom {
|
||
|
|
return 0, 0, 0, 0, false, nil
|
||
|
|
}
|
||
|
|
return apparentState(target, jde, inst)
|
||
|
|
}
|
||
|
|
|
||
|
|
// apparentState 求视位置并校验;引擎不支持时 ok=false 且 err 为 nil,状态非法时返回 err。
|
||
|
|
func apparentState(target ephem.Body, jde float64, inst *Instance) (lo, bo, tau, distAU float64, ok bool, err error) {
|
||
|
|
direction, distanceKM, tau, err := inst.Apparent(target, jde)
|
||
|
|
if errors.Is(err, ephem.ErrUnsupportedApparent) || inst.Model() != ephem.Custom && errors.Is(err, ephem.ErrNoEngine) {
|
||
|
|
return 0, 0, 0, 0, false, nil
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return 0, 0, 0, 0, false, err
|
||
|
|
}
|
||
|
|
norm := math.Hypot(math.Hypot(direction[0], direction[1]), direction[2])
|
||
|
|
if !isFiniteFloat(distanceKM) || distanceKM <= 0 || !isFiniteFloat(norm) || norm <= 0 || !isFiniteFloat(tau) || tau < 0 {
|
||
|
|
return 0, 0, 0, 0, false, errors.New("ephem: invalid apparent state")
|
||
|
|
}
|
||
|
|
lo, bo = icrfToTrueEcliptic(direction, jde)
|
||
|
|
// 天文单位须与 vectorApparent 一致,否则实例与包级距离不逐位相等。
|
||
|
|
return lo, bo, tau, distanceKM / 149597870.7, true, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func vectorApparentWithInstance(target ephem.Body, jde float64, n int, inst *Instance) (lo, bo, tau, distAU float64, ok bool, err error) {
|
||
|
|
if inst != nil {
|
||
|
|
return instanceApparent(target, jde, n, inst)
|
||
|
|
}
|
||
|
|
lo, bo, tau, distAU, ok = vectorApparent(target, jde, n)
|
||
|
|
return lo, bo, tau, distAU, ok, nil
|
||
|
|
}
|