Files
astro/doc/manual/en/sun-moon.md
T
b612 16c62a97d5 feat: 完善时标与天象几何计算并扩展输出接口
- 新增时标、ΔT 模型、质心时间与 UT1 支持
- 改进日月食、月掩、行星事件及路径边界计算
- 完善恒星三维自行与动态距离传播
- 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具
- 整理中英文手册、示例资源及回归测试
2026-09-23 18:55:12 +08:00

50 KiB
Raw Blame History

Sun and Moon

中文 | Back to README

sun and moon are the main chains; lite/sun and lite/moon are independent approximation implementations.

Unless stated otherwise, angles are in degrees, apparent diameters and semidiameters are in arcseconds, sun.EarthDistance is in AU and moon.EarthDistance is in kilometers. Observing APIs generally use civil instants.

Apparent-solar-time results instead represent local solar readings; see Time scales.

This manual covers the Sun and the Moon themselves: position, rise/set and culmination, topocentric quantities, phases and syzygies, perigee/apogee and nodes, maximum declinations, libration, apparent size, and physical ephemeris.

Eclipse geometry lives in Solar and Lunar Eclipse Charts, occultations in Lunar Occultation Charts, and general topocentric and refraction conversions in Coordinate Tools.

Contents

Sunrise, sunset and lunar phase

package main

import (
	"fmt"
	"log"
	"time"

	"b612.me/astro/moon"
	"b612.me/astro/sun"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2020, 1, 1, 8, 8, 8, 0, cst)
	lon, lat, height := 108.93, 34.27, 0.0
	rise, err := sun.RiseTime(date, lon, lat, height, true) // sunrise time
	if err != nil {
		log.Fatal(err)
	}
	set, err := sun.SetTime(date, lon, lat, height, true) // sunset time
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(rise.Format(time.RFC3339), set.Format(time.RFC3339))
	fmt.Println(moon.Phase(date), moon.PhaseDesc(date)) // lunar phase (illuminated fraction) and phase description
}

aero=true uses a rise/set criterion with refraction. Rise/set functions return an error for polar conditions or a missing event on that date. moon.Phase is the illuminated fraction, not the lunar age.

API Reference

The tables below group the exported entry points of all four packages. Most evaluation entry points also have a ...N truncated variant, described together under Truncated ...N family.

Every time.Time parameter is an absolute instant, and lon/lat are east-positive and north-positive.

sun

Name Purpose Unit and convention
TrueLo / ApparentLo True / apparent solar longitude degrees, geocentric
TrueBo True solar latitude degrees, geocentric; no separate apparent latitude
GeometricLo / MidFunc Geometric solar longitude / equation of center degrees
ApparentRa / ApparentDec / ApparentRaDec Apparent right ascension / declination degrees, geocentric
EclipticObliquity Obliquity of the ecliptic degrees; the second argument adds nutation in obliquity when true
EclipticNutation / EclipticNutation1980 Nutation in longitude degrees, IAU 2000B / IAU 1980
AxialtiltNutation / AxialtiltNutation1980 Nutation in obliquity degrees, IAU 2000B / IAU 1980
RiseTime / SetTime Sunrise / sunset (time.Time, error); aero and height are described under Parameter and result conventions
DownTime Sunset alias deprecated; calls SetTime internally
CulminationTime Upper culmination time.Time
MorningTwilight / EveningTwilight Morning / evening twilight (time.Time, error); the angle is usually -6 / -12 / -18 degrees
Altitude / Zenith / Azimuth / HourAngle Geometric altitude / zenith distance / azimuth / hour angle degrees, topocentric
ApparentAltitude / ApparentZenith Apparent altitude / apparent zenith distance degrees; requires pressure in hPa and temperature in degrees Celsius
ParallacticAngle Parallactic angle (zenith direction angle) degrees, signed
ApparentSolarTime Apparent solar time time.Time, with the zone derived from longitude
EquationTime Equation of time hours
Diameter / Semidiameter Apparent diameter / semidiameter arcseconds
EarthDistance Earth-Sun distance AU
Physical Solar disk physical quantities returns PhysicalInfo, fields in degrees

moon

Name Purpose Unit and convention
TrueLo / TrueBo / ApparentLo Geocentric true longitude / true latitude / apparent longitude degrees
TrueRa / TrueDec / TrueRaDec Geocentric true equatorial coordinates degrees
GeocentricApparentRa / GeocentricApparentDec / GeocentricApparentRaDec Geocentric apparent equatorial coordinates degrees
ApparentRa / ApparentDec / ApparentRaDec Topocentric apparent equatorial coordinates degrees; requires observer longitude and latitude
Altitude / Zenith / Azimuth / HourAngle Topocentric altitude / zenith distance / azimuth / hour angle degrees
ApparentAltitude / ApparentZenith Apparent altitude / apparent zenith distance degrees; requires pressure in hPa and temperature in degrees Celsius
ParallacticAngle Parallactic angle degrees, signed, explicitly depends on observer longitude and latitude
RiseTime / SetTime Moonrise / moonset (time.Time, error)
DownTime Moonset alias deprecated; calls SetTime internally
CulminationTime Upper culmination time.Time; requires longitude and latitude
Phase / PhaseDesc Illuminated fraction / Chinese textual phase fraction [0,1] / string
SunMoonLoDiff Apparent Moon-Sun longitude difference degrees, [0,360)
ShuoYue / ShangXianYue / WangYue / XiaXianYue New / first-quarter / full / last-quarter moon solved near a decimal-year anchor time.Time, UTC
NewMoon / FullMoon / FirstQuarter / LastQuarter English aliases for the four phases above time.Time, UTC
Next* / Last* / Closest* Next / previous / closest phase and maximum-declination events time.Time; results keep the input time zone
NextConjunctionWithPlanet / LastConjunctionWithPlanet / ClosestConjunctionWithPlanet Moon-planet conjunction (in right ascension) time.Time; the target is a ConjunctionPlanet constant
PerigeesInMonth / ApogeesInMonth All perigees / apogees in a Gregorian month []ApsisInfo, distance in km
MaximumNorthDeclinationsInMonth / MaximumSouthDeclinationsInMonth All maximum northern / southern declination events in a month []MaximumDeclinationInfo, declination in degrees
AscendingNode / DescendingNode Ascending / descending node longitude degrees
Physical / TopocentricPhysical Geocentric / topocentric libration and rotation-axis position angle returns PhysicalInfo, fields in degrees
BrightLimbPositionAngle / TopocentricBrightLimbPositionAngle Geocentric / topocentric bright-limb position angle degrees
Diameter / Semidiameter Apparent diameter / semidiameter arcseconds
EarthDistance Earth-Moon distance kilometers

The moon package also exposes occultation APIs. Their parameters, results and examples are documented under Lunar occultations.

Group Exports
Events and paths FindStarOccultations, FindPlanetOccultations, FindBestStarOccultations, FindBestPlanetOccultations, FindStarOccultationPaths, FindPlanetOccultationPaths
Instant footprints and disk geometry StarOccultationFootprintAt, PlanetOccultationFootprintsAt, StarOccultationDiagram, PlanetOccultationDiagram
UT1 label conversion StarOccultationInfoInUT1, StarOccultationPathInUT1, PlanetOccultationInfoInUT1, PlanetOccultationPathInUT1
Stellar results StarOccultationInfo, StarOccultationPath, StarOccultationInstant
Planetary results PlanetOccultationInfo, PlanetOccultationPath, PlanetOccultationInstant, PlanetOccultationFootprint
Path data OccultationFootprint, OccultationPathPoint, OccultationGreatestTimeContour, OccultationRiseSetCurve
Search and path options OccultationSearchOptions, OccultationPathOptions, OccultationPathAlgorithm
Disk diagram data StarOccultationDiagramFrame, StarOccultationDiagramOptions, StarOccultationDiagramResult, PlanetOccultationDiagramFrame, PlanetOccultationDiagramOptions, PlanetOccultationDiagramResult
Targets and coordinates StarData, StarCoordinate, StarCoordinateFromStarData, Observer, CoordinateFrame, CoordinateFrameICRS, CoordinateFrameJ2000, CoordinateFrameApparentOfDate
Event types OccultationPlanet, OccultationType, OccultationTotal, OccultationPartial, OccultationGrazing
Path algorithms OccultationPathAlgorithmOptimized, OccultationPathAlgorithmExact
Rise/set phases RiseSetPhase, RiseSetDirection, RiseSetPhaseStart, RiseSetPhaseGreatest, RiseSetPhaseEnd, RiseSetDirectionRise, RiseSetDirectionSet
Errors ErrInvalidOccultationInput, ErrOccultationPathSamplingLimit

Planet targets use constants OccultationMercury through OccultationNeptune.

lite/sun

Name Purpose Unit and convention
TrueLo / ApparentLo Lightweight true / apparent longitude degrees, geocentric
TrueRa / TrueDec / TrueRaDec Lightweight true equatorial coordinates degrees, geocentric
ApparentRa / ApparentDec / ApparentRaDec Lightweight apparent equatorial coordinates degrees, geocentric
Distance Lightweight Earth-Sun distance AU
HourAngle / Azimuth / Altitude / Zenith Lightweight hour angle / azimuth / altitude / zenith distance degrees, topocentric
RiseTime / SetTime Lightweight sunrise / sunset (time.Time, error)
ERR_SUN_NEVER_RISE / ERR_SUN_NEVER_SET Polar night / polar day error values

lite/moon

Name Purpose Unit and convention
TrueLo / TrueBo Lightweight geocentric true longitude / latitude degrees
TrueRa / TrueDec / TrueRaDec Lightweight geocentric true equatorial coordinates degrees
ApparentRa / ApparentDec / ApparentRaDec Lightweight topocentric apparent equatorial coordinates degrees; requires observer longitude and latitude
HourAngle / Azimuth / Altitude / Zenith Lightweight hour angle / azimuth / altitude / zenith distance degrees, topocentric
SunMoonLoDiff / Phase / PhaseAge Lightweight Moon-Sun longitude difference / illuminated fraction / lunar age degrees / [0,1] / days
RiseTime / SetTime Lightweight moonrise / moonset (time.Time, error)
ERR_MOON_NEVER_RISE / ERR_MOON_NEVER_SET / ERR_NOT_TODAY Polar night / polar day / event not on the queried date error values

Truncated ...N family

Most evaluation entry points of sun and moon have a ...N variant: the name is the base name plus N, one extra n int parameter is appended, and the return shape is unchanged.

n < 0 keeps every analytical term embedded in this repository and matches the non-N version; n >= 0 truncates the series to n terms, which is useful for performance comparison, bulk coarse evaluation, and error-sensitivity experiments.

  • sun: TrueLoN, TrueBoN, AltitudeN, ZenithN, AzimuthN, HourAngleN, ParallacticAngleN, ApparentAltitudeN, ApparentZenithN, DiameterN, SemidiameterN, PhysicalN, RiseTimeN, SetTimeN, DownTimeN, CulminationTimeN, MorningTwilightN, EveningTwilightN, ApparentSolarTimeN
  • moon: TrueLoN, TrueBoN, AscendingNodeN, DescendingNodeN, DiameterN, SemidiameterN, PhysicalN, TopocentricPhysicalN, BrightLimbPositionAngleN, TopocentricBrightLimbPositionAngleN

The topocentric equatorial coordinates (ApparentRa / ApparentDec / ApparentRaDec), the lunar rise/set entry points (RiseTime / SetTime), and the phase family have no N variant and are not controlled by the truncation switch.

Usage examples

Today's sunrise, sunset and twilight

fmt.Println(sun.MorningTwilight(date, lon, lat, -6)) // civil dawn
fmt.Println(sun.RiseTime(date, lon, lat, height, true))
fmt.Println(sun.SetTime(date, lon, lat, height, true))
fmt.Println(sun.EveningTwilight(date, lon, lat, -6)) // civil dusk
2020-01-01 07:22:28.138198256 +0800 CST <nil>
2020-01-01 07:49:52.591398954 +0800 CST <nil>
2020-01-01 17:45:09.366609156 +0800 CST <nil>
2020-01-01 18:12:33.801986575 +0800 CST <nil>

Passing -12 / -18 instead selects nautical and astronomical twilight. With aero = true the rise/set solution uses the horizon corrected for refraction and apparent radius; the difference from the geometric horizon is described under Rise, set and culmination.

Moonrise, moonset and the Moon's altitude now

rise, _ := moon.RiseTime(date, lon, lat, height, true)
set, _ := moon.SetTime(date, lon, lat, height, true)
fmt.Println(rise)
fmt.Println(set)
fmt.Println(moon.Altitude(date, lon, lat), moon.Azimuth(date, lon, lat))
2020-01-01 11:52:50.042243599 +0800 CST
2020-01-01 23:26:49.498263895 +0800 CST
-45.349728852972675 67.63824603392399

Lunar rise and set are computed for the local civil day and the pair is not necessarily continuous, so the full cycle after date follows from the order of the rise and set instants; see Sunrise/sunset and moonrise/moonset for the details. A negative altitude means the Moon is below the horizon, so -45.35 degrees here means it is not visible.

Lunar phase and the next new / full moon

fmt.Println(moon.Phase(date), moon.PhaseDesc(date)) // illuminated fraction and phase name
fmt.Println(moon.NextShuoYue(date))                 // next new moon
fmt.Println(moon.NextWangYue(date))                 // next full moon
0.30004130960877884 上峨眉月
2020-01-25 05:41:58.271192908 +0800 CST
2020-01-11 03:21:17.159625291 +0800 CST

Next* / Last* / Closest* are the next / previous / closest search conventions; first and last quarter are moon.NextShangXianYue / moon.NextXiaXianYue. Results keep the input time zone; the full conventions for the four phases and the synodic month are under Lunar phases.

Apparent size, Earth-Moon distance and libration

fmt.Println(moon.Diameter(date), moon.EarthDistance(date))
p := moon.Physical(date)
fmt.Println(p.LibrationLongitude, p.LibrationLatitude, p.PositionAngle)
1774.6658461637385 404238.6096080479
0.7655535663486027 6.382898400777244 -23.672356410246774

Diameter is in arcseconds and EarthDistance in kilometers; 1774.67 arcseconds (about 29.6 arcminutes) corresponds to roughly 404,000 km near apogee, about 5% smaller than the mean apparent diameter.

Physical gives the geocentric libration; the topocentric counterpart TopocentricPhysical and the field conventions are under Libration and bright-limb position angle.

Geocentric vs topocentric (getting the Moon position right)

geoRa, geoDec := moon.GeocentricApparentRaDec(date) // geocentric apparent position
topRa, topDec := moon.ApparentRaDec(date, lon, lat) // topocentric apparent position
fmt.Printf("geocentric %.4f %.4f\n", geoRa, geoDec)
fmt.Printf("topocentric %.4f %.4f\n", topRa, topDec)
fmt.Printf("delta dRA=%.4f dDec=%.4f\n", topRa-geoRa, topDec-geoDec)
fmt.Println(sun.ApparentRaDec(date)) // the Sun only exposes geocentric coordinates
geocentric 349.2322 -9.9506
topocentric 349.7343 -10.3485
delta dRA=0.5021 dDec=-0.3978
280.8950939694744 -23.05840775453492

The Moon is close, so geocentric and topocentric positions differ by half a degree (here 0.50 degrees in right ascension and 0.40 in declination), together more than one lunar apparent diameter; anything an observer sees must go through moon.ApparentRaDec. The Sun, 1 AU away, has negligible parallax and is exposed geocentrically only (of course, you can also call the topocentric coordinate conversion entry points to get a topocentric position =-=). The conventions are in Observing-angle semantics.

Main chain vs lite

fmt.Println(sun.Altitude(date, lon, lat), litesun.Altitude(date, lon, lat)) // main / lightweight geometric altitude
fmt.Println(moon.Phase(date), litemoon.Phase(date))                         // illuminated fraction
fmt.Println(litemoon.PhaseAge(date))                                        // lightweight lunar age (days)
fmt.Println(litesun.RiseTime(date, lon, lat, height, true))                 // lightweight sunrise
2.40091496867759 2.403576774819768
0.30004130960877884 0.2978124633132848
5.42608394367707
2020-01-01 07:49:51.69717729 +0800 CST <nil>

The lightweight chains mirror the main-chain shapes but not its accuracy: at this instant the altitude differs by about 0.0027 degrees and the phase by 0.0022.

Over 2026 at 8 sites the mean absolute error of lite/sun sunrise is 0.02 min (P95 0.04 min, max 0.31 min), of lite/moon moonrise 0.28 min (P95 0.57 min, max 1.44 min), and lite/moon Phase() peaks at 0.00243.

The full comparison and the truncation errors are under Differences from the main chain and error levels and in Lite chains.

Observing-angle semantics

  • Altitude: altitude angle; horizon is 0°, zenith is +90°
  • Zenith: zenith distance; zenith is 0°, horizon is 90°
  • Zenith and Altitude are complements; the two add up to 90°
  • Azimuth: azimuth, measured from north (0°) toward east, in [0°, 360°)
  • HourAngle: hour angle, 0° at upper culmination and increasing westward (afternoon), normalized to [0°, 360°)
  • ParallacticAngle: parallactic angle (zenith direction angle), signed, in degrees; the shared sign convention and topocentric geometry live in Coordinate Tools
  • Altitude / Azimuth are geometric center altitude and azimuth without refraction or semidiameter correction; ApparentAltitude / ApparentZenith add atmospheric refraction and need pressure (hPa) and temperature (degrees Celsius)
  • Solar equatorial coordinates are geocentric; for the Moon, TrueRaDec and GeocentricApparentRaDec are geocentric while ApparentRa / ApparentDec / ApparentRaDec are topocentric and require observer longitude and latitude

Combined example: rise, set, and position

The two complete examples below cover the most common entry points. Their shared setup is Xi'an (108.93°E, 34.27°N) and 2020-01-01 08:08:08 CST.

The later snippets omit shared variables and keep only the statements relevant to their capability.

Sunrise/sunset and moonrise/moonset

⚠️ Moon rise/set times are computed for the queried civil date, so the rise and set instants need not be continuous.

For example, the Moon may set at 01:00 and rise again at noon, in which case the rise time is later than the set time; the evening moonset in that scenario corresponds to the next day's date.

The full rise/set cycle follows from the order of the two instants: check whether the rise time falls after the set time to pick the correct subsequent instants.

package main

import (
	"fmt"
	"time"

	"b612.me/astro/moon"
	"b612.me/astro/sun"
)

func main() {
	// Xi'an, China. Longitude east and latitude north are positive; elevation is 0 m.
	var lon, lat, height float64 = 108.93, 34.27, 0
	cst := time.FixedZone("CST", 8*3600)
	// All "today" semantics are based on this local civil date.
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)
	// Civil morning twilight begins when the Sun is 6 degrees below the horizon.
	// Civil twilight is 6 degrees below the horizon, nautical 12, astronomical 18.
	fmt.Println(sun.MorningTwilight(date, lon, lat, -6))
	// Sunrise: dynamic standard refraction and instantaneous solar semidiameter, upper limb.
	fmt.Println(sun.RiseTime(date, lon, lat, height, true))
	// Upper culmination of the Sun in Xi'an.
	fmt.Println(sun.CulminationTime(date, lon))
	// Sunset: dynamic standard refraction and instantaneous solar semidiameter, upper limb.
	fmt.Println(sun.SetTime(date, lon, lat, height, true))
	// Civil evening twilight ends when the Sun is 6 degrees below the horizon.
	fmt.Println(sun.EveningTwilight(date, lon, lat, -6))

	// Moonrise: dynamic standard refraction and instantaneous lunar semidiameter, upper limb.
	fmt.Println(moon.RiseTime(date, lon, lat, height, true))
	// Upper culmination of the Moon in Xi'an.
	fmt.Println(moon.CulminationTime(date, lon, lat))
	// Moonset: dynamic standard refraction and instantaneous lunar semidiameter, upper limb.
	fmt.Println(moon.SetTime(date, lon, lat, height, true))
}

Output:

2020-01-01 07:22:27.960488498 +0800 CST <nil>
2020-01-01 07:49:52.413689196 +0800 CST <nil>
2020-01-01 12:47:35.933117866 +0800 CST
2020-01-01 17:45:09.188657999 +0800 CST <nil>
2020-01-01 18:12:33.624035418 +0800 CST <nil>
2020-01-01 11:52:49.860912859 +0800 CST <nil>
2020-01-01 17:36:48.811488747 +0800 CST
2020-01-01 23:26:49.313553571 +0800 CST <nil>

Sun and Moon position

package main

import (
	"fmt"
	"time"

	"b612.me/astro/moon"
	"b612.me/astro/star"
	"b612.me/astro/sun"
	"b612.me/astro/tools"
)

func main() {
	// Xi'an, China.
	var lon, lat float64 = 108.93, 34.27
	cst := time.FixedZone("CST", 8*3600)
	// Instant of observation.
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)
	// Apparent ecliptic longitude of the Sun, in degrees.
	fmt.Println(sun.ApparentLo(date))
	// True obliquity of the ecliptic at this instant.
	fmt.Println(sun.EclipticObliquity(date, true))
	// Apparent right ascension and declination of the Sun.
	ra, dec := sun.ApparentRaDec(date)
	fmt.Println("RA:", tools.Format(ra/15, 1), "Dec:", tools.Format(dec, 0))
	// English constellation containing the Sun.
	fmt.Println(star.ConstellationEN(ra, dec, date))
	// Solar azimuth, altitude, and zenith distance at Xi'an.
	fmt.Println("Azimuth:", sun.Azimuth(date, lon, lat), "Altitude:", sun.Altitude(date, lon, lat), "Zenith:", sun.Zenith(date, lon, lat))
	// Sun-Earth distance, in AU.
	fmt.Println(sun.EarthDistance(date))

	// Topocentric apparent right ascension and declination of the Moon.
	ra, dec = moon.ApparentRaDec(date, lon, lat)
	fmt.Println("RA:", tools.Format(ra/15, 1), "Dec:", tools.Format(dec, 0))
	// English constellation containing the Moon.
	fmt.Println(star.ConstellationEN(ra, dec, date))
	// Lunar azimuth, altitude, and zenith distance at Xi'an.
	fmt.Println("Azimuth:", moon.Azimuth(date, lon, lat), "Altitude:", moon.Altitude(date, lon, lat), "Zenith:", moon.Zenith(date, lon, lat))
	// Earth-Moon distance, in km.
	fmt.Println(moon.EarthDistance(date))
}

Output:

280.01526210031136
23.4362178391013
RA: 18h43m34.82s Dec: -23°3′30.27″
Sagittarius
Azimuth: 120.19477090015224 Altitude: 2.4014437419430097 Zenith: 87.59855625805699
0.983292937163176
RA: 23h18m56.24s Dec: -10°20′54.42″
Aquarius
Azimuth: 67.63889332004852 Altitude: -45.34916937173283 Zenith: 135.34916937173284
404238.6096080479

The Sun

These snippets omit the shared setup: cst := time.FixedZone("CST", 8*3600), date := time.Date(2026, 1, 1, 12, 0, 0, 0, cst), and var lon, lat float64 = 108.93, 34.27 (Xi'an); fmt, time, sun, and moon are assumed to be imported.

Position

Solar position entry points depend only on the absolute instant, not on the observer. True ecliptic latitude comes from TrueBo; there is no separate apparent latitude.

The second argument of EclipticObliquity decides whether nutation in obliquity is added.

// True and apparent solar longitude, and true latitude, in degrees.
fmt.Println(sun.TrueLo(date), sun.ApparentLo(date), sun.TrueBo(date))
// Apparent right ascension and declination of the Sun.
ra, dec := sun.ApparentRaDec(date)
fmt.Println("RA:", ra, "Dec:", dec)
fmt.Println(sun.ApparentRa(date), sun.ApparentDec(date))
// Obliquity of the ecliptic, geometric longitude, and equation of center.
fmt.Println(sun.EclipticObliquity(date, true), sun.GeometricLo(date), sun.MidFunc(date))

Output:

280.742671383543 280.7383965677222 0.00018280886212040676
RA: 281.6786097810291 Dec: -23.00369182413533
281.6786097810291 -23.00387403948847
23.438148552330773 280.83169623098 -0.08703499790144194

Every entry point in this family also has a ...N truncated variant. The comparison below truncates at n = 8; with n < 0 the result matches the non-N version exactly:

// n<0 keeps every embedded VSOP term; n>=0 truncates the series.
fmt.Println(sun.TrueLo(date), sun.TrueLoN(date, 8))
fmt.Println(sun.Altitude(date, lon, lat), sun.AltitudeN(date, lon, lat, 8))
fmt.Println(sun.Diameter(date), sun.DiameterN(date, 8))

Output:

280.742671383543 280.7439900413756
31.61569462953789 31.615524473491835
1950.9979407481142 1950.9994358395434

Rise, set and culmination

RiseTime / SetTime anchor on the local civil day of the time zone carried by date and keep the same time zone in the result. height is the observer elevation interpreted as ellipsoidal (geodetic) height in meters.

With aero = true, the upper limb crossing is computed with dynamic standard refraction and the instantaneous semidiameter; with aero = false, only the geometric center crossing is tested.

Twilight entry points take the target altitude as a parameter: civil twilight -6°, nautical -12°, astronomical -18°, with MorningTwilight and EveningTwilight for the two sides.

// Target altitudes for civil, nautical, and astronomical morning twilight.
for _, angle := range []float64{-6, -12, -18} {
	t, err := sun.MorningTwilight(date, lon, lat, angle)
	fmt.Println(angle, t.Format("15:04:05"), err)
}
// Upper culmination and sunrise; err is non-nil when no event exists.
fmt.Println(sun.CulminationTime(date, lon).Format("15:04:05"))
t, err := sun.RiseTime(date, lon, lat, 0, true)
fmt.Println(t.Format("15:04:05"), err)

Output:

-6 07:22:47 <nil>
-12 06:51:31 <nil>
-18 06:21:00 <nil>
12:47:50
07:50:10 <nil>

Topocentric quantities and parallactic angle

Altitude / Zenith / Azimuth / HourAngle follow the geometric chain without refraction; ApparentAltitude / ApparentZenith add atmospheric refraction and need pressure and temperature; ParallacticAngle is the signed parallactic angle. General topocentric and refraction conversions (including apparent altitude and topocentric equatorial coordinates) live in Coordinate Tools.

fmt.Println(sun.Azimuth(date, lon, lat), sun.Altitude(date, lon, lat), sun.Zenith(date, lon, lat))
fmt.Println(sun.ApparentAltitude(date, lon, lat, 1010, 10), sun.ApparentZenith(date, lon, lat, 1010, 10))
// Hour angle and signed parallactic angle.
fmt.Println(sun.HourAngle(date, lon, lat), sun.ParallacticAngle(date, lon, lat))

Output:

167.09774780715728 31.61569462953789 58.38430537046211
31.643010360459822 58.356989639540174
348.07820699607544 -11.564174033740159

Apparent solar time and equation of time

ApparentSolarTime returns the apparent solar time at a longitude; the result uses a fixed-offset time zone derived from that longitude, not the zone passed by the caller.

EquationTime returns the equation of time at the same instant, in hours. sundial.TrueSolarTime uses the same convention, and sundial additionally provides local mean solar time and sundial geometry; see Sundial and Apparent Solar Time.

// Apparent solar time; the result time zone is derived from longitude.
fmt.Println(sun.ApparentSolarTime(date, lon).Format("2006-01-02 15:04:05 -0700"))
// Equation of time, in hours.
fmt.Println(sun.EquationTime(date))
// sundial.TrueSolarTime uses the same convention.
fmt.Println(sundial.TrueSolarTime(date, lon).Format("2006-01-02 15:04:05 -0700"))

Output:

2026-01-01 11:12:18 +0715
-0.05674946079069686
2026-01-01 11:12:18 +0715

Physical ephemeris and apparent size

sun.Physical returns PhysicalInfo: P is the position angle of the solar north pole, B0 is the heliographic latitude of the disk center, and L0 is the Carrington heliographic longitude of the disk center, all in degrees.

Diameter / Semidiameter give the apparent diameter and semidiameter in arcseconds; EarthDistance gives the Earth-Sun distance in AU.

// Apparent diameter and semidiameter (arcseconds), and Earth-Sun distance (AU).
fmt.Println(sun.Diameter(date), sun.Semidiameter(date), sun.EarthDistance(date))
// Solar physical quantities P/B0/L0, in degrees.
p := sun.Physical(date)
fmt.Println(p.P, p.B0, p.L0)

Output:

1950.9979407481142 975.4989703740571 0.9833237486528845
1.979086377118846 -3.0131029209723916 296.9595333604375

The Sun, Moon, and seven major planets share the same interface shapes, so they can be compared side by side:

fmt.Println(sun.Diameter(date), sun.Semidiameter(date))
fmt.Println(sun.Physical(date))
fmt.Println(moon.Diameter(date), moon.Semidiameter(date))
fmt.Println(mars.Diameter(date), mars.Semidiameter(date))

Earth orbit extrema

The extrema of the Earth-Sun distance come from the earth package, with UTC times and AU distances; for the orbital eccentricity at one instant, call EarthEccentricity directly:

// Earth perihelion and aphelion in 2026; time is UTC, distance is AU.
peri := earth.Perihelion(2026)
aphe := earth.Aphelion(2026)
fmt.Printf("earth perihelion=%s distance=%.9fAU\n", peri.Time.Format(time.RFC3339), peri.Distance)
fmt.Printf("earth aphelion=%s distance=%.9fAU\n", aphe.Time.Format(time.RFC3339), aphe.Distance)

Output:

earth perihelion=2026-01-03T17:15:35Z distance=0.983302050AU
earth aphelion=2026-07-06T17:31:24Z distance=1.016643936AU
fmt.Printf("earth e=%.9f\n", earth.EarthEccentricity(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)))

The Moon

These snippets reuse the shared setup of the solar section: date is still 2026-01-01 12:00:00 CST and the observer is still in Xi'an.

Position

Lunar equatorial coordinates come in three layers: TrueRaDec is the geocentric true place, GeocentricApparentRaDec is the geocentric apparent place, and ApparentRaDec is the topocentric apparent place.

On the ecliptic side only geocentric quantities exist: TrueLo true longitude, TrueBo true latitude, and ApparentLo apparent longitude.

// Geocentric true equatorial coordinates.
fmt.Println(moon.TrueRaDec(date))
// Geocentric apparent equatorial coordinates.
fmt.Println(moon.GeocentricApparentRaDec(date))
// Topocentric apparent equatorial coordinates.
fmt.Println(moon.ApparentRaDec(date, lon, lat))
// True longitude, true latitude, and apparent longitude.
fmt.Println(moon.TrueLo(date), moon.TrueBo(date), moon.ApparentLo(date))

Output:

66.66309709020791 26.830370234236764
66.66476688311091 26.830608930005567
67.0275694157259 25.982665403390747
69.21422925147913 5.060516750865828 69.21574418700706

Rise, set and culmination

Lunar rise/set, like the solar one, anchors on the local civil day: RiseTime / SetTime return that day's event instants and CulminationTime returns that day's upper culmination.

Successive lunar rises and sets need not be continuous, and the full cycle after date follows from the order of the two instants. When an event falls outside the queried date, the rise/set entry points return ERR_NOT_TODAY.

// Moonrise, upper culmination, and moonset on the local civil day.
rise, err := moon.RiseTime(date, lon, lat, 0, true)
fmt.Println(rise.Format("15:04:05"), err)
fmt.Println(moon.CulminationTime(date, lon, lat).Format("15:04:05"))
set, err := moon.SetTime(date, lon, lat, 0, true)
fmt.Println(set.Format("15:04:05"), err)

Output:

16:17:11 <nil>
00:03:16
06:41:37 <nil>

Lunar phases

Phase returns the illuminated fraction in [0,1], PhaseDesc returns a Chinese phase name, and SunMoonLoDiff returns the apparent Moon-Sun longitude difference normalized to [0,360) (near 0° at new moon and near 180° at full moon).

Next* / Last* / Closest* are the three search conventions, and results keep the input time zone.

Each of the four phases has both a pinyin name and an English alias, for example ShuoYue / NewMoon, WangYue / FullMoon, ShangXianYue / FirstQuarter, and XiaXianYue / LastQuarter.

package main

import (
	"fmt"
	"time"

	"b612.me/astro/moon"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	// Instant of observation.
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)
	// Illuminated fraction of the lunar disk.
	fmt.Println(moon.Phase(date))
	// Chinese textual phase description.
	fmt.Println(moon.PhaseDesc(date))
	// Next new moon; moon.NextNewMoon(date) is the English alias.
	fmt.Println(moon.NextShuoYue(date))
	// Next first quarter; moon.NextFirstQuarter(date) is the English alias.
	fmt.Println(moon.NextShangXianYue(date))
	// Next full moon; moon.NextFullMoon(date) is the English alias.
	fmt.Println(moon.NextWangYue(date))
	// Next last quarter; moon.NextLastQuarter(date) is the English alias.
	fmt.Println(moon.NextXiaXianYue(date))
}

Output:

0.30004130960877884 // about 30% of the lunar disk is illuminated
上峨眉月 // Chinese phase description
2020-01-25 05:41:58.271192908 +0800 CST // next new moon
2020-01-03 12:45:23.229190707 +0800 CST // next first quarter
2020-01-11 03:21:17.159625291 +0800 CST // next full moon
2020-01-17 20:58:23.396406769 +0800 CST // next last quarter

Last*, Closest*, and the decimal-year anchors ShuoYue / FullMoon return UTC. ClosestConjunctionWithPlanet finds the closest Moon-planet conjunction, with the target given by a ConjunctionPlanet constant:

// Previous and closest new moon and full moon.
fmt.Println(moon.LastShuoYue(date), moon.ClosestShuoYue(date))
fmt.Println(moon.LastWangYue(date), moon.ClosestWangYue(date))
// First and last quarter.
fmt.Println(moon.LastFirstQuarter(date), moon.ClosestLastQuarter(date))
// Decimal-year anchors; the results are UTC.
fmt.Println(moon.ShuoYue(2025.5).Format(time.RFC3339), moon.FullMoon(2025.5).Format(time.RFC3339))
// Closest Moon-planet conjunction in right ascension.
fmt.Println(moon.ClosestConjunctionWithPlanet(date, moon.ConjunctionJupiter))

Output:

2025-12-20 09:43:19.074603617 +0800 CST 2025-12-20 09:43:19.074603617 +0800 CST
2025-12-05 07:14:03.670351803 +0800 CST 2026-01-03 18:02:53.55531156 +0800 CST
2025-12-28 03:09:50.141303837 +0800 CST 2026-01-10 23:48:22.03346461 +0800 CST
2025-06-25T10:31:35Z 2025-07-10T20:36:46Z
2026-01-04 05:59:19.9425897 +0800 CST

Perigee and apogee

PerigeesInMonth / ApogeesInMonth return every perigee and apogee event in a Gregorian month. Each element is an ApsisInfo with Time (UTC) and Distance (km); a month may contain zero, one, or several events.

// Lunar perigee and apogee in January 2026; distance is in km.
perigees := moon.PerigeesInMonth(2026, time.January)
apogees := moon.ApogeesInMonth(2026, time.January)
fmt.Printf("moon perigee=%s distance=%.1fkm count=%d\n", perigees[0].Time.Format(time.RFC3339), perigees[0].Distance, len(perigees))
fmt.Printf("moon apogee=%s distance=%.1fkm count=%d\n", apogees[0].Time.Format(time.RFC3339), apogees[0].Distance, len(apogees))

Output:

moon perigee=2026-01-01T21:44:24Z distance=360348.1km count=2
moon apogee=2026-01-13T20:47:13Z distance=405437.9km count=1

Nodes

The Moon also exposes ascending-node and descending-node longitudes, which are useful for eclipse seasons, orbital geometry, and lunar-orbit studies:

nodeDate := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
fmt.Println(moon.AscendingNode(nodeDate), moon.DescendingNode(nodeDate))

The ascending / descending node definitions here are the same as in the planets chapter:

  • AscendingNode: ecliptic longitude where the Moon crosses from south of the ecliptic to north of it
  • DescendingNode: ecliptic longitude where the Moon crosses from north of the ecliptic to south of it
  • both values are degrees, and are usually about 180° apart at the same instant

For the nodeDate := 2026-01-01 00:00:00 UTC example above, the output is:

340.95708624505863 160.9570862450587

Maximum declinations

Lunar declination reaches northern and southern extrema within one nodal month. MaximumDeclinationInfo carries Time (the event instant) and Declination (the geocentric declination at that instant, in degrees).

The monthly entry points return every event in the month, while Next* / Last* / Closest* search by instant:

// Closest maximum northern and previous maximum southern declination.
north := moon.ClosestMaximumNorthDeclination(date)
south := moon.LastMaximumSouthDeclination(date)
fmt.Println(north.Time.Format(time.RFC3339), north.Declination)
fmt.Println(south.Time.Format(time.RFC3339), south.Declination)
// Next maximum northern declination and all events in the month.
fmt.Println(moon.NextMaximumNorthDeclination(date).Time.Format(time.RFC3339))
events := moon.MaximumNorthDeclinationsInMonth(2026, time.January)
fmt.Println(len(events))
for _, event := range events {
	fmt.Println(event.Time.Format(time.RFC3339), event.Declination)
}

Output:

2026-01-02T16:10:49+08:00 28.266373428242343
2025-12-20T07:06:57+08:00 -28.23514705130737
2026-01-02T16:10:49+08:00
2 2026-01-02T08:10:49Z 28.266373428242343

The monthly-list convention for the same events looks like this (input 2026-01-01 00:00:00 UTC):

// Maximum northern and southern lunar declinations in January 2026.
north := moon.MaximumNorthDeclinationsInMonth(2026, time.January)
south := moon.MaximumSouthDeclinationsInMonth(2026, time.January)
fmt.Printf("north=%s dec=%.6f\n", north[0].Time.Format(time.RFC3339), north[0].Declination)
fmt.Printf("south=%s dec=%.6f\n", south[0].Time.Format(time.RFC3339), south[0].Declination)

Output:

north=2026-01-02T08:10:49Z dec=28.266373
south=2026-01-16T05:15:14Z dec=-28.304184

Libration and bright-limb position angle

Physical returns the geocentric libration and TopocentricPhysical the topocentric libration.

Both are PhysicalInfo, carrying the optical, physical, and total libration components plus the rotation-axis position angle PositionAngle, in degrees.

The bright-limb position angle starts at 0° at the lunar north point and increases eastward.

// Geocentric libration and rotation-axis position angle.
p := moon.Physical(date)
fmt.Println(p.LibrationLongitude, p.LibrationLatitude, p.PositionAngle)
// Topocentric libration and rotation-axis position angle.
topo := moon.TopocentricPhysical(date, lon, lat, 0)
fmt.Println(topo.LibrationLongitude, topo.LibrationLatitude, topo.PositionAngle)
// Geocentric and topocentric bright-limb position angles.
fmt.Println(moon.BrightLimbPositionAngle(date), moon.TopocentricBrightLimbPositionAngle(date, lon, lat, 0))

Output:

-0.9680924808747591 -6.547834757841939 -9.025022841390472
-0.7780085060449551 -5.659649431558411 -8.883877708625544
269.08333384819935 267.8559531949645

A complete topocentric example (Shanghai, height = 4 m):

// Lunar libration and rotation-axis position angle.
physical := moon.Physical(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
fmt.Printf("libration lon=%.6f lat=%.6f pa=%.6f\n", physical.LibrationLongitude, physical.LibrationLatitude, physical.PositionAngle)
// Bright-limb position angle; 0 degrees starts at the lunar north point and increases eastward.
fmt.Printf("bright limb=%.6f\n", moon.BrightLimbPositionAngle(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)))
// Topocentric libration, rotation-axis position angle, and bright-limb angle from Shanghai.
topo := moon.TopocentricPhysical(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), 121.4737, 31.2304, 4)
fmt.Printf("topo libration lon=%.6f lat=%.6f pa=%.6f\n", topo.LibrationLongitude, topo.LibrationLatitude, topo.PositionAngle)
fmt.Printf("topo bright limb=%.6f\n", moon.TopocentricBrightLimbPositionAngle(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), 121.4737, 31.2304, 4))

Output:

libration lon=-1.278902 lat=-6.531444 pa=-9.967050
bright limb=267.364849
topo libration lon=-1.736754 lat=-5.780730 pa=-10.072846
topo bright limb=266.038258

Apparent size and Earth-Moon distance

Diameter / Semidiameter give the apparent diameter and semidiameter in arcseconds, and EarthDistance gives the Earth-Moon distance in kilometers. All three depend only on the absolute instant:

// Apparent diameter and semidiameter (arcseconds), and Earth-Moon distance (km).
fmt.Println(moon.Diameter(date), moon.Semidiameter(date), moon.EarthDistance(date))

Output:

1986.4975069969655 993.2487534984828 360488.4234539985

Lite chains

lite/sun and lite/moon are independent approximation implementations: they do not depend on VSOP87 or the main chain's ELP2000/82 series, they target CPU- and memory-constrained environments, and their calling style matches the main chain.

Rise/set search uses fixed-step scanning plus bisection, without the main chain's high-precision nutation iteration.

package main

import (
	"fmt"
	"time"

	litemoon "b612.me/astro/lite/moon"
	litesun "b612.me/astro/lite/sun"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2026, 1, 1, 20, 0, 0, 0, cst)

	fmt.Println(litesun.Altitude(date, 121.4737, 31.2304))
	fmt.Println(litesun.RiseTime(date, 121.4737, 31.2304, 0, true))

	fmt.Println(litemoon.Phase(date))
	fmt.Println(litemoon.PhaseAge(date))
	fmt.Println(litemoon.RiseTime(date, 121.4737, 31.2304, 0, true))
}

The snippets below reuse this section's shared setup: Shanghai (121.4737°E, 31.2304°N) and 2026-01-01 20:00:00 CST.

lite/sun

The lightweight solar chain provides longitude, equatorial coordinates, distance, horizontal coordinates, and rise/set, but no apparent size, solar disk physical quantities, or twilight entry points.

Distance is the Earth-Sun distance in AU; note that the main chain names this capability EarthDistance while the lite chain names it Distance.

// Lightweight true and apparent solar longitude, and Earth-Sun distance (AU).
fmt.Println(litesun.TrueLo(date), litesun.ApparentLo(date), litesun.Distance(date))
// Lightweight apparent right ascension and declination.
ra, dec := litesun.ApparentRaDec(date)
fmt.Println(ra, dec)
// Lightweight hour angle, azimuth, altitude, and zenith distance.
fmt.Println(litesun.HourAngle(date, 121.4737, 31.2304), litesun.Azimuth(date, 121.4737, 31.2304), litesun.Altitude(date, 121.4737, 31.2304), litesun.Zenith(date, 121.4737, 31.2304))
// Lightweight rise and set.
fmt.Println(litesun.RiseTime(date, 121.4737, 31.2304, 0, true))
fmt.Println(litesun.SetTime(date, 121.4737, 31.2304, 0, true))

Output:

281.0835889076667 281.0793650422643 0.9833163427233701
282.0475744673639 -22.973803828458102
120.58011725187828 263.4579582386061 -37.07676039520773 127.07676039520773
2026-01-01 06:52:24.6475178 +0800 CST <nil>
2026-01-01 17:02:44.014452695 +0800 CST <nil>

lite/moon

The lightweight lunar chain provides a few-perturbation-term lunar position, light topocentric correction, phase and age, and rise/set, but no libration, apparent size, Earth-Moon distance, or nodes.

PhaseAge returns the lunar age in days and exists only in the lite chain:

// Lightweight true longitude and latitude.
fmt.Println(litemoon.TrueLo(date), litemoon.TrueBo(date))
// Lightweight geocentric true equatorial coordinates.
ra, dec := litemoon.TrueRaDec(date)
fmt.Println(ra, dec)
// Lightweight topocentric apparent equatorial coordinates.
fmt.Println(litemoon.ApparentRaDec(date, 121.4737, 31.2304))
// Moon-Sun longitude difference, illuminated fraction, and lunar age.
fmt.Println(litemoon.SunMoonLoDiff(date), litemoon.Phase(date), litemoon.PhaseAge(date))
// Lightweight horizontal coordinates.
fmt.Println(litemoon.Altitude(date, 121.4737, 31.2304), litemoon.Azimuth(date, 121.4737, 31.2304), litemoon.Zenith(date, 121.4737, 31.2304))

Output:

74.25630740893 5.078407838127742
72.2518040645064 27.549605139478064
72.74256784654426 27.432483413326057
153.17694236666568 0.9462021494002484 12.565014741082448
63.55513206820331 90.53047230027812 26.444867931796693

Differences from the main chain and error levels

The lite chains differ from the main chain in implementation and accuracy while keeping nearly the same interface shapes.

Pure evaluation entry points such as position and phase run about 8.3-27.3x faster than the main chain, and rise/set entry points about 1.0-3.7x, with zero heap allocation in the computation path; the rise/set scan step is 30 minutes for lite/sun and 15 minutes for lite/moon.

Errors against sun / moon (year 2026, 8 sites) are listed below, with data from Lite lightweight chains:

Capability Mean absolute error P95 Max absolute error
lite/sun sunrise 0.02 min 0.04 min 0.31 min
lite/sun sunset 0.02 min 0.06 min 0.35 min
lite/moon moonrise 0.28 min 0.57 min 1.44 min
lite/moon moonset 0.36 min 0.86 min 1.24 min
lite/moon Phase() 0.00089 0.00185 0.00243
lite/moon PhaseAge() 0.003 d 0.010 d 0.014 d
lite/moon geocentric longitude 2.41' 6.82' 9.91'
lite/moon geocentric latitude 0.87' 1.83' 2.92'

Neither package provides a ...N truncated family. On the solar side the polar-night / polar-day errors are ERR_SUN_NEVER_RISE / ERR_SUN_NEVER_SET; on the lunar side, besides ERR_MOON_NEVER_RISE / ERR_MOON_NEVER_SET, there is ERR_NOT_TODAY, with the same semantics as the main chain.

Parameter and result conventions

Units and angle conventions

  • Angles are always in degrees; RA, Lon, and Azimuth are normalized to [0°, 360°), while declination and ecliptic latitude lie in [−90°, 90°]
  • Apparent diameters and semidiameters are in arcseconds; sun.EarthDistance is AU and moon.EarthDistance is kilometers
  • Phase is the illuminated fraction in [0,1], PhaseAge (lite chain) is in days, and EquationTime is in hours
  • Distance extrema: earth.Perihelion / earth.Aphelion are AU, and ApsisInfo.Distance is kilometers

Time scale

Observing inputs and event times use the civil-time convention. Before 1972-01-01, the library treats those readings as UT1. ApparentSolarTime returns a local solar clock reading rather than another civil event time.

The time zone carried by a time.Time only affects the division of local civil days and the zone of the returned value, never the absolute instant.

The full UT1 and chart-label declaration is in the Time Scale Declaration.

Height and aero

  • The height of the rise/set entry points is the observer elevation interpreted as ellipsoidal (geodetic) height in meters, not orthometric height; see Observer Height Convention
  • aero = true: upper-limb crossing with dynamic standard refraction and the instantaneous semidiameter; aero = false: only the geometric center crossing
  • pressureHPa and temperatureC of ApparentAltitude / ApparentZenith are the observed pressure (hPa) and temperature (degrees Celsius)

Zero values and out-of-range

  • During polar night the rise/set entry points return ERR_SUN_NEVER_RISE / ERR_MOON_NEVER_RISE; during polar day they return ERR_SUN_NEVER_SET / ERR_MOON_NEVER_SET, with a zero time.Time as the first return value
  • When no twilight exists, they return ERR_TWILIGHT_NOT_EXISTS
  • When a lunar rise/set event falls outside the queried date, they return ERR_NOT_TODAY, matching the "moonrise/moonset are computed for the queried civil date" semantics above
  • DownTime / DownTimeN are deprecated aliases of SetTime, and ERR_SUN_NEVER_DOWN / ERR_MOON_NEVER_DOWN are deprecated aliases of the polar-day errors; new code should not use them
  • In ...N, n < 0 uses every embedded term and n >= 0 truncates; n only changes the series length, never the returned unit or time zone

Accuracy and scope

The Sun and planets use embedded VSOP87 analytical terms and the Moon uses an embedded ELP2000/82-style truncated series, covering roughly 4000 years around J2000 with no external ephemeris files.

Truncation errors, the lunar chain's capability boundaries, and the lite chains' quantified errors are documented in Scope And Accuracy.

The solar and lunar entry points in this manual suit calendars, observing aids, outreach, and amateur prediction; spacecraft navigation, precise occultation prediction, and rigorous dynamical integration require a professional ephemeris such as JPL DE.