Crate solar_positioning

Crate solar_positioning 

Source
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: std or no_std, with or without chrono, math via native or libm
  • 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 than libm)
  • chrono (default): Enable DateTime<Tz> based convenience API
  • libm: Use pure Rust math for no_std environments

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

§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;

Modules§

error
Error types for the solar positioning library.
grena3
Grena3 algorithm implementation.
spa
SPA algorithm implementation.
time
Time-related calculations for solar positioning.
types
Core data types for solar positioning calculations.