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