2026-05-01 22:38:44 +08:00
package basic
import (
"math"
. "b612.me/astro/tools"
)
const (
MERCURY_S_PERIOD = 1 / (( 1 / 87.9691 ) - ( 1 / 365.256363004 ))
mercuryConjunctionDerivativeStepDay = 2e-5 * 36525.0
mercuryLightTimeDaysPerAU = 0.0057755183
2026-05-22 12:24:41 +08:00
mercuryEventSearchN = 16
2026-08-06 12:00:56 +08:00
mercuryStationWindowDays = 30.0
mercuryStationDerivativeStepDay = 0.01
mercuryStationCoarseStepDay = 2.0
mercuryStationHalfWindowDay = 2.0
mercuryStationMotionTolerance = 1e-3
2026-09-17 12:27:40 +08:00
// mercuryStationAnchorAttempts 类型化「留」在锚点不满足侧向不变量时,最多推进/回退几个会合周期。
mercuryStationAnchorAttempts = 4
// mercuryConjunctionSameInstantDegrees 合搜索「同刻」快速路径的视黄经差触发阈值(度)。
// 0.05 度约合 30 分钟的时间跨度,远大于截断级数在根附近的误差,
// 从而保证「查询落在合附近」时一定走精确解 + 侧向判定,而不是被方向扫描跨过去。
mercuryConjunctionSameInstantDegrees = 5.0e-2
// mercuryConjunctionScanStepDay / mercuryConjunctionScanMaxSteps 方向性括号扫描参数:
// 8 天一步 × 80 步 = 640 天,覆盖一个水星会合周期(115.88 天)且有大量余量。
mercuryConjunctionScanStepDay = 8.0
mercuryConjunctionScanMaxSteps = 80
// mercuryConjunctionPolishToleranceDay 全项抛光的时间容差(0.01 秒):
// 割线法从 8 天括号收敛到该量级只需多 1~2 次求值,但把合时刻精度保持在毫秒级
// (与旧实现牛顿法的 1e-5 天口径一致)。
mercuryConjunctionPolishToleranceDay = 0.01 / 86400.0
2026-05-01 22:38:44 +08:00
)
type mercuryConjunctionLBR struct {
lo float64
bo float64
r float64
}
type mercuryConjunctionGeo struct {
lo float64
bo float64
dist float64
}
type mercuryConjunctionResult struct {
diff float64
sunLightDays float64
geoLightDays float64
}
2026-10-15 23:54:15 +08:00
func mercuryHelioN ( planetIndex int , jde float64 , n int , instances ...* Instance ) mercuryConjunctionLBR {
inst := instanceValue ( instances )
2026-05-01 22:38:44 +08:00
return mercuryConjunctionLBR {
2026-10-15 23:54:15 +08:00
lo : PlanetCoordinateNWithInstance ( planetIndex , 0 , jde , n , inst ),
bo : PlanetCoordinateNWithInstance ( planetIndex , 1 , jde , n , inst ),
r : PlanetCoordinateNWithInstance ( planetIndex , 2 , jde , n , inst ),
2026-05-01 22:38:44 +08:00
}
}
func mercuryGeocentric ( planetPos , earthPos mercuryConjunctionLBR ) mercuryConjunctionGeo {
x := planetPos . r * Cos ( planetPos . bo ) * Cos ( planetPos . lo ) - earthPos . r * Cos ( earthPos . bo ) * Cos ( earthPos . lo )
y := planetPos . r * Cos ( planetPos . bo ) * Sin ( planetPos . lo ) - earthPos . r * Cos ( earthPos . bo ) * Sin ( earthPos . lo )
z := planetPos . r * Sin ( planetPos . bo ) - earthPos . r * Sin ( earthPos . bo )
dist := math . Sqrt ( x * x + y * y + z * z )
return mercuryConjunctionGeo {
lo : Limit360 ( math . Atan2 ( y , x ) * 180 / math . Pi ),
bo : math . Atan2 ( z , math . Sqrt ( x * x + y * y )) * 180 / math . Pi ,
dist : dist ,
}
}
func mercuryConjunctionAngleDelta ( diff float64 ) float64 {
diff = Limit360 ( diff )
if diff > 180 {
diff -= 360
}
if diff < - 180 {
diff += 360
}
return diff
}
2026-10-15 23:54:15 +08:00
func mercuryConjunctionHeliocentricDelta ( jde , targetDeg float64 , n int , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
planetLo := PlanetCoordinateNWithInstance ( 1 , 0 , jde , n , inst )
earthLo := PlanetCoordinateNWithInstance ( - 1 , 0 , jde , n , inst )
2026-05-01 22:38:44 +08:00
return mercuryConjunctionAngleDelta ( planetLo - earthLo - targetDeg )
}
2026-10-15 23:54:15 +08:00
func mercuryConjunctionDifference ( jd float64 , n int , targetDeg , sunLightDays , geoLightDays float64 , instances ...* Instance ) mercuryConjunctionResult {
inst := instanceValue ( instances )
earthForSun := mercuryHelioN ( - 1 , jd - sunLightDays , n , inst )
2026-05-01 22:38:44 +08:00
sunLo := Limit360 ( earthForSun . lo + 180 )
2026-10-15 23:54:15 +08:00
earth := mercuryHelioN ( - 1 , jd - geoLightDays , n , inst )
planetPos := mercuryHelioN ( 1 , jd - geoLightDays , n , inst )
2026-05-01 22:38:44 +08:00
geo := mercuryGeocentric ( planetPos , earth )
return mercuryConjunctionResult {
diff : mercuryConjunctionAngleDelta ( geo . lo - sunLo - targetDeg ),
sunLightDays : earthForSun . r * mercuryLightTimeDaysPerAU ,
geoLightDays : geo . dist * mercuryLightTimeDaysPerAU ,
}
}
2026-10-15 23:54:15 +08:00
func mercuryConjunctionExactDelta ( jde float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
return mercuryConjunctionAngleDelta ( MercuryApparentLoWithInstance ( jde , inst ) - HSunApparentLoWithInstance ( jde , inst ))
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryConjunctionApproxTT ( seed float64 , inferior bool , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-05-01 22:38:44 +08:00
heliocentricTarget := 180.0
if inferior {
heliocentricTarget = 0
}
2026-09-23 18:55:12 +08:00
jde := seed
2026-05-01 22:38:44 +08:00
for i := 0 ; i < 6 ; i ++ {
2026-10-15 23:54:15 +08:00
jde -= mercuryConjunctionHeliocentricDelta ( jde , heliocentricTarget , 8 , inst ) / ( 360.0 / MERCURY_S_PERIOD )
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
startSample := mercuryConjunctionDifference ( jde , 8 , 0 , 0 , 0 , inst )
nextSample := mercuryConjunctionDifference ( jde + mercuryConjunctionDerivativeStepDay , 8 , 0 , 0 , 0 , inst )
2026-05-01 22:38:44 +08:00
diffSlope := mercuryConjunctionAngleDelta ( nextSample . diff - startSample . diff ) / mercuryConjunctionDerivativeStepDay
2026-10-15 23:54:15 +08:00
refined := mercuryConjunctionDifference ( jde , 40 , 0 , startSample . sunLightDays , startSample . geoLightDays , inst )
2026-09-23 18:55:12 +08:00
jde -= refined . diff / diffSlope
2026-10-15 23:54:15 +08:00
final := mercuryConjunctionDifference ( jde , - 1 , 0 , refined . sunLightDays , refined . geoLightDays , inst )
2026-09-23 18:55:12 +08:00
jde -= final . diff / diffSlope
return jde
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryConjunctionExactTT ( seed float64 , inferior bool , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
estimateJD := mercuryConjunctionApproxTT ( seed , inferior , inst )
2026-08-06 12:00:56 +08:00
converged := false
for i := 0 ; i < eventNewtonMaxIterations ; i ++ {
2026-09-23 18:55:12 +08:00
prevJDE := estimateJD
2026-10-15 23:54:15 +08:00
longitudeDelta := mercuryConjunctionExactDelta ( prevJDE , inst )
longitudeSlope := ( mercuryConjunctionExactDelta ( prevJDE + 0.000005 , inst ) - mercuryConjunctionExactDelta ( prevJDE - 0.000005 , inst )) / 0.00001
2026-09-23 18:55:12 +08:00
nextJD := prevJDE - longitudeDelta / longitudeSlope
2026-08-06 12:00:56 +08:00
estimateJD = nextJD
2026-09-23 18:55:12 +08:00
if math . Abs ( nextJD - prevJDE ) <= 0.00001 {
2026-08-06 12:00:56 +08:00
converged = true
2026-05-01 22:38:44 +08:00
break
}
}
2026-08-06 12:00:56 +08:00
if ! converged {
return math . NaN ()
2026-05-01 22:38:44 +08:00
}
2026-08-06 12:00:56 +08:00
return estimateJD
2026-05-01 22:38:44 +08:00
}
2026-09-17 12:27:40 +08:00
// mercuryConjunction 在 jde 的指定方向上求最近一次水星合(内合/外合)。
//
// 旧实现用「启发式跳 + 2 天一步走」:跳过头落到合之后时,|Δ| 会持续变大,
// 于是走满一个会合周期、跳过一次合(例如 2008-01-22 查到 2008-06-07 而不是 2008-02-06),
// 并且下游的类型化「留」会因此返回错事件、甚至让 Last 返回未来。
// 新实现改成方向性括号扫描:截断级数逐段找异号区间(顺序扫描 ⇒ 一定取最近的一个合),
// 再用全项级数在括号内抛光;扫满 640 天仍无括号时有界返回 NaN。
2026-10-15 23:54:15 +08:00
func mercuryConjunction ( jde float64 , next uint8 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-05-01 22:38:44 +08:00
//0=last 1=next
2026-08-06 12:00:56 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-10-15 23:54:15 +08:00
if math . Abs ( mercuryConjunctionExactDelta ( jde , inst )) <= mercuryConjunctionSameInstantDegrees {
2026-05-23 23:08:05 +08:00
best := math . NaN ()
consider := func ( inferior bool ) {
2026-10-15 23:54:15 +08:00
eventUT := TT2UTC ( mercuryConjunctionExactTT ( jde , inferior , inst ))
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( eventUT ) {
return
}
2026-05-23 23:08:05 +08:00
if next == 0 && ! eventUTQueryBeforeOrEqual ( eventUT , jde ) {
return
}
if next == 1 && ! eventUTQueryAfterOrEqual ( eventUT , jde ) {
return
}
if math . IsNaN ( best ) || math . Abs ( eventUTQueryTTDelta ( eventUT , jde )) < math . Abs ( eventUTQueryTTDelta ( best , jde )) {
best = eventUT
}
}
consider ( true )
consider ( false )
if ! math . IsNaN ( best ) {
return best
}
}
2026-09-17 12:27:40 +08:00
direction := 1.0
if next == 0 {
direction = - 1
2026-05-01 22:38:44 +08:00
}
2026-09-23 18:55:12 +08:00
leftJDE := jde
2026-10-15 23:54:15 +08:00
leftValue := mercuryConjunctionDeltaN ( leftJDE , mercuryEventSearchN , inst )
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( leftValue ) {
return math . NaN ()
2026-05-01 22:38:44 +08:00
}
2026-09-17 12:27:40 +08:00
for i := 0 ; i < mercuryConjunctionScanMaxSteps ; i ++ {
2026-09-23 18:55:12 +08:00
rightJDE := jde + direction * mercuryConjunctionScanStepDay * float64 ( i + 1 )
2026-10-15 23:54:15 +08:00
rightValue := mercuryConjunctionDeltaN ( rightJDE , mercuryEventSearchN , inst )
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( rightValue ) {
return math . NaN ()
}
if leftValue == 0 || rightValue == 0 || leftValue * rightValue < 0 {
2026-10-15 23:54:15 +08:00
return mercuryConjunctionPolish ( jde , leftJDE , rightJDE , direction , inst )
2026-09-17 12:27:40 +08:00
}
2026-09-23 18:55:12 +08:00
leftJDE , leftValue = rightJDE , rightValue
2026-05-01 22:38:44 +08:00
}
2026-09-17 12:27:40 +08:00
return math . NaN ()
}
// mercuryConjunctionDeltaN 截断级数下的水星-太阳视黄经差(度,[-180,180]),用于方向性括号扫描。
2026-10-15 23:54:15 +08:00
func mercuryConjunctionDeltaN ( jde float64 , n int , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
return mercuryConjunctionAngleDelta ( MercuryApparentLoNWithInstance ( jde , n , inst ) - HSunApparentLoNWithInstance ( jde , n , inst ))
2026-09-17 12:27:40 +08:00
}
// mercuryConjunctionPolish 用全项级数在截断级数给出的括号内抛光。
// 截断误差可能让括号两端在全项函数上同号(罕见),此时沿扫描方向再扩一两个扫描步;
// 若仍未被确认(典型情形:查询几乎正好落在合上,截断级数在根两侧的符号与全项不一致),
// 退回全项级数的方向扫描,保证有界且不返回 NaN。
2026-10-15 23:54:15 +08:00
func mercuryConjunctionPolish ( jde , leftJDE , rightJDE , direction float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-09-17 12:27:40 +08:00
for attempt := 0 ; attempt < 3 ; attempt ++ {
2026-10-15 23:54:15 +08:00
leftValue := mercuryConjunctionExactDelta ( leftJDE , inst )
rightValue := mercuryConjunctionExactDelta ( rightJDE , inst )
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( leftValue ) || ! isFiniteFloat ( rightValue ) {
2026-08-06 12:00:56 +08:00
return math . NaN ()
}
2026-09-17 12:27:40 +08:00
if leftValue == 0 || rightValue == 0 || leftValue * rightValue < 0 {
2026-09-23 18:55:12 +08:00
root , ok := eventBracketSecantRoot ( leftJDE , rightJDE , leftValue , rightValue ,
2026-10-15 23:54:15 +08:00
mercuryConjunctionPolishToleranceDay , func ( jde float64 ) float64 { return mercuryConjunctionExactDelta ( jde , inst ) })
2026-09-17 12:27:40 +08:00
if ! ok {
return math . NaN ()
2026-05-01 22:38:44 +08:00
}
2026-09-23 18:55:12 +08:00
return TT2UTC ( root )
2026-09-17 12:27:40 +08:00
}
if direction > 0 {
2026-09-23 18:55:12 +08:00
rightJDE += mercuryConjunctionScanStepDay
2026-05-01 22:38:44 +08:00
continue
}
2026-09-23 18:55:12 +08:00
leftJDE -= mercuryConjunctionScanStepDay
2026-08-06 12:00:56 +08:00
}
2026-10-15 23:54:15 +08:00
return mercuryConjunctionFullDirectionalScan ( jde , direction , inst )
2026-09-17 12:27:40 +08:00
}
2026-05-01 22:38:44 +08:00
2026-09-17 12:27:40 +08:00
// mercuryConjunctionFullDirectionalScan 全项级数的方向扫描(截断括号未被确认时的兜底)。
2026-10-15 23:54:15 +08:00
func mercuryConjunctionFullDirectionalScan ( jde , direction float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-09-23 18:55:12 +08:00
leftJDE := jde
2026-10-15 23:54:15 +08:00
leftValue := mercuryConjunctionExactDelta ( leftJDE , inst )
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( leftValue ) {
2026-08-06 12:00:56 +08:00
return math . NaN ()
}
2026-09-17 12:27:40 +08:00
for i := 0 ; i < mercuryConjunctionScanMaxSteps ; i ++ {
2026-09-23 18:55:12 +08:00
rightJDE := jde + direction * mercuryConjunctionScanStepDay * float64 ( i + 1 )
2026-10-15 23:54:15 +08:00
rightValue := mercuryConjunctionExactDelta ( rightJDE , inst )
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( rightValue ) {
return math . NaN ()
}
if leftValue == 0 || rightValue == 0 || leftValue * rightValue < 0 {
2026-09-23 18:55:12 +08:00
root , ok := eventBracketSecantRoot ( leftJDE , rightJDE , leftValue , rightValue ,
2026-10-15 23:54:15 +08:00
mercuryConjunctionPolishToleranceDay , func ( jde float64 ) float64 { return mercuryConjunctionExactDelta ( jde , inst ) })
2026-09-17 12:27:40 +08:00
if ! ok {
return math . NaN ()
}
2026-09-23 18:55:12 +08:00
return TT2UTC ( root )
2026-09-17 12:27:40 +08:00
}
2026-09-23 18:55:12 +08:00
leftJDE , leftValue = rightJDE , rightValue
2026-05-01 22:38:44 +08:00
}
2026-09-17 12:27:40 +08:00
return math . NaN ()
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryConjunctionWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryConjunctionStrictWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryConjunctionStrictWithInstance ( jde , inst ) })
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryConjunctionWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryConjunctionStrictWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryConjunctionStrictWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryConjunctionStrictWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryConjunctionStrictWithInstance ( jde float64 , inst * Instance ) float64 {
return mercuryConjunction ( jde , 0 , inst )
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryConjunctionStrictWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryConjunctionStrictWithInstance ( jde float64 , inst * Instance ) float64 {
return mercuryConjunction ( jde , 1 , inst )
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryInferiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryInferiorConjunctionWithInstance ( jde float64 , inst * Instance ) float64 {
date := NextMercuryConjunctionStrictWithInstance ( jde , inst )
if EarthMercuryAwayWithInstance ( date , inst ) > EarthAwayWithInstance ( date , inst ) {
return NextMercuryConjunctionStrictWithInstance ( date + 2 , inst )
2026-05-01 22:38:44 +08:00
}
return date
}
2026-10-15 23:54:15 +08:00
// NextMercurySuperiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercurySuperiorConjunctionWithInstance ( jde float64 , inst * Instance ) float64 {
date := NextMercuryConjunctionStrictWithInstance ( jde , inst )
if EarthMercuryAwayWithInstance ( date , inst ) < EarthAwayWithInstance ( date , inst ) {
return NextMercuryConjunctionStrictWithInstance ( date + 2 , inst )
2026-05-01 22:38:44 +08:00
}
return date
}
2026-10-15 23:54:15 +08:00
// LastMercuryInferiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryInferiorConjunctionWithInstance ( jde float64 , inst * Instance ) float64 {
date := LastMercuryConjunctionStrictWithInstance ( jde , inst )
if EarthMercuryAwayWithInstance ( date , inst ) > EarthAwayWithInstance ( date , inst ) {
return LastMercuryConjunctionStrictWithInstance ( date - 2 , inst )
2026-05-01 22:38:44 +08:00
}
return date
}
2026-10-15 23:54:15 +08:00
// LastMercurySuperiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercurySuperiorConjunctionWithInstance ( jde float64 , inst * Instance ) float64 {
date := LastMercuryConjunctionStrictWithInstance ( jde , inst )
if EarthMercuryAwayWithInstance ( date , inst ) < EarthAwayWithInstance ( date , inst ) {
return LastMercuryConjunctionStrictWithInstance ( date - 2 , inst )
2026-05-01 22:38:44 +08:00
}
return date
}
2026-10-15 23:54:15 +08:00
func mercuryRADerivative ( jde , delta float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
sub := MercuryApparentRaWithInstance ( jde + delta , inst ) - MercuryApparentRaWithInstance ( jde - delta , inst )
2026-08-06 12:00:56 +08:00
if sub > 180 {
sub -= 360
2026-05-01 22:38:44 +08:00
}
2026-08-06 12:00:56 +08:00
if sub < - 180 {
sub += 360
2026-05-01 22:38:44 +08:00
}
2026-08-06 12:00:56 +08:00
return sub / ( 2 * delta )
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryRADerivativeN ( jde , delta float64 , n int , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
sub := MercuryApparentRaNWithInstance ( jde + delta , n , inst ) - MercuryApparentRaNWithInstance ( jde - delta , n , inst )
2026-05-22 12:24:41 +08:00
if sub > 180 {
sub -= 360
}
if sub < - 180 {
sub += 360
}
return sub / ( 2 * delta )
}
2026-10-15 23:54:15 +08:00
func mercuryStationInWindow ( startTT , endTT float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-09-23 18:55:12 +08:00
bestJDE := zeroEventInWindow ( startTT , endTT , mercuryStationCoarseStepDay , mercuryStationHalfWindowDay , 30.0 / 86400.0 , func ( jd float64 ) float64 {
2026-10-15 23:54:15 +08:00
return mercuryRADerivativeN ( jd , mercuryStationDerivativeStepDay , mercuryEventSearchN , inst )
2026-08-06 12:00:56 +08:00
}, func ( jd float64 ) float64 {
2026-10-15 23:54:15 +08:00
return mercuryRADerivative ( jd , mercuryStationDerivativeStepDay , inst )
2026-08-06 12:00:56 +08:00
})
2026-09-23 18:55:12 +08:00
return TT2UTC ( bestJDE )
2026-08-06 12:00:56 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryStationBetween ( startTT , endTT float64 , instances ...* Instance ) bool {
inst := instanceValue ( instances )
2026-08-06 12:00:56 +08:00
if endTT < startTT {
startTT , endTT = endTT , startTT
}
if endTT - startTT <= 0 {
return false
}
2026-09-17 12:27:40 +08:00
// 跨度超过一个回留窗口时截断扫描已不可靠:返回 false 不再阻断快速路径。
// 此时候选事件由 mercuryConjunction 的完整方向扫描保证,快速路径无需再复核。
// A span beyond the station window makes the truncated scan unreliable; report false so the
// fast path is not blocked. The typed candidate is already guaranteed by mercuryConjunction's
// exhaustive directional scan.
2026-08-06 12:00:56 +08:00
if endTT - startTT > mercuryStationWindowDays {
2026-09-17 12:27:40 +08:00
return false
2026-08-06 12:00:56 +08:00
}
// 截断扫描足以判断单候选快速路径是否安全 / A truncated scan is enough to decide whether the one-candidate fast path is safe.
left := startTT
2026-10-15 23:54:15 +08:00
leftValue := mercuryRADerivativeN ( left , mercuryStationDerivativeStepDay , mercuryEventSearchN , inst )
2026-08-06 12:00:56 +08:00
for left < endTT {
right := left + mercuryStationCoarseStepDay
if right > endTT {
right = endTT
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
rightValue := mercuryRADerivativeN ( right , mercuryStationDerivativeStepDay , mercuryEventSearchN , inst )
2026-08-06 12:00:56 +08:00
if leftValue == 0 || leftValue * rightValue < 0 || rightValue == 0 {
return true
2026-05-22 12:24:41 +08:00
}
2026-08-06 12:00:56 +08:00
left = right
leftValue = rightValue
2026-05-22 12:24:41 +08:00
}
2026-08-06 12:00:56 +08:00
return false
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryProgradeToRetrogradeAroundInferior ( inferiorUT float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-09-23 18:55:12 +08:00
inferiorTT := UTC2TT ( inferiorUT )
2026-10-15 23:54:15 +08:00
return mercuryStationInWindow ( inferiorTT - mercuryStationWindowDays , inferiorTT , inst )
2026-08-06 12:00:56 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryRetrogradeToProgradeAroundInferior ( inferiorUT float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-09-23 18:55:12 +08:00
inferiorTT := UTC2TT ( inferiorUT )
2026-10-15 23:54:15 +08:00
return mercuryStationInWindow ( inferiorTT , inferiorTT + mercuryStationWindowDays , inst )
2026-08-06 12:00:56 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryProgradeToRetrogradeWithInstance ( jde float64 , inst * Instance ) float64 {
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-10-15 23:54:15 +08:00
// 以下合为锚点求候选,不满足 Next 不早于查询时推进一个会合周期,保证不返回过去的事件。
inferior := NextMercuryInferiorConjunctionWithInstance ( jde , inst )
2026-09-17 12:27:40 +08:00
for i := 0 ; i < mercuryStationAnchorAttempts && isFiniteFloat ( inferior ); i ++ {
2026-10-15 23:54:15 +08:00
date := mercuryProgradeToRetrogradeAroundInferior ( inferior , inst )
2026-09-17 12:27:40 +08:00
if isFiniteFloat ( date ) && stationUTQueryAfterOrEqual ( date , jde ) {
return date
}
2026-10-15 23:54:15 +08:00
inferior = NextMercuryInferiorConjunctionWithInstance ( eventUTNextQueryTT ( inferior ), inst )
2026-05-22 12:24:41 +08:00
}
2026-09-17 12:27:40 +08:00
return math . NaN ()
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryRetrogradeToProgradeWithInstance ( jde float64 , inst * Instance ) float64 {
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-10-15 23:54:15 +08:00
inferior := LastMercuryInferiorConjunctionWithInstance ( jde , inst )
2026-09-17 12:27:40 +08:00
for i := 0 ; i < mercuryStationAnchorAttempts && isFiniteFloat ( inferior ); i ++ {
2026-10-15 23:54:15 +08:00
date := mercuryRetrogradeToProgradeAroundInferior ( inferior , inst )
2026-09-17 12:27:40 +08:00
if isFiniteFloat ( date ) && stationUTQueryAfterOrEqual ( date , jde ) {
return date
}
2026-10-15 23:54:15 +08:00
inferior = NextMercuryInferiorConjunctionWithInstance ( eventUTNextQueryTT ( inferior ), inst )
2026-05-22 12:24:41 +08:00
}
2026-09-17 12:27:40 +08:00
return math . NaN ()
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryProgradeToRetrogradeWithInstance ( jde float64 , inst * Instance ) float64 {
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-10-15 23:54:15 +08:00
inferior := NextMercuryInferiorConjunctionWithInstance ( jde , inst )
2026-09-17 12:27:40 +08:00
for i := 0 ; i < mercuryStationAnchorAttempts && isFiniteFloat ( inferior ); i ++ {
2026-10-15 23:54:15 +08:00
date := mercuryProgradeToRetrogradeAroundInferior ( inferior , inst )
2026-09-17 12:27:40 +08:00
if isFiniteFloat ( date ) && stationUTQueryBeforeOrEqual ( date , jde ) {
return date
}
2026-10-15 23:54:15 +08:00
inferior = LastMercuryInferiorConjunctionWithInstance ( eventUTLastQueryTT ( inferior ), inst )
2026-05-01 22:38:44 +08:00
}
2026-09-17 12:27:40 +08:00
return math . NaN ()
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryRetrogradeToProgradeWithInstance ( jde float64 , inst * Instance ) float64 {
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-10-15 23:54:15 +08:00
inferior := LastMercuryInferiorConjunctionWithInstance ( jde , inst )
2026-09-17 12:27:40 +08:00
for i := 0 ; i < mercuryStationAnchorAttempts && isFiniteFloat ( inferior ); i ++ {
2026-10-15 23:54:15 +08:00
date := mercuryRetrogradeToProgradeAroundInferior ( inferior , inst )
2026-09-17 12:27:40 +08:00
if isFiniteFloat ( date ) && stationUTQueryBeforeOrEqual ( date , jde ) {
return date
}
2026-10-15 23:54:15 +08:00
inferior = LastMercuryInferiorConjunctionWithInstance ( eventUTLastQueryTT ( inferior ), inst )
2026-05-01 22:38:44 +08:00
}
2026-09-17 12:27:40 +08:00
return math . NaN ()
2026-05-01 22:38:44 +08:00
}
2026-09-17 12:27:40 +08:00
// earliestFiniteEventUT / latestFiniteEventUT 在候选中取最早/最晚的有限值(全部非有限时返回 NaN)。
func earliestFiniteEventUT ( candidates ... float64 ) float64 {
best := math . NaN ()
for _ , candidate := range candidates {
if ! isFiniteFloat ( candidate ) {
continue
}
if math . IsNaN ( best ) || candidate < best {
best = candidate
}
2026-08-06 12:00:56 +08:00
}
2026-09-17 12:27:40 +08:00
return best
}
func latestFiniteEventUT ( candidates ... float64 ) float64 {
best := math . NaN ()
for _ , candidate := range candidates {
if ! isFiniteFloat ( candidate ) {
continue
}
if math . IsNaN ( best ) || candidate > best {
best = candidate
}
}
return best
}
2026-10-15 23:54:15 +08:00
func nextMercuryRetrogradeFromTyped ( jde float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
return earliestFiniteEventUT ( NextMercuryProgradeToRetrogradeWithInstance ( jde , inst ), NextMercuryRetrogradeToProgradeWithInstance ( jde , inst ))
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryRetrogradeWithInstance ( jde float64 , inst * Instance ) float64 {
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-10-15 23:54:15 +08:00
motion := mercuryRADerivative ( jde , mercuryStationDerivativeStepDay , inst )
2026-08-06 12:00:56 +08:00
if motion > mercuryStationMotionTolerance {
2026-10-15 23:54:15 +08:00
p2r := NextMercuryProgradeToRetrogradeWithInstance ( jde , inst )
if isFiniteFloat ( p2r ) && ! mercuryStationBetween ( jde , UTC2TT ( p2r ), inst ) {
2026-08-06 12:00:56 +08:00
return p2r
}
2026-10-15 23:54:15 +08:00
best := earliestFiniteEventUT ( p2r , NextMercuryRetrogradeToProgradeWithInstance ( jde , inst ))
2026-09-17 12:27:40 +08:00
if ! stationUTQueryAfterOrEqual ( best , jde ) {
return math . NaN ()
2026-08-06 12:00:56 +08:00
}
2026-09-17 12:27:40 +08:00
return best
2026-08-06 12:00:56 +08:00
}
if motion < - mercuryStationMotionTolerance {
2026-10-15 23:54:15 +08:00
r2p := NextMercuryRetrogradeToProgradeWithInstance ( jde , inst )
if isFiniteFloat ( r2p ) && ! mercuryStationBetween ( jde , UTC2TT ( r2p ), inst ) {
2026-08-06 12:00:56 +08:00
return r2p
}
2026-10-15 23:54:15 +08:00
best := earliestFiniteEventUT ( NextMercuryProgradeToRetrogradeWithInstance ( jde , inst ), r2p )
2026-09-17 12:27:40 +08:00
if ! stationUTQueryAfterOrEqual ( best , jde ) {
return math . NaN ()
2026-08-06 12:00:56 +08:00
}
2026-09-17 12:27:40 +08:00
return best
2026-08-06 12:00:56 +08:00
}
2026-10-15 23:54:15 +08:00
return nextMercuryRetrogradeFromTyped ( jde , inst )
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
func lastMercuryRetrogradeFromTyped ( jde float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
return latestFiniteEventUT ( LastMercuryProgradeToRetrogradeWithInstance ( jde , inst ), LastMercuryRetrogradeToProgradeWithInstance ( jde , inst ))
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryRetrogradeWithInstance ( jde float64 , inst * Instance ) float64 {
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-10-15 23:54:15 +08:00
motion := mercuryRADerivative ( jde , mercuryStationDerivativeStepDay , inst )
2026-08-06 12:00:56 +08:00
if motion > mercuryStationMotionTolerance {
2026-10-15 23:54:15 +08:00
r2p := LastMercuryRetrogradeToProgradeWithInstance ( jde , inst )
if isFiniteFloat ( r2p ) && ! mercuryStationBetween ( UTC2TT ( r2p ), jde , inst ) {
2026-08-06 12:00:56 +08:00
return r2p
}
2026-10-15 23:54:15 +08:00
best := latestFiniteEventUT ( LastMercuryProgradeToRetrogradeWithInstance ( jde , inst ), r2p )
2026-09-17 12:27:40 +08:00
if ! stationUTQueryBeforeOrEqual ( best , jde ) {
return math . NaN ()
2026-08-06 12:00:56 +08:00
}
2026-09-17 12:27:40 +08:00
return best
2026-08-06 12:00:56 +08:00
}
if motion < - mercuryStationMotionTolerance {
2026-10-15 23:54:15 +08:00
p2r := LastMercuryProgradeToRetrogradeWithInstance ( jde , inst )
if isFiniteFloat ( p2r ) && ! mercuryStationBetween ( UTC2TT ( p2r ), jde , inst ) {
2026-08-06 12:00:56 +08:00
return p2r
}
2026-10-15 23:54:15 +08:00
best := latestFiniteEventUT ( p2r , LastMercuryRetrogradeToProgradeWithInstance ( jde , inst ))
2026-09-17 12:27:40 +08:00
if ! stationUTQueryBeforeOrEqual ( best , jde ) {
return math . NaN ()
2026-08-06 12:00:56 +08:00
}
2026-09-17 12:27:40 +08:00
return best
2026-08-06 12:00:56 +08:00
}
2026-10-15 23:54:15 +08:00
return lastMercuryRetrogradeFromTyped ( jde , inst )
2026-05-01 22:38:44 +08:00
}
2026-08-06 12:00:56 +08:00
func LastMercuryRetrogradeStrict ( jde float64 ) float64 {
return LastMercuryRetrograde ( jde )
2026-05-01 22:38:44 +08:00
}
2026-08-06 12:00:56 +08:00
func NextMercuryRetrogradeStrict ( jde float64 ) float64 {
return NextMercuryRetrograde ( jde )
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// MercurySunElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func MercurySunElongationWithInstance ( jde float64 , inst * Instance ) float64 {
lo1 , bo1 := MercuryApparentLoBoWithInstance ( jde , inst )
lo2 := HSunApparentLoWithInstance ( jde , inst )
bo2 := HSunTrueBoWithInstance ( jde , inst )
2026-05-01 22:38:44 +08:00
return StarAngularSeparation ( lo1 , bo1 , lo2 , bo2 )
}
2026-10-15 23:54:15 +08:00
func mercurySunElongationN ( jde float64 , n int , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
lo1 , bo1 := MercuryApparentLoBoNWithInstance ( jde , n , inst )
lo2 := HSunApparentLoNWithInstance ( jde , n , inst )
bo2 := HSunTrueBoNWithInstance ( jde , n , inst )
2026-05-22 12:24:41 +08:00
return StarAngularSeparation ( lo1 , bo1 , lo2 , bo2 )
}
2026-09-17 12:27:40 +08:00
// mercuryGreatestElongationInWindow 求窗口内大距:目标是公开 MercurySunElongation 的极大(视距角),
// 而不是忽略光行差/视位置修正的真距角,否则返回的时刻不是调用方能量到的那个极值。
// 窗口两端是世界时,目标函数收力学时,因此逐次换算。
2026-10-15 23:54:15 +08:00
func mercuryGreatestElongationInWindow ( start , end float64 , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-09-17 12:27:40 +08:00
return maximizeInWindow ( start , end , 2.0 , func ( utJD float64 ) float64 {
2026-10-15 23:54:15 +08:00
return mercurySunElongationN ( UTC2TT ( utJD ), mercuryEventSearchN , inst )
2026-09-17 12:27:40 +08:00
}, func ( utJD float64 ) float64 {
2026-10-15 23:54:15 +08:00
return MercurySunElongationWithInstance ( UTC2TT ( utJD ), inst )
2026-05-22 12:24:41 +08:00
})
}
2026-10-15 23:54:15 +08:00
func mercuryEastElongationWindowEndingAt ( inferior float64 , instances ...* Instance ) ( float64 , float64 ) {
inst := instanceValue ( instances )
lastSuperior := LastMercurySuperiorConjunctionWithInstance ( eventUTLastQueryTT ( inferior ), inst )
2026-05-23 23:08:05 +08:00
return lastSuperior + innerEventWindowPadding , inferior - innerEventWindowPadding
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryWestElongationWindowEndingAt ( superior float64 , instances ...* Instance ) ( float64 , float64 ) {
inst := instanceValue ( instances )
lastInferior := LastMercuryInferiorConjunctionWithInstance ( eventUTLastQueryTT ( superior ), inst )
2026-05-23 23:08:05 +08:00
return lastInferior + innerEventWindowPadding , superior - innerEventWindowPadding
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryEastElongationWindowContaining ( jde float64 , instances ...* Instance ) ( float64 , float64 ) {
inst := instanceValue ( instances )
nextInferior := NextMercuryInferiorConjunctionWithInstance ( jde , inst )
start , end := mercuryEastElongationWindowEndingAt ( nextInferior , inst )
2026-05-22 12:24:41 +08:00
if eventUTQueryBeforeOrEqual ( start , jde ) {
return start , end
}
2026-10-15 23:54:15 +08:00
currentInferior := LastMercuryInferiorConjunctionWithInstance ( jde , inst )
return mercuryEastElongationWindowEndingAt ( currentInferior , inst )
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
func mercuryWestElongationWindowContaining ( jde float64 , instances ...* Instance ) ( float64 , float64 ) {
inst := instanceValue ( instances )
nextSuperior := NextMercurySuperiorConjunctionWithInstance ( jde , inst )
start , end := mercuryWestElongationWindowEndingAt ( nextSuperior , inst )
2026-05-22 12:24:41 +08:00
if eventUTQueryBeforeOrEqual ( start , jde ) {
return start , end
}
2026-10-15 23:54:15 +08:00
currentSuperior := LastMercurySuperiorConjunctionWithInstance ( jde , inst )
return mercuryWestElongationWindowEndingAt ( currentSuperior , inst )
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
func nextMercuryGreatestElongationTyped ( jde float64 , east bool , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-08-06 12:00:56 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-05-22 12:24:41 +08:00
if east {
2026-10-15 23:54:15 +08:00
start , windowEnd := mercuryEastElongationWindowContaining ( jde , inst )
2026-08-06 12:00:56 +08:00
for i := 0 ; i < eventDirectionalSearchIterations ; i ++ {
2026-10-15 23:54:15 +08:00
date := mercuryGreatestElongationInWindow ( start , windowEnd , inst )
2026-08-06 12:00:56 +08:00
if ! isFiniteFloat ( date ) {
return math . NaN ()
}
2026-05-22 12:24:41 +08:00
if eventUTQueryAfterOrEqual ( date , jde ) {
return date
}
2026-10-15 23:54:15 +08:00
nextInferior := NextMercuryInferiorConjunctionWithInstance ( eventUTNextQueryTT ( windowEnd ), inst )
start , windowEnd = mercuryEastElongationWindowEndingAt ( nextInferior , inst )
2026-05-22 12:24:41 +08:00
}
2026-08-06 12:00:56 +08:00
return math . NaN ()
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
start , windowEnd := mercuryWestElongationWindowContaining ( jde , inst )
2026-08-06 12:00:56 +08:00
for i := 0 ; i < eventDirectionalSearchIterations ; i ++ {
2026-10-15 23:54:15 +08:00
date := mercuryGreatestElongationInWindow ( start , windowEnd , inst )
2026-08-06 12:00:56 +08:00
if ! isFiniteFloat ( date ) {
return math . NaN ()
}
2026-05-22 12:24:41 +08:00
if eventUTQueryAfterOrEqual ( date , jde ) {
return date
}
2026-10-15 23:54:15 +08:00
nextSuperior := NextMercurySuperiorConjunctionWithInstance ( eventUTNextQueryTT ( windowEnd ), inst )
start , windowEnd = mercuryWestElongationWindowEndingAt ( nextSuperior , inst )
2026-05-22 12:24:41 +08:00
}
2026-08-06 12:00:56 +08:00
return math . NaN ()
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
func lastMercuryGreatestElongationTyped ( jde float64 , east bool , instances ...* Instance ) float64 {
inst := instanceValue ( instances )
2026-08-06 12:00:56 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-05-22 12:24:41 +08:00
if east {
2026-10-15 23:54:15 +08:00
start , windowEnd := mercuryEastElongationWindowContaining ( jde , inst )
2026-08-06 12:00:56 +08:00
for i := 0 ; i < eventDirectionalSearchIterations ; i ++ {
2026-10-15 23:54:15 +08:00
date := mercuryGreatestElongationInWindow ( start , windowEnd , inst )
2026-08-06 12:00:56 +08:00
if ! isFiniteFloat ( date ) {
return math . NaN ()
}
2026-05-22 12:24:41 +08:00
if eventUTQueryBeforeOrEqual ( date , jde ) {
return date
}
2026-10-15 23:54:15 +08:00
prevInferior := LastMercuryInferiorConjunctionWithInstance ( eventUTLastQueryTT ( start ), inst )
start , windowEnd = mercuryEastElongationWindowEndingAt ( prevInferior , inst )
2026-05-22 12:24:41 +08:00
}
2026-08-06 12:00:56 +08:00
return math . NaN ()
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
start , windowEnd := mercuryWestElongationWindowContaining ( jde , inst )
2026-08-06 12:00:56 +08:00
for i := 0 ; i < eventDirectionalSearchIterations ; i ++ {
2026-10-15 23:54:15 +08:00
date := mercuryGreatestElongationInWindow ( start , windowEnd , inst )
2026-08-06 12:00:56 +08:00
if ! isFiniteFloat ( date ) {
return math . NaN ()
}
2026-05-22 12:24:41 +08:00
if eventUTQueryBeforeOrEqual ( date , jde ) {
return date
}
2026-10-15 23:54:15 +08:00
prevSuperior := LastMercurySuperiorConjunctionWithInstance ( eventUTLastQueryTT ( start ), inst )
start , windowEnd = mercuryWestElongationWindowEndingAt ( prevSuperior , inst )
2026-05-22 12:24:41 +08:00
}
2026-08-06 12:00:56 +08:00
return math . NaN ()
2026-05-22 12:24:41 +08:00
}
2026-09-17 12:27:40 +08:00
// mercuryElongationWindowAt 返回包含 jde 的该侧大距窗口;查询落在合的 4 秒内边距里时两侧都不包含。
2026-10-15 23:54:15 +08:00
func mercuryElongationWindowAt ( jde float64 , east bool , instances ...* Instance ) ( float64 , float64 , bool ) {
inst := instanceValue ( instances )
2026-09-17 12:27:40 +08:00
if east {
2026-10-15 23:54:15 +08:00
start , end := mercuryEastElongationWindowEndingAt ( NextMercuryInferiorConjunctionWithInstance ( jde , inst ), inst )
2026-09-17 12:27:40 +08:00
return start , end , eventUTQueryBeforeOrEqual ( start , jde ) && eventUTQueryAfterOrEqual ( end , jde )
}
2026-10-15 23:54:15 +08:00
start , end := mercuryWestElongationWindowEndingAt ( NextMercurySuperiorConjunctionWithInstance ( jde , inst ), inst )
2026-09-17 12:27:40 +08:00
return start , end , eventUTQueryBeforeOrEqual ( start , jde ) && eventUTQueryAfterOrEqual ( end , jde )
}
2026-10-15 23:54:15 +08:00
// NextMercuryGreatestElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryGreatestElongationWithInstance ( jde float64 , inst * Instance ) float64 {
2026-08-06 12:00:56 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
}
2026-10-15 23:54:15 +08:00
// 先只看查询所在一侧的窗口,未过即答案,已过则取另一侧的下一个;赤经差在合附近会提前变号,不能用来定窗口。
2026-09-17 12:27:40 +08:00
for _ , east := range [ 2 ] bool { true , false } {
2026-10-15 23:54:15 +08:00
start , end , ok := mercuryElongationWindowAt ( jde , east , inst )
2026-09-17 12:27:40 +08:00
if ! ok {
continue
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
if date := mercuryGreatestElongationInWindow ( start , end , inst ); isFiniteFloat ( date ) && eventUTQueryAfterOrEqual ( date , jde ) {
2026-09-17 12:27:40 +08:00
return date
2026-08-06 12:00:56 +08:00
}
2026-10-15 23:54:15 +08:00
if date := nextMercuryGreatestElongationTyped ( jde , ! east , inst ); isFiniteFloat ( date ) && eventUTQueryAfterOrEqual ( date , jde ) {
2026-09-17 12:27:40 +08:00
return date
2026-05-01 22:38:44 +08:00
}
break
}
2026-10-15 23:54:15 +08:00
return earliestFiniteEventUT ( nextMercuryGreatestElongationTyped ( jde , true , inst ), nextMercuryGreatestElongationTyped ( jde , false , inst ))
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryGreatestElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryGreatestElongationWithInstance ( jde float64 , inst * Instance ) float64 {
2026-09-17 12:27:40 +08:00
if ! isFiniteFloat ( jde ) {
return math . NaN ()
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// 先只看查询所在一侧的窗口,未过即答案,已过则取另一侧的下一个;赤经差在合附近会提前变号,不能用来定窗口。
2026-09-17 12:27:40 +08:00
for _ , east := range [ 2 ] bool { true , false } {
2026-10-15 23:54:15 +08:00
start , end , ok := mercuryElongationWindowAt ( jde , east , inst )
2026-09-17 12:27:40 +08:00
if ! ok {
continue
}
2026-10-15 23:54:15 +08:00
if date := mercuryGreatestElongationInWindow ( start , end , inst ); isFiniteFloat ( date ) && eventUTQueryBeforeOrEqual ( date , jde ) {
2026-09-17 12:27:40 +08:00
return date
}
2026-10-15 23:54:15 +08:00
if date := lastMercuryGreatestElongationTyped ( jde , ! east , inst ); isFiniteFloat ( date ) && eventUTQueryBeforeOrEqual ( date , jde ) {
2026-09-17 12:27:40 +08:00
return date
}
break
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
return latestFiniteEventUT ( lastMercuryGreatestElongationTyped ( jde , true , inst ), lastMercuryGreatestElongationTyped ( jde , false , inst ))
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryInferiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryInferiorConjunctionInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryInferiorConjunctionWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryInferiorConjunctionWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryInferiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryInferiorConjunctionInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryInferiorConjunctionWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryInferiorConjunctionWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercurySuperiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercurySuperiorConjunctionInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercurySuperiorConjunctionWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercurySuperiorConjunctionWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercurySuperiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercurySuperiorConjunctionInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercurySuperiorConjunctionWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercurySuperiorConjunctionWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryRetrogradeInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryRetrogradeWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryRetrogradeWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryRetrogradeInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryRetrogradeWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryRetrogradeWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryProgradeToRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryProgradeToRetrogradeInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryProgradeToRetrogradeWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryProgradeToRetrogradeWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryProgradeToRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryProgradeToRetrogradeInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryProgradeToRetrogradeWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryProgradeToRetrogradeWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryRetrogradeToProgradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryRetrogradeToProgradeInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryRetrogradeToProgradeWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryRetrogradeToProgradeWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryRetrogradeToProgradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryRetrogradeToProgradeInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryRetrogradeToProgradeWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryRetrogradeToProgradeWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryGreatestElongationInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryGreatestElongationInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryGreatestElongationWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryGreatestElongationWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryGreatestElongationInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryGreatestElongationInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryGreatestElongationWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryGreatestElongationWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryGreatestElongationEastInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryGreatestElongationEastInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryGreatestElongationEastWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryGreatestElongationEastWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryGreatestElongationEastInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryGreatestElongationEastInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryGreatestElongationEastWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryGreatestElongationEastWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryGreatestElongationWestInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryGreatestElongationWestInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveLastSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryGreatestElongationWestWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryGreatestElongationWestWithInstance ( jde , inst ) })
2026-05-22 12:24:41 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryGreatestElongationWestInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryGreatestElongationWestInclusiveWithInstance ( jde float64 , inst * Instance ) float64 {
return inclusiveNextSimpleEvent ( jde , func ( jde float64 ) float64 { return LastMercuryGreatestElongationWestWithInstance ( jde , inst ) }, func ( jde float64 ) float64 { return NextMercuryGreatestElongationWestWithInstance ( jde , inst ) })
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryGreatestElongationEastWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryGreatestElongationEastWithInstance ( jde float64 , inst * Instance ) float64 {
return nextMercuryGreatestElongationTyped ( jde , true , inst )
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// NextMercuryGreatestElongationWestWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func NextMercuryGreatestElongationWestWithInstance ( jde float64 , inst * Instance ) float64 {
return nextMercuryGreatestElongationTyped ( jde , false , inst )
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryGreatestElongationEastWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryGreatestElongationEastWithInstance ( jde float64 , inst * Instance ) float64 {
return lastMercuryGreatestElongationTyped ( jde , true , inst )
2026-05-01 22:38:44 +08:00
}
2026-10-15 23:54:15 +08:00
// LastMercuryGreatestElongationWestWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func LastMercuryGreatestElongationWestWithInstance ( jde float64 , inst * Instance ) float64 {
return lastMercuryGreatestElongationTyped ( jde , false , inst )
2026-05-01 22:38:44 +08:00
}