Expand description
§Solar Positioning Library
High-accuracy solar positioning algorithms for calculating sun position and sunrise/sunset times.
This library provides implementations of two complementary solar positioning algorithms:
- SPA (Solar Position Algorithm): NREL’s authoritative algorithm (±0.0003°, years -2000 to 6000)
- Grena3: Simplified algorithm (±0.01°, years 2010-2110, ~10x faster)
In addition, it provides an estimator for Delta T (ΔT) values based on the work of F. Espenak & J. Meeus.
§Features
- Multiple configurations:
stdorno_std, with or withoutchrono, math via native orlibm - Maximum accuracy: Authentic NREL SPA implementation, validated against reference data
- Performance optimized: Split functions for bulk calculations (SPA only)
- Thread-safe: Stateless, immutable data structures
§Feature Flags
std(default): Use standard library for native math functions (usually faster thanlibm)chrono(default): EnableDateTime<Tz>based convenience APIlibm: Use pure Rust math forno_stdenvironments
Configuration examples:
# Default: std + chrono (most convenient)
solar-positioning = "0.4"
# Minimal std (no chrono, smallest dependency tree)
solar-positioning = { version = "0.4", default-features = false, features = ["std"] }
# no_std + chrono (embedded with DateTime support)
solar-positioning = { version = "0.4", default-features = false, features = ["libm", "chrono"] }
# Minimal no_std (pure numeric API)
solar-positioning = { version = "0.4", default-features = false, features = ["libm"] }§References
- Reda, I.; Andreas, A. (2003). Solar position algorithm for solar radiation applications. Solar Energy, 76(5), 577-589. DOI: http://dx.doi.org/10.1016/j.solener.2003.12.003
- Grena, R. (2012). Five new algorithms for the computation of sun position from 2010 to 2110. Solar Energy, 86(5), 1323-1337. DOI: http://dx.doi.org/10.1016/j.solener.2012.01.024
§Quick Start
§Solar Position (with chrono)
use solar_positioning::{spa, RefractionCorrection, time::DeltaT};
use chrono::{DateTime, FixedOffset};
// Calculate sun position for Vienna at noon
let datetime = "2026-06-21T12:00:00+02:00".parse::<DateTime<FixedOffset>>().unwrap();
let position = spa::solar_position(
datetime,
48.21, // Vienna latitude
16.37, // Vienna longitude
190.0, // elevation (meters)
DeltaT::estimate_from_date_like(datetime).unwrap(), // delta T
Some(RefractionCorrection::standard())
).unwrap();
println!("Azimuth: {:.3}°", position.azimuth());
println!("Elevation: {:.3}°", position.elevation_angle());§Solar Position (numeric API, no chrono)
use solar_positioning::{spa, time::JulianDate, RefractionCorrection};
// Create Julian date from UTC components (2026-06-21 12:00:00 UTC + 69s ΔT)
let jd = JulianDate::from_utc(2026, 6, 21, 12, 0, 0.0, 69.0).unwrap();
// Calculate sun position (works in both std and no_std)
let position = spa::solar_position_from_julian(
jd,
48.21, // Vienna latitude
16.37, // Vienna longitude
190.0, // elevation (meters)
Some(RefractionCorrection::standard())
).unwrap();
println!("Azimuth: {:.3}°", position.azimuth());
println!("Elevation: {:.3}°", position.elevation_angle());§Sunrise and Sunset (with chrono)
use solar_positioning::{spa, Horizon, time::DeltaT};
use chrono::{DateTime, FixedOffset};
// Calculate sunrise/sunset for San Francisco
let date = "2026-06-21T00:00:00-07:00".parse::<DateTime<FixedOffset>>().unwrap();
// Note: returned timestamps are in the same timezone as `date`, but can fall on the
// previous/next local calendar date when events occur near midnight.
let result = spa::sunrise_sunset_for_horizon(
date,
37.7749, // San Francisco latitude
-122.4194, // San Francisco longitude
DeltaT::estimate_from_date_like(date).unwrap(),
Horizon::SunriseSunset
).unwrap();
match result {
solar_positioning::SunriseResult::RegularDay { sunrise, transit, sunset } => {
println!("Sunrise: {}", sunrise);
println!("Solar noon: {}", transit);
println!("Sunset: {}", sunset);
}
_ => println!("No sunrise/sunset (polar day/night)"),
}§Sunrise and Sunset (numeric API, no chrono)
use solar_positioning::{spa, Horizon};
// Calculate sunrise/sunset for San Francisco (returns hours since midnight UTC)
let result = spa::sunrise_sunset_utc_for_horizon(
2026, 6, 21, // June 21, 2026
37.7749, // San Francisco latitude
-122.4194, // San Francisco longitude
69.0, // ΔT (seconds)
Horizon::SunriseSunset
).unwrap();
match result {
solar_positioning::SunriseResult::RegularDay { sunrise, transit, sunset } => {
println!("Sunrise: {:.2} hours UTC", sunrise.hours());
println!("Solar noon: {:.2} hours UTC", transit.hours());
println!("Sunset: {:.2} hours UTC", sunset.hours());
}
_ => println!("No sunrise/sunset (polar day/night)"),
}§Algorithms
§SPA (Solar Position Algorithm)
Based on the NREL algorithm by Reda & Andreas (2003). Provides the highest accuracy with uncertainties of ±0.0003 degrees, suitable for applications requiring precise solar positioning over long time periods.
§Grena3
A simplified algorithm optimized for years 2010-2110. Approximately 10 times faster than SPA while maintaining good accuracy (maximum error 0.01°).
§Coordinate System
- Azimuth: 0° = North, measured clockwise (0° to 360°)
- Zenith angle: 0° = directly overhead (zenith), 90° = horizon (0° to 180°)
- Elevation angle: 0° = horizon, 90° = directly overhead (-90° to +90°)
Re-exports§
pub use crate::error::Error;pub use crate::error::Result;pub use crate::types::Horizon;pub use crate::types::HoursUtc;pub use crate::types::RefractionCorrection;pub use crate::types::SolarPosition;pub use crate::types::SunriseResult;