510 lines
17 KiB
Go
510 lines
17 KiB
Go
|
|
package basic
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"math"
|
|||
|
|
|
|||
|
|
. "b612.me/astro/tools"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 太阳中天时刻,通过均时差计算
|
|||
|
|
func CulminationTime(jd, lon, tz float64) float64 { //实际中天时间
|
|||
|
|
jd = math.Floor(jd)
|
|||
|
|
tmp := (tz*15 - lon) * 4 / 60
|
|||
|
|
return jd + tmp/24.0 - SunTime(jd)/24.0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func CulminationTimeN(jd, lon, tz float64, n int) float64 { //实际中天时间
|
|||
|
|
jd = math.Floor(jd)
|
|||
|
|
tmp := (tz*15 - lon) * 4 / 60
|
|||
|
|
return jd + tmp/24.0 - SunTimeN(jd, n)/24.0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* 昏朦影传入 当天0时时刻
|
|||
|
|
*/
|
|||
|
|
func EveningTwilight(jd, lon, lat, tz, targetAltitude float64) (float64, error) {
|
|||
|
|
jd = math.Floor(jd) + 1.5
|
|||
|
|
localTimeZone := math.Round(lon / 15)
|
|||
|
|
culminationTime := CulminationTime(jd, lon, localTimeZone)
|
|||
|
|
if SunHeight(culminationTime, lon, lat, localTimeZone) < targetAltitude {
|
|||
|
|
return 0, ErrNeverRise
|
|||
|
|
}
|
|||
|
|
if SunHeight(culminationTime+0.5, lon, lat, localTimeZone) > targetAltitude {
|
|||
|
|
return 0, ErrNeverSet
|
|||
|
|
}
|
|||
|
|
tmp := (Sin(targetAltitude) - Sin(HSunApparentDec(culminationTime))*Sin(lat)) / (Cos(HSunApparentDec(culminationTime)) * Cos(lat))
|
|||
|
|
var sundown float64
|
|||
|
|
if math.Abs(tmp) <= 1 && lat < 85 {
|
|||
|
|
hourOffset := ArcCos(tmp) / 15
|
|||
|
|
sundown = culminationTime + hourOffset/24.0 + 35.0/24.0/60.0
|
|||
|
|
} else {
|
|||
|
|
sundown = culminationTime
|
|||
|
|
i := 0
|
|||
|
|
for LowSunHeight(sundown, lon, lat, localTimeZone) > targetAltitude {
|
|||
|
|
i++
|
|||
|
|
sundown += 15.0 / 60.0 / 24.0
|
|||
|
|
if i > 48 {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
estimateJD := sundown - 5.00/24.00/60.00
|
|||
|
|
for {
|
|||
|
|
prevJD := estimateJD
|
|||
|
|
stDegree := SunHeight(prevJD, lon, lat, localTimeZone) - targetAltitude
|
|||
|
|
stDegreep := (SunHeight(prevJD+0.000005, lon, lat, localTimeZone) - SunHeight(prevJD-0.000005, lon, lat, localTimeZone)) / 0.00001
|
|||
|
|
estimateJD = prevJD - stDegree/stDegreep
|
|||
|
|
if math.Abs(estimateJD-prevJD) < 0.00001 {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return estimateJD - localTimeZone/24 + tz/24, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func EveningTwilightN(jd, lon, lat, tz, targetAltitude float64, n int) (float64, error) {
|
|||
|
|
jd = math.Floor(jd) + 1.5
|
|||
|
|
localTimeZone := math.Round(lon / 15)
|
|||
|
|
culminationTime := CulminationTimeN(jd, lon, localTimeZone, n)
|
|||
|
|
if SunHeightN(culminationTime, lon, lat, localTimeZone, n) < targetAltitude {
|
|||
|
|
return 0, ErrNeverRise
|
|||
|
|
}
|
|||
|
|
if SunHeightN(culminationTime+0.5, lon, lat, localTimeZone, n) > targetAltitude {
|
|||
|
|
return 0, ErrNeverSet
|
|||
|
|
}
|
|||
|
|
tmp := (Sin(targetAltitude) - Sin(HSunApparentDecN(culminationTime, n))*Sin(lat)) / (Cos(HSunApparentDecN(culminationTime, n)) * Cos(lat))
|
|||
|
|
var sundown float64
|
|||
|
|
if math.Abs(tmp) <= 1 && lat < 85 {
|
|||
|
|
hourOffset := ArcCos(tmp) / 15
|
|||
|
|
sundown = culminationTime + hourOffset/24.0 + 35.0/24.0/60.0
|
|||
|
|
} else {
|
|||
|
|
sundown = culminationTime
|
|||
|
|
i := 0
|
|||
|
|
for lowSunHeightForN(sundown, lon, lat, localTimeZone, n) > targetAltitude {
|
|||
|
|
i++
|
|||
|
|
sundown += 15.0 / 60.0 / 24.0
|
|||
|
|
if i > 48 {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
estimateJD := sundown - 5.00/24.00/60.00
|
|||
|
|
for {
|
|||
|
|
prevJD := estimateJD
|
|||
|
|
stDegree := SunHeightN(prevJD, lon, lat, localTimeZone, n) - targetAltitude
|
|||
|
|
stDegreep := (SunHeightN(prevJD+0.000005, lon, lat, localTimeZone, n) - SunHeightN(prevJD-0.000005, lon, lat, localTimeZone, n)) / 0.00001
|
|||
|
|
estimateJD = prevJD - stDegree/stDegreep
|
|||
|
|
if math.Abs(estimateJD-prevJD) < 0.00001 {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return estimateJD - localTimeZone/24 + tz/24, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func MorningTwilight(jd, lon, lat, tz, targetAltitude float64) (float64, error) {
|
|||
|
|
// 调整到中午12点
|
|||
|
|
jd = math.Floor(jd) + 1.5
|
|||
|
|
|
|||
|
|
// 计算时区
|
|||
|
|
localTimeZone := math.Round(lon / 15)
|
|||
|
|
|
|||
|
|
// 计算太阳上中天时间
|
|||
|
|
culminationTime := CulminationTime(jd, lon, localTimeZone)
|
|||
|
|
|
|||
|
|
// 检查极夜和极昼条件
|
|||
|
|
if SunHeight(culminationTime, lon, lat, localTimeZone) < targetAltitude {
|
|||
|
|
return 0, ErrNeverRise
|
|||
|
|
}
|
|||
|
|
if SunHeight(culminationTime-0.5, lon, lat, localTimeZone) > targetAltitude {
|
|||
|
|
return 0, ErrNeverSet
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算日出时间
|
|||
|
|
sunDec := HSunApparentDec(culminationTime)
|
|||
|
|
tmp := (Sin(targetAltitude) - Sin(sunDec)*Sin(lat)) / (Cos(sunDec) * Cos(lat))
|
|||
|
|
|
|||
|
|
var sunrise float64
|
|||
|
|
if math.Abs(tmp) <= 1 && lat < 85 {
|
|||
|
|
hourAngle := ArcCos(tmp) / 15
|
|||
|
|
sunrise = culminationTime - hourAngle/24 - 25.0/(24.0*60.0)
|
|||
|
|
} else {
|
|||
|
|
sunrise = culminationTime
|
|||
|
|
for i := 0; i < 48 && LowSunHeight(sunrise, lon, lat, localTimeZone) > targetAltitude; i++ {
|
|||
|
|
sunrise -= 15.0 / (60.0 * 24.0) // 每次减少15分钟
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
estimateJD := sunrise - 5.0/(24.0*60.0)
|
|||
|
|
for {
|
|||
|
|
prevJD := estimateJD
|
|||
|
|
heightDiff := SunHeight(prevJD, lon, lat, localTimeZone) - targetAltitude
|
|||
|
|
heightDerivative := (SunHeight(prevJD+0.000005, lon, lat, localTimeZone) - SunHeight(prevJD-0.000005, lon, lat, localTimeZone)) / 0.00001
|
|||
|
|
estimateJD = prevJD - heightDiff/heightDerivative
|
|||
|
|
|
|||
|
|
if math.Abs(estimateJD-prevJD) < 0.00001 {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return estimateJD - localTimeZone/24 + tz/24, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func MorningTwilightN(jd, lon, lat, tz, targetAltitude float64, n int) (float64, error) {
|
|||
|
|
jd = math.Floor(jd) + 1.5
|
|||
|
|
localTimeZone := math.Round(lon / 15)
|
|||
|
|
culminationTime := CulminationTimeN(jd, lon, localTimeZone, n)
|
|||
|
|
if SunHeightN(culminationTime, lon, lat, localTimeZone, n) < targetAltitude {
|
|||
|
|
return 0, ErrNeverRise
|
|||
|
|
}
|
|||
|
|
if SunHeightN(culminationTime-0.5, lon, lat, localTimeZone, n) > targetAltitude {
|
|||
|
|
return 0, ErrNeverSet
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
sunDec := HSunApparentDecN(culminationTime, n)
|
|||
|
|
tmp := (Sin(targetAltitude) - Sin(sunDec)*Sin(lat)) / (Cos(sunDec) * Cos(lat))
|
|||
|
|
|
|||
|
|
var sunrise float64
|
|||
|
|
if math.Abs(tmp) <= 1 && lat < 85 {
|
|||
|
|
hourAngle := ArcCos(tmp) / 15
|
|||
|
|
sunrise = culminationTime - hourAngle/24 - 25.0/(24.0*60.0)
|
|||
|
|
} else {
|
|||
|
|
sunrise = culminationTime
|
|||
|
|
for i := 0; i < 48 && lowSunHeightForN(sunrise, lon, lat, localTimeZone, n) > targetAltitude; i++ {
|
|||
|
|
sunrise -= 15.0 / (60.0 * 24.0)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
estimateJD := sunrise - 5.0/(24.0*60.0)
|
|||
|
|
for {
|
|||
|
|
prevJD := estimateJD
|
|||
|
|
heightDiff := SunHeightN(prevJD, lon, lat, localTimeZone, n) - targetAltitude
|
|||
|
|
heightDerivative := (SunHeightN(prevJD+0.000005, lon, lat, localTimeZone, n) - SunHeightN(prevJD-0.000005, lon, lat, localTimeZone, n)) / 0.00001
|
|||
|
|
estimateJD = prevJD - heightDiff/heightDerivative
|
|||
|
|
|
|||
|
|
if math.Abs(estimateJD-prevJD) < 0.00001 {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return estimateJD - localTimeZone/24 + tz/24, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* 太阳时角
|
|||
|
|
*/
|
|||
|
|
func SunTimeAngle(jd, lon, lat, tz float64) float64 {
|
|||
|
|
startime := Limit360(ApparentSiderealTime(jd-tz/24)*15 + lon)
|
|||
|
|
timeangle := startime - HSunApparentRa(TD2UT(jd-tz/24, true))
|
|||
|
|
if timeangle < 0 {
|
|||
|
|
timeangle += 360
|
|||
|
|
}
|
|||
|
|
return timeangle
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func SunTimeAngleN(jd, lon, lat, tz float64, n int) float64 {
|
|||
|
|
startime := Limit360(ApparentSiderealTime(jd-tz/24)*15 + lon)
|
|||
|
|
timeangle := startime - HSunApparentRaN(TD2UT(jd-tz/24, true), n)
|
|||
|
|
if timeangle < 0 {
|
|||
|
|
timeangle += 360
|
|||
|
|
}
|
|||
|
|
return timeangle
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetSunRiseTime 精确计算日出时间,传入当日0时JDE
|
|||
|
|
func GetSunRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) {
|
|||
|
|
return calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height, true)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func GetSunRiseTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, n int) (float64, error) {
|
|||
|
|
return calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height, true, n)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetSunSetTime 精确计算日落时间,传入当日0时JDE
|
|||
|
|
func GetSunSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) {
|
|||
|
|
return calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height, false)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func GetSunSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, n int) (float64, error) {
|
|||
|
|
return calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height, false, n)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// calculateSunRiseSetTime 统一的日出日落计算函数
|
|||
|
|
func calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64, isSunrise bool) (float64, error) {
|
|||
|
|
julianDay = math.Floor(julianDay) + 1.5
|
|||
|
|
naturalTimeZone := math.Round(longitude / 15)
|
|||
|
|
sunAngle := StandardAltitudeSun(zenithShift, height, latitude)
|
|||
|
|
|
|||
|
|
// 获取太阳上中天时间
|
|||
|
|
solarNoonTime := CulminationTime(julianDay, longitude, naturalTimeZone)
|
|||
|
|
|
|||
|
|
// 检查极夜极昼条件
|
|||
|
|
if err := checkPolarConditions(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, isSunrise); err != nil {
|
|||
|
|
return 0, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算初始估算时间
|
|||
|
|
initialTime := calculateInitialSunTime(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, isSunrise)
|
|||
|
|
|
|||
|
|
// 牛顿-拉夫逊迭代求精确解
|
|||
|
|
return sunRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, naturalTimeZone, sunAngle, timeZone), nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, isSunrise bool, n int) (float64, error) {
|
|||
|
|
julianDay = math.Floor(julianDay) + 1.5
|
|||
|
|
naturalTimeZone := math.Round(longitude / 15)
|
|||
|
|
sunAngle := StandardAltitudeSun(zenithShift, height, latitude)
|
|||
|
|
|
|||
|
|
solarNoonTime := CulminationTimeN(julianDay, longitude, naturalTimeZone, n)
|
|||
|
|
if err := checkPolarConditionsN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, isSunrise, n); err != nil {
|
|||
|
|
return 0, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
initialTime := calculateInitialSunTimeN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, isSunrise, n)
|
|||
|
|
return sunRiseSetNewtonRaphsonIterationN(initialTime, longitude, latitude, naturalTimeZone, sunAngle, timeZone, n), nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// checkPolarConditions 检查极夜极昼条件
|
|||
|
|
func checkPolarConditions(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle float64, isSunrise bool) error {
|
|||
|
|
if SunHeight(solarNoonTime, longitude, latitude, naturalTimeZone) < sunAngle {
|
|||
|
|
return ErrNeverRise
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
checkTime := solarNoonTime + 0.5
|
|||
|
|
if isSunrise {
|
|||
|
|
checkTime = solarNoonTime - 0.5
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if SunHeight(checkTime, longitude, latitude, naturalTimeZone) > sunAngle {
|
|||
|
|
return ErrNeverSet
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func checkPolarConditionsN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle float64, isSunrise bool, n int) error {
|
|||
|
|
if SunHeightN(solarNoonTime, longitude, latitude, naturalTimeZone, n) < sunAngle {
|
|||
|
|
return ErrNeverRise
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
checkTime := solarNoonTime + 0.5
|
|||
|
|
if isSunrise {
|
|||
|
|
checkTime = solarNoonTime - 0.5
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if SunHeightN(checkTime, longitude, latitude, naturalTimeZone, n) > sunAngle {
|
|||
|
|
return ErrNeverSet
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// calculateInitialSunTime 计算日出日落的初始估算时间
|
|||
|
|
func calculateInitialSunTime(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle float64, isSunrise bool) float64 {
|
|||
|
|
// 使用球面三角法计算: (sin(ho)-sin(φ)*sin(δ))/(cos(φ)*cos(δ))
|
|||
|
|
apparentDeclination := HSunApparentDec(solarNoonTime)
|
|||
|
|
cosHourAngle := (Sin(sunAngle) - Sin(apparentDeclination)*Sin(latitude)) / (Cos(apparentDeclination) * Cos(latitude))
|
|||
|
|
|
|||
|
|
if math.Abs(cosHourAngle) <= 1 && latitude < 85 {
|
|||
|
|
// 使用解析解
|
|||
|
|
hourAngle := ArcCos(cosHourAngle) / 15
|
|||
|
|
timeOffset := 25.0 / 24.0 / 60.0 // 日出偏移
|
|||
|
|
if !isSunrise {
|
|||
|
|
timeOffset = 35.0 / 24.0 / 60.0 // 日落偏移
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if isSunrise {
|
|||
|
|
return solarNoonTime - hourAngle/24 - timeOffset
|
|||
|
|
} else {
|
|||
|
|
return solarNoonTime + hourAngle/24 + timeOffset
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 使用迭代逼近法(极地条件)
|
|||
|
|
return iterativeApproach(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, isSunrise)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func calculateInitialSunTimeN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle float64, isSunrise bool, n int) float64 {
|
|||
|
|
apparentDeclination := HSunApparentDecN(solarNoonTime, n)
|
|||
|
|
cosHourAngle := (Sin(sunAngle) - Sin(apparentDeclination)*Sin(latitude)) / (Cos(apparentDeclination) * Cos(latitude))
|
|||
|
|
|
|||
|
|
if math.Abs(cosHourAngle) <= 1 && latitude < 85 {
|
|||
|
|
hourAngle := ArcCos(cosHourAngle) / 15
|
|||
|
|
timeOffset := 25.0 / 24.0 / 60.0
|
|||
|
|
if !isSunrise {
|
|||
|
|
timeOffset = 35.0 / 24.0 / 60.0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if isSunrise {
|
|||
|
|
return solarNoonTime - hourAngle/24 - timeOffset
|
|||
|
|
}
|
|||
|
|
return solarNoonTime + hourAngle/24 + timeOffset
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return iterativeApproachN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, isSunrise, n)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// iterativeApproach 迭代逼近法计算(用于极地等特殊条件)
|
|||
|
|
func iterativeApproach(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle float64, isSunrise bool) float64 {
|
|||
|
|
estimatedTime := solarNoonTime
|
|||
|
|
stepSize := 15.0 / 60.0 / 24.0 // 15分钟步长
|
|||
|
|
if isSunrise {
|
|||
|
|
stepSize = -stepSize
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const maxIterations = 48
|
|||
|
|
for i := 0; i < maxIterations && LowSunHeight(estimatedTime, longitude, latitude, naturalTimeZone) > sunAngle; i++ {
|
|||
|
|
estimatedTime += stepSize
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return estimatedTime
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func iterativeApproachN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle float64, isSunrise bool, n int) float64 {
|
|||
|
|
estimatedTime := solarNoonTime
|
|||
|
|
stepSize := 15.0 / 60.0 / 24.0
|
|||
|
|
if isSunrise {
|
|||
|
|
stepSize = -stepSize
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const maxIterations = 48
|
|||
|
|
for i := 0; i < maxIterations && lowSunHeightForN(estimatedTime, longitude, latitude, naturalTimeZone, n) > sunAngle; i++ {
|
|||
|
|
estimatedTime += stepSize
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return estimatedTime
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// sunRiseSetNewtonRaphsonIteration 牛顿-拉夫逊迭代法求精确解
|
|||
|
|
func sunRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, naturalTimeZone, sunAngle, timeZone float64) float64 {
|
|||
|
|
const (
|
|||
|
|
convergenceThreshold = 0.00001
|
|||
|
|
derivativeStep = 0.000005
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
currentTime := initialTime
|
|||
|
|
|
|||
|
|
for {
|
|||
|
|
previousTime := currentTime
|
|||
|
|
|
|||
|
|
// 计算函数值:f(t) = SunHeight(t) - targetAngle
|
|||
|
|
functionValue := SunHeight(previousTime, longitude, latitude, naturalTimeZone) - sunAngle
|
|||
|
|
|
|||
|
|
// 计算导数:f'(t) ≈ (f(t+h) - f(t-h)) / (2h)
|
|||
|
|
derivative := (SunHeight(previousTime+derivativeStep, longitude, latitude, naturalTimeZone) -
|
|||
|
|
SunHeight(previousTime-derivativeStep, longitude, latitude, naturalTimeZone)) / (2 * derivativeStep)
|
|||
|
|
|
|||
|
|
// 牛顿-拉夫逊公式:t_new = t_old - f(t) / f'(t)
|
|||
|
|
currentTime = previousTime - functionValue/derivative
|
|||
|
|
|
|||
|
|
// 检查收敛
|
|||
|
|
if math.Abs(currentTime-previousTime) <= convergenceThreshold {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 转换为指定时区
|
|||
|
|
return currentTime - naturalTimeZone/24 + timeZone/24
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func sunRiseSetNewtonRaphsonIterationN(initialTime, longitude, latitude, naturalTimeZone, sunAngle, timeZone float64, n int) float64 {
|
|||
|
|
const (
|
|||
|
|
convergenceThreshold = 0.00001
|
|||
|
|
derivativeStep = 0.000005
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
currentTime := initialTime
|
|||
|
|
|
|||
|
|
for {
|
|||
|
|
previousTime := currentTime
|
|||
|
|
functionValue := SunHeightN(previousTime, longitude, latitude, naturalTimeZone, n) - sunAngle
|
|||
|
|
derivative := (SunHeightN(previousTime+derivativeStep, longitude, latitude, naturalTimeZone, n) -
|
|||
|
|
SunHeightN(previousTime-derivativeStep, longitude, latitude, naturalTimeZone, n)) / (2 * derivativeStep)
|
|||
|
|
currentTime = previousTime - functionValue/derivative
|
|||
|
|
if math.Abs(currentTime-previousTime) <= convergenceThreshold {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return currentTime - naturalTimeZone/24 + timeZone/24
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* 太阳高度角 世界时
|
|||
|
|
*/
|
|||
|
|
func SunHeight(jd, lon, lat, tz float64) float64 {
|
|||
|
|
//tmp := (tz*15 - lon) * 4 / 60
|
|||
|
|
//truejd := jd - tmp/24
|
|||
|
|
calcjd := jd - tz/24.0
|
|||
|
|
tjde := TD2UT(calcjd, true)
|
|||
|
|
st := Limit360(ApparentSiderealTime(calcjd)*15 + lon)
|
|||
|
|
ra, dec := HSunApparentRaDec(tjde)
|
|||
|
|
hourAngle := Limit360(st - ra)
|
|||
|
|
tmp2 := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle)
|
|||
|
|
return ArcSin(tmp2)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func SunHeightN(jd, lon, lat, tz float64, n int) float64 {
|
|||
|
|
calcjd := jd - tz/24.0
|
|||
|
|
tjde := TD2UT(calcjd, true)
|
|||
|
|
st := Limit360(ApparentSiderealTime(calcjd)*15 + lon)
|
|||
|
|
ra, dec := HSunApparentRaDecN(tjde, n)
|
|||
|
|
hourAngle := Limit360(st - ra)
|
|||
|
|
tmp2 := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle)
|
|||
|
|
return ArcSin(tmp2)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func LowSunHeight(jd, lon, lat, tz float64) float64 {
|
|||
|
|
//tmp := (tz*15 - lon) * 4 / 60
|
|||
|
|
//truejd := jd - tmp/24
|
|||
|
|
calcjd := jd - tz/24
|
|||
|
|
st := Limit360(ApparentSiderealTime(calcjd)*15 + lon)
|
|||
|
|
hourAngle := Limit360(st - SunApparentRa(TD2UT(calcjd, true)))
|
|||
|
|
dec := SunApparentDec(TD2UT(calcjd, true))
|
|||
|
|
tmp2 := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle)
|
|||
|
|
return ArcSin(tmp2)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func lowSunHeightForN(jd, lon, lat, tz float64, n int) float64 {
|
|||
|
|
if n < 0 {
|
|||
|
|
return LowSunHeight(jd, lon, lat, tz)
|
|||
|
|
}
|
|||
|
|
return SunHeightN(jd, lon, lat, tz, n)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func SunAzimuth(jd, lon, lat, tz float64) float64 {
|
|||
|
|
//tmp := (tz*15 - lon) * 4 / 60
|
|||
|
|
//truejd := jd - tmp/24
|
|||
|
|
calcjd := jd - tz/24
|
|||
|
|
st := Limit360(ApparentSiderealTime(calcjd)*15 + lon)
|
|||
|
|
hourAngle := Limit360(st - HSunApparentRa(TD2UT(calcjd, true)))
|
|||
|
|
tmp2 := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(HSunApparentDec(TD2UT(calcjd, true)))*Cos(lat))
|
|||
|
|
azimuth := ArcTan(tmp2)
|
|||
|
|
if azimuth < 0 {
|
|||
|
|
if hourAngle/15 < 12 {
|
|||
|
|
return azimuth + 360
|
|||
|
|
}
|
|||
|
|
return azimuth + 180
|
|||
|
|
}
|
|||
|
|
if hourAngle/15 < 12 {
|
|||
|
|
return azimuth + 180
|
|||
|
|
}
|
|||
|
|
return azimuth
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func SunAzimuthN(jd, lon, lat, tz float64, n int) float64 {
|
|||
|
|
calcjd := jd - tz/24
|
|||
|
|
st := Limit360(ApparentSiderealTime(calcjd)*15 + lon)
|
|||
|
|
hourAngle := Limit360(st - HSunApparentRaN(TD2UT(calcjd, true), n))
|
|||
|
|
tmp2 := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(HSunApparentDecN(TD2UT(calcjd, true), n))*Cos(lat))
|
|||
|
|
azimuth := ArcTan(tmp2)
|
|||
|
|
if azimuth < 0 {
|
|||
|
|
if hourAngle/15 < 12 {
|
|||
|
|
return azimuth + 360
|
|||
|
|
}
|
|||
|
|
return azimuth + 180
|
|||
|
|
}
|
|||
|
|
if hourAngle/15 < 12 {
|
|||
|
|
return azimuth + 180
|
|||
|
|
}
|
|||
|
|
return azimuth
|
|||
|
|
}
|