feat: 引入可切换星历模型并扩展天象观测链路

- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算
- 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询
- 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出
- 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解
- 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤
- 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持
- 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理
- 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
This commit is contained in:
2026-10-15 23:54:15 +08:00
parent 16c62a97d5
commit 044f2f4381
674 changed files with 62983 additions and 8396 deletions
+203
View File
@@ -0,0 +1,203 @@
package sun
import (
"time"
)
// Altitude 太阳站心视高度角,不含大气折射 / topocentric apparent solar altitude without refraction.
func Altitude(date time.Time, lon, lat float64) float64 {
return defaultModule.Altitude(date, lon, lat)
}
// AltitudeN 太阳站心视高度角(n 变体) / n-parameterised topocentric apparent solar altitude.
func AltitudeN(date time.Time, lon, lat float64, n int) float64 {
return defaultModule.AltitudeN(date, lon, lat, n)
}
// ApparentAltitude 太阳视高度角 / apparent solar altitude.
func ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 {
return defaultModule.ApparentAltitude(date, lon, lat, pressureHPa, temperatureC)
}
// ApparentAltitudeN 太阳视高度角(n 变体) / n-parameterised apparent solar altitude.
func ApparentAltitudeN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 {
return defaultModule.ApparentAltitudeN(date, lon, lat, pressureHPa, temperatureC, n)
}
// ApparentDec 太阳地心视赤纬 / apparent geocentric declination.
func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) }
// ApparentLo 太阳视黄经 / apparent ecliptic longitude.
func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) }
// ApparentRa 太阳地心视赤经 / apparent geocentric right ascension.
func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) }
// ApparentRaDec 太阳地心视赤经、视赤纬 / apparent geocentric right ascension and declination.
func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) }
// ApparentSolarTime 真太阳时,结果时区为按经度换算的地方平太阳时 / apparent solar time, result is a longitude-derived mean-solar zone.
func ApparentSolarTime(date time.Time, lon float64) time.Time {
return defaultModule.ApparentSolarTime(date, lon)
}
// ApparentSolarTimeN 真太阳时(n 变体) / n-parameterised apparent solar time.
func ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time {
return defaultModule.ApparentSolarTimeN(date, lon, n)
}
// ApparentZenith 太阳视天顶距 / apparent solar zenith distance.
func ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 {
return defaultModule.ApparentZenith(date, lon, lat, pressureHPa, temperatureC)
}
// ApparentZenithN 太阳视天顶距(n 变体) / n-parameterised apparent solar zenith distance.
func ApparentZenithN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 {
return defaultModule.ApparentZenithN(date, lon, lat, pressureHPa, temperatureC, n)
}
// Azimuth 太阳站心方位角,自正北向东量 / topocentric solar azimuth, measured from north through east.
func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) }
// AzimuthN 太阳站心方位角(n 变体) / n-parameterised topocentric solar azimuth.
func AzimuthN(date time.Time, lon, lat float64, n int) float64 {
return defaultModule.AzimuthN(date, lon, lat, n)
}
// CulminationTime 太阳中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date.
func CulminationTime(date time.Time, lon float64) time.Time {
return defaultModule.CulminationTime(date, lon)
}
// CulminationTimeN 太阳中天时刻(n 变体) / n-parameterised culmination time.
func CulminationTimeN(date time.Time, lon float64, n int) time.Time {
return defaultModule.CulminationTimeN(date, lon, n)
}
// Diameter 太阳视直径,单位角秒 / apparent solar diameter in arcseconds.
func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) }
// DiameterN 太阳视直径(n 变体),单位角秒 / n-parameterised apparent solar diameter in arcseconds.
func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) }
// DownTime 日落时刻别名 / deprecated sunset alias.
//
// Deprecated: use SetTime instead.
func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
return defaultModule.DownTime(date, lon, lat, height, aero)
}
// DownTimeN 日落时刻别名(n 变体) / deprecated n-parameterised sunset alias.
//
// Deprecated: use SetTimeN instead.
func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
return defaultModule.DownTimeN(date, lon, lat, height, aero, n)
}
// EarthDistance 日地距离 / Earth-Sun distance.
func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) }
// EquationTime 均时差 / equation of time.
func EquationTime(date time.Time) float64 { return defaultModule.EquationTime(date) }
// EveningTwilight 暮光终时,取 date 所在当地民用日 / evening twilight, on the local civil day of date.
func EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) {
return defaultModule.EveningTwilight(date, lon, lat, angle)
}
// EveningTwilightN 暮光终时(n 变体),取 date 所在当地民用日 / n-parameterised evening twilight, on the local civil day of date.
func EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) {
return defaultModule.EveningTwilightN(date, lon, lat, angle, n)
}
// HourAngle 太阳地心视时角,不含站心视差,lat 不参与计算仅为参数形状一致 / geocentric apparent solar hour angle without topocentric parallax, lat is unused, kept for signature symmetry.
func HourAngle(date time.Time, lon, lat float64) float64 {
return defaultModule.HourAngle(date, lon, lat)
}
// HourAngleN 太阳时角(n 变体) / n-parameterised hour angle.
func HourAngleN(date time.Time, lon, lat float64, n int) float64 {
return defaultModule.HourAngleN(date, lon, lat, n)
}
// MorningTwilight 晨光始时,angle 常取 -6/-12/-18 度 / morning twilight, angle typically -6/-12/-18.
func MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) {
return defaultModule.MorningTwilight(date, lon, lat, angle)
}
// MorningTwilightN 晨光始时(n 变体) / n-parameterised morning twilight.
func MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) {
return defaultModule.MorningTwilightN(date, lon, lat, angle, n)
}
// ParallacticAngle 太阳视差角(天顶方向角) / solar parallactic angle.
func ParallacticAngle(date time.Time, lon, lat float64) float64 {
return defaultModule.ParallacticAngle(date, lon, lat)
}
// ParallacticAngleN 太阳视差角(天顶方向角)(n 变体) / n-parameterised solar parallactic angle.
func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 {
return defaultModule.ParallacticAngleN(date, lon, lat, n)
}
// Physical 太阳物理观测参数 / physical observing parameters of the Sun.
func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) }
// PhysicalN 太阳物理观测参数(n 变体) / n-parameterised physical observing parameters of the Sun.
func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) }
// RiseTime 日出时刻,取 date 所在当地民用日;aero 为 true 时按上缘过地平线 / sunrise time, on the local civil day of date; aero uses an upper-limb crossing.
func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
return defaultModule.RiseTime(date, lon, lat, height, aero)
}
// RiseTimeN 日出时刻(n 变体) / n-parameterised sunrise time.
func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
return defaultModule.RiseTimeN(date, lon, lat, height, aero, n)
}
// Semidiameter 太阳视半径,单位角秒 / apparent solar semidiameter in arcseconds.
func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) }
// SemidiameterN 太阳视半径(n 变体),单位角秒 / n-parameterised apparent solar semidiameter in arcseconds.
func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) }
// SetTime 日落时刻,取 date 所在当地民用日 / sunset time, on the local civil day of date.
func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
return defaultModule.SetTime(date, lon, lat, height, aero)
}
// SetTimeN 日落时刻(n 变体),取 date 所在当地民用日 / n-parameterised sunset time, on the local civil day of date.
func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
return defaultModule.SetTimeN(date, lon, lat, height, aero, n)
}
// TrueBo 太阳真黄纬 / true ecliptic latitude.
func TrueBo(date time.Time) float64 { return defaultModule.TrueBo(date) }
// TrueBoN 太阳真黄纬(n 变体) / n-parameterised true ecliptic latitude.
func TrueBoN(date time.Time, n int) float64 { return defaultModule.TrueBoN(date, n) }
// TrueLo 太阳真黄经 / true ecliptic longitude.
func TrueLo(date time.Time) float64 { return defaultModule.TrueLo(date) }
// TrueLoN 太阳真黄经(n 变体) / n-parameterised true ecliptic longitude.
func TrueLoN(date time.Time, n int) float64 { return defaultModule.TrueLoN(date, n) }
// Zenith 太阳天顶距 / solar zenith distance.
func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) }
// ZenithN 太阳天顶距(n 变体) / n-parameterised solar zenith distance.
func ZenithN(date time.Time, lon, lat float64, n int) float64 {
return defaultModule.ZenithN(date, lon, lat, n)
}
// RiseTimeWithAtmosphere 日出时刻,按给定气压(hPa)与气温(°C)计算大气折射 / sunrise time with explicit pressure and temperature.
func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) {
return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC)
}
// SetTimeWithAtmosphere 日落时刻,按给定气压(hPa)与气温(°C)计算大气折射 / sunset time with explicit pressure and temperature.
func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) {
return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC)
}
+26
View File
@@ -0,0 +1,26 @@
package sun
import (
"testing"
"time"
)
func TestRiseSetWithAtmosphere(t *testing.T) {
date := time.Date(2025, 1, 1, 0, 0, 0, 0, time.FixedZone("CST", 8*3600))
base, err := RiseTime(date, 116.4, 39.9, 0, true)
if err != nil {
t.Fatal(err)
}
same, err := RiseTimeWithAtmosphere(date, 116.4, 39.9, 0, 1010, 10)
if diff := same.Sub(base); err != nil || diff > 10*time.Millisecond || diff < -10*time.Millisecond {
t.Fatalf("standard weather %v vs %v (%v)", same, base, err)
}
cold, err := RiseTimeWithAtmosphere(date, 116.4, 39.9, 0, 1040, -30)
if err != nil || !cold.Before(base) {
t.Fatalf("cold dense air should advance sunrise: %v vs %v (%v)", cold, base, err)
}
set, err := SetTimeWithAtmosphere(date, 116.4, 39.9, 0, 1040, -30)
if err != nil || set.Location() != date.Location() {
t.Fatalf("sunset: %v %v", set, err)
}
}
+38
View File
@@ -0,0 +1,38 @@
package sun
import (
"time"
"b612.me/astro/internal/ephem"
)
// Sun 指定星历实例的计算模块 / computation module bound to an ephemeris instance.
type Sun struct{ inst *ephem.Instance }
// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model.
func NewWithInstance(inst *ephem.Instance) *Sun { return &Sun{inst: inst} }
var defaultModule Sun
// AxialtiltNutation 交角章动(IAU 2000B) / nutation in obliquity, IAU 2000B.
func (module *Sun) AxialtiltNutation(date time.Time) float64 { return AxialtiltNutation(date) }
// AxialtiltNutation1980 交角章动(IAU 1980) / nutation in obliquity, IAU 1980.
func (module *Sun) AxialtiltNutation1980(date time.Time) float64 { return AxialtiltNutation1980(date) }
// EclipticNutation 黄经章动(IAU 2000B) / nutation in longitude, IAU 2000B.
func (module *Sun) EclipticNutation(date time.Time) float64 { return EclipticNutation(date) }
// EclipticNutation1980 黄经章动(IAU 1980) / nutation in longitude, IAU 1980.
func (module *Sun) EclipticNutation1980(date time.Time) float64 { return EclipticNutation1980(date) }
// EclipticObliquity 黄赤交角 / ecliptic obliquity.
func (module *Sun) EclipticObliquity(date time.Time, nutation bool) float64 {
return EclipticObliquity(date, nutation)
}
// GeometricLo 太阳几何黄经 / geometric ecliptic longitude.
func (module *Sun) GeometricLo(date time.Time) float64 { return GeometricLo(date) }
// MidFunc 太阳中心差 / solar equation of center.
func (module *Sun) MidFunc(date time.Time) float64 { return MidFunc(date) }
+79
View File
@@ -0,0 +1,79 @@
package sun
import (
"b612.me/astro/basic"
"b612.me/astro/internal/ephem"
"testing"
"time"
)
type contextAPI interface {
Altitude(date time.Time, lon, lat float64) float64
AltitudeN(date time.Time, lon, lat float64, n int) float64
ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64
ApparentAltitudeN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64
ApparentDec(date time.Time) float64
ApparentLo(date time.Time) float64
ApparentRa(date time.Time) float64
ApparentRaDec(date time.Time) (float64, float64)
ApparentSolarTime(date time.Time, lon float64) time.Time
ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time
ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64
ApparentZenithN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64
AxialtiltNutation(date time.Time) float64
AxialtiltNutation1980(date time.Time) float64
Azimuth(date time.Time, lon, lat float64) float64
AzimuthN(date time.Time, lon, lat float64, n int) float64
CulminationTime(date time.Time, lon float64) time.Time
CulminationTimeN(date time.Time, lon float64, n int) time.Time
Diameter(date time.Time) float64
DiameterN(date time.Time, n int) float64
DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error)
DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error)
EarthDistance(date time.Time) float64
EclipticNutation(date time.Time) float64
EclipticNutation1980(date time.Time) float64
EclipticObliquity(date time.Time, nutation bool) float64
EquationTime(date time.Time) float64
EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error)
EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error)
GeometricLo(date time.Time) float64
HourAngle(date time.Time, lon, lat float64) float64
HourAngleN(date time.Time, lon, lat float64, n int) float64
MidFunc(date time.Time) float64
MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error)
MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error)
ParallacticAngle(date time.Time, lon, lat float64) float64
ParallacticAngleN(date time.Time, lon, lat float64, n int) float64
Physical(date time.Time) PhysicalInfo
PhysicalN(date time.Time, n int) PhysicalInfo
RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error)
RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error)
Semidiameter(date time.Time) float64
SemidiameterN(date time.Time, n int) float64
SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error)
SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error)
TrueBo(date time.Time) float64
TrueBoN(date time.Time, n int) float64
TrueLo(date time.Time) float64
TrueLoN(date time.Time, n int) float64
Zenith(date time.Time, lon, lat float64) float64
ZenithN(date time.Time, lon, lat float64, n int) float64
}
var _ contextAPI = (*Sun)(nil)
func TestContextModuleIsolation(t *testing.T) {
previous := basic.GetEphemerisModel()
t.Cleanup(func() { basic.SetEphemerisModel(previous) })
date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC)
for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} {
basic.SetEphemerisModel(basic.EphemerisModel(model))
want := ApparentLo(date)
module := NewWithInstance(ephem.NewModelInstance(model))
basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model)))
if got := module.ApparentLo(date); got != want {
t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want)
}
}
}
+10 -10
View File
@@ -7,23 +7,23 @@ import (
)
// Semidiameter 太阳视半径,单位角秒 / apparent solar semidiameter in arcseconds.
func Semidiameter(date time.Time) float64 {
return SemidiameterN(date, -1)
func (module *Sun) Semidiameter(date time.Time) float64 {
return module.SemidiameterN(date, -1)
}
// SemidiameterN 太阳视半径(截断版),单位角秒 / truncated apparent solar semidiameter in arcseconds.
func SemidiameterN(date time.Time, n int) float64 {
// SemidiameterN 太阳视半径(n 变体),单位角秒 / n-parameterised apparent solar semidiameter in arcseconds.
func (module *Sun) SemidiameterN(date time.Time, n int) float64 {
jd := basic.Date2JD(date.UTC())
return basic.SunSemidiameterN(basic.UTC2TT(jd), n)
return basic.SunSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst)
}
// Diameter 太阳视直径,单位角秒 / apparent solar diameter in arcseconds.
func Diameter(date time.Time) float64 {
return DiameterN(date, -1)
func (module *Sun) Diameter(date time.Time) float64 {
return module.DiameterN(date, -1)
}
// DiameterN 太阳视直径(截断版),单位角秒 / truncated apparent solar diameter in arcseconds.
func DiameterN(date time.Time, n int) float64 {
// DiameterN 太阳视直径(n 变体),单位角秒 / n-parameterised apparent solar diameter in arcseconds.
func (module *Sun) DiameterN(date time.Time, n int) float64 {
jd := basic.Date2JD(date.UTC())
return basic.SunDiameterN(basic.UTC2TT(jd), n)
return basic.SunDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst)
}
+4
View File
@@ -1,3 +1,7 @@
// Package sun 太阳位置、升落、晨昏朦影、均时差、真太阳时、视高度角、视差角、视直径与日面物理星历;角度单位为度。
// Package sun covers solar position, rise/set, twilight, equation of time, apparent solar time, apparent altitude, parallactic angle, diameter and solar physical ephemeris; angles are degrees.
// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。
// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n.
// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。
// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone.
package sun
+6 -8
View File
@@ -7,18 +7,16 @@ import (
)
// ParallacticAngle 太阳视差角(天顶方向角) / solar parallactic angle.
//
// lon/lat 为观测者经纬度,东正西负、北正南负;返回值为有符号视差角,单位度。
func ParallacticAngle(date time.Time, lon, lat float64) float64 {
return basic.ParallacticAngleByHourAngle(HourAngle(date, lon, lat), ApparentDec(date), lat)
func (module *Sun) ParallacticAngle(date time.Time, lon, lat float64) float64 {
return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon, lat), module.ApparentDec(date), lat)
}
// ParallacticAngleN 截断项太阳视差角(天顶方向角) / truncated solar parallactic angle.
func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 {
// ParallacticAngleN 太阳视差角(天顶方向角)(n 变体) / n-parameterised solar parallactic angle.
func (module *Sun) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 {
jd := basic.Date2JD(date.UTC())
return basic.ParallacticAngleByHourAngle(
HourAngleN(date, lon, lat, n),
basic.HSunApparentDecN(basic.UTC2TT(jd), n),
module.HourAngleN(date, lon, lat, n),
basic.HSunApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst),
lat,
)
}
+5 -5
View File
@@ -17,14 +17,14 @@ type PhysicalInfo struct {
}
// Physical 太阳物理观测参数 / physical observing parameters of the Sun.
func Physical(date time.Time) PhysicalInfo {
return PhysicalN(date, -1)
func (module *Sun) Physical(date time.Time) PhysicalInfo {
return module.PhysicalN(date, -1)
}
// PhysicalN 太阳物理观测参数(截断版) / truncated physical observing parameters of the Sun.
func PhysicalN(date time.Time, n int) PhysicalInfo {
// PhysicalN 太阳物理观测参数(n 变体) / n-parameterised physical observing parameters of the Sun.
func (module *Sun) PhysicalN(date time.Time, n int) PhysicalInfo {
jd := basic.Date2JD(date.UTC())
info := basic.SunPhysicalN(basic.UTC2TT(jd), n)
info := basic.SunPhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst)
return PhysicalInfo{
P: info.P,
B0: info.B0,
+10 -10
View File
@@ -7,21 +7,21 @@ import (
)
// ApparentAltitude 太阳视高度角 / apparent solar altitude.
func ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 {
return basic.ApparentAltitude(Altitude(date, lon, lat), pressureHPa, temperatureC)
func (module *Sun) ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 {
return basic.ApparentAltitude(module.Altitude(date, lon, lat), pressureHPa, temperatureC)
}
// ApparentZenith 太阳视天顶距 / apparent solar zenith distance.
func ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 {
return 90 - ApparentAltitude(date, lon, lat, pressureHPa, temperatureC)
func (module *Sun) ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 {
return 90 - module.ApparentAltitude(date, lon, lat, pressureHPa, temperatureC)
}
// ApparentAltitudeN 太阳视高度角(截断版) / truncated apparent solar altitude.
func ApparentAltitudeN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 {
return basic.ApparentAltitude(AltitudeN(date, lon, lat, n), pressureHPa, temperatureC)
// ApparentAltitudeN 太阳视高度角(n 变体) / n-parameterised apparent solar altitude.
func (module *Sun) ApparentAltitudeN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 {
return basic.ApparentAltitude(module.AltitudeN(date, lon, lat, n), pressureHPa, temperatureC)
}
// ApparentZenithN 太阳视天顶距(截断版) / truncated apparent solar zenith distance.
func ApparentZenithN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 {
return 90 - ApparentAltitudeN(date, lon, lat, pressureHPa, temperatureC, n)
// ApparentZenithN 太阳视天顶距(n 变体) / n-parameterised apparent solar zenith distance.
func (module *Sun) ApparentZenithN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 {
return 90 - module.ApparentAltitudeN(date, lon, lat, pressureHPa, temperatureC, n)
}
+42
View File
@@ -0,0 +1,42 @@
package sun
import (
"time"
"b612.me/astro/basic"
)
// AltitudeLimb 取高度的位置(上缘、中心、下缘)/ limb used for the altitude.
type AltitudeLimb = basic.AltitudeLimb
const (
LimbUpper = basic.AltitudeLimbUpper
LimbCenter = basic.AltitudeLimbCenter
LimbLower = basic.AltitudeLimbLower
)
// AltitudeOptions 升落选项;零值为上缘、标准气象、计入折射 / altitude crossing options.
type AltitudeOptions = basic.AltitudeCrossingOptions
// AltitudeState 一个民用日内的穿越状态 / crossing state over one civil day.
type AltitudeState = basic.AltitudeCrossingState
const (
AltitudeCrosses = basic.AltitudeCrosses
AltitudeAlwaysAbove = basic.AltitudeAlwaysAbove
AltitudeAlwaysBelow = basic.AltitudeAlwaysBelow
AltitudeTangent = basic.AltitudeTangent
)
// AltitudeCrossingResult date 所在民用日内首次升、落与残差范围(度)/ first rise and set on the civil day.
type AltitudeCrossingResult = basic.AltitudeCrossingResult
// RiseSet 太阳在 date 所在民用日内穿过观测者地平线(含 height 带来的地平俯角)的升落;options 控制太阳缘与大气 / solar rise and set on the civil day.
func (module *Sun) RiseSet(date time.Time, lon, lat, height float64, options AltitudeOptions) (AltitudeCrossingResult, error) {
return basic.SkyAltitudeCrossingOnDate(basic.SkySun, date, lon, lat, height, -basic.HeightDegreeByLat(height, lat), options, module.inst)
}
// RiseSet 太阳在 date 所在民用日内穿过观测者地平线(含 height 带来的地平俯角)的升落;options 控制太阳缘与大气 / solar rise and set on the civil day.
func RiseSet(date time.Time, lon, lat, height float64, options AltitudeOptions) (AltitudeCrossingResult, error) {
return defaultModule.RiseSet(date, lon, lat, height, options)
}
+74
View File
@@ -0,0 +1,74 @@
// 被测契约:默认选项与 RiseTime/SetTime 一致;夏令时切换日结果落在当天;实例方法使用绑定实例。
package sun
import (
"testing"
"time"
"b612.me/astro/basic"
"b612.me/astro/internal/ephem"
)
func TestRiseSetMatchesRiseTimeSetTime(t *testing.T) {
loc := time.FixedZone("CST", 8*3600)
for i := 0; i < 30; i++ {
date := time.Date(2025, 1, 1, 12, 0, 0, 0, loc).AddDate(0, 0, i*12)
got, err := RiseSet(date, 116.4, 39.9, 50, basic.AltitudeCrossingOptions{})
if err != nil {
t.Fatal(err)
}
checkEvent(t, date, "rise", got.Rise, got.HasRise, func() (time.Time, error) { return RiseTime(date, 116.4, 39.9, 50, true) })
checkEvent(t, date, "set", got.Set, got.HasSet, func() (time.Time, error) { return SetTime(date, 116.4, 39.9, 50, true) })
}
}
func TestRiseSetDaylightSavingDays(t *testing.T) {
loc, err := time.LoadLocation("America/New_York")
if err != nil {
t.Skip(err)
}
for _, date := range []time.Time{
time.Date(2023, 3, 12, 12, 0, 0, 0, loc),
time.Date(2023, 11, 5, 12, 0, 0, 0, loc),
} {
got, err := RiseSet(date, -74, 40.7, 0, basic.AltitudeCrossingOptions{})
if err != nil {
t.Fatal(err)
}
checkEvent(t, date, "rise", got.Rise, got.HasRise, func() (time.Time, error) { return RiseTime(date, -74, 40.7, 0, true) })
checkEvent(t, date, "set", got.Set, got.HasSet, func() (time.Time, error) { return SetTime(date, -74, 40.7, 0, true) })
if got.Rise.Location() != loc || got.Rise.YearDay() != date.YearDay() || got.Set.YearDay() != date.YearDay() {
t.Errorf("%s: events %v %v outside civil day", date.Format("2006-01-02"), got.Rise, got.Set)
}
}
}
func TestRiseSetUsesModuleInstance(t *testing.T) {
date := time.Date(2025, 3, 20, 12, 0, 0, 0, time.FixedZone("CST", 8*3600))
inst := ephem.NewModelInstance(ephem.Legacy)
got, err := NewWithInstance(inst).RiseSet(date, 116.4, 39.9, 0, basic.AltitudeCrossingOptions{Limb: basic.AltitudeLimbCenter})
if err != nil {
t.Fatal(err)
}
want, err := basic.SkyAltitudeCrossingOnDate(basic.SkySun, date, 116.4, 39.9, 0, 0, basic.AltitudeCrossingOptions{Limb: basic.AltitudeLimbCenter}, inst)
if err != nil {
t.Fatal(err)
}
if !got.Rise.Equal(want.Rise) || !got.Set.Equal(want.Set) {
t.Errorf("instance result %+v, want %+v", got, want)
}
}
func checkEvent(t *testing.T, date time.Time, kind string, got time.Time, has bool, ref func() (time.Time, error)) {
t.Helper()
want, err := ref()
if has != (err == nil) {
t.Errorf("%s %s: has=%v, reference err=%v", date.Format("2006-01-02"), kind, has, err)
return
}
if has {
if d := got.Sub(want); d > 100*time.Millisecond || d < -100*time.Millisecond {
t.Errorf("%s %s: %v, reference %v", date.Format("2006-01-02"), kind, got, want)
}
}
}
+117 -196
View File
@@ -1,12 +1,12 @@
package sun
import (
"b612.me/astro/internal/civiltime"
"errors"
"math"
"time"
"b612.me/astro/basic"
"b612.me/astro/internal/civiltime"
)
var (
@@ -50,15 +50,9 @@ func twilightResult(date time.Time, jd float64, err error) (time.Time, error) {
*/
// RiseTime 日出时刻 / sunrise time.
//
// date 取其所在时区的当地日期,返回值保持相同时区;lon/lat 为观测者经纬度,东正西负、北正南负;
// height 为椭球高(大地高),单位米;aero 为 true 时按动态标准大气折射和实时太阳视半径计算上缘过地平线。
// date is interpreted on its local civil day and the result keeps the same time zone. lon/lat are east-positive and north-positive;
// height 为观测者椭球高(大地高),单位米;aero 使用动态标准折射和实时太阳视半径计算上缘过地平线。
// height is observer elevation in meters; aero uses dynamic standard refraction and the instantaneous solar semidiameter for an upper-limb crossing.
func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
func (module *Sun) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return RiseTime(d, lon, lat, height, aero)
return module.RiseTime(d, lon, lat, height, aero)
}); handled {
return result, err
}
@@ -72,17 +66,14 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
// 返回值保持与输入 date 一致的时区。
riseJD, err := basic.GetSunRiseTime(localJD, lon, lat, timezone, aeroFloat, height)
riseJD, err := basic.GetSunRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst)
return riseSetResult(date, riseJD, err)
}
// RiseTimeN 截断项日出时刻 / truncated sunrise time.
//
// 参数与 RiseTime 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as RiseTime. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
// RiseTimeN 日出时刻(n 变体) / n-parameterised sunrise time.
func (module *Sun) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return RiseTimeN(d, lon, lat, height, aero, n)
return module.RiseTimeN(d, lon, lat, height, aero, n)
}); handled {
return result, err
}
@@ -94,37 +85,24 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
riseJD, err := basic.GetSunRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n)
riseJD, err := basic.GetSunRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst)
return riseSetResult(date, riseJD, err)
}
// DownTime 日落时刻别名 / deprecated sunset alias.
//
// Deprecated: use SetTime instead.
//
// 参数与 SetTime 相同,仅为兼容旧接口保留。
// Same as SetTime and kept only for backward compatibility.
func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
return SetTime(date, lon, lat, height, aero)
func (module *Sun) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
return module.SetTime(date, lon, lat, height, aero)
}
// DownTimeN 截断项日落时刻别名 / deprecated truncated sunset alias.
//
// Deprecated: use SetTimeN instead.
//
// 参数与 SetTimeN 相同,仅为兼容旧接口保留。
// Same as SetTimeN and kept only for backward compatibility.
func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
return SetTimeN(date, lon, lat, height, aero, n)
// DownTimeN 日落时刻别名(n 变体) / deprecated n-parameterised sunset alias.
func (module *Sun) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
return module.SetTimeN(date, lon, lat, height, aero, n)
}
// SetTime 日落时刻 / sunset time.
//
// 参数与 RiseTime 相同,返回给定当地日期内的日落时刻。
// Uses the same inputs as RiseTime and returns the sunset time on the corresponding local civil day.
func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
func (module *Sun) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return SetTime(d, lon, lat, height, aero)
return module.SetTime(d, lon, lat, height, aero)
}); handled {
return result, err
}
@@ -136,17 +114,14 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
downJD, err := basic.GetSunSetTime(localJD, lon, lat, timezone, aeroFloat, height)
downJD, err := basic.GetSunSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst)
return riseSetResult(date, downJD, err)
}
// SetTimeN 截断项日落时刻 / truncated sunset time.
//
// 参数与 RiseTimeN 相同,返回给定当地日期内的日落时刻。
// Uses the same inputs as RiseTimeN and returns the sunset time on the corresponding local civil day.
func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
// SetTimeN 日落时刻(n 变体) / n-parameterised sunset time.
func (module *Sun) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return SetTimeN(d, lon, lat, height, aero, n)
return module.SetTimeN(d, lon, lat, height, aero, n)
}); handled {
return result, err
}
@@ -158,19 +133,14 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
downJD, err := basic.GetSunSetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n)
downJD, err := basic.GetSunSetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst)
return riseSetResult(date, downJD, err)
}
// MorningTwilight 晨光始时 / morning twilight.
//
// date 取其所在时区的当地日期,返回值保持相同时区;lon/lat 为观测者经纬度,东正西负、北正南负;
// angle 为目标太阳高度角,常用 -6/-12/-18 度,分别对应民用、航海、天文朦影。
// date is interpreted on its local civil day and the result keeps the same time zone. lon/lat are east-positive and north-positive;
// angle is the target solar altitude in degrees, typically -6, -12, or -18 for civil, nautical, and astronomical twilight.
func MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) {
func (module *Sun) MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return MorningTwilight(d, lon, lat, angle)
return module.MorningTwilight(d, lon, lat, angle)
}); handled {
return result, err
}
@@ -178,17 +148,14 @@ func MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error)
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
calcJD, err := basic.MorningTwilight(localJD, lon, lat, timezone, angle)
calcJD, err := basic.MorningTwilightWithInstance(localJD, lon, lat, timezone, angle, module.inst)
return twilightResult(date, calcJD, err)
}
// MorningTwilightN 截断项晨光始时 / truncated morning twilight.
//
// 参数与 MorningTwilight 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as MorningTwilight. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) {
// MorningTwilightN 晨光始时(n 变体) / n-parameterised morning twilight.
func (module *Sun) MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return MorningTwilightN(d, lon, lat, angle, n)
return module.MorningTwilightN(d, lon, lat, angle, n)
}); handled {
return result, err
}
@@ -196,17 +163,14 @@ func MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
calcJD, err := basic.MorningTwilightN(localJD, lon, lat, timezone, angle, n)
calcJD, err := basic.MorningTwilightNWithInstance(localJD, lon, lat, timezone, angle, n, module.inst)
return twilightResult(date, calcJD, err)
}
// EveningTwilight 暮光终时 / evening twilight.
//
// 参数与 MorningTwilight 相同,返回对应当地日期的暮光结束时刻。
// Uses the same inputs as MorningTwilight and returns the evening-twilight time on the corresponding local civil day.
func EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) {
func (module *Sun) EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return EveningTwilight(d, lon, lat, angle)
return module.EveningTwilight(d, lon, lat, angle)
}); handled {
return result, err
}
@@ -215,17 +179,14 @@ func EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
//不需要进行力学时转换,会在GetBanTime中转换
calcJD, err := basic.EveningTwilight(localJD, lon, lat, timezone, angle)
calcJD, err := basic.EveningTwilightWithInstance(localJD, lon, lat, timezone, angle, module.inst)
return twilightResult(date, calcJD, err)
}
// EveningTwilightN 截断项暮光终时 / truncated evening twilight.
//
// 参数与 MorningTwilightN 相同,返回对应当地日期的暮光结束时刻。
// Uses the same inputs as MorningTwilightN and returns the evening-twilight time on the corresponding local civil day.
func EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) {
// EveningTwilightN 暮光终时(n 变体) / n-parameterised evening twilight.
func (module *Sun) EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return EveningTwilightN(d, lon, lat, angle, n)
return module.EveningTwilightN(d, lon, lat, angle, n)
}); handled {
return result, err
}
@@ -233,7 +194,7 @@ func EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
calcJD, err := basic.EveningTwilightN(localJD, lon, lat, timezone, angle, n)
calcJD, err := basic.EveningTwilightNWithInstance(localJD, lon, lat, timezone, angle, n, module.inst)
return twilightResult(date, calcJD, err)
}
@@ -305,81 +266,57 @@ func GeometricLo(date time.Time) float64 {
}
// TrueLo 太阳真黄经 / true ecliptic longitude.
//
// 返回 date 对应绝对时刻的太阳真黄经,单位度。
// Returns the Sun's true ecliptic longitude at the instant represented by date, in degrees.
func TrueLo(date time.Time) float64 {
func (module *Sun) TrueLo(date time.Time) float64 {
//转换为UTC时间
jd := basic.Date2JD(date.UTC())
return basic.HSunTrueLo(basic.UTC2TT(jd))
return basic.HSunTrueLoWithInstance(basic.UTC2TT(jd), module.inst)
}
// TrueLoN 截断项太阳真黄经 / truncated true ecliptic longitude.
//
// 参数与 TrueLo 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as TrueLo. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func TrueLoN(date time.Time, n int) float64 {
// TrueLoN 太阳真黄经(n 变体) / n-parameterised true ecliptic longitude.
func (module *Sun) TrueLoN(date time.Time, n int) float64 {
jd := basic.Date2JD(date.UTC())
return basic.HSunTrueLoN(basic.UTC2TT(jd), n)
return basic.HSunTrueLoNWithInstance(basic.UTC2TT(jd), n, module.inst)
}
// TrueBo 太阳真黄纬 / true ecliptic latitude.
//
// 返回 date 对应绝对时刻的太阳真黄纬,单位度。
// Returns the Sun's true ecliptic latitude at the instant represented by date, in degrees.
func TrueBo(date time.Time) float64 {
func (module *Sun) TrueBo(date time.Time) float64 {
//转换为UTC时间
jd := basic.Date2JD(date.UTC())
return basic.HSunTrueBo(basic.UTC2TT(jd))
return basic.HSunTrueBoWithInstance(basic.UTC2TT(jd), module.inst)
}
// TrueBoN 截断项太阳真黄纬 / truncated true ecliptic latitude.
//
// 参数与 TrueBo 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as TrueBo. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func TrueBoN(date time.Time, n int) float64 {
// TrueBoN 太阳真黄纬(n 变体) / n-parameterised true ecliptic latitude.
func (module *Sun) TrueBoN(date time.Time, n int) float64 {
jd := basic.Date2JD(date.UTC())
return basic.HSunTrueBoN(basic.UTC2TT(jd), n)
return basic.HSunTrueBoNWithInstance(basic.UTC2TT(jd), n, module.inst)
}
// ApparentLo 太阳视黄经 / apparent ecliptic longitude.
//
// 返回 date 对应绝对时刻的太阳视黄经,单位度。
// Returns the Sun's apparent ecliptic longitude at the instant represented by date, in degrees.
func ApparentLo(date time.Time) float64 {
func (module *Sun) ApparentLo(date time.Time) float64 {
//转换为UTC时间
jd := basic.Date2JD(date.UTC())
return basic.HSunApparentLo(basic.UTC2TT(jd))
return basic.HSunApparentLoWithInstance(basic.UTC2TT(jd), module.inst)
}
// ApparentRa 太阳地心视赤经 / apparent geocentric right ascension.
//
// 返回 date 对应绝对时刻的太阳地心视赤经,单位度。
// Returns the Sun's apparent geocentric right ascension at the instant represented by date, in degrees.
func ApparentRa(date time.Time) float64 {
func (module *Sun) ApparentRa(date time.Time) float64 {
//转换为UTC时间
jd := basic.Date2JD(date.UTC())
return basic.HSunApparentRa(basic.UTC2TT(jd))
return basic.HSunApparentRaWithInstance(basic.UTC2TT(jd), module.inst)
}
// ApparentDec 太阳地心视赤纬 / apparent geocentric declination.
//
// 返回 date 对应绝对时刻的太阳地心视赤纬,单位度。
// Returns the Sun's apparent geocentric declination at the instant represented by date, in degrees.
func ApparentDec(date time.Time) float64 {
func (module *Sun) ApparentDec(date time.Time) float64 {
//转换为UTC时间
jd := basic.Date2JD(date.UTC())
return basic.HSunApparentDec(basic.UTC2TT(jd))
return basic.HSunApparentDecWithInstance(basic.UTC2TT(jd), module.inst)
}
// ApparentRaDec 太阳地心视赤经、视赤纬 / apparent geocentric right ascension and declination.
//
// 返回 date 对应绝对时刻的太阳地心视赤经与视赤纬,单位度。
// Returns the Sun's apparent geocentric right ascension and declination at the instant represented by date, in degrees.
func ApparentRaDec(date time.Time) (float64, float64) {
func (module *Sun) ApparentRaDec(date time.Time) (float64, float64) {
//转换为UTC时间
jd := basic.Date2JD(date.UTC())
return basic.HSunApparentRaDec(basic.UTC2TT(jd))
return basic.HSunApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst)
}
// MidFunc 太阳中心差 / solar equation of center.
@@ -393,142 +330,101 @@ func MidFunc(date time.Time) float64 {
}
// EquationTime 均时差 / equation of time.
//
// 返回 date 对应绝对时刻的均时差,单位小时。
// Returns the equation of time at the instant represented by date, in hours.
func EquationTime(date time.Time) float64 {
func (module *Sun) EquationTime(date time.Time) float64 {
//转换为UTC时间
jd := basic.Date2JD(date.UTC())
return basic.SunTime(basic.UTC2TT(jd))
return basic.SunTimeWithInstance(basic.UTC2TT(jd), module.inst)
}
// HourAngle 太阳时角 / hour angle.
//
// date 为观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;返回值单位度。
// lat 目前不参与计算,仅为与其他观测接口保持参数形状一致。
// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the result is in degrees.
// lat is currently unused and kept only for API symmetry with the other observation helpers.
func HourAngle(date time.Time, lon, lat float64) float64 {
// HourAngle 太阳地心视时角,不含站心视差 / geocentric apparent solar hour angle without topocentric parallax.
func (module *Sun) HourAngle(date time.Time, lon, lat float64) float64 {
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
return basic.SunTimeAngle(localJD, lon, lat, timezone)
return basic.SunTimeAngleWithInstance(localJD, lon, lat, timezone, module.inst)
}
// HourAngleN 截断项太阳时角 / truncated hour angle.
//
// 参数与 HourAngle 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as HourAngle. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func HourAngleN(date time.Time, lon, lat float64, n int) float64 {
// HourAngleN 太阳时角(n 变体) / n-parameterised hour angle.
func (module *Sun) HourAngleN(date time.Time, lon, lat float64, n int) float64 {
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
return basic.SunTimeAngleN(localJD, lon, lat, timezone, n)
return basic.SunTimeAngleNWithInstance(localJD, lon, lat, timezone, n, module.inst)
}
// Azimuth 太阳方位角 / azimuth.
//
// date 为观测时刻,会读取其时区参与地方时计算;lon/lat 为观测者经纬度,东正西负、北正南负;返回值按正北为 0°、向东增加。
// date is the observing instant and its zone offset participates in local-time calculations. lon/lat are east-positive and north-positive; azimuth is measured from north toward east.
func Azimuth(date time.Time, lon, lat float64) float64 {
// Azimuth 太阳站心方位角 / topocentric solar azimuth.
func (module *Sun) Azimuth(date time.Time, lon, lat float64) float64 {
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
return basic.SunAzimuth(localJD, lon, lat, timezone)
return basic.SunAzimuthWithInstance(localJD, lon, lat, timezone, module.inst)
}
// AzimuthN 截断项太阳方位角 / truncated azimuth.
//
// 参数与 Azimuth 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as Azimuth. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func AzimuthN(date time.Time, lon, lat float64, n int) float64 {
// AzimuthN 太阳站心方位角(n 变体) / n-parameterised topocentric solar azimuth.
func (module *Sun) AzimuthN(date time.Time, lon, lat float64, n int) float64 {
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
return basic.SunAzimuthN(localJD, lon, lat, timezone, n)
return basic.SunAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst)
}
// Altitude 太阳高度角 / solar altitude.
//
// date 为观测时刻,会读取其时区参与地方时计算;lon/lat 为观测者经纬度,东正西负、北正南负;返回值单位度。
// date is the observing instant and its zone offset participates in local-time calculations. lon/lat are east-positive and north-positive; the result is in degrees.
func Altitude(date time.Time, lon, lat float64) float64 {
// Altitude 太阳站心视高度角,不含大气折射 / topocentric apparent solar altitude without refraction.
func (module *Sun) Altitude(date time.Time, lon, lat float64) float64 {
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
return basic.SunHeight(localJD, lon, lat, timezone)
return basic.SunHeightWithInstance(localJD, lon, lat, timezone, module.inst)
}
// Zenith 太阳天顶距 / solar zenith distance.
//
// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。
// Uses the same inputs as Altitude and returns the zenith distance in degrees.
func Zenith(date time.Time, lon, lat float64) float64 {
return 90 - Altitude(date, lon, lat)
func (module *Sun) Zenith(date time.Time, lon, lat float64) float64 {
return 90 - module.Altitude(date, lon, lat)
}
// AltitudeN 截断项太阳高度角 / truncated solar altitude.
//
// 参数与 Altitude 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as Altitude. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func AltitudeN(date time.Time, lon, lat float64, n int) float64 {
// AltitudeN 太阳站心视高度角(n 变体) / n-parameterised topocentric apparent solar altitude.
func (module *Sun) AltitudeN(date time.Time, lon, lat float64, n int) float64 {
localJD := basic.Date2JD(date)
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
return basic.SunHeightN(localJD, lon, lat, timezone, n)
return basic.SunHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst)
}
// ZenithN 截断项太阳天顶距 / truncated solar zenith distance.
//
// 参数与 AltitudeN 相同,返回值为对应时刻的天顶距,单位度。
// Uses the same inputs as AltitudeN and returns the zenith distance in degrees.
func ZenithN(date time.Time, lon, lat float64, n int) float64 {
return 90 - AltitudeN(date, lon, lat, n)
// ZenithN 太阳天顶距(n 变体) / n-parameterised solar zenith distance.
func (module *Sun) ZenithN(date time.Time, lon, lat float64, n int) float64 {
return 90 - module.AltitudeN(date, lon, lat, n)
}
// CulminationTime 太阳中天时刻 / culmination time.
//
// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。
// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude.
func CulminationTime(date time.Time, lon float64) time.Time {
func (module *Sun) CulminationTime(date time.Time, lon float64) time.Time {
date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
localJD := basic.Date2JD(date) + 0.5
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
calcJD := basic.CulminationTime(localJD, lon, timezone) - timezone/24.00
calcJD := basic.CulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00
return basic.JD2DateByZone(calcJD, date.Location(), false)
}
// CulminationTimeN 截断项太阳中天时刻 / truncated culmination time.
//
// 参数与 CulminationTime 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as CulminationTime. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func CulminationTimeN(date time.Time, lon float64, n int) time.Time {
// CulminationTimeN 太阳中天时刻(n 变体) / n-parameterised culmination time.
func (module *Sun) CulminationTimeN(date time.Time, lon float64, n int) time.Time {
date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
localJD := basic.Date2JD(date) + 0.5
_, loc := date.Zone()
timezone := float64(loc) / 3600.0
calcJD := basic.CulminationTimeN(localJD, lon, timezone, n) - timezone/24.00
calcJD := basic.CulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.00
return basic.JD2DateByZone(calcJD, date.Location(), false)
}
// EarthDistance 日地距离 / Earth-Sun distance.
//
// 返回 date 对应绝对时刻的日地距离,单位 AU。
// Returns the Earth-Sun distance at the instant represented by date, in astronomical units.
func EarthDistance(date time.Time) float64 {
func (module *Sun) EarthDistance(date time.Time) float64 {
jd := basic.Date2JD(date.UTC())
jde := basic.UTC2TT(jd)
return basic.EarthAway(jde)
return basic.EarthAwayWithInstance(jde, module.inst)
}
// ApparentSolarTime 真太阳时 / apparent solar time.
//
// 返回 date 这一绝对时刻在给定经度 lon 处对应的真太阳时,结果时区为按经度换算的地方平太阳时区。
// Returns the apparent solar time for the instant represented by date at longitude lon. The result uses a synthetic local-solar time zone derived from longitude.
func ApparentSolarTime(date time.Time, lon float64) time.Time {
func (module *Sun) ApparentSolarTime(date time.Time, lon float64) time.Time {
//真太阳时=太阳时角+12小时
trueTime := (HourAngle(date, lon, 0) + 180) / 15
trueTime := (module.HourAngle(date, lon, 0) + 180) / 15
if trueTime > 24 {
trueTime -= 24
}
@@ -548,12 +444,9 @@ func ApparentSolarTime(date time.Time, lon float64) time.Time {
time.FixedZone("LTZ", int(lon*3600.00/15.0)))
}
// ApparentSolarTimeN 截断项真太阳时 / truncated apparent solar time.
//
// 参数与 ApparentSolarTime 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。
// Uses the same inputs as ApparentSolarTime. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series.
func ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time {
trueTime := (HourAngleN(date, lon, 0, n) + 180) / 15
// ApparentSolarTimeN 真太阳时(n 变体) / n-parameterised apparent solar time.
func (module *Sun) ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time {
trueTime := (module.HourAngleN(date, lon, 0, n) + 180) / 15
if trueTime > 24 {
trueTime -= 24
}
@@ -569,3 +462,31 @@ func ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time {
int(trueTime), int(minute), int(second), int((second-math.Floor(second))*1000000000),
time.FixedZone("LTZ", int(lon*3600.00/15.0)))
}
// RiseTimeWithAtmosphere 日出时刻,按给定气压(hPa)与气温(°C)计算大气折射 / sunrise time with explicit pressure and temperature.
func (module *Sun) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return module.RiseTimeWithAtmosphere(d, lon, lat, height, pressureHPa, temperatureC)
}); handled {
return result, err
}
date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
localJD := basic.Date2JD(date)
_, loc := date.Zone()
jd, err := basic.GetSunRiseTimeWithAtmosphere(localJD, lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst)
return riseSetResult(date, jd, err)
}
// SetTimeWithAtmosphere 日落时刻,按给定气压(hPa)与气温(°C)计算大气折射 / sunset time with explicit pressure and temperature.
func (module *Sun) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) {
if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) {
return module.SetTimeWithAtmosphere(d, lon, lat, height, pressureHPa, temperatureC)
}); handled {
return result, err
}
date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
localJD := basic.Date2JD(date)
_, loc := date.Zone()
jd, err := basic.GetSunSetTimeWithAtmosphere(localJD, lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst)
return riseSetResult(date, jd, err)
}
+1 -1
View File
@@ -19,7 +19,7 @@ func TestSun(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if d.Format("2006-01-02 15:04:05") != "2020-01-01 08:41:20" {
if d.Format("2006-01-02 15:04:05") != "2020-01-01 08:41:21" {
t.Fatal(d.Format("2006-01-02 15:04:05"))
}
bo := TrueBo(now)