044f2f4381
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
770 lines
34 KiB
Go
770 lines
34 KiB
Go
package basic
|
|
|
|
import (
|
|
"math"
|
|
|
|
. "b612.me/astro/tools"
|
|
)
|
|
|
|
const (
|
|
VENUS_S_PERIOD = 1 / ((1 / 224.701) - (1 / 365.256363004))
|
|
venusEventSearchN = 16
|
|
// venusConjunctionSameInstantDegrees 「同刻」快速路径的视黄经差触发阈值(度):必须明显大于
|
|
// 截断级数与全项级数的最大视黄经差,否则「查询落在合前几秒」时起点符号相反,方向扫描会跨过这次合。
|
|
// 0.05 度约合 38 分钟;侧向判定仍用 0.1 秒口径,语义不变。
|
|
venusConjunctionSameInstantDegrees = 5.0e-2
|
|
)
|
|
|
|
func venusSunLongitudeDelta(jde float64, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
sub := Limit360(VenusApparentLoWithInstance(jde, inst) - HSunApparentLoWithInstance(jde, inst))
|
|
if sub > 180 {
|
|
sub -= 360
|
|
}
|
|
if sub < -180 {
|
|
sub += 360
|
|
}
|
|
return sub
|
|
}
|
|
|
|
func venusSunLongitudeDeltaN(jde float64, n int, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
sub := Limit360(VenusApparentLoNWithInstance(jde, n, inst) - HSunApparentLoNWithInstance(jde, n, inst))
|
|
if sub > 180 {
|
|
sub -= 360
|
|
}
|
|
if sub < -180 {
|
|
sub += 360
|
|
}
|
|
return sub
|
|
}
|
|
|
|
func venusRADerivative(jde, val float64, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
sub := VenusApparentRaWithInstance(jde+val, inst) - VenusApparentRaWithInstance(jde-val, inst)
|
|
if sub > 180 {
|
|
sub -= 360
|
|
}
|
|
if sub < -180 {
|
|
sub += 360
|
|
}
|
|
return sub / (2 * val)
|
|
}
|
|
|
|
func venusRAContinuousForMax(jde float64, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
ra := VenusApparentRaWithInstance(jde, inst)
|
|
if ra < 180 {
|
|
return ra + 360
|
|
}
|
|
return ra
|
|
}
|
|
|
|
func venusRAContinuousForMaxN(jde float64, n int, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
ra := VenusApparentRaNWithInstance(jde, n, inst)
|
|
if ra < 180 {
|
|
return ra + 360
|
|
}
|
|
return ra
|
|
}
|
|
|
|
func venusRAContinuousForMin(jde float64, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
ra := VenusApparentRaWithInstance(jde, inst)
|
|
if ra > 180 {
|
|
return ra - 360
|
|
}
|
|
return ra
|
|
}
|
|
|
|
func venusRAContinuousForMinN(jde float64, n int, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
ra := VenusApparentRaNWithInstance(jde, n, inst)
|
|
if ra > 180 {
|
|
return ra - 360
|
|
}
|
|
return ra
|
|
}
|
|
|
|
func venusRAExtremumRefine(seed, start, end, step float64, fn func(float64) float64) float64 {
|
|
centerJD := clampFloat64(seed, start, end)
|
|
halfStep := step
|
|
bestJD := centerJD
|
|
bestVal := fn(centerJD)
|
|
for i := 0; i < 8; i++ {
|
|
leftJD := clampFloat64(centerJD-halfStep, start, end)
|
|
rightJD := clampFloat64(centerJD+halfStep, start, end)
|
|
leftVal := fn(leftJD)
|
|
centerVal := fn(centerJD)
|
|
rightVal := fn(rightJD)
|
|
if leftVal > bestVal {
|
|
bestVal = leftVal
|
|
bestJD = leftJD
|
|
}
|
|
if centerVal > bestVal {
|
|
bestVal = centerVal
|
|
bestJD = centerJD
|
|
}
|
|
if rightVal > bestVal {
|
|
bestVal = rightVal
|
|
bestJD = rightJD
|
|
}
|
|
denominator := leftVal - 2*centerVal + rightVal
|
|
if denominator == 0 {
|
|
centerJD = bestJD
|
|
halfStep /= 2
|
|
continue
|
|
}
|
|
vertexJD := centerJD + 0.5*halfStep*(leftVal-rightVal)/denominator
|
|
vertexJD = clampFloat64(vertexJD, leftJD, rightJD)
|
|
vertexVal := fn(vertexJD)
|
|
if vertexVal > bestVal {
|
|
bestVal = vertexVal
|
|
bestJD = vertexJD
|
|
}
|
|
centerJD = bestJD
|
|
halfStep /= 2
|
|
}
|
|
return bestJD
|
|
}
|
|
|
|
func venusSunElongationN(jde float64, n int, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
lo1, bo1 := VenusApparentLoBoNWithInstance(jde, n, inst)
|
|
lo2 := HSunApparentLoNWithInstance(jde, n, inst)
|
|
bo2 := HSunTrueBoNWithInstance(jde, n, inst)
|
|
return StarAngularSeparation(lo1, bo1, lo2, bo2)
|
|
}
|
|
|
|
func venusConjunction(jde float64, next uint8, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
if !isFiniteFloat(jde) {
|
|
return math.NaN()
|
|
}
|
|
queryTT := jde
|
|
direction := -1.0
|
|
if next == 1 {
|
|
direction = 1
|
|
}
|
|
left := queryTT
|
|
leftVal := venusSunLongitudeDeltaN(left, venusEventSearchN, inst)
|
|
if math.Abs(venusSunLongitudeDelta(queryTT, inst)) <= venusConjunctionSameInstantDegrees {
|
|
if exact, ok := venusConjunctionRefine(left, 1.0, inst); ok {
|
|
eventUT := TT2UTC(exact)
|
|
if next == 0 && eventUTQueryBeforeOrEqual(eventUT, queryTT) {
|
|
return eventUT
|
|
}
|
|
if next == 1 && eventUTQueryAfterOrEqual(eventUT, queryTT) {
|
|
return eventUT
|
|
}
|
|
}
|
|
}
|
|
const step = 8.0
|
|
for i := 0; i < 80; i++ {
|
|
right := queryTT + direction*step*float64(i+1)
|
|
rightVal := venusSunLongitudeDeltaN(right, venusEventSearchN, inst)
|
|
if leftVal == 0 || rightVal == 0 || leftVal*rightVal <= 0 {
|
|
center := (left + right) / 2.0
|
|
halfWindow := math.Abs(right-left) / 2.0
|
|
if exact, ok := venusConjunctionRefine(center, halfWindow, inst); ok {
|
|
return TT2UTC(exact)
|
|
}
|
|
// 截断级数在根附近的符号可能与全项不一致(查询几乎正好落在合上),
|
|
// 此时改用全项级数做方向扫描兜底,而不是直接有界失败。
|
|
return venusConjunctionFullDirectionalScan(queryTT, direction, inst)
|
|
}
|
|
left = right
|
|
leftVal = rightVal
|
|
}
|
|
// 640 天已经覆盖一个金星会合周期;仍无括号通常表示输入超出解析项的可靠范围。
|
|
// 继续按 5 微日扫描整个周期会产生数亿次星历计算,因此在这里有界失败。
|
|
// The 640-day directional scan already exceeds one Venus synodic period. If it
|
|
// still finds no bracket, fail in a bounded way instead of scanning hundreds
|
|
// of millions of five-microday samples across the full fallback window.
|
|
return math.NaN()
|
|
}
|
|
|
|
// venusConjunctionFullDirectionalScan 全项级数的方向扫描(截断括号未被确认时的兜底)。
|
|
func venusConjunctionFullDirectionalScan(queryTT, direction float64, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
const (
|
|
step = 8.0
|
|
maxSteps = 80
|
|
)
|
|
left := queryTT
|
|
leftVal := venusSunLongitudeDelta(left, inst)
|
|
if !isFiniteFloat(leftVal) {
|
|
return math.NaN()
|
|
}
|
|
for i := 0; i < maxSteps; i++ {
|
|
right := queryTT + direction*step*float64(i+1)
|
|
rightVal := venusSunLongitudeDelta(right, inst)
|
|
if !isFiniteFloat(rightVal) {
|
|
return math.NaN()
|
|
}
|
|
if leftVal == 0 || rightVal == 0 || leftVal*rightVal < 0 {
|
|
center := (left + right) / 2.0
|
|
if exact, ok := venusConjunctionRefine(center, math.Abs(right-left)/2.0, inst); ok {
|
|
return TT2UTC(exact)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
left = right
|
|
leftVal = rightVal
|
|
}
|
|
return math.NaN()
|
|
}
|
|
|
|
func venusConjunctionRefine(seed, halfWindow float64, instances ...*Instance) (float64, bool) {
|
|
inst := instanceValue(instances)
|
|
|
|
leftJDE := seed - halfWindow
|
|
centerJDE := seed
|
|
rightJDE := seed + halfWindow
|
|
leftVal := venusSunLongitudeDelta(leftJDE, inst)
|
|
centerVal := venusSunLongitudeDelta(centerJDE, inst)
|
|
rightVal := venusSunLongitudeDelta(rightJDE, inst)
|
|
if !isFiniteFloat(leftVal) || !isFiniteFloat(centerVal) || !isFiniteFloat(rightVal) {
|
|
return math.NaN(), false
|
|
}
|
|
if _, _, _, _, ok := eventZeroBracket(leftJDE, leftVal, centerJDE, centerVal, rightJDE, rightVal); !ok {
|
|
return math.NaN(), false
|
|
}
|
|
return eventZeroRefine(seed, halfWindow, 0.000005, func(jde float64) float64 { return venusSunLongitudeDelta(jde, inst) }), true
|
|
}
|
|
|
|
func venusConjunctionTypeAt(eventUT float64, instances ...*Instance) bool {
|
|
inst := instanceValue(instances)
|
|
|
|
return EarthVenusAwayWithInstance(eventUT, inst) <= EarthAwayWithInstance(eventUT, inst)
|
|
}
|
|
|
|
func nextVenusTypedConjunctionFromEvent(jde float64, inferior bool, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
date := NextVenusConjunctionStrictWithInstance(jde, inst)
|
|
if venusConjunctionTypeAt(date, inst) == inferior {
|
|
return date
|
|
}
|
|
return NextVenusConjunctionStrictWithInstance(eventUTNextQueryTT(date), inst)
|
|
}
|
|
|
|
func lastVenusTypedConjunctionFromEvent(jde float64, inferior bool, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
date := LastVenusConjunctionStrictWithInstance(jde, inst)
|
|
if venusConjunctionTypeAt(date, inst) == inferior {
|
|
return date
|
|
}
|
|
return LastVenusConjunctionStrictWithInstance(eventUTLastQueryTT(date), inst)
|
|
}
|
|
|
|
// LastVenusConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusConjunctionWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusConjunctionStrictWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusConjunctionStrictWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusConjunctionWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusConjunctionStrictWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusConjunctionStrictWithInstance(jde, inst) })
|
|
}
|
|
|
|
// LastVenusConjunctionStrictWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusConjunctionStrictWithInstance(jde float64, inst *Instance) float64 {
|
|
return venusConjunction(jde, 0, inst)
|
|
}
|
|
|
|
// NextVenusConjunctionStrictWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusConjunctionStrictWithInstance(jde float64, inst *Instance) float64 {
|
|
return venusConjunction(jde, 1, inst)
|
|
}
|
|
|
|
func nextVenusTypedConjunction(jde float64, inferior bool, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
return nextVenusTypedConjunctionFromEvent(jde, inferior, inst)
|
|
}
|
|
|
|
func lastVenusTypedConjunction(jde float64, inferior bool, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
return lastVenusTypedConjunctionFromEvent(jde, inferior, inst)
|
|
}
|
|
|
|
// NextVenusInferiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusInferiorConjunctionWithInstance(jde float64, inst *Instance) float64 {
|
|
return nextVenusTypedConjunction(jde, true, inst)
|
|
}
|
|
|
|
// NextVenusSuperiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusSuperiorConjunctionWithInstance(jde float64, inst *Instance) float64 {
|
|
return nextVenusTypedConjunction(jde, false, inst)
|
|
}
|
|
|
|
// LastVenusInferiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusInferiorConjunctionWithInstance(jde float64, inst *Instance) float64 {
|
|
return lastVenusTypedConjunction(jde, true, inst)
|
|
}
|
|
|
|
// LastVenusSuperiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusSuperiorConjunctionWithInstance(jde float64, inst *Instance) float64 {
|
|
return lastVenusTypedConjunction(jde, false, inst)
|
|
}
|
|
|
|
// NextVenusRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusRetrogradeWithInstance(jde float64, inst *Instance) float64 {
|
|
p2r := NextVenusProgradeToRetrogradeWithInstance(jde, inst)
|
|
r2p := NextVenusRetrogradeToProgradeWithInstance(jde, inst)
|
|
if sameEventJD(p2r, r2p) {
|
|
return p2r
|
|
}
|
|
return earliestFiniteEventUT(p2r, r2p)
|
|
}
|
|
|
|
// LastVenusRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusRetrogradeWithInstance(jde float64, inst *Instance) float64 {
|
|
p2r := LastVenusProgradeToRetrogradeWithInstance(jde, inst)
|
|
r2p := LastVenusRetrogradeToProgradeWithInstance(jde, inst)
|
|
if sameEventJD(p2r, r2p) {
|
|
return p2r
|
|
}
|
|
return latestFiniteEventUT(p2r, r2p)
|
|
}
|
|
|
|
func venusStationInWindow(start, end float64, progradeToRetrograde bool, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
var best float64
|
|
if progradeToRetrograde {
|
|
guess := scanWindowForMax(start, end, 2.0, func(jd float64) float64 {
|
|
return venusRAContinuousForMaxN(jd, venusEventSearchN, inst)
|
|
})
|
|
best = venusRAExtremumRefine(guess, start, end, 1.0, func(jd float64) float64 {
|
|
return venusRAContinuousForMax(jd, inst)
|
|
})
|
|
} else {
|
|
guess := scanWindowForMax(start, end, 2.0, func(jd float64) float64 {
|
|
return -venusRAContinuousForMinN(jd, venusEventSearchN, inst)
|
|
})
|
|
best = venusRAExtremumRefine(guess, start, end, 1.0, func(jd float64) float64 {
|
|
return -venusRAContinuousForMin(jd, inst)
|
|
})
|
|
}
|
|
// 抛物顶点法仍有秒级残差,会让「查询落在站前 1 秒」这类边界查询跳到下一个会合周期;
|
|
// 这里再用 RA 变化率的零点做一次割线抛光,把站时刻精度压到亚秒级,
|
|
// 抛光失败(括号内无异号)时保留顶点法结果。
|
|
if polished, ok := venusStationPolish(best, progradeToRetrograde, inst); ok {
|
|
best = polished
|
|
}
|
|
return TT2UTC(best)
|
|
}
|
|
|
|
// venusStationPolish 在抛物顶点结果附近求 RA 变化率的零点。
|
|
// progradeToRetrograde=true 时 RA 变化率由正转负(极大值),否则由负转正(极小值)。
|
|
func venusStationPolish(seed float64, progradeToRetrograde bool, instances ...*Instance) (float64, bool) {
|
|
inst := instanceValue(instances)
|
|
|
|
rate := func(jd float64) float64 { return venusRADerivative(jd, stationDerivativeStepDay, inst) }
|
|
for _, halfWindow := range []float64{120.0 / 86400.0, 600.0 / 86400.0} {
|
|
leftJD := seed - halfWindow
|
|
rightJD := seed + halfWindow
|
|
leftValue := rate(leftJD)
|
|
rightValue := rate(rightJD)
|
|
if !isFiniteFloat(leftValue) || !isFiniteFloat(rightValue) {
|
|
return math.NaN(), false
|
|
}
|
|
if leftValue == 0 {
|
|
return leftJD, true
|
|
}
|
|
if rightValue == 0 {
|
|
return rightJD, true
|
|
}
|
|
if leftValue*rightValue > 0 {
|
|
continue
|
|
}
|
|
if progradeToRetrograde != (leftValue > 0) {
|
|
return math.NaN(), false
|
|
}
|
|
root, ok := eventBracketSecantRoot(leftJD, rightJD, leftValue, rightValue, 0.01/86400.0, rate)
|
|
if !ok {
|
|
return math.NaN(), false
|
|
}
|
|
return root, true
|
|
}
|
|
return math.NaN(), false
|
|
}
|
|
|
|
func venusProgradeToRetrogradeAroundInferior(inferior float64, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
return venusStationInWindow(inferior-30.0, inferior-14.0, true, inst)
|
|
}
|
|
|
|
func venusRetrogradeToProgradeAroundInferior(inferior float64, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
return venusStationInWindow(inferior+14.0, inferior+24.0, false, inst)
|
|
}
|
|
|
|
// NextVenusProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 {
|
|
inferior := NextVenusInferiorConjunctionWithInstance(jde, inst)
|
|
for i := 0; i < eventDirectionalSearchIterations; i++ {
|
|
date := venusProgradeToRetrogradeAroundInferior(inferior, inst)
|
|
if !isFiniteFloat(date) {
|
|
return math.NaN()
|
|
}
|
|
if stationUTQueryAfterOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
inferior = NextVenusInferiorConjunctionWithInstance(eventUTNextQueryTT(inferior), inst)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
|
|
// NextVenusRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 {
|
|
inferior := LastVenusInferiorConjunctionWithInstance(jde, inst)
|
|
for i := 0; i < eventDirectionalSearchIterations; i++ {
|
|
date := venusRetrogradeToProgradeAroundInferior(inferior, inst)
|
|
if !isFiniteFloat(date) {
|
|
return math.NaN()
|
|
}
|
|
if stationUTQueryAfterOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
inferior = NextVenusInferiorConjunctionWithInstance(eventUTNextQueryTT(inferior), inst)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
|
|
// LastVenusProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 {
|
|
inferior := NextVenusInferiorConjunctionWithInstance(jde, inst)
|
|
for i := 0; i < eventDirectionalSearchIterations; i++ {
|
|
date := venusProgradeToRetrogradeAroundInferior(inferior, inst)
|
|
if !isFiniteFloat(date) {
|
|
return math.NaN()
|
|
}
|
|
if stationUTQueryBeforeOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
inferior = LastVenusInferiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
|
|
// LastVenusRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 {
|
|
inferior := LastVenusInferiorConjunctionWithInstance(jde, inst)
|
|
for i := 0; i < eventDirectionalSearchIterations; i++ {
|
|
date := venusRetrogradeToProgradeAroundInferior(inferior, inst)
|
|
if !isFiniteFloat(date) {
|
|
return math.NaN()
|
|
}
|
|
if stationUTQueryBeforeOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
inferior = LastVenusInferiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
|
|
// VenusSunElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func VenusSunElongationWithInstance(jde float64, inst *Instance) float64 {
|
|
lo1, bo1 := VenusApparentLoBoWithInstance(jde, inst)
|
|
lo2 := HSunApparentLoWithInstance(jde, inst)
|
|
bo2 := HSunTrueBoWithInstance(jde, inst)
|
|
return StarAngularSeparation(lo1, bo1, lo2, bo2)
|
|
}
|
|
|
|
// venusGreatestElongationInWindow 求窗口内大距:目标是公开 VenusSunElongation 的极大(视距角),
|
|
// 而不是忽略光行差/视位置修正的真距角,否则返回的时刻不是调用方能量到的那个极值。
|
|
// 窗口两端是世界时,目标函数收力学时,因此逐次换算。
|
|
func venusGreatestElongationInWindow(start, end float64, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
return maximizeInWindow(start, end, 5.0, func(utJD float64) float64 {
|
|
return venusSunElongationN(UTC2TT(utJD), venusEventSearchN, inst)
|
|
}, func(utJD float64) float64 {
|
|
return VenusSunElongationWithInstance(UTC2TT(utJD), inst)
|
|
})
|
|
}
|
|
|
|
func venusEastElongationWindowEndingAt(inferior float64, instances ...*Instance) (float64, float64) {
|
|
inst := instanceValue(instances)
|
|
|
|
lastSuperior := LastVenusSuperiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst)
|
|
return lastSuperior + innerEventWindowPadding, inferior - innerEventWindowPadding
|
|
}
|
|
|
|
func venusWestElongationWindowEndingAt(superior float64, instances ...*Instance) (float64, float64) {
|
|
inst := instanceValue(instances)
|
|
|
|
lastInferior := LastVenusInferiorConjunctionWithInstance(eventUTLastQueryTT(superior), inst)
|
|
return lastInferior + innerEventWindowPadding, superior - innerEventWindowPadding
|
|
}
|
|
|
|
func venusEastElongationWindowContaining(jde float64, instances ...*Instance) (float64, float64) {
|
|
inst := instanceValue(instances)
|
|
|
|
nextInferior := NextVenusInferiorConjunctionWithInstance(jde, inst)
|
|
start, end := venusEastElongationWindowEndingAt(nextInferior, inst)
|
|
if eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde) {
|
|
return start, end
|
|
}
|
|
currentInferior := LastVenusInferiorConjunctionWithInstance(jde, inst)
|
|
return venusEastElongationWindowEndingAt(currentInferior, inst)
|
|
}
|
|
|
|
func venusWestElongationWindowContaining(jde float64, instances ...*Instance) (float64, float64) {
|
|
inst := instanceValue(instances)
|
|
|
|
nextSuperior := NextVenusSuperiorConjunctionWithInstance(jde, inst)
|
|
start, end := venusWestElongationWindowEndingAt(nextSuperior, inst)
|
|
if eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde) {
|
|
return start, end
|
|
}
|
|
currentSuperior := LastVenusSuperiorConjunctionWithInstance(jde, inst)
|
|
return venusWestElongationWindowEndingAt(currentSuperior, inst)
|
|
}
|
|
|
|
func nextVenusGreatestElongationTyped(jde float64, east bool, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
if !isFiniteFloat(jde) {
|
|
return math.NaN()
|
|
}
|
|
if east {
|
|
start, windowEnd := venusEastElongationWindowContaining(jde, inst)
|
|
for i := 0; i < eventDirectionalSearchIterations; i++ {
|
|
date := venusGreatestElongationInWindow(start, windowEnd, inst)
|
|
if !isFiniteFloat(date) {
|
|
return math.NaN()
|
|
}
|
|
if eventUTQueryAfterOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
nextInferior := NextVenusInferiorConjunctionWithInstance(eventUTNextQueryTT(windowEnd), inst)
|
|
start, windowEnd = venusEastElongationWindowEndingAt(nextInferior, inst)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
start, windowEnd := venusWestElongationWindowContaining(jde, inst)
|
|
for i := 0; i < eventDirectionalSearchIterations; i++ {
|
|
date := venusGreatestElongationInWindow(start, windowEnd, inst)
|
|
if !isFiniteFloat(date) {
|
|
return math.NaN()
|
|
}
|
|
if eventUTQueryAfterOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
nextSuperior := NextVenusSuperiorConjunctionWithInstance(eventUTNextQueryTT(windowEnd), inst)
|
|
start, windowEnd = venusWestElongationWindowEndingAt(nextSuperior, inst)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
|
|
func lastVenusGreatestElongationTyped(jde float64, east bool, instances ...*Instance) float64 {
|
|
inst := instanceValue(instances)
|
|
|
|
if !isFiniteFloat(jde) {
|
|
return math.NaN()
|
|
}
|
|
if east {
|
|
start, windowEnd := venusEastElongationWindowContaining(jde, inst)
|
|
for i := 0; i < eventDirectionalSearchIterations; i++ {
|
|
date := venusGreatestElongationInWindow(start, windowEnd, inst)
|
|
if !isFiniteFloat(date) {
|
|
return math.NaN()
|
|
}
|
|
if eventUTQueryBeforeOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
prevInferior := LastVenusInferiorConjunctionWithInstance(eventUTLastQueryTT(start), inst)
|
|
start, windowEnd = venusEastElongationWindowEndingAt(prevInferior, inst)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
start, windowEnd := venusWestElongationWindowContaining(jde, inst)
|
|
for i := 0; i < eventDirectionalSearchIterations; i++ {
|
|
date := venusGreatestElongationInWindow(start, windowEnd, inst)
|
|
if !isFiniteFloat(date) {
|
|
return math.NaN()
|
|
}
|
|
if eventUTQueryBeforeOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
prevSuperior := LastVenusSuperiorConjunctionWithInstance(eventUTLastQueryTT(start), inst)
|
|
start, windowEnd = venusWestElongationWindowEndingAt(prevSuperior, inst)
|
|
}
|
|
return math.NaN()
|
|
}
|
|
|
|
// venusElongationWindowAt 返回包含 jde 的该侧大距窗口;查询落在合的 4 秒内边距里时两侧都不包含。
|
|
func venusElongationWindowAt(jde float64, east bool, instances ...*Instance) (float64, float64, bool) {
|
|
inst := instanceValue(instances)
|
|
|
|
if east {
|
|
start, end := venusEastElongationWindowEndingAt(NextVenusInferiorConjunctionWithInstance(jde, inst), inst)
|
|
return start, end, eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde)
|
|
}
|
|
start, end := venusWestElongationWindowEndingAt(NextVenusSuperiorConjunctionWithInstance(jde, inst), inst)
|
|
return start, end, eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde)
|
|
}
|
|
|
|
// NextVenusGreatestElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusGreatestElongationWithInstance(jde float64, inst *Instance) float64 {
|
|
if !isFiniteFloat(jde) {
|
|
return math.NaN()
|
|
}
|
|
// 先只看查询所在一侧的窗口,未过即答案,已过则取另一侧的下一个;赤经差在合附近会提前变号,不能用来定窗口。
|
|
for _, east := range [2]bool{true, false} {
|
|
start, end, ok := venusElongationWindowAt(jde, east, inst)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if date := venusGreatestElongationInWindow(start, end, inst); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
if date := nextVenusGreatestElongationTyped(jde, !east, inst); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
break
|
|
}
|
|
return earliestFiniteEventUT(nextVenusGreatestElongationTyped(jde, true, inst), nextVenusGreatestElongationTyped(jde, false, inst))
|
|
}
|
|
|
|
// LastVenusGreatestElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusGreatestElongationWithInstance(jde float64, inst *Instance) float64 {
|
|
if !isFiniteFloat(jde) {
|
|
return math.NaN()
|
|
}
|
|
// 先只看查询所在一侧的窗口,未过即答案,已过则取另一侧的下一个;赤经差在合附近会提前变号,不能用来定窗口。
|
|
for _, east := range [2]bool{true, false} {
|
|
start, end, ok := venusElongationWindowAt(jde, east, inst)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if date := venusGreatestElongationInWindow(start, end, inst); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
if date := lastVenusGreatestElongationTyped(jde, !east, inst); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) {
|
|
return date
|
|
}
|
|
break
|
|
}
|
|
return latestFiniteEventUT(lastVenusGreatestElongationTyped(jde, true, inst), lastVenusGreatestElongationTyped(jde, false, inst))
|
|
}
|
|
|
|
// LastVenusInferiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusInferiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusInferiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusInferiorConjunctionWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusInferiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusInferiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusInferiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusInferiorConjunctionWithInstance(jde, inst) })
|
|
}
|
|
|
|
// LastVenusSuperiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusSuperiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusSuperiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusSuperiorConjunctionWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusSuperiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusSuperiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusSuperiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusSuperiorConjunctionWithInstance(jde, inst) })
|
|
}
|
|
|
|
// LastVenusRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusRetrogradeWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusRetrogradeWithInstance(jde, inst) })
|
|
}
|
|
|
|
// LastVenusProgradeToRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusProgradeToRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusProgradeToRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusProgradeToRetrogradeWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusProgradeToRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusProgradeToRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusProgradeToRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusProgradeToRetrogradeWithInstance(jde, inst) })
|
|
}
|
|
|
|
// LastVenusRetrogradeToProgradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusRetrogradeToProgradeInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusRetrogradeToProgradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusRetrogradeToProgradeWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusRetrogradeToProgradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusRetrogradeToProgradeInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusRetrogradeToProgradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusRetrogradeToProgradeWithInstance(jde, inst) })
|
|
}
|
|
|
|
// LastVenusGreatestElongationInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusGreatestElongationInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusGreatestElongationInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusGreatestElongationInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationWithInstance(jde, inst) })
|
|
}
|
|
|
|
// LastVenusGreatestElongationEastInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusGreatestElongationEastInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationEastWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationEastWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusGreatestElongationEastInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusGreatestElongationEastInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationEastWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationEastWithInstance(jde, inst) })
|
|
}
|
|
|
|
// LastVenusGreatestElongationWestInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusGreatestElongationWestInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationWestWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationWestWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusGreatestElongationWestInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusGreatestElongationWestInclusiveWithInstance(jde float64, inst *Instance) float64 {
|
|
return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationWestWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationWestWithInstance(jde, inst) })
|
|
}
|
|
|
|
// NextVenusGreatestElongationEastWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusGreatestElongationEastWithInstance(jde float64, inst *Instance) float64 {
|
|
return nextVenusGreatestElongationTyped(jde, true, inst)
|
|
}
|
|
|
|
// NextVenusGreatestElongationWestWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func NextVenusGreatestElongationWestWithInstance(jde float64, inst *Instance) float64 {
|
|
return nextVenusGreatestElongationTyped(jde, false, inst)
|
|
}
|
|
|
|
// LastVenusGreatestElongationEastWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusGreatestElongationEastWithInstance(jde float64, inst *Instance) float64 {
|
|
return lastVenusGreatestElongationTyped(jde, true, inst)
|
|
}
|
|
|
|
// LastVenusGreatestElongationWestWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
|
func LastVenusGreatestElongationWestWithInstance(jde float64, inst *Instance) float64 {
|
|
return lastVenusGreatestElongationTyped(jde, false, inst)
|
|
}
|