package basic import ( "math" "b612.me/astro/planet" . "b612.me/astro/tools" ) const ( MERCURY_S_PERIOD = 1 / ((1 / 87.9691) - (1 / 365.256363004)) mercuryConjunctionDerivativeStepDay = 2e-5 * 36525.0 mercuryLightTimeDaysPerAU = 0.0057755183 mercuryEventSearchN = 16 mercuryStationWindowDays = 30.0 mercuryStationDerivativeStepDay = 0.01 mercuryStationCoarseStepDay = 2.0 mercuryStationHalfWindowDay = 2.0 mercuryStationMotionTolerance = 1e-3 ) 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 } func mercuryHelioN(planetIndex int, jd float64, n int) mercuryConjunctionLBR { return mercuryConjunctionLBR{ lo: planet.WherePlanetN(planetIndex, 0, jd, n), bo: planet.WherePlanetN(planetIndex, 1, jd, n), r: planet.WherePlanetN(planetIndex, 2, jd, n), } } 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 } func mercuryConjunctionHeliocentricDelta(jd, targetDeg float64, n int) float64 { planetLo := planet.WherePlanetN(1, 0, jd, n) earthLo := planet.WherePlanetN(-1, 0, jd, n) return mercuryConjunctionAngleDelta(planetLo - earthLo - targetDeg) } func mercuryConjunctionDifference(jd float64, n int, targetDeg, sunLightDays, geoLightDays float64) mercuryConjunctionResult { earthForSun := mercuryHelioN(-1, jd-sunLightDays, n) sunLo := Limit360(earthForSun.lo + 180) earth := mercuryHelioN(-1, jd-geoLightDays, n) planetPos := mercuryHelioN(1, jd-geoLightDays, n) geo := mercuryGeocentric(planetPos, earth) return mercuryConjunctionResult{ diff: mercuryConjunctionAngleDelta(geo.lo - sunLo - targetDeg), sunLightDays: earthForSun.r * mercuryLightTimeDaysPerAU, geoLightDays: geo.dist * mercuryLightTimeDaysPerAU, } } func mercuryConjunctionExactDelta(jd float64) float64 { return mercuryConjunctionAngleDelta(MercuryApparentLo(jd) - HSunApparentLo(jd)) } func mercuryConjunctionApproxTT(seed float64, inferior bool) float64 { heliocentricTarget := 180.0 if inferior { heliocentricTarget = 0 } jd := seed for i := 0; i < 6; i++ { jd -= mercuryConjunctionHeliocentricDelta(jd, heliocentricTarget, 8) / (360.0 / MERCURY_S_PERIOD) } startSample := mercuryConjunctionDifference(jd, 8, 0, 0, 0) nextSample := mercuryConjunctionDifference(jd+mercuryConjunctionDerivativeStepDay, 8, 0, 0, 0) diffSlope := mercuryConjunctionAngleDelta(nextSample.diff-startSample.diff) / mercuryConjunctionDerivativeStepDay refined := mercuryConjunctionDifference(jd, 40, 0, startSample.sunLightDays, startSample.geoLightDays) jd -= refined.diff / diffSlope final := mercuryConjunctionDifference(jd, -1, 0, refined.sunLightDays, refined.geoLightDays) jd -= final.diff / diffSlope return jd } func mercuryConjunctionExactTT(seed float64, inferior bool) float64 { estimateJD := mercuryConjunctionApproxTT(seed, inferior) converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJD := estimateJD longitudeDelta := mercuryConjunctionExactDelta(prevJD) longitudeSlope := (mercuryConjunctionExactDelta(prevJD+0.000005) - mercuryConjunctionExactDelta(prevJD-0.000005)) / 0.00001 nextJD := prevJD - longitudeDelta/longitudeSlope estimateJD = nextJD if math.Abs(nextJD-prevJD) <= 0.00001 { converged = true break } } if !converged { return math.NaN() } return estimateJD } func mercuryConjunction(jde float64, next uint8) float64 { //0=last 1=next if !isFiniteFloat(jde) { return math.NaN() } if math.Abs(mercuryConjunctionExactDelta(jde)) <= 30.0/86400.0 { best := math.NaN() consider := func(inferior bool) { eventUT := TD2UT(mercuryConjunctionExactTT(jde, inferior), false) 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 } } currentDelta := mercuryConjunctionExactDelta(jde) // pos 大于0:远离太阳 小于0:靠近太阳 distanceTrend := math.Abs(mercuryConjunctionExactDelta(jde+1/86400.0)) - math.Abs(currentDelta) if distanceTrend >= 0 && next == 1 && currentDelta > 0 { jde += MERCURY_S_PERIOD/8.0 + 2 } if distanceTrend >= 0 && next == 1 && currentDelta < 0 { jde += MERCURY_S_PERIOD/6.0 + 2 } if distanceTrend <= 0 && next == 0 && currentDelta < 0 { jde -= MERCURY_S_PERIOD/8.0 + 2 } if distanceTrend <= 0 && next == 0 && currentDelta > 0 { jde -= MERCURY_S_PERIOD/6.0 + 2 } found := false for i := 0; i < eventDirectionalSearchIterations; i++ { currentDelta := mercuryConjunctionExactDelta(jde) nextDelta := mercuryConjunctionExactDelta(jde + 1/86400.0) if !isFiniteFloat(currentDelta) || !isFiniteFloat(nextDelta) { return math.NaN() } distanceTrend := math.Abs(nextDelta) - math.Abs(currentDelta) if math.Abs(currentDelta) > 12 || (distanceTrend > 0 && next == 1) || (distanceTrend < 0 && next == 0) { if next == 1 { jde += 2 } else { jde -= 2 } continue } found = true break } if !found { return math.NaN() } inferior := mercuryConjunctionExactTT(jde, true) superior := mercuryConjunctionExactTT(jde, false) if !isFiniteFloat(inferior) || !isFiniteFloat(superior) { return math.NaN() } best := inferior if math.Abs(superior-jde) < math.Abs(inferior-jde) { best = superior } return TD2UT(best, false) } func LastMercuryConjunction(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercuryConjunctionStrict, NextMercuryConjunctionStrict) } func NextMercuryConjunction(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercuryConjunctionStrict, NextMercuryConjunctionStrict) } func LastMercuryConjunctionStrict(jde float64) float64 { return mercuryConjunction(jde, 0) } func NextMercuryConjunctionStrict(jde float64) float64 { return mercuryConjunction(jde, 1) } func NextMercuryInferiorConjunction(jde float64) float64 { date := NextMercuryConjunctionStrict(jde) if EarthMercuryAway(date) > EarthAway(date) { return NextMercuryConjunctionStrict(date + 2) } return date } func NextMercurySuperiorConjunction(jde float64) float64 { date := NextMercuryConjunctionStrict(jde) if EarthMercuryAway(date) < EarthAway(date) { return NextMercuryConjunctionStrict(date + 2) } return date } func LastMercuryInferiorConjunction(jde float64) float64 { date := LastMercuryConjunctionStrict(jde) if EarthMercuryAway(date) > EarthAway(date) { return LastMercuryConjunctionStrict(date - 2) } return date } func LastMercurySuperiorConjunction(jde float64) float64 { date := LastMercuryConjunctionStrict(jde) if EarthMercuryAway(date) < EarthAway(date) { return LastMercuryConjunctionStrict(date - 2) } return date } func mercuryRADerivative(jde, delta float64) float64 { sub := MercuryApparentRa(jde+delta) - MercuryApparentRa(jde-delta) if sub > 180 { sub -= 360 } if sub < -180 { sub += 360 } return sub / (2 * delta) } func mercuryRADerivativeN(jde, delta float64, n int) float64 { sub := MercuryApparentRaN(jde+delta, n) - MercuryApparentRaN(jde-delta, n) if sub > 180 { sub -= 360 } if sub < -180 { sub += 360 } return sub / (2 * delta) } func mercuryStationInWindow(startTT, endTT float64) float64 { bestJD := zeroEventInWindow(startTT, endTT, mercuryStationCoarseStepDay, mercuryStationHalfWindowDay, 30.0/86400.0, func(jd float64) float64 { return mercuryRADerivativeN(jd, mercuryStationDerivativeStepDay, mercuryEventSearchN) }, func(jd float64) float64 { return mercuryRADerivative(jd, mercuryStationDerivativeStepDay) }) return TD2UT(bestJD, false) } func mercuryStationBetween(startTT, endTT float64) bool { if endTT < startTT { startTT, endTT = endTT, startTT } if endTT-startTT <= 0 { return false } if endTT-startTT > mercuryStationWindowDays { return true } // 截断扫描足以判断单候选快速路径是否安全 / A truncated scan is enough to decide whether the one-candidate fast path is safe. left := startTT leftValue := mercuryRADerivativeN(left, mercuryStationDerivativeStepDay, mercuryEventSearchN) for left < endTT { right := left + mercuryStationCoarseStepDay if right > endTT { right = endTT } rightValue := mercuryRADerivativeN(right, mercuryStationDerivativeStepDay, mercuryEventSearchN) if leftValue == 0 || leftValue*rightValue < 0 || rightValue == 0 { return true } left = right leftValue = rightValue } return false } func mercuryProgradeToRetrogradeAroundInferior(inferiorUT float64) float64 { inferiorTT := TD2UT(inferiorUT, true) return mercuryStationInWindow(inferiorTT-mercuryStationWindowDays, inferiorTT) } func mercuryRetrogradeToProgradeAroundInferior(inferiorUT float64) float64 { inferiorTT := TD2UT(inferiorUT, true) return mercuryStationInWindow(inferiorTT, inferiorTT+mercuryStationWindowDays) } func NextMercuryProgradeToRetrograde(jde float64) float64 { inferior := NextMercuryInferiorConjunction(jde) date := mercuryProgradeToRetrogradeAroundInferior(inferior) if eventUTQueryAfterOrEqual(date, jde) { return date } followingInferior := NextMercuryInferiorConjunction(eventUTNextQueryTT(inferior)) return mercuryProgradeToRetrogradeAroundInferior(followingInferior) } func NextMercuryRetrogradeToPrograde(jde float64) float64 { inferior := LastMercuryInferiorConjunction(jde) date := mercuryRetrogradeToProgradeAroundInferior(inferior) if eventUTQueryAfterOrEqual(date, jde) { return date } nextInferior := NextMercuryInferiorConjunction(eventUTNextQueryTT(inferior)) return mercuryRetrogradeToProgradeAroundInferior(nextInferior) } func LastMercuryProgradeToRetrograde(jde float64) float64 { inferior := NextMercuryInferiorConjunction(jde) date := mercuryProgradeToRetrogradeAroundInferior(inferior) if eventUTQueryBeforeOrEqual(date, jde) { return date } previousInferior := LastMercuryInferiorConjunction(eventUTLastQueryTT(inferior)) return mercuryProgradeToRetrogradeAroundInferior(previousInferior) } func LastMercuryRetrogradeToPrograde(jde float64) float64 { inferior := LastMercuryInferiorConjunction(jde) date := mercuryRetrogradeToProgradeAroundInferior(inferior) if eventUTQueryBeforeOrEqual(date, jde) { return date } previousInferior := LastMercuryInferiorConjunction(eventUTLastQueryTT(inferior)) return mercuryRetrogradeToProgradeAroundInferior(previousInferior) } func nextMercuryRetrogradeFromTyped(jde float64) float64 { p2r := NextMercuryProgradeToRetrograde(jde) r2p := NextMercuryRetrogradeToPrograde(jde) if p2r < r2p { return p2r } return r2p } func NextMercuryRetrograde(jde float64) float64 { motion := mercuryRADerivative(jde, mercuryStationDerivativeStepDay) if motion > mercuryStationMotionTolerance { p2r := NextMercuryProgradeToRetrograde(jde) if !mercuryStationBetween(jde, TD2UT(p2r, true)) { return p2r } r2p := NextMercuryRetrogradeToPrograde(jde) if p2r < r2p { return p2r } return r2p } if motion < -mercuryStationMotionTolerance { r2p := NextMercuryRetrogradeToPrograde(jde) if !mercuryStationBetween(jde, TD2UT(r2p, true)) { return r2p } p2r := NextMercuryProgradeToRetrograde(jde) if p2r < r2p { return p2r } return r2p } return nextMercuryRetrogradeFromTyped(jde) } func lastMercuryRetrogradeFromTyped(jde float64) float64 { p2r := LastMercuryProgradeToRetrograde(jde) r2p := LastMercuryRetrogradeToPrograde(jde) if p2r > r2p { return p2r } return r2p } func LastMercuryRetrograde(jde float64) float64 { motion := mercuryRADerivative(jde, mercuryStationDerivativeStepDay) if motion > mercuryStationMotionTolerance { r2p := LastMercuryRetrogradeToPrograde(jde) if !mercuryStationBetween(TD2UT(r2p, true), jde) { return r2p } p2r := LastMercuryProgradeToRetrograde(jde) if p2r > r2p { return p2r } return r2p } if motion < -mercuryStationMotionTolerance { p2r := LastMercuryProgradeToRetrograde(jde) if !mercuryStationBetween(TD2UT(p2r, true), jde) { return p2r } r2p := LastMercuryRetrogradeToPrograde(jde) if p2r > r2p { return p2r } return r2p } return lastMercuryRetrogradeFromTyped(jde) } func LastMercuryRetrogradeStrict(jde float64) float64 { return LastMercuryRetrograde(jde) } func NextMercuryRetrogradeStrict(jde float64) float64 { return NextMercuryRetrograde(jde) } func MercurySunElongation(jde float64) float64 { lo1, bo1 := MercuryApparentLoBo(jde) lo2 := HSunApparentLo(jde) bo2 := HSunTrueBo(jde) return StarAngularSeparation(lo1, bo1, lo2, bo2) } func mercurySunElongationN(jde float64, n int) float64 { lo1, bo1 := MercuryApparentLoBoN(jde, n) lo2 := HSunApparentLoN(jde, n) bo2 := HSunTrueBoN(jde, n) return StarAngularSeparation(lo1, bo1, lo2, bo2) } func mercuryTrueElongationN(jde float64, n int) float64 { earth := mercuryHelioN(-1, jde, n) planetPos := mercuryHelioN(1, jde, n) geo := mercuryGeocentric(planetPos, earth) return StarAngularSeparation(geo.lo, geo.bo, HSunTrueLoN(jde, n), HSunTrueBoN(jde, n)) } func mercuryGreatestElongationInWindow(start, end float64) float64 { best := maximizeInWindow(start, end, 2.0, func(jd float64) float64 { return mercuryTrueElongationN(jd, mercuryEventSearchN) }, func(jd float64) float64 { return mercuryTrueElongationN(jd, -1) }) return TD2UT(best, false) } func mercuryEastElongationWindowEndingAt(inferior float64) (float64, float64) { lastSuperior := LastMercurySuperiorConjunction(eventUTLastQueryTT(inferior)) return lastSuperior + innerEventWindowPadding, inferior - innerEventWindowPadding } func mercuryWestElongationWindowEndingAt(superior float64) (float64, float64) { lastInferior := LastMercuryInferiorConjunction(eventUTLastQueryTT(superior)) return lastInferior + innerEventWindowPadding, superior - innerEventWindowPadding } func mercuryEastElongationWindowContaining(jde float64) (float64, float64) { nextInferior := NextMercuryInferiorConjunction(jde) start, end := mercuryEastElongationWindowEndingAt(nextInferior) if eventUTQueryBeforeOrEqual(start, jde) { return start, end } currentInferior := LastMercuryInferiorConjunction(jde) return mercuryEastElongationWindowEndingAt(currentInferior) } func mercuryWestElongationWindowContaining(jde float64) (float64, float64) { nextSuperior := NextMercurySuperiorConjunction(jde) start, end := mercuryWestElongationWindowEndingAt(nextSuperior) if eventUTQueryBeforeOrEqual(start, jde) { return start, end } currentSuperior := LastMercurySuperiorConjunction(jde) return mercuryWestElongationWindowEndingAt(currentSuperior) } func nextMercuryGreatestElongationTyped(jde float64, east bool) float64 { if !isFiniteFloat(jde) { return math.NaN() } if east { start, windowEnd := mercuryEastElongationWindowContaining(jde) for i := 0; i < eventDirectionalSearchIterations; i++ { date := mercuryGreatestElongationInWindow(start, windowEnd) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryAfterOrEqual(date, jde) { return date } nextInferior := NextMercuryInferiorConjunction(eventUTNextQueryTT(windowEnd)) start, windowEnd = mercuryEastElongationWindowEndingAt(nextInferior) } return math.NaN() } start, windowEnd := mercuryWestElongationWindowContaining(jde) for i := 0; i < eventDirectionalSearchIterations; i++ { date := mercuryGreatestElongationInWindow(start, windowEnd) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryAfterOrEqual(date, jde) { return date } nextSuperior := NextMercurySuperiorConjunction(eventUTNextQueryTT(windowEnd)) start, windowEnd = mercuryWestElongationWindowEndingAt(nextSuperior) } return math.NaN() } func lastMercuryGreatestElongationTyped(jde float64, east bool) float64 { if !isFiniteFloat(jde) { return math.NaN() } if east { start, windowEnd := mercuryEastElongationWindowContaining(jde) for i := 0; i < eventDirectionalSearchIterations; i++ { date := mercuryGreatestElongationInWindow(start, windowEnd) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryBeforeOrEqual(date, jde) { return date } prevInferior := LastMercuryInferiorConjunction(eventUTLastQueryTT(start)) start, windowEnd = mercuryEastElongationWindowEndingAt(prevInferior) } return math.NaN() } start, windowEnd := mercuryWestElongationWindowContaining(jde) for i := 0; i < eventDirectionalSearchIterations; i++ { date := mercuryGreatestElongationInWindow(start, windowEnd) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryBeforeOrEqual(date, jde) { return date } prevSuperior := LastMercurySuperiorConjunction(eventUTLastQueryTT(start)) start, windowEnd = mercuryWestElongationWindowEndingAt(prevSuperior) } return math.NaN() } func mercuryGreatestElongation(jde float64) float64 { if !isFiniteFloat(jde) { return math.NaN() } solarRADelta := func(jde float64) float64 { sub := Limit360(MercuryApparentRa(jde) - SunApparentRa(jde)) if sub > 180 { sub -= 360 } if sub < -180 { sub += 360 } return sub } elongationRate := func(jde float64, delta float64) float64 { sub := MercurySunElongation(jde+delta) - MercurySunElongation(jde-delta) if sub > 180 { sub -= 360 } if sub < -180 { sub += 360 } return sub / (2 * delta) } lastConjunction := LastMercuryConjunctionStrict(jde) nextConjunction := NextMercuryConjunctionStrict(jde) currentRADelta := solarRADelta(jde) if currentRADelta > 0 { jde = lastConjunction + ((nextConjunction - lastConjunction) / 5.0 * 2.0) } else { jde = lastConjunction + ((nextConjunction - lastConjunction) / 6.0) } found := false for i := 0; i < eventDirectionalSearchIterations; i++ { currentRate := elongationRate(jde, 1.0/86400.0) if !isFiniteFloat(currentRate) { return math.NaN() } if math.Abs(currentRate) > 0.4 { jde += 2 continue } found = true break } if !found { return math.NaN() } estimateJD := jde var ok bool estimateJD, ok = eventNewtonRefine(estimateJD, 30.0/86400.0, func(prevJD float64) float64 { rateValue := elongationRate(prevJD, 2.0/86400.0) rateSlope := (elongationRate(prevJD+15.0/86400.0, 2.0/86400.0) - elongationRate(prevJD-15.0/86400.0, 2.0/86400.0)) / (30.0 / 86400.0) return rateValue / rateSlope }) if !ok { return math.NaN() } bestJD := eventZeroRefine(estimateJD, 15.0/86400.0, 0.5/86400.0, func(jd float64) float64 { return elongationRate(jd, 0.5/86400.0) }) //fmt.Println((bestJD - lastConjunction) / (nextConjunction - lastConjunction)) return TD2UT(bestJD, false) } func NextMercuryGreatestElongation(jde float64) float64 { east := NextMercuryGreatestElongationEast(jde) west := NextMercuryGreatestElongationWest(jde) if sameEventJD(east, west) { return east } if east < west { return east } return west } func LastMercuryGreatestElongation(jde float64) float64 { east := LastMercuryGreatestElongationEast(jde) west := LastMercuryGreatestElongationWest(jde) if sameEventJD(east, west) { return east } if east > west { return east } return west } func LastMercuryInferiorConjunctionInclusive(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercuryInferiorConjunction, NextMercuryInferiorConjunction) } func NextMercuryInferiorConjunctionInclusive(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercuryInferiorConjunction, NextMercuryInferiorConjunction) } func LastMercurySuperiorConjunctionInclusive(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercurySuperiorConjunction, NextMercurySuperiorConjunction) } func NextMercurySuperiorConjunctionInclusive(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercurySuperiorConjunction, NextMercurySuperiorConjunction) } func LastMercuryRetrogradeInclusive(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercuryRetrograde, NextMercuryRetrograde) } func NextMercuryRetrogradeInclusive(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercuryRetrograde, NextMercuryRetrograde) } func LastMercuryProgradeToRetrogradeInclusive(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercuryProgradeToRetrograde, NextMercuryProgradeToRetrograde) } func NextMercuryProgradeToRetrogradeInclusive(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercuryProgradeToRetrograde, NextMercuryProgradeToRetrograde) } func LastMercuryRetrogradeToProgradeInclusive(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercuryRetrogradeToPrograde, NextMercuryRetrogradeToPrograde) } func NextMercuryRetrogradeToProgradeInclusive(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercuryRetrogradeToPrograde, NextMercuryRetrogradeToPrograde) } func LastMercuryGreatestElongationInclusive(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercuryGreatestElongation, NextMercuryGreatestElongation) } func NextMercuryGreatestElongationInclusive(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercuryGreatestElongation, NextMercuryGreatestElongation) } func LastMercuryGreatestElongationEastInclusive(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercuryGreatestElongationEast, NextMercuryGreatestElongationEast) } func NextMercuryGreatestElongationEastInclusive(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercuryGreatestElongationEast, NextMercuryGreatestElongationEast) } func LastMercuryGreatestElongationWestInclusive(jde float64) float64 { return inclusiveLastSimpleEvent(jde, LastMercuryGreatestElongationWest, NextMercuryGreatestElongationWest) } func NextMercuryGreatestElongationWestInclusive(jde float64) float64 { return inclusiveNextSimpleEvent(jde, LastMercuryGreatestElongationWest, NextMercuryGreatestElongationWest) } func NextMercuryGreatestElongationEast(jde float64) float64 { return nextMercuryGreatestElongationTyped(jde, true) } func NextMercuryGreatestElongationWest(jde float64) float64 { return nextMercuryGreatestElongationTyped(jde, false) } func LastMercuryGreatestElongationEast(jde float64) float64 { return lastMercuryGreatestElongationTyped(jde, true) } func LastMercuryGreatestElongationWest(jde float64) float64 { return lastMercuryGreatestElongationTyped(jde, false) }