Lightweight, extensible library for JPL Horizons ephemeris parsing and astronomical data integration
Project description
NexusCosmos
A reusable, dataset-agnostic Python library for acquiring and analyzing astronomical observations from various data sources (JPL Horizons, Minor Planet Center, NASA GAIA, custom APIs, etc.).
Key Features
- Generic, Dataset-Agnostic Design: Core utilities (caching, rate-limiting, data models) work with any astronomical data source
- Flexible Data Models:
EphemerisDataandOrbitalElementssupport custom fields for dataset-specific parameters - Built-in Utilities: Rate limiting, exponential backoff, file-based caching with TTL
- Extensible Architecture: Base classes for implementing new datasets without reimplementing infrastructure
- Configuration System: Easy-to-use presets with full control over all parameters
- PyPI Ready: Properly packaged with
pyproject.toml, optional dependencies, and test structure
Architecture
Generic Infrastructure (Dataset-Agnostic)
These modules work with any astronomical data source:
EphemerisData(models.py): Flexible container for observations (RA, DEC, magnitude, distances, custom fields)OrbitalElements(models.py): Flexible container for orbital parameters (a, e, i, Omega, w, etc.) with optional/custom fieldsBaseAcquisitionClient(acquisition_base.py): Abstract base for implementing dataset-specific clientsBaseParser(acquisition_base.py): Abstract base for implementing dataset-specific parsersBaseTextParser,BaseCSVParser,BaseJSONParser(parsers_base.py): Mixins for common parsing patternsRateLimiter(utils.py): Generic sliding-window rate limiterExponentialBackoff(utils.py): Generic exponential backoff calculatorFileCache(cache.py): Generic file-based TTL cache with JSON serializationClientConfig,CacheConfig(config.py): Generic configuration classes
Dataset-Specific Implementations
Located in nexuscosmos/datasets/ with submodules for each source:
Horizons (JPL NASA)
nexuscosmos/datasets/horizons/
├── __init__.py # Public exports
├── client.py # HorizonsClient (inherits BaseAcquisitionClient)
└── parser.py # HorizonsParser (inherits BaseTextParser)
Future Datasets:
nexuscosmos/datasets/mpc/ # Minor Planet Center
nexuscosmos/datasets/gaia/ # ESA GAIA catalog
nexuscosmos/datasets/custom/ # Template for user datasets
Quick Start
Using Horizons (Current Implementation)
from nexuscosmos import HorizonsClient, HorizonsQueryConfig
# Create client
client = HorizonsClient(enforce_rate_limit=True)
# Use a preset configuration
config = HorizonsQueryConfig.quick_ephemeris(object_id='1I')
# Fetch data
result = client.fetch_data(config)
# Result structure
if result['success']:
print(f"Object: {result['object']}")
print(f"Data: {result['data']}")
print(f"Type: {result['ephem_type']}")
else:
print(f"Error: {result['error']}")
Custom Configuration
from nexuscosmos import HorizonsQueryConfig, HorizonsClient
# Full control over parameters
config = HorizonsQueryConfig(
object_id='1I/Oumuamua',
start_time='2025-01-01',
stop_time='2025-01-31',
step_size='1d',
observer='@399', # Earth
ephem_type='OBSERVER',
quantities='1,9,20,23,24', # RA, DEC, distance, magnitude, phase angle
use_cache=True,
cache_ttl_hours=24,
enforce_rate_limit=True,
max_requests_per_minute=10,
timeout_seconds=30
)
client = HorizonsClient()
result = client.fetch_data(config)
Available Presets
from nexuscosmos import PRESETS, HorizonsQueryConfig
# Quick presets
config1 = HorizonsQueryConfig.quick_ephemeris(object_id='2I')
config2 = HorizonsQueryConfig.quick_vectors(object_id='3I')
config3 = HorizonsQueryConfig.quick_live(object_id='1I')
# Access preset configurations
for name, preset_config in PRESETS.items():
print(f"{name}: {preset_config}")
Implementing a New Dataset
To add support for a new astronomical data source (e.g., Minor Planet Center):
1. Create Dataset-Specific Module
mkdir -p nexuscosmos/datasets/mpc
touch nexuscosmos/datasets/mpc/__init__.py
touch nexuscosmos/datasets/mpc/client.py
touch nexuscosmos/datasets/mpc/parser.py
2. Implement Client
# nexuscosmos/datasets/mpc/client.py
from nexuscosmos import BaseAcquisitionClient
import requests
class MinorPlanetCenterClient(BaseAcquisitionClient):
"""MPC-specific acquisition client."""
BASE_URL = "https://minorplanetcenter.net/..."
def fetch_data(self, query_config):
"""Fetch data from MPC API.
Inherits caching, rate-limiting, and backoff from BaseAcquisitionClient.
"""
# Check cache
cache_key = self._build_cache_key(query_config)
cached = self.get_cached_data(cache_key)
if cached:
return cached
# Build MPC-specific request
url = self._build_url(query_config)
# Enforce rate limiting (inherited)
if not self._rate_limited_request('mpc'):
return {'success': False, 'error': 'Rate limited'}
# Fetch and parse
response = requests.get(url, timeout=self.timeout_seconds)
data = self.parser.parse(response.text)
# Cache result (inherited)
self.cache_data(cache_key, data)
return {'success': True, 'data': data}
3. Implement Parser
# nexuscosmos/datasets/mpc/parser.py
from nexuscosmos import BaseCSVParser
class MinorPlanetCenterParser(BaseCSVParser):
"""MPC-specific CSV parser."""
def parse(self, csv_text: str):
"""Parse MPC CSV format."""
return self.parse_csv(
csv_text,
delimiter=' ',
skip_rows=2,
column_mapper={'# ID': 'object_id', 'a': 'semi_major_axis'}
)
4. Export from Package
# nexuscosmos/datasets/mpc/__init__.py
from .client import MinorPlanetCenterClient
from .parser import MinorPlanetCenterParser
__all__ = ["MinorPlanetCenterClient", "MinorPlanetCenterParser"]
5. Use New Dataset
from nexuscosmos.datasets.mpc import MinorPlanetCenterClient
client = MinorPlanetCenterClient()
result = client.fetch_data(config)
Data Models
EphemerisData
Container for observations at a moment in time. Supports both standard and custom fields.
from nexuscosmos import EphemerisData
# Horizons-specific data
obs = EphemerisData(
datetime_str='2025-01-15 12:00:00',
RA=45.5,
DEC=30.2,
delta=1.5, # Distance from observer (AU)
r=1.2, # Heliocentric distance (AU)
V_mag=15.5,
elong=120.0,
phase_angle=45.0,
source='horizons'
)
# Custom fields for GAIA
gaia_obs = EphemerisData(
datetime_str='2025-01-15',
RA=45.5,
DEC=30.2,
source='gaia',
extra_data={'parallax': 10.5, 'magnitude_g': 12.3}
)
# Convert to dict
data_dict = obs.to_dict()
# Safely access fields (including custom)
ra = obs.get_field('RA')
parallax = gaia_obs.get_field('parallax')
OrbitalElements
Container for orbital parameters. Supports elliptical, parabolic, and hyperbolic orbits.
from nexuscosmos import OrbitalElements
# Elliptical orbit (asteroid)
asteroid = OrbitalElements(
epoch='J2000',
a=2.5,
e=0.1,
i=10.0,
Omega=45.0,
w=90.0,
M=180.0,
H=15.5,
G=0.15,
source='horizons'
)
# Check orbit type
print(asteroid.is_elliptical()) # True
print(asteroid.is_hyperbolic()) # False
# Hyperbolic orbit (comet)
comet = OrbitalElements(
epoch='2025-01-01',
e=1.5,
q=0.5,
Tp='2025-03-15',
source='horizons'
)
print(comet.is_hyperbolic()) # True
Configuration
HorizonsQueryConfig
Detailed control over Horizons API parameters:
from nexuscosmos import HorizonsQueryConfig
config = HorizonsQueryConfig(
object_id='1I/Oumuamua',
start_time='2025-01-01',
stop_time='2025-01-31',
step_size='1d',
observer='@399', # Earth center
center='@sun', # Heliocentric
ephem_type='OBSERVER', # or 'VECTORS', 'ELEMENTS'
quantities='1,9,20,23,24', # RA, DEC, distances, magnitude, phase
use_cache=True,
cache_ttl_hours=24,
enforce_rate_limit=True,
max_requests_per_minute=10,
request_delay_seconds=1.0,
timeout_seconds=30,
return_raw_data=False
)
# Convert to API parameters
api_kwargs = config.to_client_kwargs()
# Print summary
print(config.summary())
CacheConfig
Control caching behavior:
from nexuscosmos import CacheConfig
cache_config = CacheConfig(
enabled=True,
ttl_seconds=86400, # 24 hours
cache_dir='/tmp/nexuscosmos_cache',
clear_on_init=False
)
ClientConfig
Global client settings:
from nexuscosmos import ClientConfig, CacheConfig
client_config = ClientConfig(
base_url='https://ssd.jpl.nasa.gov/api/horizons.api',
timeout_seconds=30,
request_delay_seconds=1.0,
max_retries=3,
cache=CacheConfig(enabled=True, ttl_seconds=86400),
rate_limiting_enabled=True,
max_requests_per_minute=10,
verbose_logging=True
)
Utilities
Rate Limiting
from nexuscosmos import RateLimiter
limiter = RateLimiter(max_attempts=10, window_minutes=1)
if limiter.is_allowed('horizons_api'):
# Make request
pass
else:
retry_after = limiter.get_retry_after('horizons_api')
print(f"Rate limited. Retry after {retry_after:.1f}s")
Exponential Backoff
from nexuscosmos import ExponentialBackoff
import time
backoff = ExponentialBackoff(base_delay=1.0, max_delay=60.0)
for attempt in range(5):
try:
# Make request
break
except Exception:
delay = backoff.get_delay(attempt)
time.sleep(delay)
File Caching
from nexuscosmos import FileCache
cache = FileCache(cache_dir='/tmp/cache', ttl_seconds=3600)
# Store
cache.set('horizons_1I_2025', {'data': [...], 'timestamp': ...})
# Retrieve
data = cache.get('horizons_1I_2025')
# Info
info = cache.get_cache_info('horizons_1I_2025')
print(info) # {'hit_count': 5, 'created_at': ..., 'expires_at': ...}
Installation
From PyPI (when published):
pip install nexuscosmos
With optional dependencies:
# Scientific utilities (astropy, pandas)
pip install "nexuscosmos[astropy,pandas]"
# Async support (httpx)
pip install "nexuscosmos[async]"
# Visualization (matplotlib, plotly)
pip install "nexuscosmos[viz]"
# Development
pip install "nexuscosmos[dev]"
From local development:
cd /path/to/nexuscosmos
pip install -e .
Testing
# Run all tests
pytest
# With coverage
pytest --cov=nexuscosmos
# Specific test file
pytest tests/test_basic.py
Architecture Philosophy
Generic + Dataset-Specific = Reusable Infrastructure
NexusCosmos separates concerns:
- Generic Infrastructure (core package): Rate limiting, caching, retry logic, configuration
- Data Models (generic): Flexible EphemerisData and OrbitalElements for any dataset
- Base Classes (abstract): Templates for implementing new dataset clients/parsers
- Dataset Implementations (submodules): Horizons-specific logic isolated in
datasets/horizons/
Benefits:
- Add a new dataset (MPC, GAIA) by implementing just two classes (client + parser)
- Reuse caching, rate-limiting, configuration for all datasets
- Users familiar with Horizons API can quickly learn new dataset support
- No code duplication across dataset implementations
- Clear separation of concerns for maintenance
Future Enhancements
- Minor Planet Center (MPC) dataset support
- ESA GAIA catalog integration
- Astropy integration for high-precision time/units
- Pandas DataFrame output options
- Async/await support (httpx backend)
- Visualization utilities (matplotlib, plotly)
- Sphinx documentation
- CI/CD pipeline (GitHub Actions)
- Performance benchmarking suite
Contributing
Contributions welcome! To add a new dataset:
- Create
nexuscosmos/datasets/<name>/directory - Implement
<name>/client.py(inheritBaseAcquisitionClient) - Implement
<name>/parser.py(inherit appropriate base parser) - Add tests in
tests/test_<name>.py - Update
nexuscosmos/datasets/__init__.pyto export your classes - Submit PR with example usage
License
See LICENSE file.
Citation
If you use NexusCosmos in your research, please cite:
@software{nexuscosmos2025,
author = {Pancha Narayan Sahu},
title = {NexusCosmos: Python Library for Astroprocessing},
year = {2025},
url = {https://github.com/Thevishalkumar369/nexuscosmos}
}
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nexuscosmos-0.1.0.tar.gz.
File metadata
- Download URL: nexuscosmos-0.1.0.tar.gz
- Upload date:
- Size: 36.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dcc2ae39ec7c0c5d2238b71486aceb4ac171e20246b9509a59e080f9ff15ff1a
|
|
| MD5 |
19a14f8279d7326095443bb4faeeb9e0
|
|
| BLAKE2b-256 |
bc7c4e251491b0711078532f07d5dc57557c58e5678bf0d73dd72a32b830d448
|
File details
Details for the file nexuscosmos-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nexuscosmos-0.1.0-py3-none-any.whl
- Upload date:
- Size: 35.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef64dba5e8da460cf88eb2ab54e77f98bc428fdfcad8ba3006db646158aad706
|
|
| MD5 |
c375ebcb13a3dd60470a192aca843560
|
|
| BLAKE2b-256 |
2e975a80b64d8c418ef5405cc46d5a71fe1f53403f808cf0d16eaeb874a3875c
|