jayeshmepani/jpl-moshier-ephemeris-php
PHP 8.3+ FFI wrapper for the JPL Moshier Ephemeris C library. Exposes all public jme_* functions and JME_* constants with no output reshaping. Supports AUTO/JPL/MOSHIER/VSOP engines and direct JPL/CALCEPH kernel access via shared libs.
Installation
composer require jayeshmepani/jpl-moshier-ephemeris-php
Ensure your PHP version is 8.3+ (FFI requirement).
First Use Case: Basic Ephemeris Query
use JPL\Ephemeris\Ephemeris;
// Initialize the ephemeris engine (auto-detects native lib)
$ephemeris = new Ephemeris();
// Load a built-in ephemeris file (e.g., DE440)
$ephemeris->loadEphemeris(__DIR__.'/vendor/jayeshmepani/jpl-moshier-ephemeris-php/resources/de440.bsp');
// Query the position of Earth (3) at J2000 epoch
$time = 0.0; // JD - 2451545.0
$result = $ephemeris->jme_ephem($time, 3, 0);
if ($result['status'] === JME\JME_OK) {
echo "Position: ["
. $result['state'][0] . ", "
. $result['state'][1] . ", "
. $result['state'][2] . "]";
}
Where to Look First
src/Ephemeris.php: Core class with auto-detection for native libraries.resources/: Pre-bundled ephemeris files (e.g., de440.bsp).libs/: OS-specific native libraries (auto-resolved).tests/: Example use cases and edge-case handling.Automatic Native Library Resolution
// No manual path needed - auto-detects:
// - Ubuntu 22/24 (glibc detection)
// - macOS arm64 (Darwin kernel 14/15)
// - Windows MSVC 2022
$ephemeris = new Ephemeris(); // Uses default constructor
$ephemeris = new Ephemeris('/custom/path/to/libjme.so');
Ephemeris Loading
$ephemeris = new Ephemeris();
$ephemeris->loadEphemeris('/path/to/custom.bsp'); // Supports custom files
Time Handling
JD - 2451545.0 (J2000 epoch).$jd = JME\jme_jd2cal(2451545.0 + $time); // Convert back to Gregorian
Query Patterns
jme_ephem):
$result = $ephemeris->jme_ephem($time, $bodyId, 0); // 0 = position + velocity
jme_ltex):
$correctedTime = $ephemeris->jme_ltex($time, $bodyId, $observerId);
Laravel Integration
// app/Providers/EphemerisServiceProvider.php
public function register()
{
$this->app->singleton(Ephemeris::class, function () {
return new Ephemeris(); // Auto-resolves native lib
});
}
Caching Results
$cacheKey = "ephemeris:{$bodyId}:{$time}";
if (!Cache::has($cacheKey)) {
$result = $ephemeris->jme_ephem($time, $bodyId, 0);
Cache::put($cacheKey, $result, now()->addHours(1));
}
Native Library Auto-Detection
libs/default.$ephemeris = new Ephemeris();
error_log("Using library: " . $ephemeris->getNativeLibraryPath());
FFI Requirements
php.ini:
extension=ffi
if (!extension_loaded('ffi')) {
throw new \RuntimeException('FFI extension required');
}
Error Handling
$result['status']:
if ($result['status'] !== JME\JME_OK) {
throw new \RuntimeException("JME Error: {$result['status']} - " . $result['errbuf']);
}
Precision Warnings
double precision:
$time = 123456789.123456789; // High precision
Thread Safety
libjme is NOT thread-safe. Use a single Ephemeris instance per request in Laravel.Log Library Path
error_log("Native library path: " . $ephemeris->getNativeLibraryPath());
Validate Auto-Detection
$ephemeris = new Ephemeris();
$libPath = $ephemeris->getNativeLibraryPath();
if (strpos($libPath, 'default') !== false) {
error_log("Warning: Using fallback library");
}
FFI Debugging
FFI::debug(true);
Custom Library Paths
// Override auto-detection for specific environments
if (app()->environment('staging')) {
$ephemeris = new Ephemeris('/staging/libs/libjme.so');
}
Wrap Additional Functions
public function getBodyPosition($time, $bodyId) {
$result = $this->jme_ephem($time, $bodyId, 0);
return $result['state'] ?? null;
}
Laravel Observers
// app/Observers/AstronomicalEventObserver.php
public function calculatingPosition($event) {
$position = app(Ephemeris::class)->getBodyPosition($event->time, $event->bodyId);
$event->position = $position;
}
Performance Optimization
parallel: for batch processing..env Config: Native library resolution is automatic.ubuntu22/ubuntu24 variants.libs/default/ (may require manual setup).How can I help you explore Laravel packages today?