Files
astro/basic/planet_transit_geometry.go
T
b612 ccf5880269 feat: 完善水星与金星全球凌日搜索及制图导出
- 基于平均下合周期与节点相位预筛候选,减少无效星历计算;跳过无法求解的单个会合周期,避免有限覆盖星历导致搜索提前终止
- 引入影轴 γ、半影半径及地球尺度几何判定,结合站心可见性识别地心无接触、仅部分地区可见的掠凌
- 添加金星 243 年、水星 46 年周期族编号,并明确编号适用范围及未分配时的零值语义
- 为 SVG、GeoJSON 和 KML 增加站心部分凌日可见区,适配接触等时线、几何元数据、图层样式及取景;为地心无接触凌日事件增加正射图,按可见区球面面积加权居中
2026-10-20 22:35:39 +08:00

68 lines
2.7 KiB
Go

package basic
import "math"
const (
planetTransitEarthRadiusKM = 6378.137
planetTransitEarthEccentricitySquared = 0.00669437999014
)
func planetTransitShadowGeometry(tt float64, cfg planetTransitConfig, inst *Instance) (float64, float64, Observer, bool) {
sunRA, sunDec := HSunApparentRaDecNWithInstance(tt, -1, inst)
planetRA, planetDec := cfg.apparentRaDecN(tt, -1)
sunDistance := EarthAwayNWithInstance(tt, -1, inst) * angularDiameterAstronomicalUnitKM
planetDistance := cfg.earthDistanceN(tt, -1) * angularDiameterAstronomicalUnitKM
sun := eclipticCartesian(sunRA, sunDec, sunDistance)
body := eclipticCartesian(planetRA, planetDec, planetDistance)
axis, length := pn(Vector3{body[0] - sun[0], body[1] - sun[1], body[2] - sun[2]})
projection := -(body[0]*axis[0] + body[1]*axis[1] + body[2]*axis[2])
impact := Vector3{body[0] + projection*axis[0], body[1] + projection*axis[1], body[2] + projection*axis[2]}
_, distance := pn(impact)
planetRadius := venusEquatorialRadiusKM
if cfg.planetIndex == 1 {
planetRadius = mercuryEquatorialRadiusKM
}
sine := (sunEquatorialRadiusKM + planetRadius) / length
cosine := math.Sqrt(1 - sine*sine)
penumbra := (planetRadius + projection*sine) / cosine / planetTransitEarthRadiusKM
gamma := math.Copysign(distance/planetTransitEarthRadiusKM, impact[2])
sidereal := ApparentSiderealTime(TT2UT1(tt)) * 15
seed := Observer{
Longitude: normalizeLongitude180(math.Atan2(impact[1], impact[0])*180/math.Pi - sidereal),
Latitude: math.Atan2(impact[2], math.Hypot(impact[0], impact[1])*(1-planetTransitEarthEccentricitySquared)) * 180 / math.Pi,
}
possible := isFiniteFloat(gamma) && isFiniteFloat(penumbra) && projection > 0 && planetDistance < sunDistance && math.Abs(gamma) <= penumbra+1/cosine
return gamma, penumbra, seed, possible
}
func planetTransitSurfaceOverlap(transit PlanetTransitResult, seed Observer, inst *Instance) bool {
snapshot, ok := buildPlanetTransitSnapshot(transit, inst)
if !ok {
return false
}
best := math.Inf(1)
for step := 4.0; step > 0.0001; step /= 2 {
center := seed
for _, latitudeStep := range []float64{-step, 0, step} {
for _, longitudeStep := range []float64{-step, 0, step} {
observer := Observer{Longitude: normalizeLongitude180(center.Longitude + longitudeStep), Latitude: math.Max(-90, math.Min(90, center.Latitude+latitudeStep))}
tt, valid := snapshot.localGreatestTT(UTC2TT(transit.Greatest), observer)
state, stateOK := snapshot.stateAt(tt, observer)
if !valid || !stateOK {
continue
}
if state.external < 0 {
local := snapshot.local(transit, observer)
if local.Valid && local.Visible {
return true
}
}
if state.external < best {
best, seed = state.external, observer
}
}
}
}
return false
}