← Astronomy 2 / 18

Installation

composer require edulazaro/astronomy

PHP 8.2 or newer, and the mbstring extension, which almost every distribution ships enabled by default. That is the whole requirement list, in composer.json:

"require": {
    "php": "^8.2",
    "ext-mbstring": "*"
}

mbstring is not there for anything astronomical. A star name is reduced to a lookup key by lowercasing it and stripping its accents (Stars::find('Régulus') has to match the same star as Stars::find('regulus')), and the catalogue carries names with Greek letters and accented characters. A byte-based strtolower does not lowercase a multi-byte UTF-8 sequence: it leaves an accented capital untouched, and the transliteration table that turns it into plain ASCII never matches. mb_strtolower is the one line in the engine that needs the extension, and it needs it because names are text and text is not bytes.

No provider, no config, no migration

There is nothing to publish, because there is nothing that would need publishing. No service provider registers itself, no config file ships to be copied into config/, and no migration touches a database, because the engine has no database: it computes and returns floats.

Composer finishes, the autoloader knows where Astronomy\Ephemeris is, and that is the whole installation:

use Astronomy\Body;
use Astronomy\Ephemeris;
use Astronomy\Time;

$jdTT = Time::tt(Time::julianDay(new DateTimeImmutable('2026-09-19 12:00:00', new DateTimeZone('UTC'))));

$sun = Ephemeris::position(Body::Sun, $jdTT);

echo $sun->formatted();
26° 34' 33" Virgo

No framework was booted to run that. It is a script with one require 'vendor/autoload.php' in front of it, and the same four lines work the same way inside a request, inside a queue job or inside the test suite of whatever installs the package.

Where the data lives

The series, the tables and the corrections that make a position possible, about 10 MB in total, ship inside the package at resources/astro. Astronomy\DataFolder is what every class asks when it needs one of those files, and by default it answers with that folder, deduced from where DataFolder itself sits on disk. Nothing has to be configured for that to work. An application that keeps the data somewhere else, for instance because it downloads extra bodies and does not want to write inside its own vendor/ folder, points the engine at it once, at boot, before anything is read:

use Astronomy\DataFolder;

// Once, at boot, before anything is read. The folder has to exist already.
DataFolder::useFolder('/var/www/shared/astro');

Changing it once something has been read is refused rather than allowed to run quietly:

use Astronomy\DataFolder;
use Astronomy\Body;
use Astronomy\Ephemeris;
use Astronomy\Time;

$jdTT = Time::tt(Time::julianDay(new DateTimeImmutable('2026-09-19 12:00:00', new DateTimeZone('UTC'))));
echo Ephemeris::position(Body::Sun, $jdTT)->formatted(), "\n";

try {
    DataFolder::useFolder(sys_get_temp_dir());
} catch (\LogicException $e) {
    echo $e->getMessage(), "\n";
}
26° 34' 33" Virgo
The engine data folder was already in use ([...]/resources/astro) and cannot be changed to /tmp midway: data from both would get mixed. Set it at boot.

That guard is not caution for its own sake. The engine classes remember what they have read in static variables, so a folder swapped halfway through a process would leave some tables read from one folder and others from a second one, and nothing would say so: a chart would just come out with a Moon from one data set and a Saturn from another. Setting the same folder twice is still allowed, because that is what happens every time an application boots again inside the same worker process, which is exactly what a test suite does between cases.

And the default is not a mystery to go hunting for: it is exactly this, unwrapped from the class around it, which is why it is not shown here as something to run on its own, only as what already ran above whenever useFolder() was not called first:

public static function folder(): string
{
    return self::$folder ?? dirname(__DIR__).'/resources/astro';
}

The command line

Installing the package also installs its binary:

vendor/bin/astronomy

It exists for one reason: fetching what does not ship inside the package (an asteroid by number, a satellite, a comet, or rebuilding the ten megabytes that do) needs the network, and the library itself never touches it while it computes a position. Everything the binary does lives in a class, and the binary is a thin wrapper that reads arguments and prints what happened:

astronomy asteroids 136199 --from=1980 --to=1989   fetches an asteroid from JPL Horizons
astronomy satellites Io Titan --data=/var/data/astro
astronomy comets 1P
astronomy stars                                    the whole fixed star catalogue, 1,099 objects
astronomy stars Regulus Aldebaran                  only those

astronomy vsop87              rebuilds the planetary series from CDS Strasbourg
astronomy elp2000             rebuilds the lunar series from CDS Strasbourg
astronomy delta-t             rebuilds the delta T table from the IERS
astronomy nutation            rebuilds the nutation series from ERFA
astronomy mean-elements       rebuilds the mean orbital elements from ERFA
astronomy masses              rebuilds the solar system mass table from the DE440 header
astronomy magnitudes          refits the brightness model against JPL Horizons
astronomy tables pluto        rebuilds the tabulated positions of one body
astronomy moon-correction     rebuilds the correction of the Moon towards the JPL
astronomy planet-correction   rebuilds the correction of the eight planets towards the JPL
astronomy satellite-list      rewrites the cases of the Satellite enum

astronomy check               every body against JPL Horizons, live

astronomy asteroids, satellites and comets are for whoever installs the package: a body the engine does not ship with has to be fetched before it can be placed. astronomy vsop87 and the rest of the rebuild commands are not: they regenerate the ten megabytes that already ship inside resources/astro, and running them is for whoever maintains the engine, not for whoever installs it. Both are covered in full in downloadable bodies and the data. astronomy check is neither: it asks JPL Horizons for every body live and prints how far the engine's own numbers are from it, and it is what precision is built on.

Where to go next

Quick start is a position, a set of houses and a star, run and with their real output. The reference lists every public class.