JPL Ephemeris Library for Elixir

Build StatusHex.pm VersionDocumentationLicense

A comprehensive Elixir library for astronomical calculations using NASA JPL ephemeris data. This library provides high-precision celestial mechanics calculations with built-in caching, coordinate transformations, and time scale conversions.

Features

Quick Start

# Convert current time to Julian Day
{:ok, jd} = JPLEphem.Time.utc_to_jd(DateTime.utc_now())

# Transform coordinates from ICRF to Ecliptic
{:ok, ecliptic_pos} = JPLEphem.Coord.icrf_to_ecliptic({1.0, 0.0, 0.0})

# Cache expensive calculations
key = {:mars, ~U[2025-08-24 12:00:00Z], []}
position = {{1.523, 0.321, -0.043}, {0.012, 0.009, -0.001}}
JPLEphem.Cache.put(key, position, 3600)  # Cache for 1 hour
{:ok, cached_position} = JPLEphem.Cache.get(key)

Installation

Add jpl_ephem to your list of dependencies in mix.exs:

def deps do
  [
    {:jpl_ephem, "~> 0.1.0"}
  ]
end

Then run:

mix deps.get

The library automatically starts its supervision tree with your application.

Core Modules

Time Module (JPLEphem.Time)

Handles time scale conversions and Julian Day calculations with leap second support.

# UTC to Julian Day conversion
{:ok, jd} = JPLEphem.Time.utc_to_jd(~U[2025-01-01 12:00:00Z])
# => {:ok, 2460677.0}

# Julian Day to UTC conversion
{:ok, datetime} = JPLEphem.Time.jd_to_utc(2460677.0)
# => {:ok, ~U[2025-01-01 12:00:00.000Z]}

# UTC to Terrestrial Time (TT)
{:ok, tt_jd} = JPLEphem.Time.utc_to_tt(~U[2025-01-01 12:00:00Z])

# TT to Barycentric Dynamical Time (TDB)
{:ok, tdb_jd} = JPLEphem.Time.tt_to_tdb(2460677.0)

# Get leap seconds at a specific date
leap_secs = JPLEphem.Time.leap_seconds_at(~U[2025-01-01 12:00:00Z])
# => 37  # Current leap seconds as of 2017

Coordinate Module (JPLEphem.Coord)

Provides coordinate system transformations and vector mathematics.

# ICRF to Ecliptic coordinate transformation
{:ok, ecliptic} = JPLEphem.Coord.icrf_to_ecliptic({1.0, 0.0, 0.0})

# Ecliptic to ICRF (reverse transformation)
{:ok, icrf} = JPLEphem.Coord.ecliptic_to_icrf(ecliptic)

# Cartesian to Spherical conversion (physics convention)
{:ok, {r, theta, phi}} = JPLEphem.Coord.to_spherical({3.0, 4.0, 5.0})
# phi is polar angle from Z-axis, theta is azimuthal angle

# Spherical to Cartesian conversion
{:ok, cartesian} = JPLEphem.Coord.from_spherical(r, theta, phi)

# Right Ascension / Declination
{:ok, {ra, dec}} = JPLEphem.Coord.to_ra_dec({1.0, 0.0, 0.0})
{:ok, position} = JPLEphem.Coord.from_ra_dec({ra, dec}, 1.0)

# Unit conversions
position_km = JPLEphem.Coord.au_to_km({1.0, 0.5, 0.0})  # AU to kilometers
position_au = JPLEphem.Coord.km_to_au(position_km)      # km to AU

# Vector operations
magnitude = JPLEphem.Coord.vector_magnitude({3.0, 4.0, 0.0})  # => 5.0
unit_vector = JPLEphem.Coord.normalize_vector({3.0, 4.0, 0.0})
dot = JPLEphem.Coord.dot_product({1,0,0}, {0,1,0})      # => 0.0
cross = JPLEphem.Coord.cross_product({1,0,0}, {0,1,0})   # => {0,0,1}

Cache Module (JPLEphem.Cache)

High-performance ETS-based caching with automatic TTL and garbage collection.

# Store ephemeris data with 1-hour TTL
key = {:mars, ~U[2025-08-24 12:00:00Z], [observer: :earth]}
position = {{1.523679, 0.321456, -0.043211}, {0.012345, 0.008765, -0.001234}}
JPLEphem.Cache.put(key, position, 3600)

# Retrieve cached data
case JPLEphem.Cache.get(key) do
  {:ok, cached_position} -> IO.puts("Cache hit!")
  :not_found -> IO.puts("Cache miss or expired")
end

# Cache statistics
stats = JPLEphem.Cache.stats()
# => %{size: 42, hits: 156, misses: 23, hit_rate: 0.871}

# Manual garbage collection
JPLEphem.Cache.gc()

# Configure cache
JPLEphem.Cache.set_ttl(7200)        # Set default TTL to 2 hours
JPLEphem.Cache.set_max_size(50_000)  # Set maximum cache size

# Clear all entries
JPLEphem.Cache.clear()

Time Scales and Coordinate Systems

Time Scales

Coordinate Systems

Key Constants

Error Handling

The library uses comprehensive error handling with descriptive error atoms:

case JPLEphem.Time.utc_to_jd(invalid_date) do
  {:ok, jd} -> # Success
  {:error, :time_out_of_range} -> # Date outside supported range
end

case JPLEphem.Coord.to_spherical({0.0, 0.0, 0.0}) do
  {:ok, spherical} -> # Success  
  {:error, :zero_vector} -> # Cannot convert zero vector
end

case JPLEphem.Coord.normalize_vector({0.0, 0.0, 0.0}) do
  {:ok, unit_vector} -> # Success
  {:error, :zero_vector} -> # Cannot normalize zero vector
end

Performance and Caching

The library is designed for high-performance astronomical calculations:

Cache Performance Tips

# Use structured keys for better organization
key = {body, datetime, options_list}

# Set appropriate TTL based on calculation precision needs
JPLEphem.Cache.put(key, result, 3600)  # 1 hour for ephemeris data
JPLEphem.Cache.put(key, result, 86400) # 24 hours for less precise data

# Monitor cache performance
stats = JPLEphem.Cache.stats()
if stats.hit_rate < 0.8 do
  IO.puts("Consider increasing TTL or cache size")
end

Development

Running Tests

# Run all tests
mix test

# Run tests with coverage
mix test --cover

# Run specific test modules
mix test test/jpl_ephem/time_test.exs
mix test test/jpl_ephem/coord_test.exs  
mix test test/jpl_ephem/cache_test.exs

# Run tests in watch mode during development
mix test.watch

Code Quality

# Format code
mix format

# Run static analysis
mix credo

# Type checking (if using Dialyzer)
mix dialyzer

# Generate documentation
mix docs

Project Structure

jpl_ephem/
├── lib/
│   └── jpl_ephem/
│       ├── application.ex      # Application supervisor
│       ├── time.ex            # Time scale conversions
│       ├── coord.ex           # Coordinate transformations
│       └── cache.ex           # ETS-based caching
├── test/
│   ├── support/
│   │   ├── test_helpers.ex    # Test utilities
│   │   └── test_data.ex       # Reference data
│   └── jpl_ephem/
│       ├── time_test.exs      # Time module tests
│       ├── coord_test.exs     # Coordinate tests
│       └── cache_test.exs     # Cache tests
├── docs/
│   ├── PRD.md                 # Product Requirements
│   ├── SPEC.md                # Technical Specifications
│   ├── API_SPEC.md            # API Documentation
│   └── TDD_PLAN.md            # Test-Driven Development Plan
└── mix.exs                    # Project configuration

Testing Philosophy

This library was developed using strict Test-Driven Development (TDD):

  1. Red: Write failing tests first
  2. Green: Implement minimal code to pass tests
  3. Refactor: Improve code while maintaining test coverage
  4. Commit: Regular commits at each TDD cycle

All modules have comprehensive test coverage with:

Use Cases

Astronomical Software

Scientific Computing

Web Applications

API Reference

Complete API documentation is available at HexDocs.

For detailed technical specifications, see:

Contributing

We welcome contributions! Please see our contributing guidelines:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Write tests for your changes (TDD approach)
  4. Implement the feature with tests passing
  5. Run the full test suite: mix test
  6. Commit your changes: git commit -m 'Add feature'
  7. Push to your branch: git push origin feature-name
  8. Submit a pull request

Code Standards

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Acknowledgments

Changelog

See CHANGELOG.md for version history and release notes.


Made with ❤️ for the astronomical community