Skip to main content

Sun Module

The Sun module provides calculations for solar position, rise/set times, twilight, and related phenomena. These are fundamental for planning observations and understanding seasonal changes.

Overview​

The Sun is not only our nearest star but also the fundamental timekeeping reference for astronomy. This module implements low-precision algorithms suitable for:

  • Sunrise and sunset calculations
  • Twilight (civil, nautical, astronomical)
  • Solar noon determination
  • Day length calculation
  • Equation of time

Accuracy: Position ~0.01° in ecliptic longitude, times within ~1 minute.

Quick Start​

CLI Usage​

# Current solar position
starward sun position

# Sunrise for today at a location
starward sun rise --lat 51.5 --lon -0.1

# Sunset
starward sun set --lat 51.5 --lon -0.1

# Civil twilight times
starward sun twilight civil --lat 51.5 --lon -0.1

# Solar altitude at current time
starward sun altitude --lat 51.5 --lon -0.1

Python API​

from starward.core.sun import (
sun_position, sunrise, sunset, solar_noon,
civil_twilight, nautical_twilight, astronomical_twilight,
solar_altitude, day_length
)
from starward.core.observer import Observer
from starward.core.time import jd_now

# Create an observer
greenwich = Observer.from_degrees("Greenwich", 51.4772, -0.0005)

# Get current solar position
jd = jd_now()
pos = sun_position(jd)
print(f"Sun RA: {pos.ra.hours:.2f}h, Dec: {pos.dec.degrees:.1f}°")
print(f"Distance: {pos.distance_au:.4f} AU")

# Sunrise and sunset
rise = sunrise(greenwich, jd)
set_t = sunset(greenwich, jd)
print(f"Sunrise: {rise.to_datetime()}")
print(f"Sunset: {set_t.to_datetime()}")

# Twilight times
morning_twi, evening_twi = civil_twilight(greenwich, jd)

# Day length
hours = day_length(greenwich, jd)
print(f"Day length: {hours:.1f} hours")

Solar Position​

The sun_position() function returns a SunPosition object containing:

FieldDescription
longitudeEcliptic longitude (Angle)
latitudeEcliptic latitude (Angle, always ~0)
raRight Ascension (Angle)
decDeclination (Angle)
distance_auDistance in AU
equation_of_timeEquation of time in minutes

Equation of Time​

The equation of time (EoT) is the difference between apparent solar time and mean solar time. It varies from about -14 minutes to +16 minutes throughout the year.

pos = sun_position(jd)
eot = pos.equation_of_time # in minutes

Sunrise and Sunset​

Standard definitions use the moment when the Sun's upper limb touches the horizon, accounting for atmospheric refraction.

# Default: standard refraction (34 arcminutes)
rise = sunrise(observer, jd)

# Custom horizon (e.g., elevated observer)
rise = sunrise(observer, jd, horizon=-0.5) # 0.5° below geometric horizon

Edge Cases​

  • Polar day: No sunrise/sunset during midnight sun (returns None)
  • Polar night: No sunrise/sunset during polar night (returns None)

Twilight​

Three types of twilight are defined by how far the Sun is below the horizon:

TypeSun altitudeDescription
Civil0° to -6°Enough light for outdoor activities
Nautical-6° to -12°Horizon visible at sea
Astronomical-12° to -18°Sky fully dark for observations
# Each returns (morning_twilight, evening_twilight)
civil_morning, civil_evening = civil_twilight(observer, jd)
naut_morning, naut_evening = nautical_twilight(observer, jd)
astro_morning, astro_evening = astronomical_twilight(observer, jd)

Solar Altitude​

Calculate the Sun's altitude at any time:

alt = solar_altitude(observer, jd)
print(f"Sun altitude: {alt.degrees:.1f}°")

# Check if Sun is up
if alt.degrees > 0:
print("Daytime")
elif alt.degrees > -18:
print("Twilight")
else:
print("Night")

Solar Noon​

Solar noon is when the Sun crosses the local meridian (highest point):

noon = solar_noon(observer, jd)
max_alt = solar_altitude(observer, noon)
print(f"Solar noon: {noon.to_datetime()}")
print(f"Maximum altitude: {max_alt.degrees:.1f}°")

Day Length​

Day length varies with latitude and time of year:

# Hours of daylight
hours = day_length(observer, jd)

# Near summer solstice at Arctic Circle
arctic = Observer.from_degrees("Arctic", 66.5, 0)
summer_jd = JulianDate.from_calendar(2024, 6, 21, 12, 0, 0)
hours = day_length(arctic, summer_jd)
# Result: ~24 hours (midnight sun)

Seasonal Variations​

Solar declination varies from -23.4° (winter solstice) to +23.4° (summer solstice):

from starward.core.time import JulianDate

# Check solar declination throughout the year
for month in range(1, 13):
jd = JulianDate.from_calendar(2024, month, 15, 12, 0, 0)
pos = sun_position(jd)
print(f"Month {month:2d}: Dec = {pos.dec.degrees:+.1f}°")

Verbose Mode​

Enable verbose mode to see calculation steps:

from starward.verbose import VerboseContext

ctx = VerboseContext()
pos = sun_position(jd, verbose=ctx)

for step in ctx.steps:
print(step)

Algorithm Details​

This module uses the low-precision solar position algorithm from Jean Meeus' Astronomical Algorithms. The algorithm accounts for:

  • Mean solar longitude
  • Mean anomaly
  • Equation of center (elliptical orbit)
  • Obliquity of the ecliptic (axial tilt)
  • Nutation (simplified)

For higher precision requirements, consider the VSOP87 theory (not yet implemented).

See Also​