2026-05-01 22:38:44 +08:00
|
|
|
package basic
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"math"
|
|
|
|
|
|
|
|
|
|
. "b612.me/astro/tools"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 月球方位角
|
|
|
|
|
*/
|
|
|
|
|
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
|
|
|
|
|
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)
|
|
|
|
|
hourAngle := Limit360(st - nra)
|
|
|
|
|
tmp2 := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(ndec)*Cos(lat))
|
|
|
|
|
azimuth := ArcTan(tmp2)
|
|
|
|
|
if azimuth < 0 {
|
|
|
|
|
if hourAngle/15 < 12 {
|
|
|
|
|
return azimuth + 360
|
|
|
|
|
} else {
|
|
|
|
|
return azimuth + 180
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if hourAngle/15 < 12 {
|
|
|
|
|
return azimuth + 180
|
|
|
|
|
} else {
|
|
|
|
|
return azimuth
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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)
|
|
|
|
|
hourAngle := Limit360(st - nra)
|
|
|
|
|
tmp2 := Sin(lat)*Sin(ndec) + Cos(ndec)*Cos(lat)*Cos(hourAngle)
|
|
|
|
|
return ArcSin(tmp2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func HMoonAzimuth(jd, lon, lat, tz float64) float64 {
|
|
|
|
|
return HMoonAzimuthN(jd, lon, lat, tz, -1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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)
|
|
|
|
|
hourAngle := Limit360(st - nra)
|
|
|
|
|
tmp2 := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(ndec)*Cos(lat))
|
|
|
|
|
azimuth := ArcTan(tmp2)
|
|
|
|
|
if azimuth < 0 {
|
|
|
|
|
if hourAngle/15 < 12 {
|
|
|
|
|
return azimuth + 360
|
|
|
|
|
} else {
|
|
|
|
|
return azimuth + 180
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if hourAngle/15 < 12 {
|
|
|
|
|
return azimuth + 180
|
|
|
|
|
} else {
|
|
|
|
|
return azimuth
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func HMoonHeight(jd, lon, lat, tz float64) float64 {
|
|
|
|
|
return HMoonHeightN(jd, lon, lat, tz, -1)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 12:00:56 +08:00
|
|
|
type moonObservationState struct {
|
|
|
|
|
altitude float64
|
|
|
|
|
distanceKM float64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
distanceAU := distanceKM / angularDiameterAstronomicalUnitKM
|
|
|
|
|
topocentricRA, topocentricDec := TopocentricRaDec(ra, dec, lat, lon, jd-tz/24, distanceAU, height)
|
|
|
|
|
siderealTime := Limit360(ApparentSiderealTime(jd-tz/24)*15 + lon)
|
|
|
|
|
hourAngle := Limit360(siderealTime - topocentricRA)
|
|
|
|
|
altitudeSine := Sin(lat)*Sin(topocentricDec) + Cos(topocentricDec)*Cos(lat)*Cos(hourAngle)
|
|
|
|
|
return moonObservationState{
|
|
|
|
|
altitude: ArcSin(altitudeSine),
|
|
|
|
|
distanceKM: distanceKM,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 22:38:44 +08:00
|
|
|
func HMoonHeightN(jd, lon, lat, tz float64, n int) float64 {
|
2026-08-06 12:00:56 +08:00
|
|
|
return hMoonObservationStateN(jd, lon, lat, tz, 0, n).altitude
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
residual := state.altitude + HeightDegreeByLat(height, latitude)
|
|
|
|
|
if zenithShift != 0 {
|
|
|
|
|
residual += RefractionFromTrueAltitude(state.altitude, refractionStandardPressureHPa, refractionStandardTemperatureC)
|
|
|
|
|
residual += angularSemidiameterArcsec(moonEquatorialRadiusKM, state.distanceKM) / 3600
|
|
|
|
|
}
|
|
|
|
|
return residual
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func moonRiseSetOnCivilDay(candidate, slope, civilDayStart, longitude, latitude, originalTimeZone,
|
|
|
|
|
localTimeZone, zenithShift, height float64, isRise bool, fallbackErr error) (float64, error) {
|
|
|
|
|
if eventRiseSetCandidateValid(candidate, civilDayStart, slope, isRise) {
|
|
|
|
|
return candidate, nil
|
|
|
|
|
}
|
|
|
|
|
return eventDirectionalRiseSetSearch(civilDayStart, isRise, fallbackErr, func(outputJD float64) float64 {
|
|
|
|
|
localJD := outputJD + localTimeZone/24 - originalTimeZone/24
|
|
|
|
|
return moonRiseSetResidual(localJD, longitude, latitude, localTimeZone, zenithShift, height, -1)
|
|
|
|
|
})
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 废弃
|
|
|
|
|
func GetMoonTZTime(jd, lon, lat, tz float64) float64 { //实际中天时间{
|
|
|
|
|
jd = math.Floor(jd) + 0.5
|
|
|
|
|
ttm := MoonTimeAngle(jd, lon, lat, tz)
|
|
|
|
|
if ttm > 0 && ttm < 180 {
|
|
|
|
|
jd += 0.5
|
|
|
|
|
}
|
|
|
|
|
estimateJD := jd
|
2026-08-06 12:00:56 +08:00
|
|
|
var ok bool
|
|
|
|
|
estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 {
|
2026-05-01 22:38:44 +08:00
|
|
|
stDegree := MoonTimeAngle(prevJD, lon, lat, tz) - 359.599
|
|
|
|
|
stDegreep := (MoonTimeAngle(prevJD+0.000005, lon, lat, tz) - MoonTimeAngle(prevJD-0.000005, lon, lat, tz)) / 0.00001
|
2026-08-06 12:00:56 +08:00
|
|
|
return stDegree / stDegreep
|
|
|
|
|
})
|
|
|
|
|
if !ok {
|
|
|
|
|
return math.NaN()
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
if ha < 180 {
|
|
|
|
|
ha += 360
|
|
|
|
|
}
|
|
|
|
|
return ha
|
|
|
|
|
}
|
2026-08-06 12:00:56 +08:00
|
|
|
var ok bool
|
|
|
|
|
estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 {
|
2026-05-01 22:38:44 +08:00
|
|
|
stDegree := limitHA(prevJD, lon, timezone) - 360
|
|
|
|
|
stDegreep := (limitHA(prevJD+0.000005, lon, timezone) - limitHA(prevJD-0.000005, lon, timezone)) / 0.00001
|
2026-08-06 12:00:56 +08:00
|
|
|
return stDegree / stDegreep
|
|
|
|
|
})
|
|
|
|
|
if !ok {
|
|
|
|
|
return math.NaN()
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
return estimateJD
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func MoonTimeAngle(jd, lon, lat, tz float64) float64 {
|
|
|
|
|
startime := Limit360(ApparentSiderealTime(jd-tz/24)*15 + lon)
|
|
|
|
|
timeangle := startime - HMoonApparentRa(jd, lon, lat, tz)
|
|
|
|
|
if timeangle < 0 {
|
|
|
|
|
timeangle += 360
|
|
|
|
|
}
|
|
|
|
|
return timeangle
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) {
|
2026-08-06 12:00:56 +08:00
|
|
|
if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || !isFiniteFloat(zenithShift) || !isFiniteFloat(height) {
|
|
|
|
|
return 0, ErrInvalidObservationInput
|
|
|
|
|
}
|
2026-05-01 22:38:44 +08:00
|
|
|
originalTimeZone := timeZone
|
|
|
|
|
timeZone = longitude / 15
|
|
|
|
|
var timeToMeridian float64
|
2026-08-06 12:00:56 +08:00
|
|
|
civilDayStart := math.Floor(julianDay) + 0.5
|
2026-05-01 22:38:44 +08:00
|
|
|
//julianDay = math.Floor(julianDay) + 0.5 - originalTimeZone/24 + timeZone/24 // 求0时JDE
|
|
|
|
|
//fix:这里时间分界线应当以传入的时区为准,不应当使用当地时区,否则在0时的判断会出错
|
|
|
|
|
julianDay = math.Floor(julianDay) + 0.5
|
|
|
|
|
estimatedTime := julianDay
|
2026-08-06 12:00:56 +08:00
|
|
|
moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1)
|
2026-05-01 22:38:44 +08:00
|
|
|
|
|
|
|
|
moonAngle := StandardAltitudeMoon(zenithShift, height, latitude)
|
|
|
|
|
|
|
|
|
|
moonAngleTime := MoonTimeAngle(julianDay, longitude, latitude, originalTimeZone)
|
|
|
|
|
|
2026-08-06 12:00:56 +08:00
|
|
|
if moonResidual > 0 { // 月亮在地平线上或在落下与下中天之间
|
2026-05-01 22:38:44 +08:00
|
|
|
if moonAngleTime > 180 {
|
|
|
|
|
timeToMeridian = (180 + 360 - moonAngleTime) / 15
|
|
|
|
|
} else {
|
|
|
|
|
timeToMeridian = (180 - moonAngleTime) / 15
|
|
|
|
|
}
|
|
|
|
|
estimatedTime += (timeToMeridian/24 + (timeToMeridian/24*12.0)/15.0/24.0)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 12:00:56 +08:00
|
|
|
if moonResidual < 0 && moonAngleTime > 180 {
|
2026-05-01 22:38:44 +08:00
|
|
|
timeToMeridian = (180 - moonAngleTime) / 15
|
|
|
|
|
estimatedTime += (timeToMeridian/24 + (timeToMeridian/24*12.0)/15.0/24.0)
|
2026-08-06 12:00:56 +08:00
|
|
|
} else if moonResidual < 0 && moonAngleTime < 180 {
|
2026-05-01 22:38:44 +08:00
|
|
|
timeToMeridian = (180 - moonAngleTime) / 15
|
|
|
|
|
estimatedTime += (timeToMeridian/24 + (timeToMeridian/24*12.0)/15.0/24.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentAngle := MoonTimeAngle(estimatedTime, longitude, latitude, timeZone)
|
|
|
|
|
if math.Abs(currentAngle-180) > 0.5 {
|
|
|
|
|
estimatedTime += (180 - currentAngle) * 4.0 / 60.0 / 24.0
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 12:00:56 +08:00
|
|
|
currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1)
|
|
|
|
|
if !(currentResidual < -10 && math.Abs(latitude) < 60) {
|
|
|
|
|
if currentResidual > 0 {
|
|
|
|
|
return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude,
|
|
|
|
|
originalTimeZone, timeZone, zenithShift, height, true, ErrNeverSet)
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
checkTime := estimatedTime + 12.0/24.0 + 6.0/15.0/24.0
|
|
|
|
|
checkAngle := MoonTimeAngle(checkTime, longitude, latitude, timeZone)
|
|
|
|
|
if checkAngle < 90 {
|
|
|
|
|
checkAngle += 360
|
|
|
|
|
}
|
|
|
|
|
checkTime += (360 - checkAngle) * 4.0 / 60.0 / 24.0
|
2026-08-06 12:00:56 +08:00
|
|
|
if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1) < 0 {
|
|
|
|
|
return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude,
|
|
|
|
|
originalTimeZone, timeZone, zenithShift, height, true, ErrNeverRise)
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
moonDeclination := MoonApparentDec(estimatedTime, longitude, latitude, timeZone)
|
|
|
|
|
tmp := (Sin(moonAngle) - Sin(moonDeclination)*Sin(latitude)) / (Cos(moonDeclination) * Cos(latitude))
|
|
|
|
|
|
|
|
|
|
if math.Abs(tmp) <= 1 && latitude < 85 {
|
|
|
|
|
hourAngle := (180 - ArcCos(tmp)) / 15
|
|
|
|
|
estimatedTime += hourAngle/24.00 + hourAngle/33.00/15.00
|
|
|
|
|
} else {
|
|
|
|
|
i := 0
|
2026-08-06 12:00:56 +08:00
|
|
|
for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1) < 0 {
|
2026-05-01 22:38:44 +08:00
|
|
|
i++
|
|
|
|
|
estimatedTime += 15.0 / 60.0 / 24.0
|
|
|
|
|
if i > 48 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用牛顿迭代法求精确解
|
2026-08-06 12:00:56 +08:00
|
|
|
estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002)
|
2026-05-01 22:38:44 +08:00
|
|
|
estimatedTime = estimatedTime - timeZone/24 + originalTimeZone/24
|
2026-08-06 12:00:56 +08:00
|
|
|
return moonRiseSetOnCivilDay(estimatedTime, slope, civilDayStart, longitude, latitude,
|
|
|
|
|
originalTimeZone, timeZone, zenithShift, height, true, nil)
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) {
|
2026-08-06 12:00:56 +08:00
|
|
|
if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || !isFiniteFloat(zenithShift) || !isFiniteFloat(height) {
|
|
|
|
|
return 0, ErrInvalidObservationInput
|
|
|
|
|
}
|
2026-05-01 22:38:44 +08:00
|
|
|
originalTimeZone := timeZone
|
|
|
|
|
timeZone = longitude / 15
|
|
|
|
|
var timeToMeridian float64
|
2026-08-06 12:00:56 +08:00
|
|
|
civilDayStart := math.Floor(julianDay) + 0.5
|
2026-05-01 22:38:44 +08:00
|
|
|
//julianDay = math.Floor(julianDay) + 0.5 - originalTimeZone/24 + timeZone/24 // 求0时JDE
|
|
|
|
|
//fix:这里时间分界线应当以传入的时区为准,不应当使用当地时区,否则在0时的判断会出错
|
|
|
|
|
julianDay = math.Floor(julianDay) + 0.5
|
|
|
|
|
estimatedTime := julianDay
|
2026-08-06 12:00:56 +08:00
|
|
|
moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1)
|
2026-05-01 22:38:44 +08:00
|
|
|
|
|
|
|
|
moonAngle := StandardAltitudeMoon(zenithShift, height, latitude)
|
|
|
|
|
|
|
|
|
|
moonAngleTime := MoonTimeAngle(julianDay, longitude, latitude, originalTimeZone)
|
|
|
|
|
|
2026-08-06 12:00:56 +08:00
|
|
|
if moonResidual < 0 {
|
2026-05-01 22:38:44 +08:00
|
|
|
timeToMeridian = (360 - moonAngleTime) / 15
|
|
|
|
|
estimatedTime += (timeToMeridian/24 + (timeToMeridian/24.0*12.0)/15.0/24.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 月亮在地平线上或在落下与下中天之间
|
2026-08-06 12:00:56 +08:00
|
|
|
if moonResidual > 0 && moonAngleTime < 180 {
|
2026-05-01 22:38:44 +08:00
|
|
|
timeToMeridian = (-moonAngleTime) / 15
|
|
|
|
|
estimatedTime += (timeToMeridian/24.0 + (timeToMeridian/24.0*12.0)/15.0/24.0)
|
2026-08-06 12:00:56 +08:00
|
|
|
} else if moonResidual > 0 {
|
2026-05-01 22:38:44 +08:00
|
|
|
timeToMeridian = (360 - moonAngleTime) / 15
|
|
|
|
|
estimatedTime += (timeToMeridian/24.0 + (timeToMeridian/24.0*12.0)/15.0/24.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentAngle := MoonTimeAngle(estimatedTime, longitude, latitude, timeZone)
|
|
|
|
|
if currentAngle < 180 {
|
|
|
|
|
currentAngle += 360
|
|
|
|
|
}
|
|
|
|
|
if math.Abs(currentAngle-360) > 0.5 {
|
|
|
|
|
estimatedTime += (360 - currentAngle) * 4.0 / 60.0 / 24.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// estimatedTime = 月球中天时间
|
2026-08-06 12:00:56 +08:00
|
|
|
currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1)
|
|
|
|
|
if !(currentResidual > 10 && math.Abs(latitude) < 60) {
|
|
|
|
|
if currentResidual < 0 {
|
|
|
|
|
return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude,
|
|
|
|
|
originalTimeZone, timeZone, zenithShift, height, false, ErrNeverRise)
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
checkTime := estimatedTime + 12.0/24.0 + 6.0/15.0/24.0
|
|
|
|
|
angleSubtraction := 180 - MoonTimeAngle(checkTime, longitude, latitude, timeZone)
|
|
|
|
|
checkTime += angleSubtraction * 4.0 / 60.0 / 24.0
|
2026-08-06 12:00:56 +08:00
|
|
|
if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1) > 0 {
|
|
|
|
|
return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude,
|
|
|
|
|
originalTimeZone, timeZone, zenithShift, height, false, ErrNeverSet)
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
moonDeclination := MoonApparentDec(estimatedTime, longitude, latitude, timeZone)
|
|
|
|
|
tmp := (Sin(moonAngle) - Sin(moonDeclination)*Sin(latitude)) / (Cos(moonDeclination) * Cos(latitude))
|
|
|
|
|
|
|
|
|
|
if math.Abs(tmp) <= 1 && latitude < 85 {
|
|
|
|
|
hourAngle := (ArcCos(tmp)) / 15.0
|
|
|
|
|
estimatedTime += hourAngle/24 + hourAngle/33.0/15.0
|
|
|
|
|
} else {
|
|
|
|
|
i := 0
|
2026-08-06 12:00:56 +08:00
|
|
|
for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1) > 0 {
|
2026-05-01 22:38:44 +08:00
|
|
|
i++
|
|
|
|
|
estimatedTime += 15.0 / 60.0 / 24.0
|
|
|
|
|
if i > 48 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用牛顿迭代法求精确解
|
2026-08-06 12:00:56 +08:00
|
|
|
estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002)
|
2026-05-01 22:38:44 +08:00
|
|
|
estimatedTime = estimatedTime - timeZone/24 + originalTimeZone/24
|
2026-08-06 12:00:56 +08:00
|
|
|
return moonRiseSetOnCivilDay(estimatedTime, slope, civilDayStart, longitude, latitude,
|
|
|
|
|
originalTimeZone, timeZone, zenithShift, height, false, nil)
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// heightFunction 高度函数类型定义,用于牛顿迭代法
|
|
|
|
|
type heightFunction func(time, longitude, latitude, timeZone float64) float64
|
|
|
|
|
|
|
|
|
|
// moonRiseSetNewtonRaphsonIteration 牛顿-拉夫逊迭代法求解天体高度方程
|
|
|
|
|
func moonRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, timeZone, targetAngle float64,
|
|
|
|
|
heightFunc heightFunction, tolerance float64) float64 {
|
|
|
|
|
const derivativeStep = 0.000005
|
|
|
|
|
|
|
|
|
|
currentTime := initialTime
|
|
|
|
|
|
2026-08-06 12:00:56 +08:00
|
|
|
var ok bool
|
|
|
|
|
currentTime, ok = eventNewtonRefine(currentTime, tolerance, func(previousTime float64) float64 {
|
2026-05-01 22:38:44 +08:00
|
|
|
functionValue := heightFunc(previousTime, longitude, latitude, timeZone) - targetAngle
|
|
|
|
|
derivative := (heightFunc(previousTime+derivativeStep, longitude, latitude, timeZone) -
|
|
|
|
|
heightFunc(previousTime-derivativeStep, longitude, latitude, timeZone)) / (2 * derivativeStep)
|
2026-08-06 12:00:56 +08:00
|
|
|
return functionValue / derivative
|
|
|
|
|
})
|
|
|
|
|
if !ok {
|
|
|
|
|
return math.NaN()
|
2026-05-01 22:38:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return currentTime
|
|
|
|
|
}
|
2026-08-06 12:00:56 +08:00
|
|
|
|
|
|
|
|
func moonRiseSetResidualIteration(initialTime, longitude, latitude, timeZone, zenithShift, height, tolerance float64) (float64, float64) {
|
|
|
|
|
const derivativeStep = 0.000005
|
|
|
|
|
|
|
|
|
|
slope := math.NaN()
|
|
|
|
|
currentTime, ok := eventNewtonRefine(initialTime, tolerance, func(previousTime float64) float64 {
|
|
|
|
|
functionValue := moonRiseSetResidual(previousTime, longitude, latitude, timeZone, zenithShift, height, -1)
|
|
|
|
|
slope = (moonRiseSetResidual(previousTime+derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1) -
|
|
|
|
|
moonRiseSetResidual(previousTime-derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1)) / (2 * derivativeStep)
|
|
|
|
|
return functionValue / slope
|
|
|
|
|
})
|
|
|
|
|
if !ok {
|
|
|
|
|
return math.NaN(), math.NaN()
|
|
|
|
|
}
|
|
|
|
|
return currentTime, slope
|
|
|
|
|
}
|