feat: 引入可切换星历模型并扩展天象观测链路
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
This commit is contained in:
+109
-86
@@ -52,12 +52,9 @@ func (state planetOccultationState) movingDiskContactState() movingDiskContactSt
|
||||
}
|
||||
}
|
||||
|
||||
// FindPlanetOccultations 搜索固定观测点的有限盘面行星月掩。
|
||||
// 经度东为正、纬度北为正,单位为度;高度为平均海平面以上米数。目标位置、视差和视半径会在每次候选、掩甚和接触计算时重新计算。
|
||||
// FindPlanetOccultations searches one finite-disk planet at a fixed observing site.
|
||||
// Longitude is east-positive in degrees, latitude is north-positive in degrees, and height is the observer elevation above mean sea level in meters. The target position, parallax, and semidiameter are recomputed at every candidate, greatest, and contact evaluation.
|
||||
func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64,
|
||||
options OccultationSearchOptions) ([]PlanetOccultationInfo, error) {
|
||||
// FindPlanetOccultationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
||||
func FindPlanetOccultationsWithInstance(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64,
|
||||
options OccultationSearchOptions, inst *Instance) ([]PlanetOccultationInfo, error) {
|
||||
if err := validateOccultationTimeRange(start, end); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -72,16 +69,16 @@ func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, long
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, _ := planetOccultationConfigFor(planet)
|
||||
config, _ := planetOccultationConfigFor(planet, inst)
|
||||
startTT := occultationTimeToTT(start)
|
||||
endTT := occultationTimeToTT(end)
|
||||
resultLocation := start.Location()
|
||||
candidates := planetOccultationCandidateGreatestTimes(
|
||||
startTT, endTT, planetOccultationCoarseStepDays(options), config, &observer, options.SafetyMarginArcsec,
|
||||
startTT, endTT, planetOccultationCoarseStepDays(options), config, &observer, options.SafetyMarginArcsec, inst,
|
||||
)
|
||||
results := make([]PlanetOccultationInfo, 0, len(candidates))
|
||||
for _, greatestTT := range candidates {
|
||||
info, ok := planetOccultationInfoAtGreatest(greatestTT, config, observer, options.SafetyMarginArcsec, resultLocation)
|
||||
info, ok := planetOccultationInfoWithLimb(greatestTT, config, observer, options.SafetyMarginArcsec, resultLocation, options.LunarLimb, inst)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -96,12 +93,9 @@ func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, long
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// FindBestPlanetOccultations 返回窗口内每次有限盘面行星月掩的全球海平面几何掩甚点。
|
||||
// 地心数据只用于搜索初值;返回位置与 FindPlanetOccultationPaths 一致,地平线可见性只报告、不参与点选择。距离查询端点 10 ms 内的掩甚时刻也会包含,与数值根精度一致。
|
||||
// FindBestPlanetOccultations returns the global geometric greatest point at sea level for every finite-disk planetary occultation in the window.
|
||||
// Geocentric data only seeds the search. The returned location matches FindPlanetOccultationPaths; horizon visibility is reported but does not select the point. A greatest instant within 10 ms of either query endpoint is included, matching the numerical root precision.
|
||||
func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet,
|
||||
options OccultationSearchOptions) ([]PlanetOccultationInfo, error) {
|
||||
// FindBestPlanetOccultationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
||||
func FindBestPlanetOccultationsWithInstance(start, end time.Time, planet OccultationPlanet,
|
||||
options OccultationSearchOptions, inst *Instance) ([]PlanetOccultationInfo, error) {
|
||||
if err := validateOccultationTimeRange(start, end); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -112,7 +106,7 @@ func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, _ := planetOccultationConfigFor(planet)
|
||||
config, _ := planetOccultationConfigFor(planet, inst)
|
||||
startTT := occultationTimeToTT(start)
|
||||
endTT := occultationTimeToTT(end)
|
||||
selectionStartTT := startTT - occultationEventSelectionToleranceDays
|
||||
@@ -121,15 +115,15 @@ func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet,
|
||||
candidateEndTT := endTT + occultationPathSearchSpanDays
|
||||
resultLocation := start.Location()
|
||||
candidates := planetOccultationCandidateGreatestTimes(
|
||||
candidateStartTT, candidateEndTT, planetOccultationCoarseStepDays(options), config, nil, options.SafetyMarginArcsec,
|
||||
candidateStartTT, candidateEndTT, planetOccultationCoarseStepDays(options), config, nil, options.SafetyMarginArcsec, inst,
|
||||
)
|
||||
results := make([]PlanetOccultationInfo, 0, len(candidates))
|
||||
for _, seedTT := range candidates {
|
||||
greatestTT, observer, _, observerOK := planetOccultationBestObserver(seedTT, selectionStartTT, selectionEndTT, config)
|
||||
greatestTT, observer, _, observerOK := planetOccultationBestObserver(seedTT, selectionStartTT, selectionEndTT, config, inst)
|
||||
if !observerOK {
|
||||
continue
|
||||
}
|
||||
info, ok := planetOccultationInfoAtGreatest(greatestTT, config, observer, options.SafetyMarginArcsec, resultLocation)
|
||||
info, ok := planetOccultationInfoWithLimb(greatestTT, config, observer, options.SafetyMarginArcsec, resultLocation, options.LunarLimb, inst)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -144,51 +138,53 @@ func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet,
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func planetOccultationConfigFor(planet OccultationPlanet) (planetOccultationConfig, bool) {
|
||||
func planetOccultationConfigFor(planet OccultationPlanet, instances ...*Instance) (planetOccultationConfig, bool) {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
config := planetOccultationConfig{planet: planet}
|
||||
switch planet {
|
||||
case OccultationMercury:
|
||||
config.planetIndex = 1
|
||||
config.equatorialRadiusKM = mercuryEquatorialRadiusKM
|
||||
config.apparentRaDecN = MercuryApparentRaDecN
|
||||
config.earthDistanceN = EarthMercuryAwayN
|
||||
config.semidiameterN = MercurySemidiameterN
|
||||
config.apparentRaDecN = func(jde float64, n int) (float64, float64) { return MercuryApparentRaDecNWithInstance(jde, n, inst) }
|
||||
config.earthDistanceN = func(jd float64, n int) float64 { return EarthMercuryAwayNWithInstance(jd, n, inst) }
|
||||
config.semidiameterN = func(jde float64, n int) float64 { return MercurySemidiameterNWithInstance(jde, n, inst) }
|
||||
case OccultationVenus:
|
||||
config.planetIndex = 2
|
||||
config.equatorialRadiusKM = venusEquatorialRadiusKM
|
||||
config.apparentRaDecN = VenusApparentRaDecN
|
||||
config.earthDistanceN = EarthVenusAwayN
|
||||
config.semidiameterN = VenusSemidiameterN
|
||||
config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return VenusApparentRaDecNWithInstance(jd, n, inst) }
|
||||
config.earthDistanceN = func(jd float64, n int) float64 { return EarthVenusAwayNWithInstance(jd, n, inst) }
|
||||
config.semidiameterN = func(jde float64, n int) float64 { return VenusSemidiameterNWithInstance(jde, n, inst) }
|
||||
case OccultationMars:
|
||||
config.planetIndex = 3
|
||||
config.equatorialRadiusKM = marsEquatorialRadiusKM
|
||||
config.apparentRaDecN = MarsApparentRaDecN
|
||||
config.earthDistanceN = EarthMarsAwayN
|
||||
config.semidiameterN = MarsSemidiameterN
|
||||
config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return MarsApparentRaDecNWithInstance(jd, n, inst) }
|
||||
config.earthDistanceN = func(jd float64, n int) float64 { return EarthMarsAwayNWithInstance(jd, n, inst) }
|
||||
config.semidiameterN = func(jde float64, n int) float64 { return MarsSemidiameterNWithInstance(jde, n, inst) }
|
||||
case OccultationJupiter:
|
||||
config.planetIndex = 4
|
||||
config.equatorialRadiusKM = jupiterEquatorialRadiusKM
|
||||
config.apparentRaDecN = JupiterApparentRaDecN
|
||||
config.earthDistanceN = EarthJupiterAwayN
|
||||
config.semidiameterN = JupiterSemidiameterN
|
||||
config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return JupiterApparentRaDecNWithInstance(jd, n, inst) }
|
||||
config.earthDistanceN = func(jd float64, n int) float64 { return EarthJupiterAwayNWithInstance(jd, n, inst) }
|
||||
config.semidiameterN = func(jde float64, n int) float64 { return JupiterSemidiameterNWithInstance(jde, n, inst) }
|
||||
case OccultationSaturn:
|
||||
config.planetIndex = 5
|
||||
config.equatorialRadiusKM = saturnEquatorialRadiusKM
|
||||
config.apparentRaDecN = SaturnApparentRaDecN
|
||||
config.earthDistanceN = EarthSaturnAwayN
|
||||
config.semidiameterN = SaturnSemidiameterN
|
||||
config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return SaturnApparentRaDecNWithInstance(jd, n, inst) }
|
||||
config.earthDistanceN = func(jd float64, n int) float64 { return EarthSaturnAwayNWithInstance(jd, n, inst) }
|
||||
config.semidiameterN = func(jde float64, n int) float64 { return SaturnSemidiameterNWithInstance(jde, n, inst) }
|
||||
case OccultationUranus:
|
||||
config.planetIndex = 6
|
||||
config.equatorialRadiusKM = uranusEquatorialRadiusKM
|
||||
config.apparentRaDecN = UranusApparentRaDecN
|
||||
config.earthDistanceN = EarthUranusAwayN
|
||||
config.semidiameterN = UranusSemidiameterN
|
||||
config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return UranusApparentRaDecNWithInstance(jd, n, inst) }
|
||||
config.earthDistanceN = func(jd float64, n int) float64 { return EarthUranusAwayNWithInstance(jd, n, inst) }
|
||||
config.semidiameterN = func(jde float64, n int) float64 { return UranusSemidiameterNWithInstance(jde, n, inst) }
|
||||
case OccultationNeptune:
|
||||
config.planetIndex = 7
|
||||
config.equatorialRadiusKM = neptuneEquatorialRadiusKM
|
||||
config.apparentRaDecN = NeptuneApparentRaDecN
|
||||
config.earthDistanceN = EarthNeptuneAwayN
|
||||
config.semidiameterN = NeptuneSemidiameterN
|
||||
config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return NeptuneApparentRaDecNWithInstance(jd, n, inst) }
|
||||
config.earthDistanceN = func(jd float64, n int) float64 { return EarthNeptuneAwayNWithInstance(jd, n, inst) }
|
||||
config.semidiameterN = func(jde float64, n int) float64 { return NeptuneSemidiameterNWithInstance(jde, n, inst) }
|
||||
default:
|
||||
return planetOccultationConfig{}, false
|
||||
}
|
||||
@@ -199,8 +195,9 @@ func planetOccultationCandidateGreatestTimes(
|
||||
startTT, endTT, step float64,
|
||||
config planetOccultationConfig,
|
||||
observer *Observer,
|
||||
safetyMarginArcsec float64,
|
||||
) []float64 {
|
||||
safetyMarginArcsec float64, instances ...*Instance) []float64 {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
if endTT <= startTT {
|
||||
return nil
|
||||
}
|
||||
@@ -214,20 +211,20 @@ func planetOccultationCandidateGreatestTimes(
|
||||
|
||||
leftTT := scanStart
|
||||
centerTT := math.Min(leftTT+step, scanEnd)
|
||||
leftValue := planetOccultationExternalContactMetric(leftTT, config, observer, 8)
|
||||
centerValue := planetOccultationExternalContactMetric(centerTT, config, observer, 8)
|
||||
leftValue := planetOccultationExternalContactMetric(leftTT, config, observer, 8, inst)
|
||||
centerValue := planetOccultationExternalContactMetric(centerTT, config, observer, 8, inst)
|
||||
results := make([]float64, 0)
|
||||
for centerTT < scanEnd {
|
||||
rightTT := math.Min(centerTT+step, scanEnd)
|
||||
rightValue := planetOccultationExternalContactMetric(rightTT, config, observer, 8)
|
||||
rightValue := planetOccultationExternalContactMetric(rightTT, config, observer, 8, inst)
|
||||
candidateLimit := planetOccultationCandidateLimitArcsec + safetyMarginArcsec
|
||||
if finite(leftValue) && finite(centerValue) && finite(rightValue) &&
|
||||
centerValue <= leftValue && centerValue <= rightValue && centerValue <= candidateLimit {
|
||||
// 最小外接触度量同时包含动态月面和行星盘面,因此定义事件是否存在以及报告的掩甚时刻。
|
||||
// The minimum outer-contact metric includes both dynamic disks and therefore defines event existence and the reported greatest instant.
|
||||
greatestTT := planetOccultationMinimizeExternalMetric(leftTT, rightTT, config, observer)
|
||||
greatestTT := planetOccultationMinimizeExternalMetric(leftTT, rightTT, config, observer, inst)
|
||||
if greatestTT >= startTT && greatestTT <= endTT &&
|
||||
planetOccultationLatitudePass(greatestTT, config, observer, safetyMarginArcsec) {
|
||||
planetOccultationLatitudePass(greatestTT, config, observer, safetyMarginArcsec, inst) {
|
||||
if len(results) == 0 || math.Abs(greatestTT-results[len(results)-1]) > 60.0/86400.0 {
|
||||
results = append(results, greatestTT)
|
||||
}
|
||||
@@ -239,34 +236,38 @@ func planetOccultationCandidateGreatestTimes(
|
||||
return results
|
||||
}
|
||||
|
||||
func planetOccultationMinimizeExternalMetric(left, right float64, config planetOccultationConfig, observer *Observer) float64 {
|
||||
func planetOccultationMinimizeExternalMetric(left, right float64, config planetOccultationConfig, observer *Observer, instances ...*Instance) float64 {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
if right <= left {
|
||||
return left
|
||||
}
|
||||
const goldenRatio = 0.6180339887498949
|
||||
x1 := right - goldenRatio*(right-left)
|
||||
x2 := left + goldenRatio*(right-left)
|
||||
f1 := planetOccultationExternalContactMetric(x1, config, observer, -1)
|
||||
f2 := planetOccultationExternalContactMetric(x2, config, observer, -1)
|
||||
f1 := planetOccultationExternalContactMetric(x1, config, observer, -1, inst)
|
||||
f2 := planetOccultationExternalContactMetric(x2, config, observer, -1, inst)
|
||||
for i := 0; i < 64 && right-left > planetOccultationRootToleranceDays; i++ {
|
||||
if f1 > f2 {
|
||||
left = x1
|
||||
x1, f1 = x2, f2
|
||||
x2 = left + goldenRatio*(right-left)
|
||||
f2 = planetOccultationExternalContactMetric(x2, config, observer, -1)
|
||||
f2 = planetOccultationExternalContactMetric(x2, config, observer, -1, inst)
|
||||
} else {
|
||||
right = x2
|
||||
x2, f2 = x1, f1
|
||||
x1 = right - goldenRatio*(right-left)
|
||||
f1 = planetOccultationExternalContactMetric(x1, config, observer, -1)
|
||||
f1 = planetOccultationExternalContactMetric(x1, config, observer, -1, inst)
|
||||
}
|
||||
}
|
||||
return (left + right) / 2
|
||||
}
|
||||
|
||||
func planetOccultationBestObserver(seedTT, startTT, endTT float64, config planetOccultationConfig) (float64, Observer, float64, bool) {
|
||||
func planetOccultationBestObserver(seedTT, startTT, endTT float64, config planetOccultationConfig, instances ...*Instance) (float64, Observer, float64, bool) {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
frameAt := func(tt float64) (occultationPathFrame, bool) {
|
||||
return planetOccultationPathFrameAt(tt, config)
|
||||
return planetOccultationPathFrameAt(tt, config, inst)
|
||||
}
|
||||
searchStart := seedTT - occultationPathSearchSpanDays
|
||||
searchEnd := seedTT + occultationPathSearchSpanDays
|
||||
@@ -288,14 +289,14 @@ func planetOccultationBestObserver(seedTT, startTT, endTT float64, config planet
|
||||
// outside the occultation, so it remains the last resort only.
|
||||
point, pointOK = occultationPathTrackPointForFrame(greatestTT, frameAt, time.UTC)
|
||||
if !pointOK {
|
||||
point, pointOK = occultationPathBoundaryPointForFrame(greatestTT, frameAt, time.UTC)
|
||||
point, pointOK = occultationPathBoundaryPointForFrame(greatestTT, frameAt, time.UTC, inst)
|
||||
}
|
||||
}
|
||||
if !pointOK {
|
||||
return 0, Observer{}, 0, false
|
||||
}
|
||||
observer := Observer{Longitude: point.Longitude, Latitude: point.Latitude}
|
||||
state := planetOccultationStateAt(greatestTT, config, &observer, -1)
|
||||
state := planetOccultationStateAt(greatestTT, config, &observer, -1, inst)
|
||||
if !state.valid {
|
||||
return 0, Observer{}, 0, false
|
||||
}
|
||||
@@ -307,14 +308,32 @@ func planetOccultationInfoAtGreatest(
|
||||
config planetOccultationConfig,
|
||||
observer Observer,
|
||||
safetyMarginArcsec float64,
|
||||
location *time.Location,
|
||||
) (PlanetOccultationInfo, bool) {
|
||||
if !planetOccultationLatitudePass(greatestTT, config, &observer, safetyMarginArcsec) {
|
||||
location *time.Location, instances ...*Instance) (PlanetOccultationInfo, bool) {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
info, state, ok := planetOccultationGreatestInfo(greatestTT, config, observer, safetyMarginArcsec, location, inst)
|
||||
if !ok || state.externalContactMetric > 0 {
|
||||
return PlanetOccultationInfo{}, false
|
||||
}
|
||||
state := planetOccultationStateAt(greatestTT, config, &observer, -1)
|
||||
if !state.valid || state.externalContactMetric > 0 {
|
||||
return PlanetOccultationInfo{}, false
|
||||
|
||||
if math.Abs(state.externalContactMetric) <= planetOccultationGrazingToleranceArcsec {
|
||||
info.Type = OccultationGrazing
|
||||
info.ExternalImmersion = info.Greatest
|
||||
info.ExternalEmersion = info.Greatest
|
||||
info.ContactsComplete = true
|
||||
return info, true
|
||||
}
|
||||
return planetOccultationCircularContacts(info, state, greatestTT, config, observer, location, inst)
|
||||
}
|
||||
|
||||
// planetOccultationGreatestInfo 只填掩甚几何,不判断圆月面是否成掩、不求接触。
|
||||
func planetOccultationGreatestInfo(greatestTT float64, config planetOccultationConfig, observer Observer, safetyMarginArcsec float64, location *time.Location, inst *Instance) (PlanetOccultationInfo, planetOccultationState, bool) {
|
||||
if !planetOccultationLatitudePass(greatestTT, config, &observer, safetyMarginArcsec, inst) {
|
||||
return PlanetOccultationInfo{}, planetOccultationState{}, false
|
||||
}
|
||||
state := planetOccultationStateAt(greatestTT, config, &observer, -1, inst)
|
||||
if !state.valid {
|
||||
return PlanetOccultationInfo{}, planetOccultationState{}, false
|
||||
}
|
||||
info := PlanetOccultationInfo{
|
||||
Planet: config.planet,
|
||||
@@ -330,16 +349,11 @@ func planetOccultationInfoAtGreatest(
|
||||
MoonAzimuthAtGreatest: occultationAzimuth(greatestTT, observer, state.position.moonRA, state.position.moonDec),
|
||||
}
|
||||
info.VisibleAtGreatest = info.MoonAltitudeAtGreatest >= 0
|
||||
return info, state, true
|
||||
}
|
||||
|
||||
if math.Abs(state.externalContactMetric) <= planetOccultationGrazingToleranceArcsec {
|
||||
info.Type = OccultationGrazing
|
||||
info.ExternalImmersion = info.Greatest
|
||||
info.ExternalEmersion = info.Greatest
|
||||
info.ContactsComplete = true
|
||||
return info, true
|
||||
}
|
||||
|
||||
contactEvaluator := newPlanetOccultationContactEvaluator(config, observer)
|
||||
func planetOccultationCircularContacts(info PlanetOccultationInfo, state planetOccultationState, greatestTT float64, config planetOccultationConfig, observer Observer, location *time.Location, inst *Instance) (PlanetOccultationInfo, bool) {
|
||||
contactEvaluator := newPlanetOccultationContactEvaluator(config, observer, inst)
|
||||
contactEvaluator.prime(greatestTT, state.movingDiskContactState(), state.valid)
|
||||
externalImmersionTT, externalImmersionOK := planetOccultationContactWithEvaluator(greatestTT, -1, false, contactEvaluator)
|
||||
externalEmersionTT, externalEmersionOK := planetOccultationContactWithEvaluator(greatestTT, 1, false, contactEvaluator)
|
||||
@@ -366,12 +380,14 @@ func planetOccultationInfoAtGreatest(
|
||||
return info, true
|
||||
}
|
||||
|
||||
func planetMoonPositionAt(tt float64, config planetOccultationConfig, observer *Observer, n int) planetMoonPosition {
|
||||
moonRA, moonDec := HMoonGeocentricApparentRaDecN(tt, n)
|
||||
func planetMoonPositionAt(tt float64, config planetOccultationConfig, observer *Observer, n int, instances ...*Instance) planetMoonPosition {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
moonRA, moonDec := HMoonGeocentricApparentRaDecNWithInstance(tt, n, inst)
|
||||
planetRA, planetDec := config.apparentRaDecN(tt, n)
|
||||
if observer != nil {
|
||||
ut := TT2UTC(tt)
|
||||
moonDistanceAU := HMoonAwayN(tt, n) / angularDiameterAstronomicalUnitKM
|
||||
moonDistanceAU := HMoonAwayNWithInstance(tt, n, inst) / angularDiameterAstronomicalUnitKM
|
||||
planetDistanceAU := config.earthDistanceN(tt, n)
|
||||
moonRA, moonDec = TopocentricRaDec(moonRA, moonDec, observer.Latitude, observer.Longitude, ut, moonDistanceAU, observer.Height)
|
||||
planetRA, planetDec = TopocentricRaDec(planetRA, planetDec, observer.Latitude, observer.Longitude, ut, planetDistanceAU, observer.Height)
|
||||
@@ -387,12 +403,14 @@ func planetMoonPositionAt(tt float64, config planetOccultationConfig, observer *
|
||||
}
|
||||
}
|
||||
|
||||
func planetOccultationStateAt(tt float64, config planetOccultationConfig, observer *Observer, n int) planetOccultationState {
|
||||
position := planetMoonPositionAt(tt, config, observer, n)
|
||||
moonRadius := MoonSemidiameterN(tt, n)
|
||||
func planetOccultationStateAt(tt float64, config planetOccultationConfig, observer *Observer, n int, instances ...*Instance) planetOccultationState {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
position := planetMoonPositionAt(tt, config, observer, n, inst)
|
||||
moonRadius := MoonSemidiameterNWithInstance(tt, n, inst)
|
||||
planetRadius := config.semidiameterN(tt, n)
|
||||
if observer != nil {
|
||||
moonRadius = moonTopocentricSemidiameterN(tt, *observer, n)
|
||||
moonRadius = moonTopocentricSemidiameterN(tt, *observer, n, inst)
|
||||
planetRadius = planetTopocentricSemidiameterN(tt, config, *observer, n)
|
||||
}
|
||||
if !position.valid || !finite(moonRadius) || !finite(planetRadius) || moonRadius <= planetRadius || planetRadius <= 0 {
|
||||
@@ -432,16 +450,20 @@ func planetTopocentricSemidiameterN(tt float64, config planetOccultationConfig,
|
||||
return angularSemidiameterArcsec(config.equatorialRadiusKM, distanceKM)
|
||||
}
|
||||
|
||||
func planetOccultationExternalContactMetric(tt float64, config planetOccultationConfig, observer *Observer, n int) float64 {
|
||||
state := planetOccultationStateAt(tt, config, observer, n)
|
||||
func planetOccultationExternalContactMetric(tt float64, config planetOccultationConfig, observer *Observer, n int, instances ...*Instance) float64 {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
state := planetOccultationStateAt(tt, config, observer, n, inst)
|
||||
if !state.valid {
|
||||
return math.Inf(1)
|
||||
}
|
||||
return state.externalContactMetric
|
||||
}
|
||||
|
||||
func planetOccultationLatitudePass(tt float64, config planetOccultationConfig, observer *Observer, safetyMarginArcsec float64) bool {
|
||||
state := planetOccultationStateAt(tt, config, observer, -1)
|
||||
func planetOccultationLatitudePass(tt float64, config planetOccultationConfig, observer *Observer, safetyMarginArcsec float64, instances ...*Instance) bool {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
state := planetOccultationStateAt(tt, config, observer, -1, inst)
|
||||
if !state.valid {
|
||||
return false
|
||||
}
|
||||
@@ -453,10 +475,11 @@ func planetOccultationLatitudePass(tt float64, config planetOccultationConfig, o
|
||||
|
||||
func newPlanetOccultationContactEvaluator(
|
||||
config planetOccultationConfig,
|
||||
observer Observer,
|
||||
) *movingDiskContactEvaluator {
|
||||
observer Observer, instances ...*Instance) *movingDiskContactEvaluator {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
return newMovingDiskContactEvaluator(func(tt float64) (movingDiskContactState, bool) {
|
||||
state := planetOccultationStateAt(tt, config, &observer, -1)
|
||||
state := planetOccultationStateAt(tt, config, &observer, -1, inst)
|
||||
return state.movingDiskContactState(), state.valid
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user