astro/jupiter/phenomena_contact_events.go

126 lines
5.1 KiB
Go
Raw Normal View History

package jupiter
import (
"time"
"b612.me/astro/basic"
)
// GalileanPhenomenonContactPhase 接触阶段 / contact phase.
type GalileanPhenomenonContactPhase string
const (
// GalileanPhenomenonContactDisappearance 初亏/初入接触阶段 / disappearance ingress contact.
GalileanPhenomenonContactDisappearance GalileanPhenomenonContactPhase = "disappearance"
// GalileanPhenomenonContactReappearance 复圆/复出接触阶段 / reappearance egress contact.
GalileanPhenomenonContactReappearance GalileanPhenomenonContactPhase = "reappearance"
)
// GalileanPhenomenonContact 伽利略卫星接触窗口 / Galilean-satellite contact window.
//
// Start/End 是有限圆盘或有限影斑开始/结束接触的时刻ModelCrossing 是这套连续接触模型下,
// 零半径参考点穿越边界的时刻。
// Start/End mark the beginning/end of the finite-disk or finite-shadow contact interval.
// ModelCrossing is the zero-radius boundary crossing in this continuous contact model.
type GalileanPhenomenonContact struct {
Valid bool
Phase GalileanPhenomenonContactPhase
Start time.Time
ModelCrossing time.Time
End time.Time
Duration time.Duration
}
// GalileanPhenomenonContactEvent IMCCE 风格的 D/F 接触事件 / IMCCE-style D/F contact event.
//
// 这个 API 返回有限圆盘/有限影斑的接触窗口,适合和 IMCCE 的 `TR.D/TR.F/OC.D/OC.F/EC.D/EC.F/SH.D/SH.F` 对齐;
// 现有 `GalileanPhenomenonEvent` 返回的是零半径几何模型保持 active 的整段区间,两者语义不同。
// 其中 `shadow_transit` 先用半影/本影边界求部分相持续时间,再把这段持续时间中心放在旧影轴过盘时刻上。
// This API returns finite-disk / finite-shadow contact windows and is intended to align with IMCCE
// `TR.D/TR.F/OC.D/OC.F/EC.D/EC.F/SH.D/SH.F` rows. The existing `GalileanPhenomenonEvent` returns the
// whole active interval of the zero-radius geometric model, so the semantics are different.
// For `shadow_transit`, the partial-phase duration comes from penumbra/umbra boundaries,
// while the reported D/F time is centered on the shadow-axis limb crossing from the existing full-event model.
type GalileanPhenomenonContactEvent struct {
Valid bool
Satellite int
Type GalileanPhenomenonType
Disappearance GalileanPhenomenonContact
Greatest time.Time
Reappearance GalileanPhenomenonContact
GreatestState GalileanSatellitePhenomenon
}
// LastGalileanPhenomenonContactEvent 上一次 IMCCE 风格接触事件 / previous IMCCE-style contact event.
func LastGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent {
return galileanPhenomenonContactEventFromBasic(
basic.LastJupiterGalileanPhenomenonContactEvent(
basic.Date2JDE(date.UTC()),
satellite,
basic.JupiterGalileanPhenomenonType(phenomenonType),
),
date.Location(),
)
}
// NextGalileanPhenomenonContactEvent 下一次 IMCCE 风格接触事件 / next IMCCE-style contact event.
func NextGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent {
return galileanPhenomenonContactEventFromBasic(
basic.NextJupiterGalileanPhenomenonContactEvent(
basic.Date2JDE(date.UTC()),
satellite,
basic.JupiterGalileanPhenomenonType(phenomenonType),
),
date.Location(),
)
}
// ClosestGalileanPhenomenonContactEvent 最近一次 IMCCE 风格接触事件 / closest IMCCE-style contact event.
func ClosestGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent {
return galileanPhenomenonContactEventFromBasic(
basic.ClosestJupiterGalileanPhenomenonContactEvent(
basic.Date2JDE(date.UTC()),
satellite,
basic.JupiterGalileanPhenomenonType(phenomenonType),
),
date.Location(),
)
}
func galileanPhenomenonContactEventFromBasic(event basic.JupiterGalileanPhenomenonContactEvent, loc *time.Location) GalileanPhenomenonContactEvent {
if !event.Valid {
return GalileanPhenomenonContactEvent{}
}
greatest := basic.JDE2DateByZone(event.Greatest, loc, false)
return GalileanPhenomenonContactEvent{
Valid: true,
Satellite: event.Satellite,
Type: GalileanPhenomenonType(event.Type),
Disappearance: galileanPhenomenonContactFromBasic(event.Disappearance, loc),
Greatest: greatest,
Reappearance: galileanPhenomenonContactFromBasic(event.Reappearance, loc),
GreatestState: galileanPhenomenonFromBasic(event.GreatestPhenomenon),
}
}
func galileanPhenomenonContactFromBasic(contact basic.JupiterGalileanPhenomenonContact, loc *time.Location) GalileanPhenomenonContact {
if !contact.Valid {
return GalileanPhenomenonContact{}
}
start := basic.JDE2DateByZone(contact.Start, loc, false)
modelCrossing := basic.JDE2DateByZone(contact.ModelCrossing, loc, false)
end := basic.JDE2DateByZone(contact.End, loc, false)
return GalileanPhenomenonContact{
Valid: true,
Phase: GalileanPhenomenonContactPhase(contact.Phase),
Start: start,
ModelCrossing: modelCrossing,
End: end,
Duration: end.Sub(start),
}
}