- 新增日食、月食、本地可见性、中心线、半影区域、SVG 图示与沙罗周期信息 - 新增行星冲合、留、方照、物理星历、视直径、相位、亮肢角、轨道节点等计算 - 新增木星伽利略卫星位置、现象与接触事件计算 - 新增恒星星表、星座判定、自行修正与观测辅助能力 - 新增 coord、formula、orbit、sundial、lite/sun、lite/moon 等扩展包 - 完善农历年号、月相英文别名、视差角、大气质量、折射、日晷与双星计算 - 增加 NASA、JPL Horizons、IMCCE 等回归测试数据与基线测试 - 重构基础算法文件组织,补充大量公开 API 注释和语义回归测试 - 更新中文和英文 README,补充示例、精度说明、SVG 配图
117 lines
4.6 KiB
Go
117 lines
4.6 KiB
Go
package sundial
|
||
|
||
import (
|
||
"math"
|
||
"time"
|
||
|
||
"b612.me/astro/sun"
|
||
)
|
||
|
||
// TrueSolarTime 真太阳时 / apparent solar time.
|
||
//
|
||
// 返回 date 在经度 lon 处对应的真太阳时,口径沿用 `sun.ApparentSolarTime`。
|
||
// Returns the apparent solar time for date at longitude lon.
|
||
func TrueSolarTime(date time.Time, lon float64) time.Time {
|
||
return sun.ApparentSolarTime(date, lon)
|
||
}
|
||
|
||
// MeanSolarTime 地方平太阳时 / local mean solar time.
|
||
//
|
||
// 返回 date 在经度 lon 处对应的地方平太阳时,结果时区为按经度换算的地方平太阳时区。
|
||
// 该实现直接按“真太阳时 - 均时差”构造,以与本仓库的真太阳时和均时差口径保持一致。
|
||
// Returns the local mean solar time for date at longitude lon. The result uses
|
||
// a synthetic local-mean-solar time zone derived from longitude and is built as
|
||
// apparent solar time minus equation of time to keep the package's conventions aligned.
|
||
func MeanSolarTime(date time.Time, lon float64) time.Time {
|
||
return TrueSolarTime(date, lon).Add(time.Duration(-sun.EquationTime(date) * float64(time.Hour)))
|
||
}
|
||
|
||
// HourAngle 太阳时角 / solar hour angle.
|
||
//
|
||
// 返回经度 lon 处的有符号太阳时角,单位度。上午为负,下午为正,中午为 0°。
|
||
// Returns the signed apparent-solar hour angle at longitude lon, in degrees.
|
||
func HourAngle(date time.Time, lon float64) float64 {
|
||
return normalizeSigned180(sun.HourAngle(date, lon, 0))
|
||
}
|
||
|
||
// MeanSolarHourAngle 平太阳时对应的太阳时角 / hour angle for local mean solar time.
|
||
//
|
||
// date 负责提供地方平太阳时日期与时区,原有钟面时间会被 meanSolarHours 替换;
|
||
// meanSolarHours 为地方平太阳时钟面读数,单位小时,例如 9.5 表示地方平太阳时 09:30。
|
||
// 返回对应的视太阳时角,单位度,上午为负,下午为正。
|
||
func MeanSolarHourAngle(date time.Time, meanSolarHours float64) float64 {
|
||
if !isFinite(meanSolarHours) {
|
||
return math.NaN()
|
||
}
|
||
sampleTime := dateWithClockHours(date, meanSolarHours)
|
||
return normalizeSigned180(HourAngle(sampleTime, longitudeFromTimeZone(sampleTime)))
|
||
}
|
||
|
||
// ZoneTimeHourAngle 区时对应的太阳时角 / hour angle for zone time.
|
||
//
|
||
// zoneTimeHours 为 date 所在时区下的钟面读数,单位小时;lon 为当地经度,东正西负。
|
||
// 返回该区时在给定经度上对应的视太阳时角,单位度,上午为负,下午为正。
|
||
// date 提供民用日期和时区;其原有钟面时间会被 zoneTimeHours 替换。
|
||
func ZoneTimeHourAngle(date time.Time, lon, zoneTimeHours float64) float64 {
|
||
if !isFinite(zoneTimeHours) || !isFinite(lon) {
|
||
return math.NaN()
|
||
}
|
||
return normalizeSigned180(HourAngle(dateWithClockHours(date, zoneTimeHours), lon))
|
||
}
|
||
|
||
// HorizontalHourLineAngle 水平日晷时线角 / horizontal sundial hour-line angle.
|
||
//
|
||
// lat 为纬度(北正南负),hourAngle 为有符号太阳时角,单位度。返回值是相对午线的时线角,
|
||
// 上午在东侧为负,下午在西侧为正。
|
||
// lat is the observer latitude in degrees and hourAngle is the signed solar
|
||
// hour angle in degrees. The result is the hour-line angle from the noon line:
|
||
// east/morning is negative and west/afternoon is positive.
|
||
func HorizontalHourLineAngle(lat, hourAngle float64) float64 {
|
||
if !isFinite(hourAngle) || !isFinite(lat) {
|
||
return math.NaN()
|
||
}
|
||
hourAngle = normalizeSigned180(hourAngle)
|
||
latRad := lat * math.Pi / 180
|
||
hourAngleRad := hourAngle * math.Pi / 180
|
||
return math.Atan2(math.Sin(latRad)*math.Sin(hourAngleRad), math.Cos(hourAngleRad)) * 180 / math.Pi
|
||
}
|
||
|
||
// HorizontalHourLineAngleAt 水平日晷时线角 / horizontal sundial hour-line angle.
|
||
//
|
||
// 先按给定时刻和经度求瞬时视太阳时角,再结合纬度返回水平日晷的时线角。
|
||
func HorizontalHourLineAngleAt(date time.Time, lon, lat float64) float64 {
|
||
return HorizontalHourLineAngle(lat, HourAngle(date, lon))
|
||
}
|
||
|
||
func dateWithClockHours(date time.Time, hours float64) time.Time {
|
||
startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
|
||
return startOfDay.Add(time.Duration(hours * float64(time.Hour)))
|
||
}
|
||
|
||
func longitudeFromTimeZone(date time.Time) float64 {
|
||
_, offsetSeconds := date.Zone()
|
||
return float64(offsetSeconds) / 240.0
|
||
}
|
||
|
||
func clockHours(date time.Time) float64 {
|
||
return float64(date.Hour()) +
|
||
float64(date.Minute())/60 +
|
||
float64(date.Second())/3600 +
|
||
float64(date.Nanosecond())/3.6e12
|
||
}
|
||
|
||
func normalizeSigned180(value float64) float64 {
|
||
value = math.Mod(value, 360)
|
||
if value < -180 {
|
||
value += 360
|
||
}
|
||
if value >= 180 {
|
||
value -= 360
|
||
}
|
||
return value
|
||
}
|
||
|
||
func isFinite(value float64) bool {
|
||
return !math.IsNaN(value) && !math.IsInf(value, 0)
|
||
}
|