feat: 完善时标与天象几何计算并扩展输出接口
- 新增时标、ΔT 模型、质心时间与 UT1 支持 - 改进日月食、月掩、行星事件及路径边界计算 - 完善恒星三维自行与动态距离传播 - 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具 - 整理中英文手册、示例资源及回归测试
This commit is contained in:
+122
-34
@@ -12,14 +12,14 @@ import (
|
||||
func MoonAzimuth(jd, lon, lat, tz float64) float64 {
|
||||
|
||||
//tmp := (tz*15 - lon) * 4 / 60
|
||||
calcjd := TD2UT(jd-tz/24, true)
|
||||
ra := MoonTrueRa(calcjd)
|
||||
dec := MoonTrueDec(calcjd)
|
||||
away := MoonAway(calcjd) / 149597870.7
|
||||
jde := UTC2TT(jd - tz/24)
|
||||
ra := MoonTrueRa(jde)
|
||||
dec := MoonTrueDec(jde)
|
||||
away := MoonAway(jde) / 149597870.7
|
||||
ndec := TopocentricDec(ra, dec, lat, lon, jd-tz/24, away, 0)
|
||||
nra := TopocentricRa(ra, dec, lat, lon, jd-tz/24, away, 0)
|
||||
calcjd = jd - tz/24
|
||||
st := Limit360(ApparentSiderealTime(calcjd)*15 + lon)
|
||||
jdUT := jd - tz/24
|
||||
st := Limit360(ApparentSiderealTime(UTC2UT1(jdUT))*15 + lon)
|
||||
hourAngle := Limit360(st - nra)
|
||||
tmp2 := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(ndec)*Cos(lat))
|
||||
azimuth := ArcTan(tmp2)
|
||||
@@ -42,14 +42,14 @@ func MoonAzimuth(jd, lon, lat, tz float64) float64 {
|
||||
func MoonHeight(jd, lon, lat, tz float64) float64 {
|
||||
// tmp := (tz*15 - lon) * 4 / 60
|
||||
//truejd=jd-tmp/24;
|
||||
calcjd := TD2UT(jd-tz/24, true)
|
||||
ra := MoonTrueRa(calcjd)
|
||||
dec := MoonTrueDec(calcjd)
|
||||
away := MoonAway(calcjd) / 149597870.7
|
||||
jde := UTC2TT(jd - tz/24)
|
||||
ra := MoonTrueRa(jde)
|
||||
dec := MoonTrueDec(jde)
|
||||
away := MoonAway(jde) / 149597870.7
|
||||
ndec := TopocentricDec(ra, dec, lat, lon, jd-tz/24, away, 0)
|
||||
nra := TopocentricRa(ra, dec, lat, lon, jd-tz/24, away, 0)
|
||||
calcjd = jd - tz/24
|
||||
st := Limit360(ApparentSiderealTime(calcjd)*15 + lon)
|
||||
jdUT := jd - tz/24
|
||||
st := Limit360(ApparentSiderealTime(UTC2UT1(jdUT))*15 + lon)
|
||||
hourAngle := Limit360(st - nra)
|
||||
tmp2 := Sin(lat)*Sin(ndec) + Cos(ndec)*Cos(lat)*Cos(hourAngle)
|
||||
return ArcSin(tmp2)
|
||||
@@ -60,14 +60,14 @@ func HMoonAzimuth(jd, lon, lat, tz float64) float64 {
|
||||
}
|
||||
|
||||
func HMoonAzimuthN(jd, lon, lat, tz float64, n int) float64 {
|
||||
calcjd := TD2UT(jd-tz/24, true)
|
||||
ra := HMoonTrueRaN(calcjd, n)
|
||||
dec := HMoonTrueDecN(calcjd, n)
|
||||
away := HMoonAwayN(calcjd, n) / 149597870.7
|
||||
jde := UTC2TT(jd - tz/24)
|
||||
ra := HMoonTrueRaN(jde, n)
|
||||
dec := HMoonTrueDecN(jde, n)
|
||||
away := HMoonAwayN(jde, n) / 149597870.7
|
||||
ndec := TopocentricDec(ra, dec, lat, lon, jd-tz/24, away, 0)
|
||||
nra := TopocentricRa(ra, dec, lat, lon, jd-tz/24, away, 0)
|
||||
calcjd = jd - tz/24
|
||||
st := Limit360(ApparentSiderealTime(calcjd)*15 + lon)
|
||||
jdUT := jd - tz/24
|
||||
st := Limit360(ApparentSiderealTime(UTC2UT1(jdUT))*15 + lon)
|
||||
hourAngle := Limit360(st - nra)
|
||||
tmp2 := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(ndec)*Cos(lat))
|
||||
azimuth := ArcTan(tmp2)
|
||||
@@ -85,6 +85,14 @@ func HMoonAzimuthN(jd, lon, lat, tz float64, n int) float64 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HMoonHeight 当地民用时儒略日下的月心几何高度角(度,不含折射)/ geometric Moon-centre altitude in degrees for a local civil Julian day.
|
||||
//
|
||||
// jd 是该时区的当地民用时(墙上时刻)儒略日,tz 是时区偏移小时数,库内按 jd−tz/24 换成 UTC。
|
||||
// 只有 tz 给 0 时 jd 才是 UTC 儒略日;不要拿 UTC 数值再配非零 tz,那会多减一次时区。
|
||||
// jd is that zone's local civil (wall-clock) Julian day and tz is the zone offset in hours,
|
||||
// converted internally as jd-tz/24. Only tz 0 makes jd a UTC Julian day: pairing a UTC value with a
|
||||
// non-zero tz subtracts the offset twice.
|
||||
func HMoonHeight(jd, lon, lat, tz float64) float64 {
|
||||
return HMoonHeightN(jd, lon, lat, tz, -1)
|
||||
}
|
||||
@@ -95,12 +103,12 @@ type moonObservationState struct {
|
||||
}
|
||||
|
||||
func hMoonObservationStateN(jd, lon, lat, tz, height float64, n int) moonObservationState {
|
||||
calculationJD := TD2UT(jd-tz/24, true)
|
||||
ra, dec := HMoonTrueRaDecN(calculationJD, n)
|
||||
distanceKM := HMoonAwayN(calculationJD, n)
|
||||
calculationJDE := UTC2TT(jd - tz/24)
|
||||
ra, dec := HMoonTrueRaDecN(calculationJDE, n)
|
||||
distanceKM := HMoonAwayN(calculationJDE, n)
|
||||
distanceAU := distanceKM / angularDiameterAstronomicalUnitKM
|
||||
topocentricRA, topocentricDec := TopocentricRaDec(ra, dec, lat, lon, jd-tz/24, distanceAU, height)
|
||||
siderealTime := Limit360(ApparentSiderealTime(jd-tz/24)*15 + lon)
|
||||
siderealTime := Limit360(ApparentSiderealTime(UTC2UT1(jd-tz/24))*15 + lon)
|
||||
hourAngle := Limit360(siderealTime - topocentricRA)
|
||||
altitudeSine := Sin(lat)*Sin(topocentricDec) + Cos(topocentricDec)*Cos(lat)*Cos(hourAngle)
|
||||
return moonObservationState{
|
||||
@@ -113,6 +121,89 @@ func HMoonHeightN(jd, lon, lat, tz float64, n int) float64 {
|
||||
return hMoonObservationStateN(jd, lon, lat, tz, 0, n).altitude
|
||||
}
|
||||
|
||||
// MoonState 同一瞬间可对任意观测点复用的月球位置与恒星时 / one instant's lunar position and sidereal time, reusable across observers.
|
||||
type MoonState struct {
|
||||
rightAscension float64
|
||||
declination float64
|
||||
distanceAU float64
|
||||
siderealTime float64
|
||||
}
|
||||
|
||||
// MoonStateAt 由 UTC 儒略日构造该瞬间的可复用月球状态 / builds the reusable state for one UTC Julian day.
|
||||
func MoonStateAt(utcJD float64) MoonState {
|
||||
jde := UTC2TT(utcJD)
|
||||
rightAscension, declination := HMoonTrueRaDec(jde)
|
||||
return MoonState{
|
||||
rightAscension: rightAscension,
|
||||
declination: declination,
|
||||
distanceAU: HMoonAway(jde) / angularDiameterAstronomicalUnitKM,
|
||||
siderealTime: ApparentSiderealTime(UTC2UT1(utcJD)) * 15,
|
||||
}
|
||||
}
|
||||
|
||||
func (state MoonState) finite() bool {
|
||||
return finite(state.rightAscension) && finite(state.declination) &&
|
||||
finite(state.distanceAU) && finite(state.siderealTime)
|
||||
}
|
||||
|
||||
// HMoonHeight 给定观测者经度、纬度(度,椭球高 0)的月心几何高度角,等于 HMoonHeight(构造本状态时的 UTC 儒略日, 经, 纬, 0)。
|
||||
// HMoonHeight returns the geometric Moon-centre altitude for one observer, equal to HMoonHeight(the UTC Julian day given to MoonStateAt, lon, lat, 0).
|
||||
func (state MoonState) HMoonHeight(longitude, latitude float64) float64 {
|
||||
// 本状态固定是 UTC 瞬间、椭球高 0,因此只对应包级 tz=0、height=0 的用法。
|
||||
// 恒星时已在状态里算好,这里不再走会重算恒星时与时标换算的 TopocentricRaDec。
|
||||
topocentricRA, topocentricDec := topocentricRaDecWithSidereal(
|
||||
state.rightAscension, state.declination, latitude, longitude, state.siderealTime, state.distanceAU, 0,
|
||||
)
|
||||
hourAngle := Limit360(Limit360(state.siderealTime+longitude) - topocentricRA)
|
||||
return ArcSin(Sin(latitude)*Sin(topocentricDec) + Cos(topocentricDec)*Cos(latitude)*Cos(hourAngle))
|
||||
}
|
||||
|
||||
// MoonHorizon 用本状态生成海平面几何月心地平圈,口径同包级 MoonHorizon / sea-level geometric Moon-centre horizon ring from this state.
|
||||
func (state MoonState) MoonHorizon(samples int) [][2]float64 {
|
||||
if !state.finite() {
|
||||
return nil
|
||||
}
|
||||
if samples <= 0 {
|
||||
samples = 360
|
||||
}
|
||||
if samples < 12 {
|
||||
samples = 12
|
||||
} else if samples > 1440 {
|
||||
samples = 1440
|
||||
}
|
||||
parallax := math.Sin(0.0024427777777*rad) / state.distanceAU
|
||||
longitude := (state.rightAscension - state.siderealTime) * rad
|
||||
latitude := state.declination * rad
|
||||
if !finite(parallax) || parallax <= 0 || parallax >= 1 || !finite(longitude) || !finite(latitude) {
|
||||
return nil
|
||||
}
|
||||
center := [3]float64{math.Cos(latitude) * math.Cos(longitude), math.Cos(latitude) * math.Sin(longitude), math.Sin(latitude)}
|
||||
north := [3]float64{-math.Sin(latitude) * math.Cos(longitude), -math.Sin(latitude) * math.Sin(longitude), math.Cos(latitude)}
|
||||
east := [3]float64{-math.Sin(longitude), math.Cos(longitude), 0}
|
||||
points := make([][2]float64, samples)
|
||||
for index := range points {
|
||||
bearing := 2 * math.Pi * float64(index) / float64(samples)
|
||||
radius := math.Acos(parallax)
|
||||
var point [3]float64
|
||||
for iteration := 0; iteration < 8; iteration++ {
|
||||
for axis := range point {
|
||||
point[axis] = center[axis]*math.Cos(radius) +
|
||||
(north[axis]*math.Cos(bearing)+east[axis]*math.Sin(bearing))*math.Sin(radius)
|
||||
}
|
||||
lat := math.Asin(math.Max(-1, math.Min(1, point[2]))) / rad
|
||||
// The topocentric direction is horizontal when its dot product
|
||||
// with the geodetic zenith vanishes: cos(radius)=observer/range.
|
||||
next := math.Acos(parallax * (pcosi(lat, 0)*math.Cos(lat*rad) + psini(lat, 0)*math.Sin(lat*rad)))
|
||||
if math.Abs(next-radius) < 1e-14 {
|
||||
break
|
||||
}
|
||||
radius = next
|
||||
}
|
||||
points[index] = [2]float64{math.Atan2(point[1], point[0]) / rad, math.Asin(math.Max(-1, math.Min(1, point[2]))) / rad}
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
func moonRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height float64, n int) float64 {
|
||||
state := hMoonObservationStateN(jd, longitude, latitude, timeZone, height, n)
|
||||
// 相对观测者下沉地平线的视上缘高度角 / Apparent upper-limb altitude relative to the observer's depressed horizon.
|
||||
@@ -157,13 +248,12 @@ func GetMoonTZTime(jd, lon, lat, tz float64) float64 { //实际中天时间{
|
||||
return estimateJD
|
||||
}
|
||||
|
||||
func MoonCulminationTime(jde, lon, lat, timezone float64) float64 {
|
||||
//jde 世界时,非力学时,当地时区 0时,无需转换力学时
|
||||
//ra,dec 瞬时天球座标,非J2000等时间天球坐标
|
||||
jde = math.Floor(jde) + 0.5
|
||||
estimateJD := jde + Limit360(360-MoonTimeAngle(jde, lon, lat, timezone))/15.0/24.0/0.9
|
||||
limitHA := func(jde, lon, timezone float64) float64 {
|
||||
ha := MoonTimeAngle(jde, lon, lat, timezone)
|
||||
func MoonCulminationTime(localJD, lon, lat, timezone float64) float64 {
|
||||
// localJD 是本地民用日锚点(当地 0 时),不是力学时;ra/dec 为瞬时天球坐标,非 J2000 等固定历元。
|
||||
localJD = math.Floor(localJD) + 0.5
|
||||
estimateJD := localJD + Limit360(360-MoonTimeAngle(localJD, lon, lat, timezone))/15.0/24.0/0.9
|
||||
limitHA := func(localJD, lon, timezone float64) float64 {
|
||||
ha := MoonTimeAngle(localJD, lon, lat, timezone)
|
||||
if ha < 180 {
|
||||
ha += 360
|
||||
}
|
||||
@@ -182,7 +272,7 @@ func MoonCulminationTime(jde, lon, lat, timezone float64) float64 {
|
||||
}
|
||||
|
||||
func MoonTimeAngle(jd, lon, lat, tz float64) float64 {
|
||||
startime := Limit360(ApparentSiderealTime(jd-tz/24)*15 + lon)
|
||||
startime := Limit360(ApparentSiderealTime(UTC2UT1(jd-tz/24))*15 + lon)
|
||||
timeangle := startime - HMoonApparentRa(jd, lon, lat, tz)
|
||||
if timeangle < 0 {
|
||||
timeangle += 360
|
||||
@@ -198,8 +288,7 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig
|
||||
timeZone = longitude / 15
|
||||
var timeToMeridian float64
|
||||
civilDayStart := math.Floor(julianDay) + 0.5
|
||||
//julianDay = math.Floor(julianDay) + 0.5 - originalTimeZone/24 + timeZone/24 // 求0时JDE
|
||||
//fix:这里时间分界线应当以传入的时区为准,不应当使用当地时区,否则在0时的判断会出错
|
||||
// 时间分界线以传入的时区为准,不用当地时区,否则 0 时的判断会出错。
|
||||
julianDay = math.Floor(julianDay) + 0.5
|
||||
estimatedTime := julianDay
|
||||
moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1)
|
||||
@@ -282,8 +371,7 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh
|
||||
timeZone = longitude / 15
|
||||
var timeToMeridian float64
|
||||
civilDayStart := math.Floor(julianDay) + 0.5
|
||||
//julianDay = math.Floor(julianDay) + 0.5 - originalTimeZone/24 + timeZone/24 // 求0时JDE
|
||||
//fix:这里时间分界线应当以传入的时区为准,不应当使用当地时区,否则在0时的判断会出错
|
||||
// 时间分界线以传入的时区为准,不用当地时区,否则 0 时的判断会出错。
|
||||
julianDay = math.Floor(julianDay) + 0.5
|
||||
estimatedTime := julianDay
|
||||
moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1)
|
||||
|
||||
Reference in New Issue
Block a user