2026-08-06 12:00:56 +08:00
package basic
import (
"errors"
"fmt"
"math"
"strings"
"time"
)
// ErrInvalidOccultationInput 表示月掩输入契约无效。
// ErrInvalidOccultationInput reports invalid lunar-occultation contract input.
var ErrInvalidOccultationInput = errors . New ( "invalid lunar occultation input" )
// ErrOccultationPathSamplingLimit 表示请求的时间步长、中心线间距或有限盘面采样超过确定性的工作量或输出预算。
// ErrOccultationPathSamplingLimit reports that the requested time step, center-line spacing, or aggregate finite-disk sampling would exceed the implementation's deterministic work or output budget.
var ErrOccultationPathSamplingLimit = errors . New ( "lunar occultation path sampling limit exceeded" )
const (
occultationSearchMinimumStep = 250 * time . Millisecond
occultationPathMinimumStep = time . Second
occultationPathMinimumTargetSpacingKM = 1.0
occultationEventSelectionTolerance = 10 * time . Millisecond
occultationEventSelectionToleranceDays = float64 ( occultationEventSelectionTolerance ) / float64 ( 24 * time . Hour )
)
// CoordinateFrame 标识恒星输入坐标使用的赤道坐标系。
// CoordinateFrame identifies the equatorial coordinate frame used by an input stellar coordinate.
type CoordinateFrame string
const (
// CoordinateFrameICRS 表示 ICRS 星表坐标系。
// CoordinateFrameICRS is the ICRS catalog frame.
CoordinateFrameICRS CoordinateFrame = "icrs"
// CoordinateFrameJ2000 表示 J2000 平均赤道坐标系。
// CoordinateFrameJ2000 is the mean equatorial J2000 frame.
CoordinateFrameJ2000 CoordinateFrame = "j2000"
// CoordinateFrameApparentOfDate 表示历元时刻的视赤道坐标系。
// CoordinateFrameApparentOfDate is the apparent equatorial frame of date.
CoordinateFrameApparentOfDate CoordinateFrame = "apparent_of_date"
)
// OccultationType 标识月掩结果的几何类型。
// OccultationType identifies the result geometry.
type OccultationType string
const (
// OccultationTotal 表示掩甚时目标盘面被完全覆盖。
// OccultationTotal means the target disk is fully covered at greatest occultation.
OccultationTotal OccultationType = "total"
// OccultationPartial 表示掩甚时有限目标盘面只有部分被覆盖。
// OccultationPartial means only part of a finite target disk is covered at greatest occultation.
OccultationPartial OccultationType = "partial"
// OccultationGrazing 表示两边缘相切,且没有正持续时间的重叠。
// OccultationGrazing means the limbs are tangent without a positive-duration overlap.
OccultationGrazing OccultationType = "grazing"
)
// OccultationPlanet 标识有限盘面的行星目标。
// OccultationPlanet identifies a finite-disk planetary target.
type OccultationPlanet string
const (
// OccultationMercury 表示水星有限盘面目标。
// OccultationMercury identifies Mercury as the finite-disk target.
OccultationMercury OccultationPlanet = "mercury"
// OccultationVenus 表示金星有限盘面目标。
// OccultationVenus identifies Venus as the finite-disk target.
OccultationVenus OccultationPlanet = "venus"
// OccultationMars 表示火星有限盘面目标。
// OccultationMars identifies Mars as the finite-disk target.
OccultationMars OccultationPlanet = "mars"
// OccultationJupiter 表示木星有限盘面目标。
// OccultationJupiter identifies Jupiter as the finite-disk target.
OccultationJupiter OccultationPlanet = "jupiter"
// OccultationSaturn 表示土星有限盘面目标。
// OccultationSaturn identifies Saturn as the finite-disk target.
OccultationSaturn OccultationPlanet = "saturn"
// OccultationUranus 表示天王星有限盘面目标。
// OccultationUranus identifies Uranus as the finite-disk target.
OccultationUranus OccultationPlanet = "uranus"
// OccultationNeptune 表示海王星有限盘面目标。
// OccultationNeptune identifies Neptune as the finite-disk target.
OccultationNeptune OccultationPlanet = "neptune"
)
// String 返回结果标识中使用的英文目标名称。
// String returns the English target name used in result identifiers.
func ( p OccultationPlanet ) String () string {
switch p {
case OccultationMercury :
return "Mercury"
case OccultationVenus :
return "Venus"
case OccultationMars :
return "Mars"
case OccultationJupiter :
return "Jupiter"
case OccultationSaturn :
return "Saturn"
case OccultationUranus :
return "Uranus"
case OccultationNeptune :
return "Neptune"
default :
return ""
}
}
// Validate 检查行星目标是否受支持。
// Validate checks whether the planetary target is supported.
func ( p OccultationPlanet ) Validate () error {
if p . String () == "" {
return fmt . Errorf ( "%w: unsupported occultation planet %q" , ErrInvalidOccultationInput , p )
}
return nil
}
func validateOccultationTimeRange ( start , end time . Time ) error {
if start . IsZero () || end . IsZero () {
return fmt . Errorf ( "%w: start and end are required" , ErrInvalidOccultationInput )
}
if ! end . After ( start ) {
return fmt . Errorf ( "%w: end must be after start" , ErrInvalidOccultationInput )
}
return nil
}
func occultationTimeInSelectionWindow ( value , start , end time . Time ) bool {
return value . Sub ( start ) >= - occultationEventSelectionTolerance &&
value . Sub ( end ) <= occultationEventSelectionTolerance
}
// Observer 描述站心观测地点。
// Observer describes the topocentric observing site.
type Observer struct {
// Longitude 是经度,东经为正,单位为度。
// Longitude is east-positive, in degrees.
Longitude float64
// Latitude 是纬度,北纬为正,单位为度。
// Latitude is north-positive, in degrees.
Latitude float64
// Height 是观测者相对平均海平面的高度,单位为米。
// Height is the observer elevation above mean sea level, in meters.
Height float64
}
// moonTopocentricSemidiameterN 返回指定地点看到的月球角半径。
// 通用 MoonSemidiameterN 使用地心距离;月掩接触使用同一站心视差修正后的观测者到月球距离。
// moonTopocentricSemidiameterN returns the lunar angular radius as seen from the supplied site.
// The usual MoonSemidiameterN uses geocentric distance; occultation contacts use the observer-to-Moon distance after the same topocentric parallax correction as the direction.
func moonTopocentricSemidiameterN ( tt float64 , observer Observer , n int ) float64 {
moonRA , moonDec := HMoonGeocentricApparentRaDecN ( tt , n )
moonDistanceKM := HMoonAwayN ( tt , n )
if ! finite ( moonRA ) || ! finite ( moonDec ) || ! finite ( moonDistanceKM ) || moonDistanceKM <= 0 {
return math . NaN ()
}
distanceKM := topocentricDistanceKM ( moonRA , moonDec , moonDistanceKM , observer , TD2UT ( tt , false ))
if ! finite ( distanceKM ) || distanceKM <= 0 {
return math . NaN ()
}
return angularSemidiameterArcsec ( moonEquatorialRadiusKM , distanceKM )
}
2026-09-17 12:27:40 +08:00
// topocentricDistanceKM 使用与 TopocentricRaDec 相同的 WGS-84 风格站点因子计算观测者到目标的距离。
// 目标采用视赤道坐标,恒星时与 TopocentricRaDec 一样基于 UTC/UT。
// topocentricDistanceKM uses the same WGS-84-style site factors as TopocentricRaDec.
// The target uses apparent equatorial coordinates, and the sidereal angle is based on UTC/UT.
2026-08-06 12:00:56 +08:00
func topocentricDistanceKM ( ra , dec , distanceKM float64 , observer Observer , ut float64 ) float64 {
2026-09-17 12:27:40 +08:00
return topocentricDistanceKMWithSidereal ( ra , dec , distanceKM , observer , ApparentSiderealTime ( ut ) * 15 )
}
func topocentricDistanceKMWithSidereal (
ra , dec , distanceKM float64 ,
observer Observer ,
siderealDegrees float64 ,
) float64 {
2026-08-06 12:00:56 +08:00
const earthEquatorialRadius = 6378.14
const astronomicalUnitKM = angularDiameterAstronomicalUnitKM
distanceAU := distanceKM / astronomicalUnitKM
if distanceAU <= 0 {
return math . NaN ()
}
raRad := ra * math . Pi / 180
decRad := dec * math . Pi / 180
moon := [ 3 ] float64 {
distanceAU * math . Cos ( decRad ) * math . Cos ( raRad ),
distanceAU * math . Cos ( decRad ) * math . Sin ( raRad ),
distanceAU * math . Sin ( decRad ),
}
2026-09-17 12:27:40 +08:00
theta := ( siderealDegrees + observer . Longitude ) * math . Pi / 180
2026-08-06 12:00:56 +08:00
observerAU := earthEquatorialRadius / astronomicalUnitKM
observerVector := [ 3 ] float64 {
observerAU * pcosi ( observer . Latitude , observer . Height ) * math . Cos ( theta ),
observerAU * pcosi ( observer . Latitude , observer . Height ) * math . Sin ( theta ),
observerAU * psini ( observer . Latitude , observer . Height ),
}
dx := moon [ 0 ] - observerVector [ 0 ]
dy := moon [ 1 ] - observerVector [ 1 ]
dz := moon [ 2 ] - observerVector [ 2 ]
return math . Sqrt ( dx * dx + dy * dy + dz * dz ) * astronomicalUnitKM
}
// Validate 检查站心计算所需的地理范围。
// Validate checks the geographic bounds needed by topocentric calculations.
func ( o Observer ) Validate () error {
if ! finite ( o . Longitude ) || ! finite ( o . Latitude ) || ! finite ( o . Height ) {
return fmt . Errorf ( "%w: observer values must be finite" , ErrInvalidOccultationInput )
}
if o . Longitude < - 180 || o . Longitude > 180 {
return fmt . Errorf ( "%w: observer longitude must be in [-180, 180]" , ErrInvalidOccultationInput )
}
if o . Latitude < - 90 || o . Latitude > 90 {
return fmt . Errorf ( "%w: observer latitude must be in [-90, 90]" , ErrInvalidOccultationInput )
}
return nil
}
// StarCoordinate 是调用者为恒星提供的星表坐标或视位置坐标。
// RA 和 Dec 的单位为度;ProperMotionRACosDecMasPerYear 使用星表常见的 dRA*cos(Dec) 约定,单位为毫角秒/年。
// StarCoordinate is a catalog or apparent coordinate supplied for a star.
// RA and Dec are degrees; ProperMotionRACosDecMasPerYear uses the usual catalog convention of dRA*cos(Dec), in milliarcseconds per year.
type StarCoordinate struct {
ID string
RA float64
Dec float64
Epoch time . Time
Frame CoordinateFrame
ProperMotionRACosDecMasPerYear float64
ProperMotionDecMasPerYear float64
ParallaxMas float64
}
// Validate 在构造目标前检查恒星坐标契约。
// Validate checks the coordinate contract before a target is constructed.
func ( s StarCoordinate ) Validate () error {
if ! finite ( s . RA ) || s . RA < 0 || s . RA >= 360 {
return fmt . Errorf ( "%w: star RA must be in [0, 360)" , ErrInvalidOccultationInput )
}
if ! finite ( s . Dec ) || s . Dec < - 90 || s . Dec > 90 {
return fmt . Errorf ( "%w: star Dec must be in [-90, 90]" , ErrInvalidOccultationInput )
}
if s . Epoch . IsZero () {
return fmt . Errorf ( "%w: star epoch is required" , ErrInvalidOccultationInput )
}
if ! validCoordinateFrame ( s . Frame ) {
return fmt . Errorf ( "%w: unsupported star coordinate frame %q" , ErrInvalidOccultationInput , s . Frame )
}
if ! finite ( s . ProperMotionRACosDecMasPerYear ) || ! finite ( s . ProperMotionDecMasPerYear ) {
return fmt . Errorf ( "%w: star proper motion must be finite" , ErrInvalidOccultationInput )
}
if ! finite ( s . ParallaxMas ) || s . ParallaxMas < 0 {
return fmt . Errorf ( "%w: star parallax must be finite and non-negative" , ErrInvalidOccultationInput )
}
return nil
}
// StarCoordinateFromStarData 将一条内嵌星表记录转换为月掩搜索使用的 J2000 坐标契约。
// 星表自行从角秒/年转换为毫角秒/年;正的秒差距距离转换为毫角秒年视差。本函数只转换传入值,不会加载星表。
// StarCoordinateFromStarData converts one embedded-catalog entry into the J2000 coordinate contract used by lunar-occultation searches.
// The catalog's proper motions are converted from arcseconds/year to milliarcseconds/year; a positive parsec distance is converted to annual parallax in milliarcseconds.
// This function only converts the supplied value and never loads the catalog.
func StarCoordinateFromStarData ( star StarData ) ( StarCoordinate , error ) {
if star . HR == 0 {
return StarCoordinate {}, fmt . Errorf ( "%w: star catalog HR number is required" , ErrInvalidOccultationInput )
}
if ! finite ( star . Pc ) || star . Pc < 0 {
return StarCoordinate {}, fmt . Errorf ( "%w: star distance must be finite and non-negative" , ErrInvalidOccultationInput )
}
parallaxMas := 0.0
if star . Pc > 0 {
parallaxMas = 1000 / star . Pc
}
coordinate := StarCoordinate {
ID : starCoordinateIDFromStarData ( star ),
RA : star . Ra ,
Dec : star . Dec ,
Epoch : time . Date ( 2000 , time . January , 1 , 12 , 0 , 0 , 0 , time . UTC ),
Frame : CoordinateFrameJ2000 ,
ProperMotionRACosDecMasPerYear : star . PmRA * 1000 ,
ProperMotionDecMasPerYear : star . PmDec * 1000 ,
ParallaxMas : parallaxMas ,
}
if err := coordinate . Validate (); err != nil {
return StarCoordinate {}, fmt . Errorf ( "convert star catalog coordinate: %w" , err )
}
return coordinate , nil
}
func starCoordinateIDFromStarData ( star StarData ) string {
for _ , name := range [] string { star . ChineseName , star . ChineseAlias , star . CommonName , star . Name } {
if name = strings . TrimSpace ( name ); name != "" {
return name
}
}
if star . HR > 0 {
return fmt . Sprintf ( "HR %d" , star . HR )
}
return ""
}
// OccultationSearchOptions 控制固定目标和行星月掩搜索。
// 零值使用实现默认值;MaxEvents == 0 表示不限制数量。
// OccultationSearchOptions controls fixed-target and planetary occultation searches.
// Zero values select implementation defaults; MaxEvents == 0 means unlimited.
type OccultationSearchOptions struct {
// MaxStep 是粗略搜索的最大步长;小于 250ms 的正值会被拒绝,因为在支持的时间范围内无法可靠地用儒略日浮点数推进。
// MaxStep is the maximum coarse-search step. Positive values below 250 ms are rejected because they cannot be advanced reliably in Julian-day floating-point arithmetic over the supported time span.
MaxStep time . Duration
// SafetyMarginArcsec 是加入粗略候选和黄纬预筛的安全余量,单位为角秒。
// SafetyMarginArcsec is added to coarse candidate and latitude prefilters.
SafetyMarginArcsec float64
// MaxEvents 为正时限制返回事件数量。
// MaxEvents limits the number of returned events when positive.
MaxEvents int
}
2026-09-17 12:27:40 +08:00
// OccultationPathAlgorithm 选择全球月掩路径的星历求解分支。
// OccultationPathAlgorithm selects the ephemeris branch for global occultation paths.
type OccultationPathAlgorithm string
const (
// OccultationPathAlgorithmOptimized 使用经抽检的密集星历插值,保留站心方程与连续包络。
// OccultationPathAlgorithmOptimized uses checked dense ephemeris interpolation with the same station equations and continuous envelopes.
OccultationPathAlgorithmOptimized OccultationPathAlgorithm = "optimized"
// OccultationPathAlgorithmExact 保留原分支:插值预测候选,最终求解使用全项星历。
// OccultationPathAlgorithmExact retains the original branch: interpolated candidates and full-term ephemerides for final solving.
OccultationPathAlgorithmExact OccultationPathAlgorithm = "exact"
)
2026-08-06 12:00:56 +08:00
// OccultationPathOptions 控制全球月掩路径采样。
//
// Step 为路径采样的基础时间步长,正值至少为 1 秒。TargetSpacingKM 要求相邻中心线点超过目标地面距离时进行自适应加密。
2026-09-17 12:27:40 +08:00
// 正的 TargetSpacingKM 至少为 1 km;超过中心线或有限盘面路径工作量预算时返回 ErrOccultationPathSamplingLimit,不会静默降低请求分辨率。恒星和行星瞬时足迹使用 1 分钟目标步长和独立样本上限。
2026-08-06 12:00:56 +08:00
// OccultationPathOptions controls global occultation-path sampling.
// Step is the base time step used for path samples; positive values must be at least one second. TargetSpacingKM requests adaptive refinement when adjacent center-line points exceed the requested ground distance.
2026-09-17 12:27:40 +08:00
// Positive TargetSpacingKM values must be at least 1 km. Requests that exceed the center-line or aggregate finite-disk work budgets return ErrOccultationPathSamplingLimit instead of silently reducing resolution. Stellar and planetary instantaneous footprints use a one-minute target step and a separate sample cap.
2026-08-06 12:00:56 +08:00
type OccultationPathOptions struct {
2026-09-17 12:27:40 +08:00
// Algorithm 的零值等同 optimized;exact 可选择原有精确分支。此选项不影响独立的单时刻月影与事件查询接口。
// Algorithm defaults to optimized; exact selects the original branch. Independent instant-footprint and event-search APIs are unaffected.
Algorithm OccultationPathAlgorithm
2026-08-06 12:00:56 +08:00
Step time . Duration
TargetSpacingKM float64
2026-09-17 12:27:40 +08:00
// RiseSetStep 独立控制六类升落阶段线的采样步长;零值使用 5 分钟。
// RiseSetStep controls horizon-curve sampling independently from the path step; zero uses five minutes.
RiseSetStep time . Duration
// DisableRiseSet 在不需要时跳过六类初掩、掩甚、终掩月升/月落线。
// DisableRiseSet skips the six local phase/horizon curves when they are not needed.
DisableRiseSet bool
// DisableFootprints 跳过密集的独立瞬时可见区,改用稀疏支撑样本构造紧凑掩带;中心线、边界和升落阶段线仍保留。
// DisableFootprints skips dense standalone instantaneous visible regions and uses sparse support samples to construct compact bands; the center line, limits, and rise/set curves remain available.
DisableFootprints bool
// IncludeFootprintTimeline 在保留紧凑静态掩带的同时,增加独立采样的瞬时足迹时间线。
// IncludeFootprintTimeline adds independently sampled instantaneous footprints while retaining the compact static band.
IncludeFootprintTimeline bool
// FootprintTimelineStep 控制瞬时足迹时间线的采样间隔;启用时零值使用 5 分钟。
// FootprintTimelineStep controls the instantaneous timeline interval; zero uses five minutes when the timeline is enabled.
FootprintTimelineStep time . Duration
// GreatestTimeValues 是要计算的地方掩甚时刻等值线的时刻取值(力学时儒略日,最多 64 条,超出按时间截断);空值时改用 GreatestTimeStep。
// 只有确实存在该时刻掩甚轨迹的取值才会出现在结果里,所以返回条数可能少于请求条数。
// 每条等时线用固定时刻的残差零集延拓,成本正比于曲线长度而不是可见域面积。
// GreatestTimeValues requests local greatest-occultation time isolines as TT Julian ephemeris days;
// when empty, GreatestTimeStep is used instead. Each isochrone is continued along the zero set of a
// fixed-instant residual so the cost scales with curve length rather than with the visible area.
GreatestTimeValues [] float64
// GreatestTimeStep 是等时线间隔;仅在 GreatestTimeValues 为空时生效,非正值不计算等时线。
// GreatestTimeStep is the isochrone interval; it applies only when GreatestTimeValues is empty,
// and non-positive values disable the isolines.
GreatestTimeStep time . Duration
2026-08-06 12:00:56 +08:00
}
// Validate 检查全球路径采样选项。
// Validate checks global path sampling options.
func ( o OccultationPathOptions ) Validate () error {
2026-09-17 12:27:40 +08:00
switch o . Algorithm {
case "" , OccultationPathAlgorithmOptimized , OccultationPathAlgorithmExact :
default :
return fmt . Errorf ( "%w: unsupported path algorithm %q" , ErrInvalidOccultationInput , o . Algorithm )
}
2026-08-06 12:00:56 +08:00
if o . Step < 0 {
return fmt . Errorf ( "%w: path step cannot be negative" , ErrInvalidOccultationInput )
}
if o . Step > 0 && o . Step < occultationPathMinimumStep {
return fmt . Errorf ( "%w: path step must be zero or at least %s" , ErrInvalidOccultationInput , occultationPathMinimumStep )
}
if ! finite ( o . TargetSpacingKM ) || o . TargetSpacingKM < 0 {
return fmt . Errorf ( "%w: path target spacing must be finite and non-negative" , ErrInvalidOccultationInput )
}
if o . TargetSpacingKM > 0 && o . TargetSpacingKM < occultationPathMinimumTargetSpacingKM {
return fmt . Errorf ( "%w: path target spacing must be zero or at least %.0f km" , ErrInvalidOccultationInput , occultationPathMinimumTargetSpacingKM )
}
2026-09-17 12:27:40 +08:00
if o . RiseSetStep < 0 {
return fmt . Errorf ( "%w: rise/set step cannot be negative" , ErrInvalidOccultationInput )
}
if o . RiseSetStep > 0 && o . RiseSetStep < occultationPathMinimumStep {
return fmt . Errorf ( "%w: rise/set step must be zero or at least %s" , ErrInvalidOccultationInput , occultationPathMinimumStep )
}
if o . FootprintTimelineStep < 0 {
return fmt . Errorf ( "%w: footprint step cannot be negative" , ErrInvalidOccultationInput )
}
if o . FootprintTimelineStep > 0 && o . FootprintTimelineStep < occultationPathMinimumStep {
return fmt . Errorf ( "%w: footprint step must be zero or at least %s" , ErrInvalidOccultationInput , occultationPathMinimumStep )
}
2026-08-06 12:00:56 +08:00
return nil
}
// Validate 检查选项值,但不选择算法专用默认值。
// Validate checks option values without selecting algorithm-specific defaults.
func ( o OccultationSearchOptions ) Validate () error {
if o . MaxStep < 0 {
return fmt . Errorf ( "%w: search max step cannot be negative" , ErrInvalidOccultationInput )
}
if o . MaxStep > 0 && o . MaxStep < occultationSearchMinimumStep {
return fmt . Errorf ( "%w: search max step must be zero or at least %s" , ErrInvalidOccultationInput , occultationSearchMinimumStep )
}
if ! finite ( o . SafetyMarginArcsec ) || o . SafetyMarginArcsec < 0 {
return fmt . Errorf ( "%w: search safety margin must be finite and non-negative" , ErrInvalidOccultationInput )
}
if o . MaxEvents < 0 {
return fmt . Errorf ( "%w: search max events cannot be negative" , ErrInvalidOccultationInput )
}
return nil
}
// StarOccultationInfo 描述点光源恒星月掩;掩始和掩终是月缘交点。
// StarOccultationInfo describes a point-source stellar occultation. The immersion and emersion times are the Moon-limb crossings.
type StarOccultationInfo struct {
TargetID string
Observer Observer
Type OccultationType
Immersion time . Time
Greatest time . Time
Emersion time . Time
// ContactsComplete 表示两个月缘接触时刻均已求解。
// ContactsComplete is true when both lunar-limb contacts were solved.
ContactsComplete bool
MinimumSeparationArcsec float64
PositionAngleDeg float64
MoonSemidiameterArcsec float64
MoonAltitudeAtGreatest float64
MoonAzimuthAtGreatest float64
VisibleAtGreatest bool
}
// PlanetOccultationInfo 描述有限盘面行星月掩。
// ExternalImmersion 和 ExternalEmersion 分别是 C1 和 C4;全掩事件的 InternalImmersion 和 InternalEmersion 分别是 C2 和 C3,偏掩和掠掩时为零。
// ContactsComplete 表示报告几何适用的所有接触均已求解;目标按赤道半径建模为圆盘,环、大气延伸和扁率不在模型内。
// PlanetOccultationInfo describes a finite-disk planetary occultation.
// ExternalImmersion and ExternalEmersion are C1 and C4. For a total event, InternalImmersion and InternalEmersion are C2 and C3; they are zero for partial and grazing events.
// ContactsComplete means every contact applicable to the reported geometry was solved. The target is modeled as a circular disk using its equatorial body radius; rings, atmospheric extensions, and oblateness are outside this contact model.
type PlanetOccultationInfo struct {
Planet OccultationPlanet
TargetID string
Observer Observer
Type OccultationType
ExternalImmersion time . Time
InternalImmersion time . Time
Greatest time . Time
InternalEmersion time . Time
ExternalEmersion time . Time
HasInternalContacts bool
ContactsComplete bool
MinimumSeparationArcsec float64
PositionAngleDeg float64
MoonSemidiameterArcsec float64
PlanetSemidiameterArcsec float64
MoonAltitudeAtGreatest float64
MoonAzimuthAtGreatest float64
VisibleAtGreatest bool
}
// OccultationPathPoint 是全球月掩路径上的一个地理采样点。
2026-09-17 12:27:40 +08:00
// Start 和 End 描述月缘外接触掩带。
// WidthKM 是中心线采样处的地面横向宽度:接触锥可见弧上地面横向偏移的极差;投影折叠的退化事件改用同刻两条横切母线与椭球交点间的弦长。
// LimitSeparationKM 只对南北限采样点(含全掩限)非零,是同一时刻对侧限线的地面间距;它与 WidthKM 构造不同,不要互相换算。
2026-08-06 12:00:56 +08:00
// 基础采样直接求解,自适应插入点使用宽度插值并进行五米采样误差检查。
// OccultationPathPoint is a geographic sample of a global lunar-occultation path.
2026-09-17 12:27:40 +08:00
// Start and End describe the outer lunar-limb footprint.
// WidthKM is the ground cross-track width at a center-line sample: the spread of ground cross-track offsets over the visible contact-cone arc, falling back to the same-instant chord between the two cross-track generators when the projection folds.
// LimitSeparationKM is non-zero only on northern/southern limit samples (total limits included) and is the same-instant ground distance to the opposite limit; it is constructed differently from WidthKM and must not be converted into it.
2026-08-06 12:00:56 +08:00
// Base samples are solved directly; adaptive samples use width interpolation and five-meter error checks.
type OccultationPathPoint struct {
2026-09-17 12:27:40 +08:00
Time time . Time
Longitude float64
Latitude float64
MoonAltitude float64
WidthKM float64
LimitSeparationKM float64
}
// OccultationGreatestTimeContour 是一个固定地方掩甚时刻的等值线支路集合。
// OccultationGreatestTimeContour contains the continuous branches of one fixed local greatest-occultation time.
type OccultationGreatestTimeContour struct {
// JDE 是该等值线表示的力学时儒略日,也就是各支路上地方掩甚发生的时刻。
// JDE is the TT Julian ephemeris day represented by this contour, the local greatest-occultation instant along every branch.
JDE float64
// Time 是 JDE 对应的时刻;按步长请求时它是原始对齐时刻,避免 JDE 往返把整分取值截断成前一分钟。
// Time is the instant matching JDE; for step-derived levels it is the original aligned instant,
// so a JDE round trip cannot truncate a whole-minute level into the previous minute.
Time time . Time
// Segments 是该时刻的连续等时线支路;一条支路两端止于月平线(几何地平,无蒙气差修正)或掩可见域边界,
// 纬度 ±88° 以上不再延拓,同一时刻可能有多条不相连的支路。
// Segments are continuous isochrone branches; each branch ends at the lunar horizon or the occultation-visibility boundary.
Segments [][] OccultationPathPoint
2026-08-06 12:00:56 +08:00
}
// StarOccultationPath 包含点光源恒星月掩的全球掩带。
2026-09-17 12:27:40 +08:00
// 中心线是月心与恒星对齐的轨迹;NorthernLimit 和 SouthernLimit 是月缘外接触锥的切点轨迹(掩星在该线上恰好退化为擦边),不是中心线的等距横向平移。
2026-08-06 12:00:56 +08:00
// StarOccultationPath contains the global footprint of a point-source stellar occultation.
2026-09-17 12:27:40 +08:00
// The center line is the locus where the lunar center aligns with the star; NorthernLimit and SouthernLimit are the tangency tracks of the outer-contact cone, where the occultation degenerates to a graze, so they are not a constant offset of the center line.
2026-08-06 12:00:56 +08:00
type StarOccultationPath struct {
TargetID string
Start OccultationPathPoint
Greatest OccultationPathPoint
End OccultationPathPoint
// Complete 表示 Start 和 End 是全球月缘外接触点,而不是查询窗口裁剪点。
// Complete is true when Start and End are the global outer-limb contacts rather than query-window clipping points.
Complete bool
CenterLine [] OccultationPathPoint
NorthernLimit [] OccultationPathPoint
SouthernLimit [] OccultationPathPoint
2026-09-17 12:27:40 +08:00
// GreatestLimitSeparationKM 是掩甚处南北限的地面间距(取与掩甚时刻最近的限线采样对),与 Greatest.WidthKM 口径不同,不要互相换算。
// GreatestLimitSeparationKM is the ground separation of the two limits at greatest, taken from the limit sample pair nearest to the greatest instant; it uses a different construction from Greatest.WidthKM and must not be converted into it.
GreatestLimitSeparationKM float64
// BandContours 是构造静态可见掩带的连续接触包络;点光源恒星等价于月缘外接触边界。
// BandContours are the continuous contact envelopes used to construct the static visible band; for point-source stars they are the lunar-limb outer-contact boundaries.
BandContours [][] OccultationPathPoint
// VisibilityContours 是掩星有效期间月球可见性的连续时间外包络。
// VisibilityContours are the continuous temporal envelopes of lunar visibility while the occultation is active.
VisibilityContours [][] OccultationPathPoint
// Footprints 是时刻采样的可见点源掩区,其扫掠构成全球掩带;采样口径与行星外接触足迹一致。
// Footprints are sampled visible point-source regions whose sweep forms the global band, using the same sampling contract as planetary outer-contact footprints.
Footprints [] OccultationFootprint
// BandFootprints 是关闭密集瞬时足迹时用于构造紧凑掩带的稀疏可见区样本;展示层应合并它们,不应逐个输出。
// BandFootprints are sparse visible-region samples used to construct a compact band when dense instantaneous footprints are disabled. Renderers should merge rather than emit them individually.
BandFootprints [] OccultationFootprint
// RiseSetCurves 是初掩、掩甚和终掩分别发生在月升或月落时的六类边界。
// RiseSetCurves are the six boundaries where local start, greatest, or end occurs at moonrise or moonset.
RiseSetCurves [] OccultationRiseSetCurve
// GreatestTimeContours 是按掩甚时刻采样的等时线。
// GreatestTimeContours are sampled local greatest-occultation time isolines.
GreatestTimeContours [] OccultationGreatestTimeContour
2026-08-06 12:00:56 +08:00
Step time . Duration
TargetSpacingKM float64
}
2026-09-17 12:27:40 +08:00
// OccultationFootprint 是一个时刻的可见接触足迹。
// Polygons 包含供独立时刻绘制的闭合可见区;Boundaries 保留未经地平线封口的接触锥弧,供稀疏静态样本连续扫掠。
// Closed 表示 Boundaries 本身是否为闭合交线。
// OccultationFootprint is one instantaneous visible contact footprint.
// Polygons contain closed visible regions for rendering one instant. Boundaries retain contact-cone arcs before horizon closure for continuously sweeping sparse static samples.
// Closed reports whether Boundaries themselves form a closed intersection.
type OccultationFootprint struct {
// Time 是该瞬时足迹对应的事件时刻。
// Time is the event time represented by this instantaneous footprint.
Time time . Time
// Polygons 是供独立时刻绘制的闭合可见区域。
// Polygons are closed visible regions for rendering one instant.
2026-08-06 12:00:56 +08:00
Polygons [][] OccultationPathPoint
2026-09-17 12:27:40 +08:00
// InteriorPolygons 是为单时刻绘制保留的闭合可见修复面。静态掩带构建器会将其与连续扫掠的接触边界弧分开,避免数值中心修复扩大长时间地理掩带。
// InteriorPolygons are closed visible repair faces retained for one-instant
// rendering. Static band builders keep them separate from the continuously
// swept contact-boundary arcs so a numerical center repair cannot enlarge a
// long-lived geographic band.
InteriorPolygons [][] OccultationPathPoint
// Boundaries 是尚未经过地平线封口的接触边界弧。
// Boundaries are contact-boundary arcs before horizon closure.
Boundaries [][] OccultationPathPoint
// Closed 表示 Boundaries 是否构成闭合交线。
// Closed reports whether Boundaries form a closed intersection.
Closed bool
2026-08-06 12:00:56 +08:00
}
2026-09-17 12:27:40 +08:00
// OccultationRiseSetCurve 是一种局部月掩阶段与月升/月落同时发生的边界。
// OccultationRiseSetCurve is one boundary where a local occultation phase coincides with moonrise or moonset.
type OccultationRiseSetCurve struct {
// Phase 是与月升或月落同时发生的局部月掩阶段。
// Phase is the local occultation phase coinciding with moonrise or moonset.
Phase RiseSetPhase
// Direction 标识月球正在升起还是落下。
// Direction identifies whether the Moon is rising or setting.
Direction RiseSetDirection
// Segments 是反经线和支路跳变安全分段后的边界采样。
// Segments are boundary samples split safely at the antimeridian and branch changes.
Segments [][] OccultationPathPoint
}
// PlanetOccultationFootprint 保留有限盘面行星月掩足迹的兼容名称。
// PlanetOccultationFootprint retains the compatibility name for finite-disk planetary footprints.
type PlanetOccultationFootprint = OccultationFootprint
2026-08-06 12:00:56 +08:00
// PlanetOccultationPath 包含有限盘面行星月掩的全球掩带。
// NorthernLimit 和 SouthernLimit 是行星盘面任意部分被覆盖的外接触边界。
// HasTotalBand 为 true 时,NorthernTotalLimit 和 SouthernTotalLimit 是行星圆盘完全被月球覆盖的内接触边界;
// 环、大气延伸和扁率不在两种接触模型内。
// PlanetOccultationPath contains the global footprint of a finite-disk planetary occultation.
// NorthernLimit and SouthernLimit are the outer-contact boundaries where any part of the planet disk is covered.
// When HasTotalBand is true, NorthernTotalLimit and SouthernTotalLimit are the inner-contact boundaries where the complete circular planet disk is covered by the Moon. Rings, atmospheric extensions, and oblateness are outside both contact models.
type PlanetOccultationPath struct {
Planet OccultationPlanet
TargetID string
Start OccultationPathPoint
Greatest OccultationPathPoint
End OccultationPathPoint
// Complete 表示 Start 和 End 是全球外接触点。
// Complete is true when Start and End are the global outer contacts.
Complete bool
2026-09-17 12:27:40 +08:00
// CenterLine 是影轴与椭球交点的轨迹;非中心事件退化为最接近影轴的椭球点。
// CenterLine is the track of the shadow-axis/ellipsoid intersection, degenerating to the ellipsoid point nearest the axis for non-central events.
CenterLine [] OccultationPathPoint
// NorthernLimit 和 SouthernLimit 是外接触锥的切点轨迹:掩星在该线上恰好退化为擦边,不是中心线的等距横向平移。
// NorthernLimit and SouthernLimit are the tangency tracks of the outer-contact cone, where the occultation degenerates to a graze, so they are not a constant-width offset of the center line.
2026-08-06 12:00:56 +08:00
NorthernLimit [] OccultationPathPoint
SouthernLimit [] OccultationPathPoint
2026-09-17 12:27:40 +08:00
// GreatestLimitSeparationKM 是掩甚处南北限的地面间距(取与掩甚时刻最近的限线采样对),与 Greatest.WidthKM 口径不同,不要互相换算。
// GreatestLimitSeparationKM is the ground separation of the two limits at greatest, taken from the limit sample pair nearest to the greatest instant; it uses a different construction from Greatest.WidthKM and must not be converted into it.
GreatestLimitSeparationKM float64
// PartialBandContours 是偏掩静态带的连续外接触包络;任意目标盘面重叠时取零。
// PartialBandContours are the continuous outer-contact envelopes for the static partial band where any target-disk overlap begins.
PartialBandContours [][] OccultationPathPoint
// PartialVisibilityContours 是月球可见性在偏掩阶段内的连续时间外包络。
// PartialVisibilityContours are the continuous temporal envelopes of lunar visibility while partial occultation is active.
PartialVisibilityContours [][] OccultationPathPoint
// PartialFootprints 是时刻采样的可见外接触区域,其扫掠构成全球偏掩区域;足迹以 1 分钟为目标步长,超出独立样本上限时会变粗,
2026-08-06 12:00:56 +08:00
// 每个足迹携带实际采样时刻。
2026-09-17 12:27:40 +08:00
// PartialFootprints are instantaneous visible outer-contact regions whose sweep forms the global partial-occultation area. Footprints target one-minute intervals and become coarser when their independent sample cap is reached; each footprint carries its actual sample time.
2026-08-06 12:00:56 +08:00
PartialFootprints [] PlanetOccultationFootprint
2026-09-17 12:27:40 +08:00
// PartialBandFootprints 是关闭密集瞬时足迹时用于构造紧凑偏掩带的稀疏可见区样本。
// PartialBandFootprints are sparse visible-region samples used to construct the compact partial band when dense instantaneous footprints are disabled.
PartialBandFootprints [] PlanetOccultationFootprint
// RiseSetCurves 是初掩、掩甚和终掩分别发生在月升或月落时的六类边界。
// RiseSetCurves are the six boundaries where local start, greatest, or end occurs at moonrise or moonset.
RiseSetCurves [] OccultationRiseSetCurve
2026-08-06 12:00:56 +08:00
HasTotalBand bool
// TotalStart 和 TotalEnd 是全球内接触的起止点。
// TotalStart and TotalEnd are the first and last global inner contacts.
TotalStart OccultationPathPoint
TotalEnd OccultationPathPoint
// TotalComplete 表示 TotalStart 和 TotalEnd 未被内部搜索范围截断。
// TotalComplete is true when TotalStart and TotalEnd are not clipped by the internal search span.
TotalComplete bool
2026-09-17 12:27:40 +08:00
// NorthernTotalLimit 和 SouthernTotalLimit 是内接触(全掩)锥的切点轨迹,同刻间距同样记录在 LimitSeparationKM 上。
// NorthernTotalLimit and SouthernTotalLimit are the tangency tracks of the inner-contact cone; their same-instant separation is recorded in LimitSeparationKM as well.
2026-08-06 12:00:56 +08:00
NorthernTotalLimit [] OccultationPathPoint
SouthernTotalLimit [] OccultationPathPoint
2026-09-17 12:27:40 +08:00
// TotalBandContours 是全掩静态带的连续内接触包络;仅在目标圆盘完全被月球覆盖时取零。
// TotalBandContours are the continuous inner-contact envelopes for the static total band where the complete target disk is covered by the Moon.
TotalBandContours [][] OccultationPathPoint
// TotalVisibilityContours 是全掩阶段内月球可见性的连续时间外包络。
// TotalVisibilityContours are the temporal envelopes of lunar visibility while the total-occultation contact condition is active.
TotalVisibilityContours [][] OccultationPathPoint
// TotalFootprints 是时刻采样的可见内接触区域,其扫掠构成全球全掩区域;采样使用与 PartialFootprints 相同的 1 分钟目标步长和样本上限。
// TotalFootprints are instantaneous visible inner-contact regions whose sweep forms the global full-coverage area. Their sampling uses the same one-minute target step and sample cap as PartialFootprints.
2026-08-06 12:00:56 +08:00
TotalFootprints [] PlanetOccultationFootprint
2026-09-17 12:27:40 +08:00
// TotalBandFootprints 是关闭密集瞬时足迹时用于构造紧凑全掩带的稀疏可见区样本。
// TotalBandFootprints are sparse visible-region samples used to construct the compact total band when dense instantaneous footprints are disabled.
TotalBandFootprints [] PlanetOccultationFootprint
// TotalRiseSetCurves 是全掩带使用的内接触月升/月落边界;其相位与 RiseSetCurves 相同,但 contact metric 采用内接触而非外接触。
// TotalRiseSetCurves are the inner-contact moonrise/moonset boundaries used by the total band; they share the same phase labels as RiseSetCurves but evaluate the inner-contact metric instead of the outer-contact metric.
TotalRiseSetCurves [] OccultationRiseSetCurve
// GreatestTotalWidthKM 是全球掩甚时的全掩带宽度,与 Greatest.WidthKM 同口径而作用于内接触锥,且恒小于它。
// GreatestTotalWidthKM is the full-coverage band width at global greatest, using the same construction as Greatest.WidthKM on the inner-contact cone and always below it.
2026-08-06 12:00:56 +08:00
GreatestTotalWidthKM float64
2026-09-17 12:27:40 +08:00
// GreatestTimeContours 是按掩甚时刻采样的等时线,判据用外接触锥,与偏掩可见域一致。
// GreatestTimeContours are sampled local greatest-occultation time isolines, gated by the outer-contact cone so they match the partial-occultation visibility area.
GreatestTimeContours [] OccultationGreatestTimeContour
2026-08-06 12:00:56 +08:00
Step time . Duration
TargetSpacingKM float64
}
func finite ( value float64 ) bool {
return ! math . IsNaN ( value ) && ! math . IsInf ( value , 0 )
}
func validCoordinateFrame ( frame CoordinateFrame ) bool {
switch frame {
case CoordinateFrameICRS , CoordinateFrameJ2000 , CoordinateFrameApparentOfDate :
return true
default :
return false
}
}